diff --git a/ServiceWorker.js b/ServiceWorker.js index f13c0dccd..09d6137df 100644 --- a/ServiceWorker.js +++ b/ServiceWorker.js @@ -5,5 +5,5 @@ self.addEventListener('install', function(e) { self.addEventListener('fetch', function(e) { // nothing here yet - return; + }); diff --git a/Zotlabs/Access/AccessControl.php b/Zotlabs/Access/AccessControl.php index c46e282e9..9eeef3594 100644 --- a/Zotlabs/Access/AccessControl.php +++ b/Zotlabs/Access/AccessControl.php @@ -12,153 +12,158 @@ namespace Zotlabs\Access; * and @ref ::Zotlabs::Lib::Permcat "Permcat"s individual content ACLs are evaluated. * These answer the question "Can Joe view *this* album/photo?". */ -class AccessControl { - /** - * @brief Allow contacts - * @var string - */ - private $allow_cid; - /** - * @brief Allow groups - * @var string - */ - private $allow_gid; - /** - * @brief Deny contacts - * @var string - */ - private $deny_cid; - /** - * @brief Deny groups - * @var string - */ - private $deny_gid; - /** - * @brief Indicates if we are using the default constructor values or - * values that have been set explicitly. - * @var boolean - */ - private $explicit; +class AccessControl +{ + /** + * @brief Allow contacts + * @var string + */ + private $allow_cid; + /** + * @brief Allow groups + * @var string + */ + private $allow_gid; + /** + * @brief Deny contacts + * @var string + */ + private $deny_cid; + /** + * @brief Deny groups + * @var string + */ + private $deny_gid; + /** + * @brief Indicates if we are using the default constructor values or + * values that have been set explicitly. + * @var bool + */ + private $explicit; - /** - * @brief Constructor for AccessList class. - * - * @note The array to pass to the constructor is different from the array - * that you provide to the set() or set_from_array() functions. - * - * @param array $channel A channel array, where these entries are evaluated: - * * \e string \b channel_allow_cid => string of allowed cids - * * \e string \b channel_allow_gid => string of allowed gids - * * \e string \b channel_deny_cid => string of denied cids - * * \e string \b channel_deny_gid => string of denied gids - */ - function __construct($channel) { - if($channel) { - $this->allow_cid = $channel['channel_allow_cid']; - $this->allow_gid = $channel['channel_allow_gid']; - $this->deny_cid = $channel['channel_deny_cid']; - $this->deny_gid = $channel['channel_deny_gid']; - } - else { - $this->allow_cid = ''; - $this->allow_gid = ''; - $this->deny_cid = ''; - $this->deny_gid = ''; - } + /** + * @brief Constructor for AccessList class. + * + * @note The array to pass to the constructor is different from the array + * that you provide to the set() or set_from_array() functions. + * + * @param array $channel A channel array, where these entries are evaluated: + * * \e string \b channel_allow_cid => string of allowed cids + * * \e string \b channel_allow_gid => string of allowed gids + * * \e string \b channel_deny_cid => string of denied cids + * * \e string \b channel_deny_gid => string of denied gids + */ + public function __construct($channel) + { + if ($channel) { + $this->allow_cid = $channel['channel_allow_cid']; + $this->allow_gid = $channel['channel_allow_gid']; + $this->deny_cid = $channel['channel_deny_cid']; + $this->deny_gid = $channel['channel_deny_gid']; + } else { + $this->allow_cid = ''; + $this->allow_gid = ''; + $this->deny_cid = ''; + $this->deny_gid = ''; + } - $this->explicit = false; - } + $this->explicit = false; + } - /** - * @brief Get if we are using the default constructor values - * or values that have been set explicitly. - * - * @return boolean - */ - function get_explicit() { - return $this->explicit; - } + /** + * @brief Get if we are using the default constructor values + * or values that have been set explicitly. + * + * @return bool + */ + public function get_explicit() + { + return $this->explicit; + } - /** - * @brief Set access list from strings such as those in already - * existing stored data items. - * - * @note The array to pass to this set function is different from the array - * that you provide to the constructor or set_from_array(). - * - * @param array $arr - * * \e string \b allow_cid => string of allowed cids - * * \e string \b allow_gid => string of allowed gids - * * \e string \b deny_cid => string of denied cids - * * \e string \b deny_gid => string of denied gids - * @param boolean $explicit (optional) default true - */ - function set($arr, $explicit = true) { - $this->allow_cid = $arr['allow_cid']; - $this->allow_gid = $arr['allow_gid']; - $this->deny_cid = $arr['deny_cid']; - $this->deny_gid = $arr['deny_gid']; + /** + * @brief Set access list from strings such as those in already + * existing stored data items. + * + * @note The array to pass to this set function is different from the array + * that you provide to the constructor or set_from_array(). + * + * @param array $arr + * * \e string \b allow_cid => string of allowed cids + * * \e string \b allow_gid => string of allowed gids + * * \e string \b deny_cid => string of denied cids + * * \e string \b deny_gid => string of denied gids + * @param bool $explicit (optional) default true + */ + public function set($arr, $explicit = true) + { + $this->allow_cid = $arr['allow_cid']; + $this->allow_gid = $arr['allow_gid']; + $this->deny_cid = $arr['deny_cid']; + $this->deny_gid = $arr['deny_gid']; - $this->explicit = $explicit; - } + $this->explicit = $explicit; + } - /** - * @brief Return an array consisting of the current access list components - * where the elements are directly storable. - * - * @return array An associative array with: - * * \e string \b allow_cid => string of allowed cids - * * \e string \b allow_gid => string of allowed gids - * * \e string \b deny_cid => string of denied cids - * * \e string \b deny_gid => string of denied gids - */ - function get() { - return [ - 'allow_cid' => $this->allow_cid, - 'allow_gid' => $this->allow_gid, - 'deny_cid' => $this->deny_cid, - 'deny_gid' => $this->deny_gid, - ]; - } + /** + * @brief Return an array consisting of the current access list components + * where the elements are directly storable. + * + * @return array An associative array with: + * * \e string \b allow_cid => string of allowed cids + * * \e string \b allow_gid => string of allowed gids + * * \e string \b deny_cid => string of denied cids + * * \e string \b deny_gid => string of denied gids + */ + public function get() + { + return [ + 'allow_cid' => $this->allow_cid, + 'allow_gid' => $this->allow_gid, + 'deny_cid' => $this->deny_cid, + 'deny_gid' => $this->deny_gid, + ]; + } - /** - * @brief Set access list components from arrays, such as those provided by - * acl_selector(). - * - * For convenience, a string (or non-array) input is assumed to be a - * comma-separated list and auto-converted into an array. - * - * @note The array to pass to this set function is different from the array - * that you provide to the constructor or set(). - * - * @param array $arr An associative array with: - * * \e array|string \b contact_allow => array with cids or comma-seperated string - * * \e array|string \b group_allow => array with gids or comma-seperated string - * * \e array|string \b contact_deny => array with cids or comma-seperated string - * * \e array|string \b group_deny => array with gids or comma-seperated string - * @param boolean $explicit (optional) default true - */ - function set_from_array($arr, $explicit = true) { - $this->allow_cid = perms2str((is_array($arr['contact_allow'])) - ? $arr['contact_allow'] : explode(',', $arr['contact_allow'])); - $this->allow_gid = perms2str((is_array($arr['group_allow'])) - ? $arr['group_allow'] : explode(',', $arr['group_allow'])); - $this->deny_cid = perms2str((is_array($arr['contact_deny'])) - ? $arr['contact_deny'] : explode(',', $arr['contact_deny'])); - $this->deny_gid = perms2str((is_array($arr['group_deny'])) - ? $arr['group_deny'] : explode(',', $arr['group_deny'])); + /** + * @brief Set access list components from arrays, such as those provided by + * acl_selector(). + * + * For convenience, a string (or non-array) input is assumed to be a + * comma-separated list and auto-converted into an array. + * + * @note The array to pass to this set function is different from the array + * that you provide to the constructor or set(). + * + * @param array $arr An associative array with: + * * \e array|string \b contact_allow => array with cids or comma-seperated string + * * \e array|string \b group_allow => array with gids or comma-seperated string + * * \e array|string \b contact_deny => array with cids or comma-seperated string + * * \e array|string \b group_deny => array with gids or comma-seperated string + * @param bool $explicit (optional) default true + */ + public function set_from_array($arr, $explicit = true) + { + $this->allow_cid = perms2str((is_array($arr['contact_allow'])) + ? $arr['contact_allow'] : explode(',', $arr['contact_allow'])); + $this->allow_gid = perms2str((is_array($arr['group_allow'])) + ? $arr['group_allow'] : explode(',', $arr['group_allow'])); + $this->deny_cid = perms2str((is_array($arr['contact_deny'])) + ? $arr['contact_deny'] : explode(',', $arr['contact_deny'])); + $this->deny_gid = perms2str((is_array($arr['group_deny'])) + ? $arr['group_deny'] : explode(',', $arr['group_deny'])); - $this->explicit = $explicit; - } - - /** - * @brief Returns true if any access lists component is set. - * - * @return boolean Return true if any of allow_* deny_* values is set. - */ - function is_private() { - return (($this->allow_cid || $this->allow_gid || $this->deny_cid || $this->deny_gid) ? true : false); - } + $this->explicit = $explicit; + } + /** + * @brief Returns true if any access lists component is set. + * + * @return bool Return true if any of allow_* deny_* values is set. + */ + public function is_private() + { + return (($this->allow_cid || $this->allow_gid || $this->deny_cid || $this->deny_gid) ? true : false); + } } diff --git a/Zotlabs/Access/PermissionLimits.php b/Zotlabs/Access/PermissionLimits.php index 608e62599..1ad259d95 100644 --- a/Zotlabs/Access/PermissionLimits.php +++ b/Zotlabs/Access/PermissionLimits.php @@ -26,78 +26,83 @@ use Zotlabs\Lib\PConfig; * * @see Permissions */ -class PermissionLimits { +class PermissionLimits +{ - /** - * @brief Get standard permission limits. - * - * Viewing permissions and post_comments permission are set to 'anybody', - * other permissions are set to 'those I allow'. - * - * The list of permissions comes from Permissions::Perms(). - * - * @return array - */ - static public function Std_Limits() { - $limits = []; - $perms = Permissions::Perms(); + /** + * @brief Get standard permission limits. + * + * Viewing permissions and post_comments permission are set to 'anybody', + * other permissions are set to 'those I allow'. + * + * The list of permissions comes from Permissions::Perms(). + * + * @return array + */ + public static function Std_Limits() + { + $limits = []; + $perms = Permissions::Perms(); - foreach($perms as $k => $v) { - if(strstr($k, 'view')) - $limits[$k] = PERMS_PUBLIC; - else - $limits[$k] = PERMS_SPECIFIC; - } + foreach ($perms as $k => $v) { + if (strstr($k, 'view')) { + $limits[$k] = PERMS_PUBLIC; + } else { + $limits[$k] = PERMS_SPECIFIC; + } + } - return $limits; - } + return $limits; + } - /** - * @brief Sets a permission limit for a channel. - * - * @param int $channel_id - * @param string $perm - * @param int $perm_limit one of PERMS_* constants - */ - static public function Set($channel_id, $perm, $perm_limit) { - PConfig::Set($channel_id, 'perm_limits', $perm, $perm_limit); - } + /** + * @brief Sets a permission limit for a channel. + * + * @param int $channel_id + * @param string $perm + * @param int $perm_limit one of PERMS_* constants + */ + public static function Set($channel_id, $perm, $perm_limit) + { + PConfig::Set($channel_id, 'perm_limits', $perm, $perm_limit); + } - /** - * @brief Get a channel's permission limits. - * - * Return a channel's permission limits from PConfig. If $perm is set just - * return this permission limit, if not set, return an array with all - * permission limits. - * - * @param int $channel_id - * @param string $perm (optional) - * @return - * * \b false if no perm_limits set for this channel - * * \b int if $perm is set, return one of PERMS_* constants for this permission, default 0 - * * \b array with all permission limits, if $perm is not set - */ - static public function Get($channel_id, $perm = '') { + /** + * @brief Get a channel's permission limits. + * + * Return a channel's permission limits from PConfig. If $perm is set just + * return this permission limit, if not set, return an array with all + * permission limits. + * + * @param int $channel_id + * @param string $perm (optional) + * @return + * * \b false if no perm_limits set for this channel + * * \b int if $perm is set, return one of PERMS_* constants for this permission, default 0 + * * \b array with all permission limits, if $perm is not set + */ + public static function Get($channel_id, $perm = '') + { - if (! intval($channel_id)) { - return false; - } - - if($perm) { - $x = PConfig::Get($channel_id, 'perm_limits', $perm); - if($x === false) { - $a = [ 'channel_id' => $channel_id, 'permission' => $perm, 'value' => $x ]; - call_hooks('permission_limits_get',$a); - return intval($a['value']); - } - return intval($x); - } + if (! intval($channel_id)) { + return false; + } + + if ($perm) { + $x = PConfig::Get($channel_id, 'perm_limits', $perm); + if ($x === false) { + $a = [ 'channel_id' => $channel_id, 'permission' => $perm, 'value' => $x ]; + call_hooks('permission_limits_get', $a); + return intval($a['value']); + } + return intval($x); + } - PConfig::Load($channel_id); - if(array_key_exists($channel_id, App::$config) && array_key_exists('perm_limits', App::$config[$channel_id])) { - return App::$config[$channel_id]['perm_limits']; - } + PConfig::Load($channel_id); + if (array_key_exists($channel_id, App::$config) && array_key_exists('perm_limits', App::$config[$channel_id])) { + return App::$config[$channel_id]['perm_limits']; + } - return false; - } -} \ No newline at end of file + return false; + } +} diff --git a/Zotlabs/Access/PermissionRoles.php b/Zotlabs/Access/PermissionRoles.php index 34a1ad0ba..901dfbd7a 100644 --- a/Zotlabs/Access/PermissionRoles.php +++ b/Zotlabs/Access/PermissionRoles.php @@ -7,193 +7,146 @@ namespace Zotlabs\Access; * * @see Permissions */ -class PermissionRoles { +class PermissionRoles +{ - /** - * @brief PermissionRoles version. - * - * This must match the version in Permissions.php before permission updates can run. - * - * @return number - */ - static public function version() { - return 3; - } + /** + * @brief PermissionRoles version. + * + * This must match the version in Permissions.php before permission updates can run. + * + * @return number + */ + public static function version() + { + return 3; + } - static function role_perms($role) { + public static function role_perms($role) + { - $ret = []; + $ret = []; - $ret['role'] = $role; + $ret['role'] = $role; - switch($role) { - case 'social': - $ret['perms_auto'] = false; - $ret['default_collection'] = false; - $ret['directory_publish'] = true; - $ret['online'] = true; - $ret['perms_connect'] = [ - 'view_stream', 'view_profile', 'view_contacts', 'view_storage', - 'view_pages', 'send_stream', 'post_mail', 'post_wall', 'post_comments' - ]; - $ret['limits'] = PermissionLimits::Std_Limits(); - break; + switch ($role) { + case 'social': + $ret['perms_auto'] = false; + $ret['default_collection'] = false; + $ret['directory_publish'] = true; + $ret['online'] = true; + $ret['perms_connect'] = [ + 'view_stream', 'view_profile', 'view_contacts', 'view_storage', + 'view_pages', 'send_stream', 'post_mail', 'post_wall', 'post_comments' + ]; + $ret['limits'] = PermissionLimits::Std_Limits(); + break; - case 'social_restricted': - $ret['perms_auto'] = false; - $ret['default_collection'] = true; - $ret['directory_publish'] = true; - $ret['online'] = false; - $ret['perms_connect'] = [ - 'view_stream', 'view_profile', 'view_storage', - 'view_pages', 'send_stream', 'post_mail', 'post_wall', 'post_comments' - ]; - $ret['limits'] = PermissionLimits::Std_Limits(); - $ret['limits']['view_contacts'] = PERMS_SPECIFIC; - break; + case 'social_restricted': + $ret['perms_auto'] = false; + $ret['default_collection'] = true; + $ret['directory_publish'] = true; + $ret['online'] = false; + $ret['perms_connect'] = [ + 'view_stream', 'view_profile', 'view_storage', + 'view_pages', 'send_stream', 'post_mail', 'post_wall', 'post_comments' + ]; + $ret['limits'] = PermissionLimits::Std_Limits(); + $ret['limits']['view_contacts'] = PERMS_SPECIFIC; + break; - case 'forum': - $ret['perms_auto'] = true; - $ret['default_collection'] = false; - $ret['directory_publish'] = true; - $ret['online'] = false; - $ret['perms_connect'] = [ - 'view_stream', 'view_profile', 'view_contacts', 'view_storage', 'write_storage', - 'view_pages', 'post_mail', 'post_wall', 'post_comments' - ]; - $ret['limits'] = PermissionLimits::Std_Limits(); - $ret['channel_type'] = 'group'; - - break; + case 'forum': + $ret['perms_auto'] = true; + $ret['default_collection'] = false; + $ret['directory_publish'] = true; + $ret['online'] = false; + $ret['perms_connect'] = [ + 'view_stream', 'view_profile', 'view_contacts', 'view_storage', 'write_storage', + 'view_pages', 'post_mail', 'post_wall', 'post_comments' + ]; + $ret['limits'] = PermissionLimits::Std_Limits(); + $ret['channel_type'] = 'group'; + + break; - case 'forum_moderated': - $ret['perms_auto'] = true; - $ret['default_collection'] = false; - $ret['directory_publish'] = true; - $ret['online'] = false; - $ret['perms_connect'] = [ - 'view_stream', 'view_profile', 'view_contacts', 'view_storage', - 'view_pages', 'post_mail', 'post_wall', 'post_comments', 'moderated' - ]; - $ret['limits'] = PermissionLimits::Std_Limits(); - $ret['channel_type'] = 'group'; - - break; + case 'forum_moderated': + $ret['perms_auto'] = true; + $ret['default_collection'] = false; + $ret['directory_publish'] = true; + $ret['online'] = false; + $ret['perms_connect'] = [ + 'view_stream', 'view_profile', 'view_contacts', 'view_storage', + 'view_pages', 'post_mail', 'post_wall', 'post_comments', 'moderated' + ]; + $ret['limits'] = PermissionLimits::Std_Limits(); + $ret['channel_type'] = 'group'; - case 'forum_restricted': - $ret['perms_auto'] = false; - $ret['default_collection'] = true; - $ret['directory_publish'] = true; - $ret['online'] = false; - $ret['perms_connect'] = [ - 'view_stream', 'view_profile', 'view_contacts', 'view_storage', 'write_storage', - 'view_pages', 'post_mail', 'post_wall', 'post_comments' - ]; - $ret['limits'] = PermissionLimits::Std_Limits(); - $ret['limits']['view_contacts'] = PERMS_SPECIFIC; - $ret['channel_type'] = 'group'; - break; + break; + + case 'forum_restricted': + $ret['perms_auto'] = false; + $ret['default_collection'] = true; + $ret['directory_publish'] = true; + $ret['online'] = false; + $ret['perms_connect'] = [ + 'view_stream', 'view_profile', 'view_contacts', 'view_storage', 'write_storage', + 'view_pages', 'post_mail', 'post_wall', 'post_comments' + ]; + $ret['limits'] = PermissionLimits::Std_Limits(); + $ret['limits']['view_contacts'] = PERMS_SPECIFIC; + $ret['channel_type'] = 'group'; + break; + + default: + break; + } -// case 'collection': -// $ret['perms_auto'] = true; -// $ret['default_collection'] = false; -// $ret['directory_publish'] = true; -// $ret['online'] = false; -// $ret['perms_connect'] = [ -// 'view_stream', 'view_profile', 'view_contacts', 'view_storage', -// 'view_pages', 'post_mail', 'post_comments' -// ]; -// $ret['limits'] = PermissionLimits::Std_Limits(); -// $ret['channel_type'] = 'collection'; -// -// break; -// -// case 'collection_restricted': -// $ret['perms_auto'] = false; -// $ret['default_collection'] = true; -// $ret['directory_publish'] = true; -// $ret['online'] = false; -// $ret['perms_connect'] = [ -// 'view_stream', 'view_profile', 'view_storage', -// 'view_pages', 'post_mail', 'post_comments' -// ]; -// $ret['limits'] = PermissionLimits::Std_Limits(); -// $ret['limits']['view_contacts'] = PERMS_SPECIFIC; -// $ret['channel_type'] = 'collection'; -// break; -// -// case 'feed': -// $ret['perms_auto'] = true; -// $ret['default_collection'] = false; -// $ret['directory_publish'] = true; -// $ret['online'] = false; -// $ret['perms_connect'] = [ -// 'view_stream', 'view_profile', 'view_contacts', 'view_storage', -// 'view_pages', 'send_stream', 'post_wall', 'post_mail', 'post_comments', -// 'republish' -// ]; -// $ret['limits'] = PermissionLimits::Std_Limits(); -// -// break; -// -// case 'repository': -// //Legacy settings to cover all channel_types previously in Libzot.php -// $ret['channel_type'] = 'group'; - default: - break; - } + $x = get_config('system', 'role_perms'); + // let system settings over-ride any or all + if ($x && is_array($x) && array_key_exists($role, $x)) { + $ret = array_merge($ret, $x[$role]); + } + + /** + * @hooks get_role_perms + * * \e array + */ + $x = ['role' => $role, 'result' => $ret]; + + call_hooks('get_role_perms', $x); + + return $x['result']; + } - $x = get_config('system','role_perms'); - // let system settings over-ride any or all - if($x && is_array($x) && array_key_exists($role,$x)) - $ret = array_merge($ret,$x[$role]); + /** + * @brief Array with translated role names and grouping. + * + * Return an associative array with grouped role names that can be used + * to create select groups like in \e field_select_grouped.tpl. + * + * @return array + */ + public static function roles() + { + $roles = [ + t('Social Networking') => [ + 'social' => t('Social - Normal'), + 'social_restricted' => t('Social - Restricted') + ], - /** - * @hooks get_role_perms - * * \e array - */ - $x = [ 'role' => $role, 'result' => $ret ]; + t('Community Group') => [ + 'forum' => t('Group - Normal'), + 'forum_restricted' => t('Group - Restricted'), + 'forum_moderated' => t('Group - Moderated') + ], + ]; - call_hooks('get_role_perms', $x); + call_hooks('list_permission_roles', $roles); - return $x['result']; - } - - - /** - * @brief Array with translated role names and grouping. - * - * Return an associative array with grouped role names that can be used - * to create select groups like in \e field_select_grouped.tpl. - * - * @return array - */ - static public function roles() { - $roles = [ - t('Social Networking') => [ - 'social' => t('Social - Normal'), - 'social_restricted' => t('Social - Restricted') - ], - - t('Community Group') => [ - 'forum' => t('Group - Normal'), - 'forum_restricted' => t('Group - Restricted'), - 'forum_moderated' => t('Group - Moderated') - ], - -// t('Collection') => [ -// 'collection' => t('Collection - Normal'), -// 'collection_restricted' => t('Collection - Restricted') -// ] - - ]; - - call_hooks('list_permission_roles',$roles); - - return $roles; - } + return $roles; + } } diff --git a/Zotlabs/Access/Permissions.php b/Zotlabs/Access/Permissions.php index bb4ff3d53..cf8eddb2e 100644 --- a/Zotlabs/Access/Permissions.php +++ b/Zotlabs/Access/Permissions.php @@ -31,252 +31,266 @@ use Zotlabs\Lib as Zlib; * something different for a specific permission within the given role. * */ -class Permissions { +class Permissions +{ - /** - * @brief Permissions version. - * - * This must match the version in PermissionRoles.php before permission updates can run. - * - * @return number - */ - static public function version() { - return 3; - } + /** + * @brief Permissions version. + * + * This must match the version in PermissionRoles.php before permission updates can run. + * + * @return number + */ + public static function version() + { + return 3; + } - /** - * @brief Return an array with Permissions. - * - * @param string $filter (optional) only passed to hook permissions_list - * @return array Associative array with permissions and short description. - */ - static public function Perms($filter = '') { + /** + * @brief Return an array with Permissions. + * + * @param string $filter (optional) only passed to hook permissions_list + * @return array Associative array with permissions and short description. + */ + public static function Perms($filter = '') + { - $perms = [ - 'view_stream' => t('Grant viewing access to and delivery of your channel stream and posts'), - 'view_profile' => t('Grant viewing access to your default channel profile'), - 'view_contacts' => t('Grant viewing access to your address book (connections)'), - 'view_storage' => t('Grant viewing access to your file storage and photos'), - 'post_wall' => t('Grant permission to post on your channel (wall) page'), - 'post_mail' => t('Accept delivery of direct messages and personal mail'), - 'send_stream' => t('Accept delivery of their posts and all comments to their posts'), - 'post_comments' => t('Accept delivery of their comments and likes on your posts'), - 'write_storage' => t('Grant upload permissions to your file storage and photos'), - 'republish' => t('Grant permission to republish/mirror your posts'), - 'moderated' => t('Accept comments and wall posts only after approval (moderation)'), - 'delegate' => t('Grant channel administration (delegation) permission') - ]; + $perms = [ + 'view_stream' => t('Grant viewing access to and delivery of your channel stream and posts'), + 'view_profile' => t('Grant viewing access to your default channel profile'), + 'view_contacts' => t('Grant viewing access to your address book (connections)'), + 'view_storage' => t('Grant viewing access to your file storage and photos'), + 'post_wall' => t('Grant permission to post on your channel (wall) page'), + 'post_mail' => t('Accept delivery of direct messages and personal mail'), + 'send_stream' => t('Accept delivery of their posts and all comments to their posts'), + 'post_comments' => t('Accept delivery of their comments and likes on your posts'), + 'write_storage' => t('Grant upload permissions to your file storage and photos'), + 'republish' => t('Grant permission to republish/mirror your posts'), + 'moderated' => t('Accept comments and wall posts only after approval (moderation)'), + 'delegate' => t('Grant channel administration (delegation) permission') + ]; - $x = [ - 'permissions' => $perms, - 'filter' => $filter - ]; - /** - * @hooks permissions_list - * * \e array \b permissions - * * \e string \b filter - */ - call_hooks('permissions_list', $x); + $x = [ + 'permissions' => $perms, + 'filter' => $filter + ]; + /** + * @hooks permissions_list + * * \e array \b permissions + * * \e string \b filter + */ + call_hooks('permissions_list', $x); - return($x['permissions']); - } + return($x['permissions']); + } - /** - * @brief Perms from the above list that are blocked from anonymous observers. - * - * e.g. you must be authenticated. - * - * @return array Associative array with permissions and short description. - */ - static public function BlockedAnonPerms() { + /** + * @brief Perms from the above list that are blocked from anonymous observers. + * + * e.g. you must be authenticated. + * + * @return array Associative array with permissions and short description. + */ + public static function BlockedAnonPerms() + { - $res = []; - $perms = PermissionLimits::Std_limits(); - foreach($perms as $perm => $limit) { - if($limit != PERMS_PUBLIC) { - $res[] = $perm; - } - } + $res = []; + $perms = PermissionLimits::Std_limits(); + foreach ($perms as $perm => $limit) { + if ($limit != PERMS_PUBLIC) { + $res[] = $perm; + } + } - $x = ['permissions' => $res]; - /** - * @hooks write_perms - * * \e array \b permissions - */ - call_hooks('write_perms', $x); + $x = ['permissions' => $res]; + /** + * @hooks write_perms + * * \e array \b permissions + */ + call_hooks('write_perms', $x); - return($x['permissions']); - } + return($x['permissions']); + } - /** - * @brief Converts indexed perms array to associative perms array. - * - * Converts [ 0 => 'view_stream', ... ] - * to [ 'view_stream' => 1 ] for any permissions in $arr; - * Undeclared permissions which exist in Perms() are added and set to 0. - * - * @param array $arr - * @return array - */ - static public function FilledPerms($arr) { - if(is_null($arr) || (! is_array($arr))) { - btlogger('FilledPerms: ' . print_r($arr,true)); - $arr = []; - } + /** + * @brief Converts indexed perms array to associative perms array. + * + * Converts [ 0 => 'view_stream', ... ] + * to [ 'view_stream' => 1 ] for any permissions in $arr; + * Undeclared permissions which exist in Perms() are added and set to 0. + * + * @param array $arr + * @return array + */ + public static function FilledPerms($arr) + { + if (is_null($arr) || (! is_array($arr))) { + btlogger('FilledPerms: ' . print_r($arr, true)); + $arr = []; + } - $everything = self::Perms(); - $ret = []; - foreach($everything as $k => $v) { - if(in_array($k, $arr)) - $ret[$k] = 1; - else - $ret[$k] = 0; - } + $everything = self::Perms(); + $ret = []; + foreach ($everything as $k => $v) { + if (in_array($k, $arr)) { + $ret[$k] = 1; + } else { + $ret[$k] = 0; + } + } - return $ret; - } + return $ret; + } - /** - * @brief Convert perms array to indexed array. - * - * Converts [ 'view_stream' => 1 ] for any permissions in $arr - * to [ 0 => ['name' => 'view_stream', 'value' => 1], ... ] - * - * @param array $arr associative perms array 'view_stream' => 1 - * @return array Indexed array with elements that look like - * * \e string \b name the perm name (e.g. view_stream) - * * \e int \b value the value of the perm (e.g. 1) - */ - static public function OPerms($arr) { - $ret = []; - if($arr) { - foreach($arr as $k => $v) { - $ret[] = [ 'name' => $k, 'value' => $v ]; - } - } - return $ret; - } + /** + * @brief Convert perms array to indexed array. + * + * Converts [ 'view_stream' => 1 ] for any permissions in $arr + * to [ 0 => ['name' => 'view_stream', 'value' => 1], ... ] + * + * @param array $arr associative perms array 'view_stream' => 1 + * @return array Indexed array with elements that look like + * * \e string \b name the perm name (e.g. view_stream) + * * \e int \b value the value of the perm (e.g. 1) + */ + public static function OPerms($arr) + { + $ret = []; + if ($arr) { + foreach ($arr as $k => $v) { + $ret[] = [ 'name' => $k, 'value' => $v ]; + } + } + return $ret; + } - /** - * @brief - * - * @param int $channel_id - * @return boolean|array - */ - static public function FilledAutoperms($channel_id) { - if(! intval(get_pconfig($channel_id,'system','autoperms'))) - return false; + /** + * @brief + * + * @param int $channel_id + * @return bool|array + */ + public static function FilledAutoperms($channel_id) + { + if (! intval(get_pconfig($channel_id, 'system', 'autoperms'))) { + return false; + } - $arr = []; - $r = q("select * from pconfig where uid = %d and cat = 'autoperms'", - intval($channel_id) - ); - if($r) { - foreach($r as $rr) { - $arr[$rr['k']] = intval($rr['v']); - } - } - return $arr; - } + $arr = []; + $r = q( + "select * from pconfig where uid = %d and cat = 'autoperms'", + intval($channel_id) + ); + if ($r) { + foreach ($r as $rr) { + $arr[$rr['k']] = intval($rr['v']); + } + } + return $arr; + } - /** - * @brief Compares that all Permissions from $p1 exist also in $p2. - * - * @param array $p1 The perms that have to exist in $p2 - * @param array $p2 The perms to compare against - * @return boolean true if all perms from $p1 exist also in $p2 - */ - static public function PermsCompare($p1, $p2) { - foreach($p1 as $k => $v) { - if(! array_key_exists($k, $p2)) - return false; + /** + * @brief Compares that all Permissions from $p1 exist also in $p2. + * + * @param array $p1 The perms that have to exist in $p2 + * @param array $p2 The perms to compare against + * @return bool true if all perms from $p1 exist also in $p2 + */ + public static function PermsCompare($p1, $p2) + { + foreach ($p1 as $k => $v) { + if (! array_key_exists($k, $p2)) { + return false; + } - if($p1[$k] != $p2[$k]) - return false; - } + if ($p1[$k] != $p2[$k]) { + return false; + } + } - return true; - } + return true; + } - /** - * @brief - * - * @param int $channel_id A channel id - * @return array Associative array with - * * \e array \b perms Permission array - * * \e int \b automatic 0 or 1 - */ - static public function connect_perms($channel_id) { + /** + * @brief + * + * @param int $channel_id A channel id + * @return array Associative array with + * * \e array \b perms Permission array + * * \e int \b automatic 0 or 1 + */ + public static function connect_perms($channel_id) + { - $my_perms = []; - $permcat = null; - $automatic = 0; + $my_perms = []; + $permcat = null; + $automatic = 0; - // If a default permcat exists, use that + // If a default permcat exists, use that - $pc = ((feature_enabled($channel_id,'permcats')) ? get_pconfig($channel_id,'system','default_permcat') : 'default'); - if(! in_array($pc, [ '','default' ])) { - $pcp = new Zlib\Permcat($channel_id); - $permcat = $pcp->fetch($pc); - if($permcat && $permcat['perms']) { - foreach($permcat['perms'] as $p) { - $my_perms[$p['name']] = $p['value']; - } - } - } + $pc = ((feature_enabled($channel_id, 'permcats')) ? get_pconfig($channel_id, 'system', 'default_permcat') : 'default'); + if (! in_array($pc, [ '','default' ])) { + $pcp = new Zlib\Permcat($channel_id); + $permcat = $pcp->fetch($pc); + if ($permcat && $permcat['perms']) { + foreach ($permcat['perms'] as $p) { + $my_perms[$p['name']] = $p['value']; + } + } + } - $automatic = intval(get_pconfig($channel_id,'system','autoperms')); + $automatic = intval(get_pconfig($channel_id, 'system', 'autoperms')); - // look up the permission role to see if it specified auto-connect - // and if there was no permcat or a default permcat, set the perms - // from the role + // look up the permission role to see if it specified auto-connect + // and if there was no permcat or a default permcat, set the perms + // from the role - $role = get_pconfig($channel_id,'system','permissions_role'); - if($role) { - $xx = PermissionRoles::role_perms($role); + $role = get_pconfig($channel_id, 'system', 'permissions_role'); + if ($role) { + $xx = PermissionRoles::role_perms($role); - if((! $my_perms) && ($xx['perms_connect'])) { - $default_perms = $xx['perms_connect']; - $my_perms = Permissions::FilledPerms($default_perms); - } - } + if ((! $my_perms) && ($xx['perms_connect'])) { + $default_perms = $xx['perms_connect']; + $my_perms = Permissions::FilledPerms($default_perms); + } + } - // If we reached this point without having any permission information, - // it is likely a custom permissions role. First see if there are any - // automatic permissions. + // If we reached this point without having any permission information, + // it is likely a custom permissions role. First see if there are any + // automatic permissions. - if(! $my_perms) { - $m = Permissions::FilledAutoperms($channel_id); - if($m) { - $my_perms = $m; - } - } + if (! $my_perms) { + $m = Permissions::FilledAutoperms($channel_id); + if ($m) { + $my_perms = $m; + } + } - // If we reached this point with no permissions, the channel is using - // custom perms but they are not automatic. They will be stored in abconfig with - // the channel's channel_hash (the 'self' connection). + // If we reached this point with no permissions, the channel is using + // custom perms but they are not automatic. They will be stored in abconfig with + // the channel's channel_hash (the 'self' connection). - if(! $my_perms) { - $c = channelx_by_n($channel_id); - if($c) { - $my_perms = Permissions::FilledPerms(explode(',',get_abconfig($channel_id,$c['channel_hash'],'system','my_perms',EMPTY_STR))); - } - } + if (! $my_perms) { + $c = channelx_by_n($channel_id); + if ($c) { + $my_perms = Permissions::FilledPerms(explode(',', get_abconfig($channel_id, $c['channel_hash'], 'system', 'my_perms', EMPTY_STR))); + } + } - return ( [ 'perms' => $my_perms, 'automatic' => $automatic ] ); - } + return ( [ 'perms' => $my_perms, 'automatic' => $automatic ] ); + } - static public function serialise($p) { - $n = []; - if($p) { - foreach($p as $k => $v) { - if(intval($v)) { - $n[] = $k; - } - } - } - return implode(',',$n); - } - + public static function serialise($p) + { + $n = []; + if ($p) { + foreach ($p as $k => $v) { + if (intval($v)) { + $n[] = $k; + } + } + } + return implode(',', $n); + } } diff --git a/Zotlabs/Daemon/Addon.php b/Zotlabs/Daemon/Addon.php index 76b9bcbb4..97c473e53 100644 --- a/Zotlabs/Daemon/Addon.php +++ b/Zotlabs/Daemon/Addon.php @@ -2,12 +2,12 @@ namespace Zotlabs\Daemon; +class Addon +{ -class Addon { + public static function run($argc, $argv) + { - static public function run($argc,$argv) { - - call_hooks('daemon_addon',$argv); - - } + call_hooks('daemon_addon', $argv); + } } diff --git a/Zotlabs/Daemon/CacheThumb.php b/Zotlabs/Daemon/CacheThumb.php index 3508590cb..94a419b14 100644 --- a/Zotlabs/Daemon/CacheThumb.php +++ b/Zotlabs/Daemon/CacheThumb.php @@ -1,50 +1,54 @@ - $max_thumb || $height > $max_thumb) { - $imagick_path = get_config('system','imagick_convert_path'); - if ($imagick_path && @file_exists($imagick_path)) { - $tmp_name = $path . '-001'; - $newsize = photo_calculate_scale(array_merge($is,['max' => $max_thumb])); - $cmd = $imagick_path . ' ' . escapeshellarg(PROJECT_BASE . '/' . $path) . ' -resize ' . $newsize . ' ' . escapeshellarg(PROJECT_BASE . '/' . $tmp_name); + $max_thumb = get_config('system', 'max_cache_thumbnail', 1024); - for ($x = 0; $x < 4; $x ++) { - exec($cmd); - if (file_exists($tmp_name)) { - break; - } - continue; - } - - if (! file_exists($tmp_name)) { - return; - } - @rename($tmp_name,$path); - } - } - } -} \ No newline at end of file + if ($width > $max_thumb || $height > $max_thumb) { + $imagick_path = get_config('system', 'imagick_convert_path'); + if ($imagick_path && @file_exists($imagick_path)) { + $tmp_name = $path . '-001'; + $newsize = photo_calculate_scale(array_merge($is, ['max' => $max_thumb])); + $cmd = $imagick_path . ' ' . escapeshellarg(PROJECT_BASE . '/' . $path) . ' -resize ' . $newsize . ' ' . escapeshellarg(PROJECT_BASE . '/' . $tmp_name); + + for ($x = 0; $x < 4; $x++) { + exec($cmd); + if (file_exists($tmp_name)) { + break; + } + continue; + } + + if (! file_exists($tmp_name)) { + return; + } + @rename($tmp_name, $path); + } + } + } +} diff --git a/Zotlabs/Daemon/Cache_embeds.php b/Zotlabs/Daemon/Cache_embeds.php index 8dd6d5c9c..1698f9741 100644 --- a/Zotlabs/Daemon/Cache_embeds.php +++ b/Zotlabs/Daemon/Cache_embeds.php @@ -2,33 +2,34 @@ namespace Zotlabs\Daemon; +class Cache_embeds +{ -class Cache_embeds { + public static function run($argc, $argv) + { - static public function run($argc,$argv) { + if (! $argc == 2) { + return; + } - if (! $argc == 2) { - return; - } + $c = q( + "select body, html, created from item where id = %d ", + dbesc(intval($argv[1])) + ); - $c = q("select body, html, created from item where id = %d ", - dbesc(intval($argv[1])) - ); + if (! $c) { + return; + } - if (! $c) { - return; - } + $item = array_shift($c); - $item = array_shift($c); + $cache_expire = intval(get_config('system', 'default_expire_days')); + if ($cache_expire <= 0) { + $cache_expire = 60; + } + $cache_enable = ((($cache_expire) && ($item['created'] < datetime_convert('UTC', 'UTC', 'now - ' . $cache_expire . ' days'))) ? false : true); - $cache_expire = intval(get_config('system', 'default_expire_days')); - if ($cache_expire <= 0) { - $cache_expire = 60; - } - $cache_enable = ((($cache_expire) && ($item['created'] < datetime_convert('UTC','UTC', 'now - ' . $cache_expire . ' days'))) ? false : true); - - $s = bbcode($item['body']); - $s = sslify($s, $cache_enable); - - } + $s = bbcode($item['body']); + $s = sslify($s, $cache_enable); + } } diff --git a/Zotlabs/Daemon/Cache_image.php b/Zotlabs/Daemon/Cache_image.php index b5256f75a..0371c7002 100644 --- a/Zotlabs/Daemon/Cache_image.php +++ b/Zotlabs/Daemon/Cache_image.php @@ -4,15 +4,16 @@ namespace Zotlabs\Daemon; use Zotlabs\Lib\Img_cache; -class Cache_image { +class Cache_image +{ - static public function run($argc,$argv) { + public static function run($argc, $argv) + { - cli_startup(); - logger('caching: ' . $argv[1] . ' to ' . $argv[2]); - if ($argc === 3) { - Img_cache::url_to_cache($argv[1],$argv[2]); - } - - } + cli_startup(); + logger('caching: ' . $argv[1] . ' to ' . $argv[2]); + if ($argc === 3) { + Img_cache::url_to_cache($argv[1], $argv[2]); + } + } } diff --git a/Zotlabs/Daemon/Channel_purge.php b/Zotlabs/Daemon/Channel_purge.php index b02045cd2..02f49bb7c 100644 --- a/Zotlabs/Daemon/Channel_purge.php +++ b/Zotlabs/Daemon/Channel_purge.php @@ -2,32 +2,35 @@ namespace Zotlabs\Daemon; +class Channel_purge +{ -class Channel_purge { + public static function run($argc, $argv) + { - static public function run($argc,$argv) { + cli_startup(); - cli_startup(); + $channel_id = intval($argv[1]); - $channel_id = intval($argv[1]); + $channel = q( + "select * from channel where channel_id = %d and channel_removed = 1", + intval($channel_id) + ); - $channel = q("select * from channel where channel_id = %d and channel_removed = 1", - intval($channel_id) - ); + if (! $channel) { + return; + } - if (! $channel) { - return; - } - - do { - $r = q("select id from item where uid = %d and item_deleted = 0 limit 1000", - intval($channel_id) - ); - if ($r) { - foreach ($r as $rv) { - drop_item($rv['id'],false); - } - } - } while ($r); - } + do { + $r = q( + "select id from item where uid = %d and item_deleted = 0 limit 1000", + intval($channel_id) + ); + if ($r) { + foreach ($r as $rv) { + drop_item($rv['id'], false); + } + } + } while ($r); + } } diff --git a/Zotlabs/Daemon/Checksites.php b/Zotlabs/Daemon/Checksites.php index 1c05450b9..07d632d82 100644 --- a/Zotlabs/Daemon/Checksites.php +++ b/Zotlabs/Daemon/Checksites.php @@ -1,54 +1,66 @@ - 1) && ($argv[1])) - $site_id = $argv[1]; + logger('checksites: start'); - if($site_id) - $sql_options = " and site_url = '" . dbesc($argv[1]) . "' "; + if (($argc > 1) && ($argv[1])) { + $site_id = $argv[1]; + } - $days = intval(get_config('system','sitecheckdays')); - if($days < 1) - $days = 30; + if ($site_id) { + $sql_options = " and site_url = '" . dbesc($argv[1]) . "' "; + } - $r = q("select * from site where site_dead = 0 and site_update < %s - INTERVAL %s and site_type = %d $sql_options ", - db_utcnow(), db_quoteinterval($days . ' DAY'), - intval(SITE_TYPE_ZOT) - ); + $days = intval(get_config('system', 'sitecheckdays')); + if ($days < 1) { + $days = 30; + } - if(! $r) - return; + $r = q( + "select * from site where site_dead = 0 and site_update < %s - INTERVAL %s and site_type = %d $sql_options ", + db_utcnow(), + db_quoteinterval($days . ' DAY'), + intval(SITE_TYPE_ZOT) + ); - foreach($r as $rr) { - if(! strcasecmp($rr['site_url'],z_root())) - continue; + if (! $r) { + return; + } - $x = ping_site($rr['site_url']); - if($x['success']) { - logger('checksites: ' . $rr['site_url']); - q("update site set site_update = '%s' where site_url = '%s' ", - dbesc(datetime_convert()), - dbesc($rr['site_url']) - ); - } - else { - logger('marking dead site: ' . $x['message']); - q("update site set site_dead = 1 where site_url = '%s' ", - dbesc($rr['site_url']) - ); - } - } + foreach ($r as $rr) { + if (! strcasecmp($rr['site_url'], z_root())) { + continue; + } - return; - } + $x = ping_site($rr['site_url']); + if ($x['success']) { + logger('checksites: ' . $rr['site_url']); + q( + "update site set site_update = '%s' where site_url = '%s' ", + dbesc(datetime_convert()), + dbesc($rr['site_url']) + ); + } else { + logger('marking dead site: ' . $x['message']); + q( + "update site set site_dead = 1 where site_url = '%s' ", + dbesc($rr['site_url']) + ); + } + } + + return; + } } diff --git a/Zotlabs/Daemon/Content_importer.php b/Zotlabs/Daemon/Content_importer.php index adc180a2c..c1be35d62 100644 --- a/Zotlabs/Daemon/Content_importer.php +++ b/Zotlabs/Daemon/Content_importer.php @@ -8,52 +8,55 @@ require_once('include/cli_startup.php'); require_once('include/attach.php'); require_once('include/import.php'); -class Content_importer { +class Content_importer +{ - static public function run($argc,$argv) { - cli_startup(); + public static function run($argc, $argv) + { + cli_startup(); - $page = $argv[1]; - $since = $argv[2]; - $until = $argv[3]; - $channel_address = $argv[4]; - $hz_server = urldecode($argv[5]); + $page = $argv[1]; + $since = $argv[2]; + $until = $argv[3]; + $channel_address = $argv[4]; + $hz_server = urldecode($argv[5]); - $m = parse_url($hz_server); + $m = parse_url($hz_server); - $channel = channelx_by_nick($channel_address); - if(! $channel) { - logger('itemhelper: channel not found'); - killme(); - } + $channel = channelx_by_nick($channel_address); + if (! $channel) { + logger('itemhelper: channel not found'); + killme(); + } - $headers = [ - 'X-API-Token' => random_string(), - 'X-API-Request' => $hz_server . '/api/z/1.0/item/export_page?f=&zap_compat=1&since=' . urlencode($since) . '&until=' . urlencode($until) . '&page=' . $page , - 'Host' => $m['host'], - '(request-target)' => 'get /api/z/1.0/item/export_page?f=&zap_compat=1&since=' . urlencode($since) . '&until=' . urlencode($until) . '&page=' . $page , - ]; + $headers = [ + 'X-API-Token' => random_string(), + 'X-API-Request' => $hz_server . '/api/z/1.0/item/export_page?f=&zap_compat=1&since=' . urlencode($since) . '&until=' . urlencode($until) . '&page=' . $page , + 'Host' => $m['host'], + '(request-target)' => 'get /api/z/1.0/item/export_page?f=&zap_compat=1&since=' . urlencode($since) . '&until=' . urlencode($until) . '&page=' . $page , + ]; - $headers = HTTPSig::create_sig($headers,$channel['channel_prvkey'], channel_url($channel),true,'sha512'); + $headers = HTTPSig::create_sig($headers, $channel['channel_prvkey'], channel_url($channel), true, 'sha512'); - $x = z_fetch_url($hz_server . '/api/z/1.0/item/export_page?f=&zap_compat=1&since=' . urlencode($since) . '&until=' . urlencode($until) . '&page=' . $page,false,$redirects,[ 'headers' => $headers ]); + $x = z_fetch_url($hz_server . '/api/z/1.0/item/export_page?f=&zap_compat=1&since=' . urlencode($since) . '&until=' . urlencode($until) . '&page=' . $page, false, $redirects, [ 'headers' => $headers ]); - if(! $x['success']) { - logger('no API response',LOGGER_DEBUG); - killme(); - } + if (! $x['success']) { + logger('no API response', LOGGER_DEBUG); + killme(); + } - $j = json_decode($x['body'],true); + $j = json_decode($x['body'], true); - if (! $j) { - killme(); - } + if (! $j) { + killme(); + } - if(! ($j['item'] || count($j['item']))) - killme(); + if (! ($j['item'] || count($j['item']))) { + killme(); + } - import_items($channel,$j['item'],false,((array_key_exists('relocate',$j)) ? $j['relocate'] : null)); + import_items($channel, $j['item'], false, ((array_key_exists('relocate', $j)) ? $j['relocate'] : null)); - killme(); - } + killme(); + } } diff --git a/Zotlabs/Daemon/Convo.php b/Zotlabs/Daemon/Convo.php index 482f70c53..7a4f2bd1e 100644 --- a/Zotlabs/Daemon/Convo.php +++ b/Zotlabs/Daemon/Convo.php @@ -1,4 +1,4 @@ -get(); + $obj = new ASCollection($id, $channel); - if ($messages) { - foreach ($messages as $message) { - if (is_string($message)) { - $message = Activity::fetch($message,$channel); - } - // set client flag because comments will probably just be objects and not full blown activities - // and that lets us use implied_create - $AS = new ActivityStreams($message, null, true); - if ($AS->is_valid() && is_array($AS->obj)) { - $item = Activity::decode_note($AS,true); - Activity::store($channel,$contact['abook_xchan'],$AS,$item,true,true); - } - } - } - } + $messages = $obj->get(); + + if ($messages) { + foreach ($messages as $message) { + if (is_string($message)) { + $message = Activity::fetch($message, $channel); + } + // set client flag because comments will probably just be objects and not full blown activities + // and that lets us use implied_create + $AS = new ActivityStreams($message, null, true); + if ($AS->is_valid() && is_array($AS->obj)) { + $item = Activity::decode_note($AS, true); + Activity::store($channel, $contact['abook_xchan'], $AS, $item, true, true); + } + } + } + } } diff --git a/Zotlabs/Daemon/Cron.php b/Zotlabs/Daemon/Cron.php index bb1bf2dee..b5672e334 100644 --- a/Zotlabs/Daemon/Cron.php +++ b/Zotlabs/Daemon/Cron.php @@ -1,201 +1,221 @@ - $maxsysload) { - logger('system: load ' . $load . ' too high. Cron deferred to next scheduled run.'); - return; - } - } + $maxsysload = intval(get_config('system', 'maxloadavg')); + if ($maxsysload < 1) { + $maxsysload = 50; + } + if (function_exists('sys_getloadavg')) { + $load = sys_getloadavg(); + if (intval($load[0]) > $maxsysload) { + logger('system: load ' . $load . ' too high. Cron deferred to next scheduled run.'); + return; + } + } - // Check for a lockfile. If it exists, but is over an hour old, it's stale. Ignore it. - $lockfile = 'cache/cron'; - if((file_exists($lockfile)) && (filemtime($lockfile) > (time() - 3600)) - && (! get_config('system','override_cron_lockfile'))) { - logger("cron: Already running"); - return; - } - - // Create a lockfile. Needs two vars, but $x doesn't need to contain anything. - file_put_contents($lockfile, $x); + // Check for a lockfile. If it exists, but is over an hour old, it's stale. Ignore it. + $lockfile = 'cache/cron'; + if ( + (file_exists($lockfile)) && (filemtime($lockfile) > (time() - 3600)) + && (! get_config('system', 'override_cron_lockfile')) + ) { + logger("cron: Already running"); + return; + } - logger('cron: start'); - - // run queue delivery process in the background + // Create a lockfile. Needs two vars, but $x doesn't need to contain anything. + file_put_contents($lockfile, $x); - Run::Summon( [ 'Queue' ] ); + logger('cron: start'); - Run::Summon( [ 'Poller' ] ); + // run queue delivery process in the background - // maintenance for mod sharedwithme - check for updated items and remove them + Run::Summon([ 'Queue' ]); - require_once('include/sharedwithme.php'); - apply_updates(); - - // expire any expired items + Run::Summon([ 'Poller' ]); - $r = q("select id,item_wall from item where expires > '2001-01-01 00:00:00' and expires < %s + // maintenance for mod sharedwithme - check for updated items and remove them + + require_once('include/sharedwithme.php'); + apply_updates(); + + // expire any expired items + + $r = q( + "select id,item_wall from item where expires > '2001-01-01 00:00:00' and expires < %s and item_deleted = 0 ", - db_utcnow() - ); - if($r) { - require_once('include/items.php'); - foreach($r as $rr) { - drop_item($rr['id'],false,(($rr['item_wall']) ? DROPITEM_PHASE1 : DROPITEM_NORMAL)); - if($rr['item_wall']) { - // The notifier isn't normally invoked unless item_drop is interactive. - Run::Summon( [ 'Notifier', 'drop', $rr['id'] ] ); - } - } - } + db_utcnow() + ); + if ($r) { + require_once('include/items.php'); + foreach ($r as $rr) { + drop_item($rr['id'], false, (($rr['item_wall']) ? DROPITEM_PHASE1 : DROPITEM_NORMAL)); + if ($rr['item_wall']) { + // The notifier isn't normally invoked unless item_drop is interactive. + Run::Summon([ 'Notifier', 'drop', $rr['id'] ]); + } + } + } - // delete expired access tokens + // delete expired access tokens - $r = q("select atoken_id from atoken where atoken_expires > '%s' and atoken_expires < %s", - dbesc(NULL_DATE), - db_utcnow() - ); - if($r) { - require_once('include/security.php'); - foreach($r as $rr) { - atoken_delete($rr['atoken_id']); - } - } + $r = q( + "select atoken_id from atoken where atoken_expires > '%s' and atoken_expires < %s", + dbesc(NULL_DATE), + db_utcnow() + ); + if ($r) { + require_once('include/security.php'); + foreach ($r as $rr) { + atoken_delete($rr['atoken_id']); + } + } - // Ensure that every channel pings their directory occasionally. + // Ensure that every channel pings their directory occasionally. - $r = q("select channel_id from channel where channel_dirdate < %s - INTERVAL %s and channel_removed = 0", - db_utcnow(), - db_quoteinterval('7 DAY') - ); - if($r) { - foreach($r as $rr) { - Run::Summon( [ 'Directory', $rr['channel_id'], 'force' ] ); - if($interval) - @time_sleep_until(microtime(true) + (float) $interval); - } - } + $r = q( + "select channel_id from channel where channel_dirdate < %s - INTERVAL %s and channel_removed = 0", + db_utcnow(), + db_quoteinterval('7 DAY') + ); + if ($r) { + foreach ($r as $rr) { + Run::Summon([ 'Directory', $rr['channel_id'], 'force' ]); + if ($interval) { + @time_sleep_until(microtime(true) + (float) $interval); + } + } + } - // publish any applicable items that were set to be published in the future - // (time travel posts). Restrict to items that have come of age in the last - // couple of days to limit the query to something reasonable. + // publish any applicable items that were set to be published in the future + // (time travel posts). Restrict to items that have come of age in the last + // couple of days to limit the query to something reasonable. - $r = q("select id from item where item_delayed = 1 and created <= %s and created > '%s' ", - db_utcnow(), - dbesc(datetime_convert('UTC','UTC','now - 2 days')) - ); - if($r) { - foreach($r as $rr) { - $x = q("update item set item_delayed = 0 where id = %d", - intval($rr['id']) - ); - if($x) { - $z = q("select * from item where id = %d", - intval($message_id) - ); - if($z) { - xchan_query($z); - $sync_item = fetch_post_tags($z); - Libsync::build_sync_packet($sync_item[0]['uid'], - [ - 'item' => [ encode_item($sync_item[0],true) ] - ] - ); - } - Run::Summon( [ 'Notifier','wall-new',$rr['id'] ] ); - } - } - } + $r = q( + "select id from item where item_delayed = 1 and created <= %s and created > '%s' ", + db_utcnow(), + dbesc(datetime_convert('UTC', 'UTC', 'now - 2 days')) + ); + if ($r) { + foreach ($r as $rr) { + $x = q( + "update item set item_delayed = 0 where id = %d", + intval($rr['id']) + ); + if ($x) { + $z = q( + "select * from item where id = %d", + intval($message_id) + ); + if ($z) { + xchan_query($z); + $sync_item = fetch_post_tags($z); + Libsync::build_sync_packet( + $sync_item[0]['uid'], + [ + 'item' => [ encode_item($sync_item[0], true) ] + ] + ); + } + Run::Summon([ 'Notifier','wall-new',$rr['id'] ]); + } + } + } - require_once('include/attach.php'); - attach_upgrade(); + require_once('include/attach.php'); + attach_upgrade(); - $abandon_days = intval(get_config('system','account_abandon_days')); - if($abandon_days < 1) - $abandon_days = 0; - - - // once daily run birthday_updates and then expire in background - - // FIXME: add birthday updates, both locally and for xprof for use - // by directory servers - - $d1 = intval(get_config('system','last_expire_day')); - $d2 = intval(datetime_convert('UTC','UTC','now','d')); - - // Allow somebody to staggger daily activities if they have more than one site on their server, - // or if it happens at an inconvenient (busy) hour. - - $h1 = intval(get_config('system','cron_hour')); - $h2 = intval(datetime_convert('UTC','UTC','now','G')); + $abandon_days = intval(get_config('system', 'account_abandon_days')); + if ($abandon_days < 1) { + $abandon_days = 0; + } - if(($d2 != $d1) && ($h1 == $h2)) { - Run::Summon( [ 'Cron_daily' ] ); - } + // once daily run birthday_updates and then expire in background - // update any photos which didn't get imported properly - // This should be rare + // FIXME: add birthday updates, both locally and for xprof for use + // by directory servers - $r = q("select xchan_photo_l, xchan_hash from xchan where xchan_photo_l != '' and xchan_photo_m = '' + $d1 = intval(get_config('system', 'last_expire_day')); + $d2 = intval(datetime_convert('UTC', 'UTC', 'now', 'd')); + + // Allow somebody to staggger daily activities if they have more than one site on their server, + // or if it happens at an inconvenient (busy) hour. + + $h1 = intval(get_config('system', 'cron_hour')); + $h2 = intval(datetime_convert('UTC', 'UTC', 'now', 'G')); + + + if (($d2 != $d1) && ($h1 == $h2)) { + Run::Summon([ 'Cron_daily' ]); + } + + // update any photos which didn't get imported properly + // This should be rare + + $r = q( + "select xchan_photo_l, xchan_hash from xchan where xchan_photo_l != '' and xchan_photo_m = '' and xchan_photo_date < %s - INTERVAL %s", - db_utcnow(), - db_quoteinterval('1 DAY') - ); - if($r) { - require_once('include/photo_factory.php'); - foreach($r as $rr) { - $photos = import_remote_xchan_photo($rr['xchan_photo_l'],$rr['xchan_hash']); - if ($photos) { - $x = q("update xchan set xchan_photo_l = '%s', xchan_photo_m = '%s', xchan_photo_s = '%s', xchan_photo_mimetype = '%s' + db_utcnow(), + db_quoteinterval('1 DAY') + ); + if ($r) { + require_once('include/photo_factory.php'); + foreach ($r as $rr) { + $photos = import_remote_xchan_photo($rr['xchan_photo_l'], $rr['xchan_hash']); + if ($photos) { + $x = q( + "update xchan set xchan_photo_l = '%s', xchan_photo_m = '%s', xchan_photo_s = '%s', xchan_photo_mimetype = '%s' where xchan_hash = '%s'", - dbesc($photos[0]), - dbesc($photos[1]), - dbesc($photos[2]), - dbesc($photos[3]), - dbesc($rr['xchan_hash']) - ); - } - } - } + dbesc($photos[0]), + dbesc($photos[1]), + dbesc($photos[2]), + dbesc($photos[3]), + dbesc($rr['xchan_hash']) + ); + } + } + } - $generation = 0; + $generation = 0; - $restart = false; + $restart = false; - if(($argc > 1) && ($argv[1] == 'restart')) { - $restart = true; - $generation = intval($argv[2]); - if(! $generation) - return; - } + if (($argc > 1) && ($argv[1] == 'restart')) { + $restart = true; + $generation = intval($argv[2]); + if (! $generation) { + return; + } + } - reload_plugins(); + reload_plugins(); - $d = datetime_convert(); + $d = datetime_convert(); - // TODO check to see if there are any cronhooks before wasting a process + // TODO check to see if there are any cronhooks before wasting a process - if(! $restart) - Run::Summon( [ 'Cronhooks' ] ); + if (! $restart) { + Run::Summon([ 'Cronhooks' ]); + } - set_config('system','lastcron',datetime_convert()); + set_config('system', 'lastcron', datetime_convert()); - //All done - clear the lockfile - @unlink($lockfile); + //All done - clear the lockfile + @unlink($lockfile); - return; - } + return; + } } diff --git a/Zotlabs/Daemon/Cron_daily.php b/Zotlabs/Daemon/Cron_daily.php index 8d33b9fc9..9ecb02064 100644 --- a/Zotlabs/Daemon/Cron_daily.php +++ b/Zotlabs/Daemon/Cron_daily.php @@ -1,110 +1,122 @@ - %s - INTERVAL %s and channel_deleted < %s - INTERVAL %s", - db_utcnow(), db_quoteinterval('21 DAY'), - db_utcnow(), db_quoteinterval('10 DAY') - ); - if($r) { - foreach($r as $rv) { - channel_remove_final($rv['channel_id']); - } - } + db_utcnow(), + db_quoteinterval('21 DAY'), + db_utcnow(), + db_quoteinterval('10 DAY') + ); + if ($r) { + foreach ($r as $rv) { + channel_remove_final($rv['channel_id']); + } + } - // get rid of really old poco records + // get rid of really old poco records - q("delete from xlink where xlink_updated < %s - INTERVAL %s and xlink_static = 0 ", - db_utcnow(), db_quoteinterval('14 DAY') - ); + q( + "delete from xlink where xlink_updated < %s - INTERVAL %s and xlink_static = 0 ", + db_utcnow(), + db_quoteinterval('14 DAY') + ); - // Check for dead sites - Run::Summon( ['Checksites' ] ); + // Check for dead sites + Run::Summon(['Checksites' ]); - // clean up image cache - use site expiration or 60 days if not set or zero - - $files = glob('cache/img/*/*'); - $expire_days = intval(get_config('system','default_expire_days')); - if ($expire_days <= 0) { - $expire_days = 60; - } - $now = time(); - $maxage = 86400 * $expire_days; - if ($files) { - foreach ($files as $file) { - if (is_file($file)) { - if ($now - filemtime($file) >= $maxage) { - unlink($file); - } - } - } - } + // clean up image cache - use site expiration or 60 days if not set or zero - // update searchable doc indexes + $files = glob('cache/img/*/*'); + $expire_days = intval(get_config('system', 'default_expire_days')); + if ($expire_days <= 0) { + $expire_days = 60; + } + $now = time(); + $maxage = 86400 * $expire_days; + if ($files) { + foreach ($files as $file) { + if (is_file($file)) { + if ($now - filemtime($file) >= $maxage) { + unlink($file); + } + } + } + } - Run::Summon( [ 'Importdoc'] ); + // update searchable doc indexes - /** - * End Cron Weekly - */ + Run::Summon([ 'Importdoc']); - } -} \ No newline at end of file + /** + * End Cron Weekly + */ + } +} diff --git a/Zotlabs/Daemon/Cronhooks.php b/Zotlabs/Daemon/Cronhooks.php index df0a5442e..44d9f159e 100644 --- a/Zotlabs/Daemon/Cronhooks.php +++ b/Zotlabs/Daemon/Cronhooks.php @@ -1,17 +1,21 @@ -start(); + if ($argc != 2) { + return; + } - $_SESSION['authenticated'] = 1; - $_SESSION['uid'] = $argv[1]; + App::$session->start(); - $x = session_id(); + $_SESSION['authenticated'] = 1; + $_SESSION['uid'] = $argv[1]; - $f = 'cache/cookie_' . $argv[1]; - $c = 'cache/cookien_' . $argv[1]; + $x = session_id(); - $e = file_exists($f); + $f = 'cache/cookie_' . $argv[1]; + $c = 'cache/cookien_' . $argv[1]; - $output = ''; + $e = file_exists($f); - if($e) { - $lines = file($f); - if($lines) { - foreach($lines as $line) { - if(strlen($line) > 0 && $line[0] != '#' && substr_count($line, "\t") == 6) { - $tokens = explode("\t", $line); - $tokens = array_map('trim', $tokens); - if($tokens[4] > time()) { - $output .= $line . "\n"; - } - } - else - $output .= $line; - } - } - } - $t = time() + (24 * 3600); - file_put_contents($f, $output . 'HttpOnly_' . \App::get_hostname() . "\tFALSE\t/\tTRUE\t$t\tPHPSESSID\t" . $x, (($e) ? FILE_APPEND : 0)); + $output = ''; - file_put_contents($c,$x); + if ($e) { + $lines = file($f); + if ($lines) { + foreach ($lines as $line) { + if (strlen($line) > 0 && $line[0] != '#' && substr_count($line, "\t") == 6) { + $tokens = explode("\t", $line); + $tokens = array_map('trim', $tokens); + if ($tokens[4] > time()) { + $output .= $line . "\n"; + } + } else { + $output .= $line; + } + } + } + } + $t = time() + (24 * 3600); + file_put_contents($f, $output . 'HttpOnly_' . App::get_hostname() . "\tFALSE\t/\tTRUE\t$t\tPHPSESSID\t" . $x, (($e) ? FILE_APPEND : 0)); - return; - } + file_put_contents($c, $x); + + return; + } } diff --git a/Zotlabs/Daemon/Deliver.php b/Zotlabs/Daemon/Deliver.php index 3844f0b4e..2aecaa1b9 100644 --- a/Zotlabs/Daemon/Deliver.php +++ b/Zotlabs/Daemon/Deliver.php @@ -1,31 +1,36 @@ - 2) { + if ($argv[2] === 'force') { + $force = true; + } + if ($argv[2] === 'nopush') { + $pushall = false; + } + } - $force = false; - $pushall = true; - - if ($argc > 2) { - if ($argv[2] === 'force') { - $force = true; - } - if ($argv[2] === 'nopush') { - $pushall = false; - } - } + logger('directory update', LOGGER_DEBUG); - logger('directory update', LOGGER_DEBUG); + $channel = channelx_by_n($argv[1]); + if (! $channel) { + return; + } - $channel = channelx_by_n($argv[1]); - if (! $channel) { - return; - } + // update the local directory - was optional, but now done regardless - // update the local directory - was optional, but now done regardless - - Libzotdir::local_dir_update($argv[1],$force); + Libzotdir::local_dir_update($argv[1], $force); - q("update channel set channel_dirdate = '%s' where channel_id = %d", - dbesc(datetime_convert()), - intval($channel['channel_id']) - ); + q( + "update channel set channel_dirdate = '%s' where channel_id = %d", + dbesc(datetime_convert()), + intval($channel['channel_id']) + ); - // Now update all the connections - if ($pushall) { - Run::Summon( [ 'Notifier','refresh_all',$channel['channel_id'] ] ); - } - } + // Now update all the connections + if ($pushall) { + Run::Summon([ 'Notifier','refresh_all',$channel['channel_id'] ]); + } + } } diff --git a/Zotlabs/Daemon/Expire.php b/Zotlabs/Daemon/Expire.php index fee1e021d..d1214fca6 100644 --- a/Zotlabs/Daemon/Expire.php +++ b/Zotlabs/Daemon/Expire.php @@ -2,90 +2,94 @@ namespace Zotlabs\Daemon; +class Expire +{ -class Expire { + public static function run($argc, $argv) + { - static public function run($argc,$argv){ + cli_startup(); - cli_startup(); + // perform final cleanup on previously delete items - // perform final cleanup on previously delete items + $r = q( + "select id from item where item_deleted = 1 and item_pending_remove = 0 and changed < %s - INTERVAL %s", + db_utcnow(), + db_quoteinterval('10 DAY') + ); + if ($r) { + foreach ($r as $rr) { + drop_item($rr['id'], false, DROPITEM_PHASE2); + } + } - $r = q("select id from item where item_deleted = 1 and item_pending_remove = 0 and changed < %s - INTERVAL %s", - db_utcnow(), db_quoteinterval('10 DAY') - ); - if ($r) { - foreach ($r as $rr) { - drop_item($rr['id'], false, DROPITEM_PHASE2); - } - } + // physically remove anything that has been deleted for more than two months + /** @FIXME - this is a wretchedly inefficient query */ - // physically remove anything that has been deleted for more than two months - /** @FIXME - this is a wretchedly inefficient query */ + $r = q( + "delete from item where item_pending_remove = 1 and changed < %s - INTERVAL %s", + db_utcnow(), + db_quoteinterval('36 DAY') + ); - $r = q("delete from item where item_pending_remove = 1 and changed < %s - INTERVAL %s", - db_utcnow(), db_quoteinterval('36 DAY') - ); + logger('expire: start', LOGGER_DEBUG); - logger('expire: start', LOGGER_DEBUG); + $site_expire = intval(get_config('system', 'default_expire_days')); + $commented_days = intval(get_config('system', 'active_expire_days')); - $site_expire = intval(get_config('system', 'default_expire_days')); - $commented_days = intval(get_config('system','active_expire_days')); + logger('site_expire: ' . $site_expire); - logger('site_expire: ' . $site_expire); + $r = q("SELECT channel_id, channel_system, channel_address, channel_expire_days from channel where true"); - $r = q("SELECT channel_id, channel_system, channel_address, channel_expire_days from channel where true"); + if ($r) { + foreach ($r as $rr) { + // expire the sys channel separately + if (intval($rr['channel_system'])) { + continue; + } - if ($r) { - foreach ($r as $rr) { + // service class default (if non-zero) over-rides the site default - // expire the sys channel separately - if (intval($rr['channel_system'])) - continue; + $service_class_expire = service_class_fetch($rr['channel_id'], 'expire_days'); + if (intval($service_class_expire)) { + $channel_expire = $service_class_expire; + } else { + $channel_expire = $site_expire; + } - // service class default (if non-zero) over-rides the site default + if ( + intval($channel_expire) && (intval($channel_expire) < intval($rr['channel_expire_days'])) || + intval($rr['channel_expire_days'] == 0) + ) { + $expire_days = $channel_expire; + } else { + $expire_days = $rr['channel_expire_days']; + } - $service_class_expire = service_class_fetch($rr['channel_id'], 'expire_days'); - if (intval($service_class_expire)) { - $channel_expire = $service_class_expire; - } - else { - $channel_expire = $site_expire; - } - - if (intval($channel_expire) && (intval($channel_expire) < intval($rr['channel_expire_days'])) || - intval($rr['channel_expire_days'] == 0)) { - $expire_days = $channel_expire; - } - else { - $expire_days = $rr['channel_expire_days']; - } + // if the site or service class expiration is non-zero and less than person expiration, use that + logger('Expire: ' . $rr['channel_address'] . ' interval: ' . $expire_days, LOGGER_DEBUG); + item_expire($rr['channel_id'], $expire_days, $commented_days); + } + } - // if the site or service class expiration is non-zero and less than person expiration, use that - logger('Expire: ' . $rr['channel_address'] . ' interval: ' . $expire_days, LOGGER_DEBUG); - item_expire($rr['channel_id'], $expire_days, $commented_days); - } - } + $x = get_sys_channel(); + if ($x) { + // this should probably just fetch the channel_expire_days from the sys channel, + // but there's no convenient way to set it. - $x = get_sys_channel(); - if ($x) { + $expire_days = get_config('system', 'sys_expire_days', 30); - // this should probably just fetch the channel_expire_days from the sys channel, - // but there's no convenient way to set it. + if (intval($site_expire) && (intval($site_expire) < intval($expire_days))) { + $expire_days = $site_expire; + } - $expire_days = get_config('system', 'sys_expire_days',30); + logger('Expire: sys interval: ' . $expire_days, LOGGER_DEBUG); - if (intval($site_expire) && (intval($site_expire) < intval($expire_days))) { - $expire_days = $site_expire; - } + if ($expire_days) { + item_expire($x['channel_id'], $expire_days, $commented_days); + } - logger('Expire: sys interval: ' . $expire_days, LOGGER_DEBUG); - - if ($expire_days) { - item_expire($x['channel_id'], $expire_days, $commented_days); - } - - logger('Expire: sys: done', LOGGER_DEBUG); - } - } + logger('Expire: sys: done', LOGGER_DEBUG); + } + } } diff --git a/Zotlabs/Daemon/File_importer.php b/Zotlabs/Daemon/File_importer.php index f3fe785bb..55c4469c6 100644 --- a/Zotlabs/Daemon/File_importer.php +++ b/Zotlabs/Daemon/File_importer.php @@ -8,43 +8,45 @@ require_once('include/cli_startup.php'); require_once('include/attach.php'); require_once('include/import.php'); -class File_importer { +class File_importer +{ - static public function run($argc,$argv) { + public static function run($argc, $argv) + { - cli_startup(); + cli_startup(); - $attach_id = $argv[1]; - $channel_address = $argv[2]; - $hz_server = urldecode($argv[3]); + $attach_id = $argv[1]; + $channel_address = $argv[2]; + $hz_server = urldecode($argv[3]); - $m = parse_url($hz_server); + $m = parse_url($hz_server); - $channel = channelx_by_nick($channel_address); - if(! $channel) { - logger('filehelper: channel not found'); - killme(); - } + $channel = channelx_by_nick($channel_address); + if (! $channel) { + logger('filehelper: channel not found'); + killme(); + } - $headers = [ - 'X-API-Token' => random_string(), - 'X-API-Request' => $hz_server . '/api/z/1.0/file/export?f=&zap_compat=1&file_id=' . $attach_id, - 'Host' => $m['host'], - '(request-target)' => 'get /api/z/1.0/file/export?f=&zap_compat=1&file_id=' . $attach_id, - ]; + $headers = [ + 'X-API-Token' => random_string(), + 'X-API-Request' => $hz_server . '/api/z/1.0/file/export?f=&zap_compat=1&file_id=' . $attach_id, + 'Host' => $m['host'], + '(request-target)' => 'get /api/z/1.0/file/export?f=&zap_compat=1&file_id=' . $attach_id, + ]; - $headers = HTTPSig::create_sig($headers,$channel['channel_prvkey'],channel_url($channel),true,'sha512'); - $x = z_fetch_url($hz_server . '/api/z/1.0/file/export?f=&zap_compat=1&file_id=' . $attach_id,false,$redirects,[ 'headers' => $headers ]); + $headers = HTTPSig::create_sig($headers, $channel['channel_prvkey'], channel_url($channel), true, 'sha512'); + $x = z_fetch_url($hz_server . '/api/z/1.0/file/export?f=&zap_compat=1&file_id=' . $attach_id, false, $redirects, [ 'headers' => $headers ]); - if(! $x['success']) { - logger('no API response',LOGGER_DEBUG); - return; - } + if (! $x['success']) { + logger('no API response', LOGGER_DEBUG); + return; + } - $j = json_decode($x['body'],true); + $j = json_decode($x['body'], true); - $r = sync_files($channel,[$j]); + $r = sync_files($channel, [$j]); - killme(); - } + killme(); + } } diff --git a/Zotlabs/Daemon/Gprobe.php b/Zotlabs/Daemon/Gprobe.php index ae138d1b8..a42a14c5b 100644 --- a/Zotlabs/Daemon/Gprobe.php +++ b/Zotlabs/Daemon/Gprobe.php @@ -1,4 +1,6 @@ - 3) ? $argv[3] : ''); - $dstname = (($argc > 4) ? $argv[4] : ''); + $srcfile = $argv[2]; + $folder = (($argc > 3) ? $argv[3] : ''); + $dstname = (($argc > 4) ? $argv[4] : ''); - $hash = random_string(); + $hash = random_string(); - $arr = [ - 'src' => $srcfile, - 'filename' => (($dstname) ? $dstname : basename($srcfile)), - 'hash' => $hash, - 'allow_cid' => $channel['channel_allow_cid'], - 'allow_gid' => $channel['channel_allow_gid'], - 'deny_cid' => $channel['channel_deny_cid'], - 'deny_gid' => $channel['channel_deny_gid'], - 'preserve_original' => true, - 'replace' => true - ]; + $arr = [ + 'src' => $srcfile, + 'filename' => (($dstname) ? $dstname : basename($srcfile)), + 'hash' => $hash, + 'allow_cid' => $channel['channel_allow_cid'], + 'allow_gid' => $channel['channel_allow_gid'], + 'deny_cid' => $channel['channel_deny_cid'], + 'deny_gid' => $channel['channel_deny_gid'], + 'preserve_original' => true, + 'replace' => true + ]; - if($folder) - $arr['folder'] = $folder; + if ($folder) { + $arr['folder'] = $folder; + } - attach_store($channel,$channel['channel_hash'],'import',$arr); + attach_store($channel, $channel['channel_hash'], 'import', $arr); - $sync = attach_export_data($channel,$hash); - if($sync) - Libsync::build_sync_packet($channel['channel_id'],array('file' => array($sync))); - - return; - } + $sync = attach_export_data($channel, $hash); + if ($sync) { + Libsync::build_sync_packet($channel['channel_id'], array('file' => array($sync))); + } + + return; + } } diff --git a/Zotlabs/Daemon/Notifier.php b/Zotlabs/Daemon/Notifier.php index b19d5ae37..aca8e3f51 100644 --- a/Zotlabs/Daemon/Notifier.php +++ b/Zotlabs/Daemon/Notifier.php @@ -1,8 +1,7 @@ - $argv[4] ]; - self::$encoding = 'zot'; - $normal_mode = false; - } - elseif ($cmd === 'keychange') { - self::$channel = channelx_by_n($item_id); - $r = q("select abook_xchan from abook where abook_channel = %d", - intval($item_id) - ); - if ($r) { - foreach ($r as $rr) { - self::$recipients[] = $rr['abook_xchan']; - } - } - self::$private = false; - self::$packet_type = 'keychange'; - self::$encoded_item = get_pconfig(self::$channel['channel_id'],'system','keychange'); - self::$encoding = 'zot'; - $normal_mode = false; - } - elseif (in_array($cmd, [ 'permissions_update', 'permissions_reject', 'permissions_accept', 'permissions_create' ])) { - - // Get the (single) recipient - - $r = q("select * from abook left join xchan on abook_xchan = xchan_hash where abook_id = %d and abook_self = 0", - intval($item_id) - ); - if ($r) { - - $recip = array_shift($r); - $uid = $recip['abook_channel']; - // Get the sender - self::$channel = channelx_by_n($uid); - if (self::$channel) { - - $perm_update = [ 'sender' => self::$channel, 'recipient' => $recip, 'success' => false, 'deliveries' => '' ]; - - switch ($cmd) { - case 'permissions_create': - ActivityPub::permissions_create($perm_update); - break; - case 'permissions_accept': - ActivityPub::permissions_accept($perm_update); - break; - case 'permissions_update': - ActivityPub::permissions_update($perm_update); - break; - - default: - break; - } - if (! $perm_update['success']) { - call_hooks($cmd,$perm_update); - } - - if ($perm_update['success']) { - if ($perm_update['deliveries']) { - self::$deliveries[] = $perm_update['deliveries']; - do_delivery(self::$deliveries); - } - return; - } - else { - self::$recipients[] = $recip['abook_xchan']; - self::$private = false; - self::$packet_type = 'refresh'; - self::$env_recips = [ $recip['xchan_hash'] ]; - } - } - } - } - elseif ($cmd === 'refresh_all') { - logger('notifier: refresh_all: ' . $item_id); - - self::$channel = channelx_by_n($item_id,true); - $r = q("select abook_xchan from abook where abook_channel = %d", - intval($item_id) - ); - if ($r) { - foreach ($r as $rr) { - self::$recipients[] = $rr['abook_xchan']; - } - } - self::$recipients[] = self::$channel['channel_hash']; - self::$private = false; - self::$packet_type = 'refresh'; - } - elseif ($cmd === 'purge') { - $xchan = $argv[3]; - logger('notifier: purge: ' . $item_id . ' => ' . $xchan); - if (! $xchan) { - return; - } - - self::$channel = channelx_by_n($item_id,true); - self::$recipients = [ $xchan ]; - self::$private = true; - self::$packet_type = 'purge'; - } - elseif ($cmd === 'purge_all') { - logger('notifier: purge_all: ' . $item_id); - self::$channel = channelx_by_n($item_id,true); - - self::$recipients = []; - $r = q("select abook_xchan from abook where abook_channel = %d and abook_self = 0", - intval($item_id) - ); - if (! $r) { - return; - } - foreach ($r as $rr) { - self::$recipients[] = $rr['abook_xchan']; - } - - self::$private = false; - self::$packet_type = 'purge'; - } - else { - - // Normal items - - // Fetch the target item - - $r = q("SELECT * FROM item WHERE id = %d and parent != 0 LIMIT 1", - intval($item_id) - ); - - if (! $r) { - return; - } - - xchan_query($r); - $r = fetch_post_tags($r); - - $target_item = array_shift($r); - - if($target_item['author']['xchan_network'] === 'anon') { - logger('notifier: target item author is not a fetchable actor', LOGGER_DEBUG); - return; - } - - $deleted_item = false; - - if (intval($target_item['item_deleted'])) { - logger('notifier: target item ITEM_DELETED', LOGGER_DEBUG); - $deleted_item = true; - } - - if (! in_array(intval($target_item['item_type']), [ ITEM_TYPE_POST, ITEM_TYPE_MAIL ] )) { - - if (intval($target_item['item_type'] == ITEM_TYPE_CUSTOM)) { - - $hookinfo=[ - 'targetitem' => $target_item, - 'deliver' => false - ]; - - call_hooks('customitem_deliver',$hookinfo); - } - - if (! $hookinfo['deliver']) { - logger('notifier: target item not forwardable: type ' . $target_item['item_type'], LOGGER_DEBUG); - return; - } - } - - // Check for non published items, but allow an exclusion for transmitting hidden file activities - - if (intval($target_item['item_unpublished']) || intval($target_item['item_delayed']) || - intval($target_item['item_blocked']) || - ( intval($target_item['item_hidden']) && ($target_item['obj_type'] !== ACTIVITY_OBJ_FILE))) { - logger('notifier: target item not published, so not forwardable', LOGGER_DEBUG); - return; - } - - if (in_array($target_item['verb'], [ ACTIVITY_FOLLOW, ACTIVITY_IGNORE ])) { - logger('not fowarding follow|unfollow->note activity'); - return; - } - - $s = q("select * from channel left join xchan on channel_hash = xchan_hash where channel_id = %d limit 1", - intval($target_item['uid']) - ); - if ($s) { - self::$channel = array_shift($s); - } - - if (self::$channel['channel_hash'] !== $target_item['author_xchan'] && self::$channel['channel_hash'] !== $target_item['owner_xchan']) { - logger("notifier: Sending channel is not owner {$target_item['owner_xchan']} or author {$target_item['author_xchan']}", LOGGER_NORMAL, LOG_WARNING); - return; - } - - $thread_is_public = false; - - if ($target_item['mid'] === $target_item['parent_mid']) { - $parent_item = $target_item; - $top_level_post = true; - } - else { - - // fetch the parent item - $r = q("SELECT * from item where id = %d order by id asc", - intval($target_item['parent']) - ); - - if (! $r) { - return; - } - - xchan_query($r); - $r = fetch_post_tags($r); - - $parent_item = array_shift($r); - $top_level_post = false; - $thread_is_public = ((intval($parent_item['item_private'])) ? false : true) ; - } - - // avoid looping of discover items 12/4/2014 - - if ($sys && $parent_item['uid'] == $sys['channel_id']) { - return; - } - - $m = get_iconfig($target_item,'activitypub','signed_data'); - // Re-use existing signature unless the activity type changed to a Tombstone, which won't verify. - if ($m && (! intval($target_item['item_deleted']))) { - self::$encoded_item = json_decode($m,true); - } - else { - self::$encoded_item = array_merge(['@context' => [ - ACTIVITYSTREAMS_JSONLD_REV, - 'https://w3id.org/security/v1', - Activity::ap_schema() - ]], Activity::encode_activity($target_item,true) - ); - self::$encoded_item['signature'] = LDSignatures::sign(self::$encoded_item,self::$channel); - } - - - logger('target_item: ' . print_r($target_item,true), LOGGER_DEBUG); - logger('encoded: ' . print_r(self::$encoded_item,true), LOGGER_DEBUG); - - // Send comments to the owner to re-deliver to everybody in the conversation - // We only do this if the item in question originated on this site. This prevents looping. - // To clarify, a site accepting a new comment is responsible for sending it to the owner for relay. - // Relaying should never be initiated on a post that arrived from elsewhere. - - // We should normally be able to rely on ITEM_ORIGIN, but start_delivery_chain() incorrectly set this - // flag on comments for an extended period. So we'll also call comment_local_origin() which looks at - // the hostname in the message_id and provides a second (fallback) opinion. - - $relay_to_owner = (((! $top_level_post) && (intval($target_item['item_origin'])) && comment_local_origin($target_item) && $cmd !== 'hyper') ? true : false); - - $uplink = false; - - // $cmd === 'relay' indicates the owner is sending it to the original recipients - // don't allow the item in the relay command to relay to owner under any circumstances, it will loop - - logger('notifier: relay_to_owner: ' . (($relay_to_owner) ? 'true' : 'false'), LOGGER_DATA, LOG_DEBUG); - logger('notifier: top_level_post: ' . (($top_level_post) ? 'true' : 'false'), LOGGER_DATA, LOG_DEBUG); - - // tag_deliver'd post which needs to be sent back to the original author - - if (($cmd === 'uplink') && intval($parent_item['item_uplink']) && (! $top_level_post)) { - logger('notifier: uplink'); - $uplink = true; - self::$packet_type = 'response'; - } - - if (($relay_to_owner || $uplink) && ($cmd !== 'relay')) { - logger('followup relay (upstream delivery)', LOGGER_DEBUG); - $sendto = ($uplink) ? $parent_item['source_xchan'] : $parent_item['owner_xchan']; - self::$recipients = [ $sendto ]; - // over-ride upstream recipients if 'replyTo' was set in the parent. - if ($parent_item['replyto'] && (! $uplink)) { - logger('replyto: over-riding owner ' . $sendto, LOGGER_DEBUG); - // unserialise is a no-op if presented with data that wasn't serialised. - $ptr = unserialise($parent_item['replyto']); - if (is_string($ptr)) { - if (ActivityStreams::is_url($sendto)) { - $sendto = $ptr; - self::$recipients = [ $sendto ]; - } - } - elseif (is_array($ptr)) { - $sendto = []; - foreach ($ptr as $rto) { - if (is_string($rto)) { - $sendto[] = $rto; - } - elseif (is_array($rto) && isset($rto['id'])) { - $sendto[] = $rto['id']; - } - } - self::$recipients = $sendto; - } - } - - logger('replyto: upstream recipients ' . print_r($sendto,true), LOGGER_DEBUG); - - - self::$private = true; - $upstream = true; - self::$packet_type = 'response'; - $is_moderated = their_perms_contains($parent_item['uid'],$sendto,'moderated'); - if ($relay_to_owner && $thread_is_public && (! $is_moderated) && (! is_group($parent_item['uid']))) { - if (get_pconfig($target_item['uid'],'system','hyperdrive',true)) { - Run::Summon([ 'Notifier' , 'hyper', $item_id ]); - } - } - - } - else { - if ($cmd === 'relay') { - logger('owner relay (downstream delivery)'); - } - else { - logger('normal (downstream) distribution', LOGGER_DEBUG); - } - $upstream = false; - - if ($parent_item && $parent_item['item_private'] !== $target_item['item_private']) { - - logger('parent_item: ' . $parent_item['id'] . ' item_private: ' . $parent_item['item_private']); - logger('target_item: ' . $target_item['id'] . ' item_private: ' . $target_item['item_private']); - - logger('conversation privacy mismatch - downstream delivery prevented'); - return; - } - - // if our parent is a tag_delivery recipient, uplink to the original author causing - // a delivery fork. - - if (($parent_item) && intval($parent_item['item_uplink']) && (! $top_level_post) && ($cmd !== 'uplink')) { - // don't uplink a relayed post to the relay owner - if ($parent_item['source_xchan'] !== $parent_item['owner_xchan']) { - logger('notifier: uplinking this item'); - Run::Summon( [ 'Notifier','uplink',$item_id ] ); - } - } - - if ($thread_is_public && $cmd === 'hyper') { - self::$recipients = []; - $r = q("select abook_xchan, xchan_network from abook left join xchan on abook_xchan = xchan_hash where abook_channel = %d and abook_self = 0 and abook_pending = 0 and abook_archived = 0 and not abook_xchan in ( '%s', '%s', '%s' ) ", - intval($target_item['uid']), - dbesc($target_item['author_xchan']), - dbesc($target_item['owner_xchan']), - dbesc($target_item['source_xchan']) - ); - if ($r) { - foreach ($r as $rv) { - self::$recipients[] = $rv['abook_xchan']; - } - } - self::$private = false; - } - else { - self::$private = false; - self::$recipients = collect_recipients($parent_item,self::$private); - } - - // @FIXME add any additional recipients such as mentions, etc. - - if ($top_level_post) { - // remove clones who will receive the post via sync - self::$recipients = array_values(array_diff(self::$recipients, [ $target_item['owner_xchan'] ])); - } - - // don't send deletions onward for other people's stuff - - if (intval($target_item['item_deleted']) && (! intval($target_item['item_wall']))) { - logger('notifier: ignoring delete notification for non-wall item', LOGGER_NORMAL, LOG_NOTICE); - return; - } - } - } - - // Generic delivery section, we have an encoded item and recipients - // Now start the delivery process - - logger('encoded item: ' . print_r(self::$encoded_item,true), LOGGER_DATA, LOG_DEBUG); +class Notifier +{ + + public static $deliveries = []; + public static $recipients = []; + public static $env_recips = []; + public static $packet_type = 'activity'; + public static $encoding = 'activitystreams'; + public static $encoded_item = null; + public static $channel = null; + public static $private = false; + + public static function run($argc, $argv) + { + + if ($argc < 3) { + return; + } + + logger('notifier: invoked: ' . print_r($argv, true), LOGGER_DEBUG, LOG_INFO); + + $cmd = $argv[1]; + + $item_id = $argv[2]; + + if (! $item_id) { + return; + } + + self::$deliveries = []; + self::$recipients = []; + self::$env_recips = []; + self::$packet_type = 'activity'; + self::$encoding = 'activitystreams'; + self::$encoded_item = null; + self::$channel = null; + self::$private = false; + + $sys = get_sys_channel(); + + $top_level = false; + + $url_recipients = []; + $normal_mode = true; + + if ($cmd === 'request') { + $xchan = $argv[3]; + if ($argc < 5) { + return; + } + + self::$channel = channelx_by_n($item_id); + + self::$private = true; + self::$recipients[] = $xchan; + self::$packet_type = 'request'; + self::$encoded_item = [ 'message_id' => $argv[4] ]; + self::$encoding = 'zot'; + $normal_mode = false; + } elseif ($cmd === 'keychange') { + self::$channel = channelx_by_n($item_id); + $r = q( + "select abook_xchan from abook where abook_channel = %d", + intval($item_id) + ); + if ($r) { + foreach ($r as $rr) { + self::$recipients[] = $rr['abook_xchan']; + } + } + self::$private = false; + self::$packet_type = 'keychange'; + self::$encoded_item = get_pconfig(self::$channel['channel_id'], 'system', 'keychange'); + self::$encoding = 'zot'; + $normal_mode = false; + } elseif (in_array($cmd, [ 'permissions_update', 'permissions_reject', 'permissions_accept', 'permissions_create' ])) { + // Get the (single) recipient + + $r = q( + "select * from abook left join xchan on abook_xchan = xchan_hash where abook_id = %d and abook_self = 0", + intval($item_id) + ); + if ($r) { + $recip = array_shift($r); + $uid = $recip['abook_channel']; + // Get the sender + self::$channel = channelx_by_n($uid); + if (self::$channel) { + $perm_update = [ 'sender' => self::$channel, 'recipient' => $recip, 'success' => false, 'deliveries' => '' ]; + + switch ($cmd) { + case 'permissions_create': + ActivityPub::permissions_create($perm_update); + break; + case 'permissions_accept': + ActivityPub::permissions_accept($perm_update); + break; + case 'permissions_update': + ActivityPub::permissions_update($perm_update); + break; + + default: + break; + } + if (! $perm_update['success']) { + call_hooks($cmd, $perm_update); + } + + if ($perm_update['success']) { + if ($perm_update['deliveries']) { + self::$deliveries[] = $perm_update['deliveries']; + do_delivery(self::$deliveries); + } + return; + } else { + self::$recipients[] = $recip['abook_xchan']; + self::$private = false; + self::$packet_type = 'refresh'; + self::$env_recips = [ $recip['xchan_hash'] ]; + } + } + } + } elseif ($cmd === 'refresh_all') { + logger('notifier: refresh_all: ' . $item_id); + + self::$channel = channelx_by_n($item_id, true); + $r = q( + "select abook_xchan from abook where abook_channel = %d", + intval($item_id) + ); + if ($r) { + foreach ($r as $rr) { + self::$recipients[] = $rr['abook_xchan']; + } + } + self::$recipients[] = self::$channel['channel_hash']; + self::$private = false; + self::$packet_type = 'refresh'; + } elseif ($cmd === 'purge') { + $xchan = $argv[3]; + logger('notifier: purge: ' . $item_id . ' => ' . $xchan); + if (! $xchan) { + return; + } + + self::$channel = channelx_by_n($item_id, true); + self::$recipients = [ $xchan ]; + self::$private = true; + self::$packet_type = 'purge'; + } elseif ($cmd === 'purge_all') { + logger('notifier: purge_all: ' . $item_id); + self::$channel = channelx_by_n($item_id, true); + + self::$recipients = []; + $r = q( + "select abook_xchan from abook where abook_channel = %d and abook_self = 0", + intval($item_id) + ); + if (! $r) { + return; + } + foreach ($r as $rr) { + self::$recipients[] = $rr['abook_xchan']; + } + + self::$private = false; + self::$packet_type = 'purge'; + } else { + // Normal items + + // Fetch the target item + + $r = q( + "SELECT * FROM item WHERE id = %d and parent != 0 LIMIT 1", + intval($item_id) + ); + + if (! $r) { + return; + } + + xchan_query($r); + $r = fetch_post_tags($r); + + $target_item = array_shift($r); + + if ($target_item['author']['xchan_network'] === 'anon') { + logger('notifier: target item author is not a fetchable actor', LOGGER_DEBUG); + return; + } + + $deleted_item = false; + + if (intval($target_item['item_deleted'])) { + logger('notifier: target item ITEM_DELETED', LOGGER_DEBUG); + $deleted_item = true; + } + + if (! in_array(intval($target_item['item_type']), [ ITEM_TYPE_POST, ITEM_TYPE_MAIL ])) { + if (intval($target_item['item_type'] == ITEM_TYPE_CUSTOM)) { + $hookinfo=[ + 'targetitem' => $target_item, + 'deliver' => false + ]; + + call_hooks('customitem_deliver', $hookinfo); + } + + if (! $hookinfo['deliver']) { + logger('notifier: target item not forwardable: type ' . $target_item['item_type'], LOGGER_DEBUG); + return; + } + } + + // Check for non published items, but allow an exclusion for transmitting hidden file activities + + if (intval($target_item['item_unpublished']) || intval($target_item['item_delayed']) || + intval($target_item['item_blocked']) || + ( intval($target_item['item_hidden']) && ($target_item['obj_type'] !== ACTIVITY_OBJ_FILE))) { + logger('notifier: target item not published, so not forwardable', LOGGER_DEBUG); + return; + } + + if (in_array($target_item['verb'], [ ACTIVITY_FOLLOW, ACTIVITY_IGNORE ])) { + logger('not fowarding follow|unfollow->note activity'); + return; + } + + $s = q( + "select * from channel left join xchan on channel_hash = xchan_hash where channel_id = %d limit 1", + intval($target_item['uid']) + ); + if ($s) { + self::$channel = array_shift($s); + } + + if (self::$channel['channel_hash'] !== $target_item['author_xchan'] && self::$channel['channel_hash'] !== $target_item['owner_xchan']) { + logger("notifier: Sending channel is not owner {$target_item['owner_xchan']} or author {$target_item['author_xchan']}", LOGGER_NORMAL, LOG_WARNING); + return; + } + + $thread_is_public = false; + + if ($target_item['mid'] === $target_item['parent_mid']) { + $parent_item = $target_item; + $top_level_post = true; + } else { + // fetch the parent item + $r = q( + "SELECT * from item where id = %d order by id asc", + intval($target_item['parent']) + ); + + if (! $r) { + return; + } + + xchan_query($r); + $r = fetch_post_tags($r); + + $parent_item = array_shift($r); + $top_level_post = false; + $thread_is_public = ((intval($parent_item['item_private'])) ? false : true) ; + } + + // avoid looping of discover items 12/4/2014 + + if ($sys && $parent_item['uid'] == $sys['channel_id']) { + return; + } + + $m = get_iconfig($target_item, 'activitypub', 'signed_data'); + // Re-use existing signature unless the activity type changed to a Tombstone, which won't verify. + if ($m && (! intval($target_item['item_deleted']))) { + self::$encoded_item = json_decode($m, true); + } else { + self::$encoded_item = array_merge(['@context' => [ + ACTIVITYSTREAMS_JSONLD_REV, + 'https://w3id.org/security/v1', + Activity::ap_schema() + ]], Activity::encode_activity($target_item, true)); + self::$encoded_item['signature'] = LDSignatures::sign(self::$encoded_item, self::$channel); + } + + + logger('target_item: ' . print_r($target_item, true), LOGGER_DEBUG); + logger('encoded: ' . print_r(self::$encoded_item, true), LOGGER_DEBUG); + + // Send comments to the owner to re-deliver to everybody in the conversation + // We only do this if the item in question originated on this site. This prevents looping. + // To clarify, a site accepting a new comment is responsible for sending it to the owner for relay. + // Relaying should never be initiated on a post that arrived from elsewhere. + + // We should normally be able to rely on ITEM_ORIGIN, but start_delivery_chain() incorrectly set this + // flag on comments for an extended period. So we'll also call comment_local_origin() which looks at + // the hostname in the message_id and provides a second (fallback) opinion. + + $relay_to_owner = (((! $top_level_post) && (intval($target_item['item_origin'])) && comment_local_origin($target_item) && $cmd !== 'hyper') ? true : false); + + $uplink = false; + + // $cmd === 'relay' indicates the owner is sending it to the original recipients + // don't allow the item in the relay command to relay to owner under any circumstances, it will loop + + logger('notifier: relay_to_owner: ' . (($relay_to_owner) ? 'true' : 'false'), LOGGER_DATA, LOG_DEBUG); + logger('notifier: top_level_post: ' . (($top_level_post) ? 'true' : 'false'), LOGGER_DATA, LOG_DEBUG); + + // tag_deliver'd post which needs to be sent back to the original author + + if (($cmd === 'uplink') && intval($parent_item['item_uplink']) && (! $top_level_post)) { + logger('notifier: uplink'); + $uplink = true; + self::$packet_type = 'response'; + } + + if (($relay_to_owner || $uplink) && ($cmd !== 'relay')) { + logger('followup relay (upstream delivery)', LOGGER_DEBUG); + $sendto = ($uplink) ? $parent_item['source_xchan'] : $parent_item['owner_xchan']; + self::$recipients = [ $sendto ]; + // over-ride upstream recipients if 'replyTo' was set in the parent. + if ($parent_item['replyto'] && (! $uplink)) { + logger('replyto: over-riding owner ' . $sendto, LOGGER_DEBUG); + // unserialise is a no-op if presented with data that wasn't serialised. + $ptr = unserialise($parent_item['replyto']); + if (is_string($ptr)) { + if (ActivityStreams::is_url($sendto)) { + $sendto = $ptr; + self::$recipients = [ $sendto ]; + } + } elseif (is_array($ptr)) { + $sendto = []; + foreach ($ptr as $rto) { + if (is_string($rto)) { + $sendto[] = $rto; + } elseif (is_array($rto) && isset($rto['id'])) { + $sendto[] = $rto['id']; + } + } + self::$recipients = $sendto; + } + } + + logger('replyto: upstream recipients ' . print_r($sendto, true), LOGGER_DEBUG); + + + self::$private = true; + $upstream = true; + self::$packet_type = 'response'; + $is_moderated = their_perms_contains($parent_item['uid'], $sendto, 'moderated'); + if ($relay_to_owner && $thread_is_public && (! $is_moderated) && (! is_group($parent_item['uid']))) { + if (get_pconfig($target_item['uid'], 'system', 'hyperdrive', true)) { + Run::Summon([ 'Notifier' , 'hyper', $item_id ]); + } + } + } else { + if ($cmd === 'relay') { + logger('owner relay (downstream delivery)'); + } else { + logger('normal (downstream) distribution', LOGGER_DEBUG); + } + $upstream = false; + + if ($parent_item && $parent_item['item_private'] !== $target_item['item_private']) { + logger('parent_item: ' . $parent_item['id'] . ' item_private: ' . $parent_item['item_private']); + logger('target_item: ' . $target_item['id'] . ' item_private: ' . $target_item['item_private']); + + logger('conversation privacy mismatch - downstream delivery prevented'); + return; + } + + // if our parent is a tag_delivery recipient, uplink to the original author causing + // a delivery fork. + + if (($parent_item) && intval($parent_item['item_uplink']) && (! $top_level_post) && ($cmd !== 'uplink')) { + // don't uplink a relayed post to the relay owner + if ($parent_item['source_xchan'] !== $parent_item['owner_xchan']) { + logger('notifier: uplinking this item'); + Run::Summon([ 'Notifier','uplink',$item_id ]); + } + } + + if ($thread_is_public && $cmd === 'hyper') { + self::$recipients = []; + $r = q( + "select abook_xchan, xchan_network from abook left join xchan on abook_xchan = xchan_hash where abook_channel = %d and abook_self = 0 and abook_pending = 0 and abook_archived = 0 and not abook_xchan in ( '%s', '%s', '%s' ) ", + intval($target_item['uid']), + dbesc($target_item['author_xchan']), + dbesc($target_item['owner_xchan']), + dbesc($target_item['source_xchan']) + ); + if ($r) { + foreach ($r as $rv) { + self::$recipients[] = $rv['abook_xchan']; + } + } + self::$private = false; + } else { + self::$private = false; + self::$recipients = collect_recipients($parent_item, self::$private); + } + + // @FIXME add any additional recipients such as mentions, etc. + + if ($top_level_post) { + // remove clones who will receive the post via sync + self::$recipients = array_values(array_diff(self::$recipients, [ $target_item['owner_xchan'] ])); + } + + // don't send deletions onward for other people's stuff + + if (intval($target_item['item_deleted']) && (! intval($target_item['item_wall']))) { + logger('notifier: ignoring delete notification for non-wall item', LOGGER_NORMAL, LOG_NOTICE); + return; + } + } + } + + // Generic delivery section, we have an encoded item and recipients + // Now start the delivery process + + logger('encoded item: ' . print_r(self::$encoded_item, true), LOGGER_DATA, LOG_DEBUG); // This addresses an issue that crossposting addons weren't being called if the sender had no friends // and only wanted to crosspost. @@ -524,313 +505,310 @@ class Notifier { return; } - // logger('recipients: ' . print_r(self::$recipients,true), LOGGER_NORMAL, LOG_DEBUG); + // logger('recipients: ' . print_r(self::$recipients,true), LOGGER_NORMAL, LOG_DEBUG); - if (! count(self::$env_recips)) { - self::$env_recips = ((self::$private) ? [] : null); - } + if (! count(self::$env_recips)) { + self::$env_recips = ((self::$private) ? [] : null); + } - $recip_list = []; + $recip_list = []; if (self::$recipients) { - $details = q("select xchan_hash, xchan_network, xchan_addr, xchan_guid, xchan_guid_sig from xchan - where xchan_hash in (" . protect_sprintf(implode(',',self::$recipients)) . ")"); + $details = q("select xchan_hash, xchan_network, xchan_addr, xchan_guid, xchan_guid_sig from xchan + where xchan_hash in (" . protect_sprintf(implode(',', self::$recipients)) . ")"); } else { $details = []; } - if ($details) { - foreach ($details as $d) { - $recip_list[] = $d['xchan_addr'] . ' (' . $d['xchan_hash'] . ')'; - if (self::$private) { - self::$env_recips[] = $d['xchan_hash']; - } - } - } + if ($details) { + foreach ($details as $d) { + $recip_list[] = $d['xchan_addr'] . ' (' . $d['xchan_hash'] . ')'; + if (self::$private) { + self::$env_recips[] = $d['xchan_hash']; + } + } + } - $narr = [ - 'channel' => self::$channel, - 'upstream' => $upstream, - 'env_recips' => self::$env_recips, - 'recipients' => self::$recipients, - 'item' => $item, - 'target_item' => $target_item, - 'parent_item' => $parent_item, - 'top_level_post' => $top_level_post, - 'private' => self::$private, - 'relay_to_owner' => $relay_to_owner, - 'uplink' => $uplink, - 'cmd' => $cmd, - 'single' => (($cmd === 'single_activity') ? true : false), - 'request' => $request, - 'normal_mode' => $normal_mode, - 'packet_type' => self::$packet_type, - 'queued' => [] - ]; + $narr = [ + 'channel' => self::$channel, + 'upstream' => $upstream, + 'env_recips' => self::$env_recips, + 'recipients' => self::$recipients, + 'item' => $item, + 'target_item' => $target_item, + 'parent_item' => $parent_item, + 'top_level_post' => $top_level_post, + 'private' => self::$private, + 'relay_to_owner' => $relay_to_owner, + 'uplink' => $uplink, + 'cmd' => $cmd, + 'single' => (($cmd === 'single_activity') ? true : false), + 'request' => $request, + 'normal_mode' => $normal_mode, + 'packet_type' => self::$packet_type, + 'queued' => [] + ]; - call_hooks('notifier_process', $narr); - if ($narr['queued']) { - foreach ($narr['queued'] as $pq) { - self::$deliveries[] = $pq; - } - } + call_hooks('notifier_process', $narr); + if ($narr['queued']) { + foreach ($narr['queued'] as $pq) { + self::$deliveries[] = $pq; + } + } - // notifier_process can alter the recipient list + // notifier_process can alter the recipient list - self::$recipients = $narr['recipients']; - self::$env_recips = $narr['env_recips']; + self::$recipients = $narr['recipients']; + self::$env_recips = $narr['env_recips']; - if ((self::$private) && (! self::$env_recips)) { - // shouldn't happen - logger('private message with no envelope recipients.' . print_r($argv,true), LOGGER_NORMAL, LOG_NOTICE); - return; - } - - logger('notifier: recipients (may be delivered to more if public): ' . print_r($recip_list,true), LOGGER_DEBUG); - - // Now we have collected recipients (except for external mentions, @FIXME) - // Let's reduce this to a set of hubs; checking that the site is not dead. + if ((self::$private) && (! self::$env_recips)) { + // shouldn't happen + logger('private message with no envelope recipients.' . print_r($argv, true), LOGGER_NORMAL, LOG_NOTICE); + return; + } + + logger('notifier: recipients (may be delivered to more if public): ' . print_r($recip_list, true), LOGGER_DEBUG); + + // Now we have collected recipients (except for external mentions, @FIXME) + // Let's reduce this to a set of hubs; checking that the site is not dead. if (self::$recipients) { - $hubs = q("select hubloc.*, site.site_crypto, site.site_flags from hubloc left join site on site_url = hubloc_url - where hubloc_hash in (" . protect_sprintf(implode(',',self::$recipients)) . ") - and hubloc_error = 0 and hubloc_deleted = 0 " - ); + $hubs = q("select hubloc.*, site.site_crypto, site.site_flags from hubloc left join site on site_url = hubloc_url + where hubloc_hash in (" . protect_sprintf(implode(',', self::$recipients)) . ") + and hubloc_error = 0 and hubloc_deleted = 0 "); } else { $hubs = []; } - - // public posts won't make it to the local public stream unless there's a recipient on this site. - // This code block sees if it's a public post and localhost is missing, and if so adds an entry for the local sys channel to the $hubs list - - if (! self::$private) { - $found_localhost = false; - if ($hubs) { - foreach ($hubs as $h) { - if ($h['hubloc_url'] === z_root()) { - $found_localhost = true; - break; - } - } - } - if (! $found_localhost) { - $localhub = q("select hubloc.*, site.site_crypto, site.site_flags, site.site_dead from hubloc + // public posts won't make it to the local public stream unless there's a recipient on this site. + // This code block sees if it's a public post and localhost is missing, and if so adds an entry for the local sys channel to the $hubs list + + if (! self::$private) { + $found_localhost = false; + if ($hubs) { + foreach ($hubs as $h) { + if ($h['hubloc_url'] === z_root()) { + $found_localhost = true; + break; + } + } + } + if (! $found_localhost) { + $localhub = q( + "select hubloc.*, site.site_crypto, site.site_flags, site.site_dead from hubloc left join site on site_url = hubloc_url where hubloc_id_url = '%s' and hubloc_error = 0 and hubloc_deleted = 0 ", - dbesc(z_root() . '/channel/sys') - ); - if ($localhub) { - $hubs = array_merge($hubs,$localhub); - } - } - } + dbesc(z_root() . '/channel/sys') + ); + if ($localhub) { + $hubs = array_merge($hubs, $localhub); + } + } + } - if (! $hubs) { - logger('notifier: no hubs', LOGGER_NORMAL, LOG_NOTICE); - return; - } + if (! $hubs) { + logger('notifier: no hubs', LOGGER_NORMAL, LOG_NOTICE); + return; + } - /** - * Reduce the hubs to those that are unique. For zot hubs, we need to verify uniqueness by the sitekey, - * since it may have been a re-install which has not yet been detected and pruned. - * For other networks which don't have or require sitekeys, we'll have to use the URL - */ + /** + * Reduce the hubs to those that are unique. For zot hubs, we need to verify uniqueness by the sitekey, + * since it may have been a re-install which has not yet been detected and pruned. + * For other networks which don't have or require sitekeys, we'll have to use the URL + */ - $hublist = []; // this provides an easily printable list for the logs - $dhubs = []; // delivery hubs where we store our resulting unique array - $keys = []; // array of keys to check uniquness for zot hubs - $urls = []; // array of urls to check uniqueness of hubs from other networks - $hub_env = []; // per-hub envelope so we don't broadcast the entire envelope to all - $dead = []; // known dead hubs - report them as undeliverable - - foreach ($hubs as $hub) { - if (isset($hub['site_dead']) && intval($hub['site_dead'])) { - $dead[] = $hub; - continue; - } - - if (self::$env_recips) { - foreach (self::$env_recips as $er) { - if ($hub['hubloc_hash'] === $er) { - if (! array_key_exists($hub['hubloc_site_id'], $hub_env)) { - $hub_env[$hub['hubloc_site_id']] = []; - } - $hub_env[$hub['hubloc_site_id']][] = $er; - } - } - } - + $hublist = []; // this provides an easily printable list for the logs + $dhubs = []; // delivery hubs where we store our resulting unique array + $keys = []; // array of keys to check uniquness for zot hubs + $urls = []; // array of urls to check uniqueness of hubs from other networks + $hub_env = []; // per-hub envelope so we don't broadcast the entire envelope to all + $dead = []; // known dead hubs - report them as undeliverable + + foreach ($hubs as $hub) { + if (isset($hub['site_dead']) && intval($hub['site_dead'])) { + $dead[] = $hub; + continue; + } + if (self::$env_recips) { + foreach (self::$env_recips as $er) { + if ($hub['hubloc_hash'] === $er) { + if (! array_key_exists($hub['hubloc_site_id'], $hub_env)) { + $hub_env[$hub['hubloc_site_id']] = []; + } + $hub_env[$hub['hubloc_site_id']][] = $er; + } + } + } + if (in_array($hub['hubloc_network'],['nomad','zot6'])) { - if (! in_array($hub['hubloc_sitekey'],$keys)) { - $hublist[] = $hub['hubloc_host'] . ' ' . $hub['hubloc_network']; - $dhubs[] = $hub; - $keys[] = $hub['hubloc_sitekey']; - } - } - else { - if (! in_array($hub['hubloc_url'],$urls)) { - $hublist[] = $hub['hubloc_host'] . ' ' . $hub['hubloc_network']; - $dhubs[] = $hub; - $urls[] = $hub['hubloc_url']; - } - } - } + if (! in_array($hub['hubloc_sitekey'], $keys)) { + $hublist[] = $hub['hubloc_host'] . ' ' . $hub['hubloc_network']; + $dhubs[] = $hub; + $keys[] = $hub['hubloc_sitekey']; + } + } else { + if (! in_array($hub['hubloc_url'], $urls)) { + $hublist[] = $hub['hubloc_host'] . ' ' . $hub['hubloc_network']; + $dhubs[] = $hub; + $urls[] = $hub['hubloc_url']; + } + } + } - logger('notifier: will notify/deliver to these hubs: ' . print_r($hublist,true), LOGGER_DEBUG, LOG_DEBUG); + logger('notifier: will notify/deliver to these hubs: ' . print_r($hublist, true), LOGGER_DEBUG, LOG_DEBUG); - foreach ($dhubs as $hub) { - - logger('notifier_hub: ' . $hub['hubloc_url'],LOGGER_DEBUG, LOG_DEBUG); + foreach ($dhubs as $hub) { + logger('notifier_hub: ' . $hub['hubloc_url'], LOGGER_DEBUG, LOG_DEBUG); - // deliver to any non-zot networks + // deliver to any non-zot networks - if (! in_array($hub['hubloc_network'],['nomad','zot6'])) { - $narr = [ - 'channel' => self::$channel, - 'upstream' => $upstream, - 'env_recips' => self::$env_recips, - 'recipients' => self::$recipients, - 'item' => $item, - 'target_item' => $target_item, - 'parent_item' => $parent_item, - 'hub' => $hub, - 'top_level_post' => $top_level_post, - 'private' => self::$private, - 'relay_to_owner' => $relay_to_owner, - 'uplink' => $uplink, - 'cmd' => $cmd, - 'single' => (($cmd === 'single_activity') ? true : false), - 'request' => $request, - 'normal_mode' => $normal_mode, - 'packet_type' => self::$packet_type, - 'queued' => [] - ]; + if (! in_array($hub['hubloc_network'], ['zot6', 'nomad' ])) { + $narr = [ + 'channel' => self::$channel, + 'upstream' => $upstream, + 'env_recips' => self::$env_recips, + 'recipients' => self::$recipients, + 'item' => $item, + 'target_item' => $target_item, + 'parent_item' => $parent_item, + 'hub' => $hub, + 'top_level_post' => $top_level_post, + 'private' => self::$private, + 'relay_to_owner' => $relay_to_owner, + 'uplink' => $uplink, + 'cmd' => $cmd, + 'single' => (($cmd === 'single_activity') ? true : false), + 'request' => $request, + 'normal_mode' => $normal_mode, + 'packet_type' => self::$packet_type, + 'queued' => [] + ]; - ActivityPub::notifier_process($narr); + ActivityPub::notifier_process($narr); - call_hooks('notifier_hub',$narr); - if($narr['queued']) { - foreach($narr['queued'] as $pq) - self::$deliveries[] = $pq; - } - continue; - } + call_hooks('notifier_hub', $narr); + if ($narr['queued']) { + foreach ($narr['queued'] as $pq) { + self::$deliveries[] = $pq; + } + } + continue; + } // Single deliveries are for non-nomadic federated networks and we're essentially - // delivering only to those that have this site url in their abook_instance - // and only from within a sync operation. This means if you post from a clone, - // and a connection is connected to one of your other clones; assuming that hub - // is running it will receive a sync packet. On receipt of this sync packet it - // will invoke a delivery to those connections which are connected to just that - // hub instance. + // delivering only to those that have this site url in their abook_instance + // and only from within a sync operation. This means if you post from a clone, + // and a connection is connected to one of your other clones; assuming that hub + // is running it will receive a sync packet. On receipt of this sync packet it + // will invoke a delivery to those connections which are connected to just that + // hub instance. - if ($cmd === 'single_activity') { - continue; - } + if ($cmd === 'single_activity') { + continue; + } - // default: nomad or zot protocol + // default: zot or nomad protocol - // Prevent zot6 delivery of group comment boosts, which are not required for conversational platforms. - // ActivityPub conversational platforms may wish to filter these if they don't want or require them. - // We will assume here that if $target_item exists and has a verb that it is an actual item structure - // so we won't need to check the existence of the other item fields prior to evaluation. + // Prevent zot6/Nomad delivery of group comment boosts, which are not required for conversational platforms. + // ActivityPub conversational platforms may wish to filter these if they don't want or require them. + // We will assume here that if $target_item exists and has a verb that it is an actual item structure + // so we won't need to check the existence of the other item fields prior to evaluation. - // This shouldn't produce false positives on comment boosts that were generated on other platforms - // because we won't be delivering them. - - if (isset($target_item) && isset($target_item['verb']) && $target_item['verb'] === 'Announce' && $target_item['author_xchan'] === $target_item['owner_xchan'] && ! intval($target_item['item_thread_top'])) { - continue; - } + // This shouldn't produce false positives on comment boosts that were generated on other platforms + // because we won't be delivering them. + + if (isset($target_item) && isset($target_item['verb']) && $target_item['verb'] === 'Announce' && $target_item['author_xchan'] === $target_item['owner_xchan'] && ! intval($target_item['item_thread_top'])) { + continue; + } - $hash = new_uuid(); + $hash = new_uuid(); - $env = (($hub_env && $hub_env[$hub['hubloc_site_id']]) ? $hub_env[$hub['hubloc_site_id']] : ''); - if ((self::$private) && (! $env)) { - continue; - } + $env = (($hub_env && $hub_env[$hub['hubloc_site_id']]) ? $hub_env[$hub['hubloc_site_id']] : ''); + if ((self::$private) && (! $env)) { + continue; + } - $packet = Libzot::build_packet(self::$channel, self::$packet_type, $env, self::$encoded_item, self::$encoding, - ((self::$private) ? $hub['hubloc_sitekey'] : null), $hub['site_crypto']); + $packet = Libzot::build_packet( + self::$channel, + self::$packet_type, + $env, + self::$encoded_item, + self::$encoding, + ((self::$private) ? $hub['hubloc_sitekey'] : null), + $hub['site_crypto'] + ); - Queue::insert( - [ - 'hash' => $hash, - 'account_id' => self::$channel['channel_account_id'], - 'channel_id' => self::$channel['channel_id'], - 'posturl' => $hub['hubloc_callback'], - 'driver' => $hub['hubloc_network'], - 'notify' => $packet, - 'msg' => EMPTY_STR - ] - ); + Queue::insert( + [ + 'hash' => $hash, + 'account_id' => self::$channel['channel_account_id'], + 'channel_id' => self::$channel['channel_id'], + 'posturl' => $hub['hubloc_callback'], + 'driver' => $hub['hubloc_network'], + 'notify' => $packet, + 'msg' => EMPTY_STR + ] + ); - // only create delivery reports for normal undeleted items - if (is_array($target_item) && (! $target_item['item_deleted']) && (! get_config('system','disable_dreport'))) { - q("insert into dreport ( dreport_mid, dreport_site, dreport_recip, dreport_name, dreport_result, dreport_time, dreport_xchan, dreport_queue, dreport_log ) + // only create delivery reports for normal undeleted items + if (is_array($target_item) && (! $target_item['item_deleted']) && (! get_config('system', 'disable_dreport'))) { + q( + "insert into dreport ( dreport_mid, dreport_site, dreport_recip, dreport_name, dreport_result, dreport_time, dreport_xchan, dreport_queue, dreport_log ) values ( '%s', '%s','%s','%s','%s','%s','%s','%s', '%s' ) ", - dbesc($target_item['mid']), - dbesc($hub['hubloc_host']), - dbesc($hub['hubloc_host']), - dbesc($hub['hubloc_host']), - dbesc('queued'), - dbesc(datetime_convert()), - dbesc(self::$channel['channel_hash']), - dbesc($hash), - dbesc(EMPTY_STR) - ); - } + dbesc($target_item['mid']), + dbesc($hub['hubloc_host']), + dbesc($hub['hubloc_host']), + dbesc($hub['hubloc_host']), + dbesc('queued'), + dbesc(datetime_convert()), + dbesc(self::$channel['channel_hash']), + dbesc($hash), + dbesc(EMPTY_STR) + ); + } - self::$deliveries[] = $hash; - } - - if ($normal_mode) { - // This wastes a process if there are no delivery hooks configured, so check this before launching the new process - $x = q("select * from hook where hook = 'notifier_normal'"); - if ($x) { - Run::Summon( [ 'Deliver_hooks', $target_item['id'] ] ); - } - } + self::$deliveries[] = $hash; + } + + if ($normal_mode) { + // This wastes a process if there are no delivery hooks configured, so check this before launching the new process + $x = q("select * from hook where hook = 'notifier_normal'"); + if ($x) { + Run::Summon([ 'Deliver_hooks', $target_item['id'] ]); + } + } - if (self::$deliveries) { - do_delivery(self::$deliveries); - } + if (self::$deliveries) { + do_delivery(self::$deliveries); + } - if ($dead) { - foreach ($dead as $deceased) { - if (is_array($target_item) && (! $target_item['item_deleted']) && (! get_config('system','disable_dreport'))) { - q("insert into dreport ( dreport_mid, dreport_site, dreport_recip, dreport_name, dreport_result, dreport_time, dreport_xchan, dreport_queue, dreport_log ) + if ($dead) { + foreach ($dead as $deceased) { + if (is_array($target_item) && (! $target_item['item_deleted']) && (! get_config('system', 'disable_dreport'))) { + q( + "insert into dreport ( dreport_mid, dreport_site, dreport_recip, dreport_name, dreport_result, dreport_time, dreport_xchan, dreport_queue, dreport_log ) values ( '%s', '%s','%s','%s','%s','%s','%s','%s','%s' ) ", - dbesc($target_item['mid']), - dbesc($deceased['hubloc_host']), - dbesc($deceased['hubloc_host']), - dbesc($deceased['hubloc_host']), - dbesc('undeliverable/unresponsive site'), - dbesc(datetime_convert()), - dbesc(self::$channel['channel_hash']), - dbesc(new_uuid()), - dbesc(EMPTY_STR) - ); - } - } - } - - call_hooks('notifier_end',$target_item); - - logger('notifer: complete.'); - - - - - return; - - } - + dbesc($target_item['mid']), + dbesc($deceased['hubloc_host']), + dbesc($deceased['hubloc_host']), + dbesc($deceased['hubloc_host']), + dbesc('undeliverable/unresponsive site'), + dbesc(datetime_convert()), + dbesc(self::$channel['channel_hash']), + dbesc(new_uuid()), + dbesc(EMPTY_STR) + ); + } + } + } + call_hooks('notifier_end', $target_item); + logger('notifer: complete.'); + return; + } } - diff --git a/Zotlabs/Daemon/Onedirsync.php b/Zotlabs/Daemon/Onedirsync.php index 3d134bb65..16f7f9f5d 100644 --- a/Zotlabs/Daemon/Onedirsync.php +++ b/Zotlabs/Daemon/Onedirsync.php @@ -1,75 +1,87 @@ - 1) && (intval($argv[1]))) - $update_id = intval($argv[1]); + if (($argc > 1) && (intval($argv[1]))) { + $update_id = intval($argv[1]); + } - if(! $update_id) { - logger('onedirsync: no update'); - return; - } - - $r = q("select * from updates where ud_id = %d limit 1", - intval($update_id) - ); + if (! $update_id) { + logger('onedirsync: no update'); + return; + } - if(! $r) - return; - if(($r[0]['ud_flags'] & UPDATE_FLAGS_UPDATED) || (! $r[0]['ud_addr'])) - return; + $r = q( + "select * from updates where ud_id = %d limit 1", + intval($update_id) + ); - // Have we probed this channel more recently than the other directory server - // (where we received this update from) ? - // If we have, we don't need to do anything except mark any older entries updated + if (! $r) { + return; + } + if (($r[0]['ud_flags'] & UPDATE_FLAGS_UPDATED) || (! $r[0]['ud_addr'])) { + return; + } - $x = q("select * from updates where ud_addr = '%s' and ud_date > '%s' and ( ud_flags & %d ) > 0 order by ud_date desc limit 1", - dbesc($r[0]['ud_addr']), - dbesc($r[0]['ud_date']), - intval(UPDATE_FLAGS_UPDATED) - ); - if($x) { - $y = q("update updates set ud_flags = ( ud_flags | %d ) where ud_addr = '%s' and ( ud_flags & %d ) = 0 and ud_date != '%s'", - intval(UPDATE_FLAGS_UPDATED), - dbesc($r[0]['ud_addr']), - intval(UPDATE_FLAGS_UPDATED), - dbesc($x[0]['ud_date']) - ); + // Have we probed this channel more recently than the other directory server + // (where we received this update from) ? + // If we have, we don't need to do anything except mark any older entries updated + + $x = q( + "select * from updates where ud_addr = '%s' and ud_date > '%s' and ( ud_flags & %d )>0 order by ud_date desc limit 1", + dbesc($r[0]['ud_addr']), + dbesc($r[0]['ud_date']), + intval(UPDATE_FLAGS_UPDATED) + ); + if ($x) { + $y = q( + "update updates set ud_flags = ( ud_flags | %d ) where ud_addr = '%s' and ( ud_flags & %d ) = 0 and ud_date != '%s'", + intval(UPDATE_FLAGS_UPDATED), + dbesc($r[0]['ud_addr']), + intval(UPDATE_FLAGS_UPDATED), + dbesc($x[0]['ud_date']) + ); + return; + } + + // ignore doing an update if this ud_addr refers to a known dead hubloc + + $h = q( + "select * from hubloc where hubloc_addr = '%s' limit 1", + dbesc($r[0]['ud_addr']) + ); + if (($h) && ($h[0]['hubloc_status'] & HUBLOC_OFFLINE)) { + $y = q( + "update updates set ud_flags = ( ud_flags | %d ) where ud_addr = '%s' and ( ud_flags & %d ) = 0 ", + intval(UPDATE_FLAGS_UPDATED), + dbesc($r[0]['ud_addr']), + intval(UPDATE_FLAGS_UPDATED) + ); + + return; + } + + // we might have to pull this out some day, but for now update_directory_entry() + // runs zot_finger() and is kind of zot specific + + if ($h && in_array($h[0]['hubloc_network'],['nomad','zot6'])) { return; } - // ignore doing an update if this ud_addr refers to a known dead hubloc + Libzotdir::update_directory_entry($r[0]); - $h = q("select * from hubloc where hubloc_addr = '%s' limit 1", - dbesc($r[0]['ud_addr']) - ); - if(($h) && ($h[0]['hubloc_status'] & HUBLOC_OFFLINE)) { - $y = q("update updates set ud_flags = ( ud_flags | %d ) where ud_addr = '%s' and ( ud_flags & %d ) = 0 ", - intval(UPDATE_FLAGS_UPDATED), - dbesc($r[0]['ud_addr']), - intval(UPDATE_FLAGS_UPDATED) - ); - - return; - } - - // we might have to pull this out some day, but for now update_directory_entry() - // runs zot_finger() and is kind of zot specific - - if($h && in_array($h[0]['hubloc_network'],['nomad','zot6'])) - return; - - Libzotdir::update_directory_entry($r[0]); - - return; - } + return; + } } diff --git a/Zotlabs/Daemon/Onepoll.php b/Zotlabs/Daemon/Onepoll.php index 078f928cd..ece2ce0d9 100644 --- a/Zotlabs/Daemon/Onepoll.php +++ b/Zotlabs/Daemon/Onepoll.php @@ -1,4 +1,6 @@ - 1) && (intval($argv[1]))) - $contact_id = intval($argv[1]); + logger('onepoll: start'); - if(! $contact_id) { - logger('onepoll: no contact'); - return; - } + if (($argc > 1) && (intval($argv[1]))) { + $contact_id = intval($argv[1]); + } - $d = datetime_convert(); + if (! $contact_id) { + logger('onepoll: no contact'); + return; + } - $contacts = q("SELECT abook.*, xchan.*, account.* + $d = datetime_convert(); + + $contacts = q( + "SELECT abook.*, xchan.*, account.* FROM abook LEFT JOIN account on abook_account = account_id left join xchan on xchan_hash = abook_xchan where abook_id = %d and abook_pending = 0 and abook_archived = 0 and abook_blocked = 0 and abook_ignored = 0 AND (( account_flags = %d ) OR ( account_flags = %d )) limit 1", - intval($contact_id), - intval(ACCOUNT_OK), - intval(ACCOUNT_UNVERIFIED) - ); + intval($contact_id), + intval(ACCOUNT_OK), + intval(ACCOUNT_UNVERIFIED) + ); - if(! $contacts) { - logger('onepoll: abook_id not found: ' . $contact_id); - return; - } + if (! $contacts) { + logger('onepoll: abook_id not found: ' . $contact_id); + return; + } - $contact = array_shift($contacts); + $contact = array_shift($contacts); - $t = $contact['abook_updated']; + $t = $contact['abook_updated']; - $importer_uid = $contact['abook_channel']; - - $r = q("SELECT * from channel left join xchan on channel_hash = xchan_hash where channel_id = %d limit 1", - intval($importer_uid) - ); + $importer_uid = $contact['abook_channel']; - if(! $r) - return; + $r = q( + "SELECT * from channel left join xchan on channel_hash = xchan_hash where channel_id = %d limit 1", + intval($importer_uid) + ); - $importer = $r[0]; + if (! $r) { + return; + } - logger("onepoll: poll: ({$contact['id']}) IMPORTER: {$importer['xchan_name']}, CONTACT: {$contact['xchan_name']}"); + $importer = $r[0]; - $last_update = ((($contact['abook_updated'] === $contact['abook_created']) || ($contact['abook_updated'] <= NULL_DATE)) - ? datetime_convert('UTC','UTC','now - 7 days') - : datetime_convert('UTC','UTC',$contact['abook_updated'] . ' - 2 days') - ); + logger("onepoll: poll: ({$contact['id']}) IMPORTER: {$importer['xchan_name']}, CONTACT: {$contact['xchan_name']}"); - if(in_array($contact['xchan_network'],['nomad']['zot6'])) { + $last_update = ((($contact['abook_updated'] === $contact['abook_created']) || ($contact['abook_updated'] <= NULL_DATE)) + ? datetime_convert('UTC', 'UTC', 'now - 7 days') + : datetime_convert('UTC', 'UTC', $contact['abook_updated'] . ' - 2 days') + ); - // update permissions + if (in_array($contact['xchan_network'],['nomad']['zot6'])) { + // update permissions - $x = Libzot::refresh($contact,$importer); + $x = Libzot::refresh($contact, $importer); - $responded = false; - $updated = datetime_convert(); - $connected = datetime_convert(); - if(! $x) { - // mark for death by not updating abook_connected, this is caught in include/poller.php - q("update abook set abook_updated = '%s' where abook_id = %d", - dbesc($updated), - intval($contact['abook_id']) - ); - } - else { - q("update abook set abook_updated = '%s', abook_connected = '%s' where abook_id = %d", - dbesc($updated), - dbesc($connected), - intval($contact['abook_id']) - ); - $responded = true; - } + $responded = false; + $updated = datetime_convert(); + $connected = datetime_convert(); + if (! $x) { + // mark for death by not updating abook_connected, this is caught in include/poller.php + q( + "update abook set abook_updated = '%s' where abook_id = %d", + dbesc($updated), + intval($contact['abook_id']) + ); + } else { + q( + "update abook set abook_updated = '%s', abook_connected = '%s' where abook_id = %d", + dbesc($updated), + dbesc($connected), + intval($contact['abook_id']) + ); + $responded = true; + } - if (! $responded) { - return; - } - } - - $fetch_feed = true; + if (! $responded) { + return; + } + } - // They haven't given us permission to see their stream + $fetch_feed = true; - $can_view_stream = intval(get_abconfig($importer_uid,$contact['abook_xchan'],'their_perms','view_stream')); + // They haven't given us permission to see their stream - if (! $can_view_stream) { - $fetch_feed = false; - } + $can_view_stream = intval(get_abconfig($importer_uid, $contact['abook_xchan'], 'their_perms', 'view_stream')); - // we haven't given them permission to send us their stream + if (! $can_view_stream) { + $fetch_feed = false; + } - $can_send_stream = intval(get_abconfig($importer_uid,$contact['abook_xchan'],'my_perms','send_stream')); - - if (! $can_send_stream) { - $fetch_feed = false; - } + // we haven't given them permission to send us their stream - if ($contact['abook_created'] < datetime_convert('UTC','UTC', 'now - 1 week')) { - $fetch_feed = false; - } + $can_send_stream = intval(get_abconfig($importer_uid, $contact['abook_xchan'], 'my_perms', 'send_stream')); - // In previous releases there was a mechanism to fetch 'external' or public stream posts from a site - // (as opposed to a channel). This mechanism was deprecated as there is no reliable/scalable method - // for informing downstream publishers when/if the content has expired or been deleted. - // We can use the ThreadListener interface to implement this on the owner's outbox, however this is still a - // work in progress and may present scaling issues. Making this work correctly with third-party fetches is - // prohibitive as deletion requests would need to be relayed over potentially hostile networks. + if (! $can_send_stream) { + $fetch_feed = false; + } - if($fetch_feed) { - $max = intval(get_config('system','max_imported_posts',20)); - if (intval($max)) { - $cl = get_xconfig($xchan,'activitypub','collections'); - if (is_array($cl) && $cl) { - $url = ((array_key_exists('outbox',$cl)) ? $cl['outbox'] : ''); - if ($url) { - logger('fetching outbox'); - $url = $url . '?date_begin=' . urlencode($last_update); - $obj = new ASCollection($url, $importer, 0, $max); - $messages = $obj->get(); - if ($messages) { - foreach($messages as $message) { - if (is_string($message)) { - $message = Activity::fetch($message,$importer); - } - if (is_array($message)) { - $AS = new ActivityStreams($message,null,true); - if ($AS->is_valid() && is_array($AS->obj)) { - $item = Activity::decode_note($AS,true); - if ($item) { - Activity::store($importer,$contact['abook_xchan'],$AS, $item, true, true); - } - } - } - } - } - } - } - } - } + if ($contact['abook_created'] < datetime_convert('UTC', 'UTC', 'now - 1 week')) { + $fetch_feed = false; + } - // update the poco details for this connection + // In previous releases there was a mechanism to fetch 'external' or public stream posts from a site + // (as opposed to a channel). This mechanism was deprecated as there is no reliable/scalable method + // for informing downstream publishers when/if the content has expired or been deleted. + // We can use the ThreadListener interface to implement this on the owner's outbox, however this is still a + // work in progress and may present scaling issues. Making this work correctly with third-party fetches is + // prohibitive as deletion requests would need to be relayed over potentially hostile networks. - $r = q("SELECT xlink_id from xlink + if ($fetch_feed) { + $max = intval(get_config('system', 'max_imported_posts', 20)); + if (intval($max)) { + $cl = get_xconfig($xchan, 'activitypub', 'collections'); + if (is_array($cl) && $cl) { + $url = ((array_key_exists('outbox', $cl)) ? $cl['outbox'] : ''); + if ($url) { + logger('fetching outbox'); + $url = $url . '?date_begin=' . urlencode($last_update); + $obj = new ASCollection($url, $importer, 0, $max); + $messages = $obj->get(); + if ($messages) { + foreach ($messages as $message) { + if (is_string($message)) { + $message = Activity::fetch($message, $importer); + } + if (is_array($message)) { + $AS = new ActivityStreams($message, null, true); + if ($AS->is_valid() && is_array($AS->obj)) { + $item = Activity::decode_note($AS, true); + if ($item) { + Activity::store($importer, $contact['abook_xchan'], $AS, $item, true, true); + } + } + } + } + } + } + } + } + } + + // update the poco details for this connection + + $r = q( + "SELECT xlink_id from xlink where xlink_xchan = '%s' and xlink_updated > %s - INTERVAL %s and xlink_static = 0 limit 1", - intval($contact['xchan_hash']), - db_utcnow(), db_quoteinterval('7 DAY') - ); - if(! $r) { - poco_load($contact['xchan_hash'],$contact['xchan_connurl']); - } - return; - } + intval($contact['xchan_hash']), + db_utcnow(), + db_quoteinterval('7 DAY') + ); + if (! $r) { + poco_load($contact['xchan_hash'], $contact['xchan_connurl']); + } + return; + } } diff --git a/Zotlabs/Daemon/Poller.php b/Zotlabs/Daemon/Poller.php index 4b6f45f0b..6ba44d18b 100644 --- a/Zotlabs/Daemon/Poller.php +++ b/Zotlabs/Daemon/Poller.php @@ -1,79 +1,90 @@ - $maxsysload) { - logger('system: load ' . $load . ' too high. Poller deferred to next scheduled run.'); - return; - } - } + $maxsysload = intval(get_config('system', 'maxloadavg')); + if ($maxsysload < 1) { + $maxsysload = 50; + } + if (function_exists('sys_getloadavg')) { + $load = sys_getloadavg(); + if (intval($load[0]) > $maxsysload) { + logger('system: load ' . $load . ' too high. Poller deferred to next scheduled run.'); + return; + } + } - $interval = intval(get_config('system','poll_interval')); - if(! $interval) - $interval = ((get_config('system','delivery_interval') === false) ? 3 : intval(get_config('system','delivery_interval'))); + $interval = intval(get_config('system', 'poll_interval')); + if (! $interval) { + $interval = ((get_config('system', 'delivery_interval') === false) ? 3 : intval(get_config('system', 'delivery_interval'))); + } - // Check for a lockfile. If it exists, but is over an hour old, it's stale. Ignore it. - $lockfile = 'cache/poller'; - if((file_exists($lockfile)) && (filemtime($lockfile) > (time() - 3600)) - && (! get_config('system','override_poll_lockfile'))) { - logger("poller: Already running"); - return; - } - - // Create a lockfile. - file_put_contents($lockfile, EMPTY_STR); + // Check for a lockfile. If it exists, but is over an hour old, it's stale. Ignore it. + $lockfile = 'cache/poller'; + if ( + (file_exists($lockfile)) && (filemtime($lockfile) > (time() - 3600)) + && (! get_config('system', 'override_poll_lockfile')) + ) { + logger("poller: Already running"); + return; + } - logger('poller: start'); - - $manual_id = 0; - $generation = 0; + // Create a lockfile. + file_put_contents($lockfile, EMPTY_STR); - $force = false; - $restart = false; + logger('poller: start'); - if(($argc > 1) && ($argv[1] == 'force')) - $force = true; + $manual_id = 0; + $generation = 0; - if(($argc > 1) && ($argv[1] == 'restart')) { - $restart = true; - $generation = intval($argv[2]); - if(! $generation) - return; - } + $force = false; + $restart = false; - if(($argc > 1) && intval($argv[1])) { - $manual_id = intval($argv[1]); - $force = true; - } + if (($argc > 1) && ($argv[1] == 'force')) { + $force = true; + } + + if (($argc > 1) && ($argv[1] == 'restart')) { + $restart = true; + $generation = intval($argv[2]); + if (! $generation) { + return; + } + } + + if (($argc > 1) && intval($argv[1])) { + $manual_id = intval($argv[1]); + $force = true; + } - $sql_extra = (($manual_id) ? " AND abook_id = " . intval($manual_id) . " " : ""); + $sql_extra = (($manual_id) ? " AND abook_id = " . intval($manual_id) . " " : ""); - reload_plugins(); + reload_plugins(); - $d = datetime_convert(); + $d = datetime_convert(); - // Only poll from those with suitable relationships + // Only poll from those with suitable relationships -// $abandon_sql = (($abandon_days) -// ? sprintf(" AND account_lastlog > %s - INTERVAL %s ", db_utcnow(), db_quoteinterval(intval($abandon_days).' DAY')) -// : '' -// ); +// $abandon_sql = (($abandon_days) +// ? sprintf(" AND account_lastlog > %s - INTERVAL %s ", db_utcnow(), db_quoteinterval(intval($abandon_days).' DAY')) +// : '' +// ); - $abandon_sql = EMPTY_STR; - - $randfunc = db_getfunc('RAND'); - - $contacts = q("SELECT abook.abook_updated, abook.abook_connected, abook.abook_feed, + $abandon_sql = EMPTY_STR; + + $randfunc = db_getfunc('RAND'); + + $contacts = q( + "SELECT abook.abook_updated, abook.abook_connected, abook.abook_feed, abook.abook_channel, abook.abook_id, abook.abook_archived, abook.abook_pending, abook.abook_ignored, abook.abook_blocked, xchan.xchan_network, @@ -83,109 +94,115 @@ class Poller { where abook_self = 0 $sql_extra AND (( account_flags = %d ) OR ( account_flags = %d )) $abandon_sql ORDER BY $randfunc", - intval(ACCOUNT_OK), - intval(ACCOUNT_UNVERIFIED) // FIXME - ); + intval(ACCOUNT_OK), + intval(ACCOUNT_UNVERIFIED) // FIXME + ); - if($contacts) { + if ($contacts) { + foreach ($contacts as $contact) { + $update = false; - foreach($contacts as $contact) { + $t = $contact['abook_updated']; + $c = $contact['abook_connected']; - $update = false; + if (intval($contact['abook_feed'])) { + $min = service_class_fetch($contact['abook_channel'], 'minimum_feedcheck_minutes'); + if (! $min) { + $min = intval(get_config('system', 'minimum_feedcheck_minutes')); + } + if (! $min) { + $min = 60; + } + $x = datetime_convert('UTC', 'UTC', "now - $min minutes"); + if ($c < $x) { + Run::Summon([ 'Onepoll', $contact['abook_id'] ]); + if ($interval) { + @time_sleep_until(microtime(true) + (float) $interval); + } + } + continue; + } - $t = $contact['abook_updated']; - $c = $contact['abook_connected']; - - if(intval($contact['abook_feed'])) { - $min = service_class_fetch($contact['abook_channel'],'minimum_feedcheck_minutes'); - if(! $min) - $min = intval(get_config('system','minimum_feedcheck_minutes')); - if(! $min) - $min = 60; - $x = datetime_convert('UTC','UTC',"now - $min minutes"); - if($c < $x) { - Run::Summon( [ 'Onepoll', $contact['abook_id'] ] ); - if($interval) - @time_sleep_until(microtime(true) + (float) $interval); - } + if (! in_array($contact['xchan_network'],['nomad','zot6'])) { continue; } + if ($c == $t) { + if (datetime_convert('UTC', 'UTC', 'now') > datetime_convert('UTC', 'UTC', $t . " + 1 day")) { + $update = true; + } + } else { + // if we've never connected with them, start the mark for death countdown from now - if(! in_array($contact['xchan_network'],['nomad','zot6'])) - continue; + if ($c <= NULL_DATE) { + $r = q( + "update abook set abook_connected = '%s' where abook_id = %d", + dbesc(datetime_convert()), + intval($contact['abook_id']) + ); + $c = datetime_convert(); + $update = true; + } - if($c == $t) { - if(datetime_convert('UTC','UTC', 'now') > datetime_convert('UTC','UTC', $t . " + 1 day")) - $update = true; - } - else { - - // if we've never connected with them, start the mark for death countdown from now - - if($c <= NULL_DATE) { - $r = q("update abook set abook_connected = '%s' where abook_id = %d", - dbesc(datetime_convert()), - intval($contact['abook_id']) - ); - $c = datetime_convert(); - $update = true; - } + // He's dead, Jim - // He's dead, Jim + if (strcmp(datetime_convert('UTC', 'UTC', 'now'), datetime_convert('UTC', 'UTC', $c . " + 30 day")) > 0) { + $r = q( + "update abook set abook_archived = 1 where abook_id = %d", + intval($contact['abook_id']) + ); + $update = false; + continue; + } - if(strcmp(datetime_convert('UTC','UTC', 'now'),datetime_convert('UTC','UTC', $c . " + 30 day")) > 0) { - $r = q("update abook set abook_archived = 1 where abook_id = %d", - intval($contact['abook_id']) - ); - $update = false; - continue; - } + if (intval($contact['abook_archived'])) { + $update = false; + continue; + } - if(intval($contact['abook_archived'])) { - $update = false; - continue; - } + // might be dead, so maybe don't poll quite so often - // might be dead, so maybe don't poll quite so often - - // recently deceased, so keep up the regular schedule for 3 days - - if((strcmp(datetime_convert('UTC','UTC', 'now'),datetime_convert('UTC','UTC', $c . " + 3 day")) > 0) - && (strcmp(datetime_convert('UTC','UTC', 'now'),datetime_convert('UTC','UTC', $t . " + 1 day")) > 0)) - $update = true; + // recently deceased, so keep up the regular schedule for 3 days - // After that back off and put them on a morphine drip + if ( + (strcmp(datetime_convert('UTC', 'UTC', 'now'), datetime_convert('UTC', 'UTC', $c . " + 3 day")) > 0) + && (strcmp(datetime_convert('UTC', 'UTC', 'now'), datetime_convert('UTC', 'UTC', $t . " + 1 day")) > 0) + ) { + $update = true; + } - if(strcmp(datetime_convert('UTC','UTC', 'now'),datetime_convert('UTC','UTC', $t . " + 2 day")) > 0) { - $update = true; - } + // After that back off and put them on a morphine drip - } + if (strcmp(datetime_convert('UTC', 'UTC', 'now'), datetime_convert('UTC', 'UTC', $t . " + 2 day")) > 0) { + $update = true; + } + } - if(intval($contact['abook_pending']) || intval($contact['abook_archived']) || intval($contact['abook_ignored']) || intval($contact['abook_blocked'])) - continue; + if (intval($contact['abook_pending']) || intval($contact['abook_archived']) || intval($contact['abook_ignored']) || intval($contact['abook_blocked'])) { + continue; + } - if((! $update) && (! $force)) - continue; + if ((! $update) && (! $force)) { + continue; + } - Run::Summon( [ 'Onepoll',$contact['abook_id'] ] ); - if($interval) - @time_sleep_until(microtime(true) + (float) $interval); + Run::Summon([ 'Onepoll',$contact['abook_id'] ]); + if ($interval) { + @time_sleep_until(microtime(true) + (float) $interval); + } + } + } - } - } + // migrate a few photos - eventually we'll migrate them all but without killing somebody's site + // trying to do them all at once - // migrate a few photos - eventually we'll migrate them all but without killing somebody's site - // trying to do them all at once - - migrate_xchan_photos(5); + migrate_xchan_photos(5); - set_config('system','lastpoll',datetime_convert()); + set_config('system', 'lastpoll', datetime_convert()); - //All done - clear the lockfile - @unlink($lockfile); + //All done - clear the lockfile + @unlink($lockfile); - return; - } + return; + } } diff --git a/Zotlabs/Daemon/Queue.php b/Zotlabs/Daemon/Queue.php index 491fc7c3d..7490724a7 100644 --- a/Zotlabs/Daemon/Queue.php +++ b/Zotlabs/Daemon/Queue.php @@ -1,82 +1,94 @@ - 1) { + $queue_id = $argv[1]; + } else { + $queue_id = EMPTY_STR; + } - if($argc > 1) - $queue_id = $argv[1]; - else - $queue_id = EMPTY_STR; + logger('queue: start'); - logger('queue: start'); + // delete all queue items more than 3 days old + // but first mark these sites dead if we haven't heard from them in a month - // delete all queue items more than 3 days old - // but first mark these sites dead if we haven't heard from them in a month + $r = q( + "select outq_posturl from outq where outq_created < %s - INTERVAL %s", + db_utcnow(), + db_quoteinterval('3 DAY') + ); + if ($r) { + foreach ($r as $rr) { + $site_url = ''; + $h = parse_url($rr['outq_posturl']); + $desturl = $h['scheme'] . '://' . $h['host'] . (($h['port']) ? ':' . $h['port'] : ''); + q( + "update site set site_dead = 1 where site_dead = 0 and site_url = '%s' and site_update < %s - INTERVAL %s", + dbesc($desturl), + db_utcnow(), + db_quoteinterval('1 MONTH') + ); + } + } - $r = q("select outq_posturl from outq where outq_created < %s - INTERVAL %s", - db_utcnow(), db_quoteinterval('3 DAY') - ); - if($r) { - foreach($r as $rr) { - $site_url = ''; - $h = parse_url($rr['outq_posturl']); - $desturl = $h['scheme'] . '://' . $h['host'] . (($h['port']) ? ':' . $h['port'] : ''); - q("update site set site_dead = 1 where site_dead = 0 and site_url = '%s' and site_update < %s - INTERVAL %s", - dbesc($desturl), - db_utcnow(), db_quoteinterval('1 MONTH') - ); - } - } + $r = q( + "DELETE FROM outq WHERE outq_created < %s - INTERVAL %s", + db_utcnow(), + db_quoteinterval('3 DAY') + ); - $r = q("DELETE FROM outq WHERE outq_created < %s - INTERVAL %s", - db_utcnow(), db_quoteinterval('3 DAY') - ); + if ($queue_id) { + $r = q( + "SELECT * FROM outq WHERE outq_hash = '%s' LIMIT 1", + dbesc($queue_id) + ); + } else { + // For the first 12 hours we'll try to deliver every 15 minutes + // After that, we'll only attempt delivery once per hour. + // This currently only handles the default queue drivers ('zot' or '') which we will group by posturl + // so that we don't start off a thousand deliveries for a couple of dead hubs. + // The zot driver will deliver everything destined for a single hub once contact is made (*if* contact is made). + // Other drivers will have to do something different here and may need their own query. - if($queue_id) { - $r = q("SELECT * FROM outq WHERE outq_hash = '%s' LIMIT 1", - dbesc($queue_id) - ); - } - else { - - // For the first 12 hours we'll try to deliver every 15 minutes - // After that, we'll only attempt delivery once per hour. - // This currently only handles the default queue drivers ('zot' or '') which we will group by posturl - // so that we don't start off a thousand deliveries for a couple of dead hubs. - // The zot driver will deliver everything destined for a single hub once contact is made (*if* contact is made). - // Other drivers will have to do something different here and may need their own query. - - // Note: this requires some tweaking as new posts to long dead hubs once a day will keep them in the - // "every 15 minutes" category. We probably need to prioritise them when inserted into the queue - // or just prior to this query based on recent and long-term delivery history. If we have good reason to believe - // the site is permanently down, there's no reason to attempt delivery at all, or at most not more than once - // or twice a day. + // Note: this requires some tweaking as new posts to long dead hubs once a day will keep them in the + // "every 15 minutes" category. We probably need to prioritise them when inserted into the queue + // or just prior to this query based on recent and long-term delivery history. If we have good reason to believe + // the site is permanently down, there's no reason to attempt delivery at all, or at most not more than once + // or twice a day. $sqlrandfunc = db_getfunc('rand'); - - $r = q("SELECT *,$sqlrandfunc as rn FROM outq WHERE outq_delivered = 0 and outq_scheduled < %s order by rn limit 1", - db_utcnow() - ); - while ($r) { - foreach($r as $rv) { - Zlib\Queue::deliver($rv); - } - $r = q("SELECT *,$sqlrandfunc as rn FROM outq WHERE outq_delivered = 0 and outq_scheduled < %s order by rn limit 1", - db_utcnow() - ); - } - } - if(! $r) - return; - foreach($r as $rv) { - Zlib\Queue::deliver($rv); - } - } + $r = q( + "SELECT *,$sqlrandfunc as rn FROM outq WHERE outq_delivered = 0 and outq_scheduled < %s order by rn limit 1", + db_utcnow() + ); + while ($r) { + foreach ($r as $rv) { + Zlib\Queue::deliver($rv); + } + $r = q( + "SELECT *,$sqlrandfunc as rn FROM outq WHERE outq_delivered = 0 and outq_scheduled < %s order by rn limit 1", + db_utcnow() + ); + } + } + if (! $r) { + return; + } + + foreach ($r as $rv) { + Zlib\Queue::deliver($rv); + } + } } diff --git a/Zotlabs/Daemon/Run.php b/Zotlabs/Daemon/Run.php index 8f573e457..86245852a 100644 --- a/Zotlabs/Daemon/Run.php +++ b/Zotlabs/Daemon/Run.php @@ -2,74 +2,76 @@ namespace Zotlabs\Daemon; -if (array_search( __file__ , get_included_files()) === 0) { +if (array_search(__file__, get_included_files()) === 0) { + require_once('include/cli_startup.php'); + array_shift($argv); + $argc = count($argv); - require_once('include/cli_startup.php'); - array_shift($argv); - $argc = count($argv); - - if ($argc) { - Run::Release($argc,$argv); - } - return; + if ($argc) { + Run::Release($argc, $argv); + } + return; } -class Run { +class Run +{ - // These processes should be ignored by addons which enforce timeouts (e.g. queueworker) - // as it could result in corrupt data. Please add additional long running tasks to this list as they arise. - // Ideally the queueworker should probably be provided an allow list rather than a deny list as it will be easier - // to maintain. This was a quick hack to fix truncation of very large synced files when the queueworker addon is installed. - - public static $long_running = [ 'Addon', 'Channel_purge', 'Checksites', 'Content_importer', 'Convo', - 'Cron', 'Cron_daily', 'Cron_weekly', 'Delxitems', 'Expire', 'File_importer', 'Importfile' - ]; + // These processes should be ignored by addons which enforce timeouts (e.g. queueworker) + // as it could result in corrupt data. Please add additional long running tasks to this list as they arise. + // Ideally the queueworker should probably be provided an allow list rather than a deny list as it will be easier + // to maintain. This was a quick hack to fix truncation of very large synced files when the queueworker addon is installed. - static public function Summon($arr) { - if (file_exists('maintenance_lock') || file_exists('cache/maintenance_lock')) { - return; - } + public static $long_running = [ 'Addon', 'Channel_purge', 'Checksites', 'Content_importer', 'Convo', + 'Cron', 'Cron_daily', 'Cron_weekly', 'Delxitems', 'Expire', 'File_importer', 'Importfile' + ]; - $hookinfo = [ - 'argv' => $arr, - 'long_running' => self::$long_running - ]; + public static function Summon($arr) + { + if (file_exists('maintenance_lock') || file_exists('cache/maintenance_lock')) { + return; + } - call_hooks('daemon_summon', $hookinfo); + $hookinfo = [ + 'argv' => $arr, + 'long_running' => self::$long_running + ]; - $arr = $hookinfo['argv']; - $argc = count($arr); + call_hooks('daemon_summon', $hookinfo); - if ((! is_array($arr) || ($argc < 1))) { - logger("Summon handled by hook.", LOGGER_DEBUG); - return; - } + $arr = $hookinfo['argv']; + $argc = count($arr); - proc_run('php','Zotlabs/Daemon/Run.php',$arr); - } + if ((! is_array($arr) || ($argc < 1))) { + logger("Summon handled by hook.", LOGGER_DEBUG); + return; + } - static public function Release($argc,$argv) { - cli_startup(); + proc_run('php', 'Zotlabs/Daemon/Run.php', $arr); + } - $hookinfo = [ - 'argv' => $argv, - 'long_running' => self::$long_running - ]; + public static function Release($argc, $argv) + { + cli_startup(); - call_hooks('daemon_release', $hookinfo); + $hookinfo = [ + 'argv' => $argv, + 'long_running' => self::$long_running + ]; - $argv = $hookinfo['argv']; - $argc = count($argv); + call_hooks('daemon_release', $hookinfo); - if ((! is_array($argv) || ($argc < 1))) { - logger("Release handled by hook.", LOGGER_DEBUG); - return; - } + $argv = $hookinfo['argv']; + $argc = count($argv); - logger('Run: release: ' . print_r($argv,true), LOGGER_ALL,LOG_DEBUG); - $cls = '\\Zotlabs\\Daemon\\' . $argv[0]; - $cls::run($argc,$argv); - } + if ((! is_array($argv) || ($argc < 1))) { + logger("Release handled by hook.", LOGGER_DEBUG); + return; + } + + logger('Run: release: ' . print_r($argv, true), LOGGER_ALL, LOG_DEBUG); + $cls = '\\Zotlabs\\Daemon\\' . $argv[0]; + $cls::run($argc, $argv); + } } diff --git a/Zotlabs/Daemon/Thumbnail.php b/Zotlabs/Daemon/Thumbnail.php index 793ed088f..dfc00f3db 100644 --- a/Zotlabs/Daemon/Thumbnail.php +++ b/Zotlabs/Daemon/Thumbnail.php @@ -1,79 +1,85 @@ - $attach, + 'preview_style' => $preview_style, + 'preview_width' => $preview_width, + 'preview_height' => $preview_height, + 'thumbnail' => null + ]; - $p = [ - 'attach' => $attach, - 'preview_style' => $preview_style, - 'preview_width' => $preview_width, - 'preview_height' => $preview_height, - 'thumbnail' => null - ]; + /** + * @hooks thumbnail + * * \e array \b attach + * * \e int \b preview_style + * * \e int \b preview_width + * * \e int \b preview_height + * * \e string \b thumbnail + */ - /** - * @hooks thumbnail - * * \e array \b attach - * * \e int \b preview_style - * * \e int \b preview_width - * * \e int \b preview_height - * * \e string \b thumbnail - */ + call_hooks('thumbnail', $p); + if ($p['thumbnail']) { + return; + } - call_hooks('thumbnail',$p); - if ($p['thumbnail']) { - return; - } + $default_controller = null; - $default_controller = null; - - $files = glob('Zotlabs/Thumbs/*.php'); - if ($files) { - foreach ($files as $f) { - $clsname = '\\Zotlabs\\Thumbs\\' . ucfirst(basename($f,'.php')); - if (class_exists($clsname)) { - $x = new $clsname(); - if (method_exists($x,'Match')) { - $matched = $x->Match($attach['filetype']); - if ($matched) { - $x->Thumb($attach,$preview_style,$preview_width,$preview_height); - } - } - if (method_exists($x,'MatchDefault')) { - $default_matched = $x->MatchDefault(substr($attach['filetype'],0,strpos($attach['filetype'],'/'))); - if ($default_matched) { - $default_controller = $x; - } - } - } - } - } - if (($default_controller) - && ((! file_exists(dbunescbin($attach['content']) . '.thumb')) - || (filectime(dbunescbin($attach['content']) . 'thumb') < (time() - 60)))) { - $default_controller->Thumb($attach,$preview_style,$preview_width,$preview_height); - } - } + $files = glob('Zotlabs/Thumbs/*.php'); + if ($files) { + foreach ($files as $f) { + $clsname = '\\Zotlabs\\Thumbs\\' . ucfirst(basename($f, '.php')); + if (class_exists($clsname)) { + $x = new $clsname(); + if (method_exists($x, 'Match')) { + $matched = $x->Match($attach['filetype']); + if ($matched) { + $x->Thumb($attach, $preview_style, $preview_width, $preview_height); + } + } + if (method_exists($x, 'MatchDefault')) { + $default_matched = $x->MatchDefault(substr($attach['filetype'], 0, strpos($attach['filetype'], '/'))); + if ($default_matched) { + $default_controller = $x; + } + } + } + } + } + if ( + ($default_controller) + && ((! file_exists(dbunescbin($attach['content']) . '.thumb')) + || (filectime(dbunescbin($attach['content']) . 'thumb') < (time() - 60))) + ) { + $default_controller->Thumb($attach, $preview_style, $preview_width, $preview_height); + } + } } diff --git a/Zotlabs/Daemon/Xchan_photo.php b/Zotlabs/Daemon/Xchan_photo.php index 8263b4126..f943a498b 100644 --- a/Zotlabs/Daemon/Xchan_photo.php +++ b/Zotlabs/Daemon/Xchan_photo.php @@ -1,35 +1,39 @@ - $v) { - self::register($k,$file,$v); - } - } - } + return $r; + } + + public static function register_array($file, $arr) + { + if ($arr) { + foreach ($arr as $k => $v) { + self::register($k, $file, $v); + } + } + } - static public function unregister($hook,$file,$function,$version = 1,$priority = 0) { - if (is_array($function)) { - $function = serialize($function); - } - $r = q("DELETE FROM hook WHERE hook = '%s' AND file = '%s' AND fn = '%s' and priority = %d and hook_version = %d", - dbesc($hook), - dbesc($file), - dbesc($function), - intval($priority), - intval($version) - ); + public static function unregister($hook, $file, $function, $version = 1, $priority = 0) + { + if (is_array($function)) { + $function = serialize($function); + } + $r = q( + "DELETE FROM hook WHERE hook = '%s' AND file = '%s' AND fn = '%s' and priority = %d and hook_version = %d", + dbesc($hook), + dbesc($file), + dbesc($function), + intval($priority), + intval($version) + ); - return $r; - } + return $r; + } - /** - * @brief Unregister all hooks with this file component. - * - * Useful for addon upgrades where you want to clean out old interfaces. - * - * @param string $file - */ - - static public function unregister_by_file($file) { - $r = q("DELETE FROM hook WHERE file = '%s' ", - dbesc($file) - ); + /** + * @brief Unregister all hooks with this file component. + * + * Useful for addon upgrades where you want to clean out old interfaces. + * + * @param string $file + */ - return $r; - } + public static function unregister_by_file($file) + { + $r = q( + "DELETE FROM hook WHERE file = '%s' ", + dbesc($file) + ); - /** - * @brief Inserts a hook into a page request. - * - * Insert a short-lived hook into the running page request. - * Hooks are normally persistent so that they can be called - * across asynchronous processes such as delivery and poll - * processes. - * - * insert_hook lets you attach a hook callback immediately - * which will not persist beyond the life of this page request - * or the current process. - * - * @param string $hook - * name of hook to attach callback - * @param string $fn - * function name of callback handler - * @param int $version - * hook interface version, 0 uses two callback params, 1 uses one callback param - * @param int $priority - * currently not implemented in this function, would require the hook array to be resorted - */ - static public function insert($hook, $fn, $version = 0, $priority = 0) { - if (is_array($fn)) { - $fn = serialize($fn); - } + return $r; + } - if (! is_array(App::$hooks)) { - App::$hooks = []; - } + /** + * @brief Inserts a hook into a page request. + * + * Insert a short-lived hook into the running page request. + * Hooks are normally persistent so that they can be called + * across asynchronous processes such as delivery and poll + * processes. + * + * insert_hook lets you attach a hook callback immediately + * which will not persist beyond the life of this page request + * or the current process. + * + * @param string $hook + * name of hook to attach callback + * @param string $fn + * function name of callback handler + * @param int $version + * hook interface version, 0 uses two callback params, 1 uses one callback param + * @param int $priority + * currently not implemented in this function, would require the hook array to be resorted + */ + public static function insert($hook, $fn, $version = 0, $priority = 0) + { + if (is_array($fn)) { + $fn = serialize($fn); + } - if (! array_key_exists($hook, App::$hooks)) { - App::$hooks[$hook] = []; - } + if (! is_array(App::$hooks)) { + App::$hooks = []; + } - App::$hooks[$hook][] = [ '', $fn, $priority, $version ]; - } -} \ No newline at end of file + if (! array_key_exists($hook, App::$hooks)) { + App::$hooks[$hook] = []; + } + + App::$hooks[$hook][] = [ '', $fn, $priority, $version ]; + } +} diff --git a/Zotlabs/Extend/Route.php b/Zotlabs/Extend/Route.php index c93f04aac..695c0d2bb 100644 --- a/Zotlabs/Extend/Route.php +++ b/Zotlabs/Extend/Route.php @@ -2,47 +2,51 @@ namespace Zotlabs\Extend; +class Route +{ -class Route { + public static function register($file, $modname) + { + $rt = self::get(); + $rt[] = [$file, $modname]; + self::set($rt); + } - static function register($file,$modname) { - $rt = self::get(); - $rt[] = [ $file, $modname ]; - self::set($rt); - } + public static function unregister($file, $modname) + { + $rt = self::get(); + if ($rt) { + $n = []; + foreach ($rt as $r) { + if ($r[0] !== $file && $r[1] !== $modname) { + $n[] = $r; + } + } + self::set($n); + } + } - static function unregister($file,$modname) { - $rt = self::get(); - if ($rt) { - $n = []; - foreach ($rt as $r) { - if ($r[0] !== $file && $r[1] !== $modname) { - $n[] = $r; - } - } - self::set($n); - } - } + public static function unregister_by_file($file) + { + $rt = self::get(); + if ($rt) { + $n = []; + foreach ($rt as $r) { + if ($r[0] !== $file) { + $n[] = $r; + } + } + self::set($n); + } + } - static function unregister_by_file($file) { - $rt = self::get(); - if ($rt) { - $n = []; - foreach ($rt as $r) { - if ($r[0] !== $file) { - $n[] = $r; - } - } - self::set($n); - } - } + public static function get() + { + return get_config('system', 'routes', []); + } - static function get() { - return get_config('system','routes',[]); - } - - static function set($r) { - return set_config('system','routes',$r); - } + public static function set($r) + { + return set_config('system', 'routes', $r); + } } - diff --git a/Zotlabs/Extend/Widget.php b/Zotlabs/Extend/Widget.php index 44597b952..f261798aa 100644 --- a/Zotlabs/Extend/Widget.php +++ b/Zotlabs/Extend/Widget.php @@ -2,46 +2,51 @@ namespace Zotlabs\Extend; +class Widget +{ -class Widget { + public static function register($file, $widget) + { + $rt = self::get(); + $rt[] = [$file, $widget]; + self::set($rt); + } - static function register($file,$widget) { - $rt = self::get(); - $rt[] = [ $file, $widget ]; - self::set($rt); - } + public static function unregister($file, $widget) + { + $rt = self::get(); + if ($rt) { + $n = []; + foreach ($rt as $r) { + if ($r[0] !== $file && $r[1] !== $widget) { + $n[] = $r; + } + } + self::set($n); + } + } - static function unregister($file,$widget) { - $rt = self::get(); - if ($rt) { - $n = []; - foreach ($rt as $r) { - if ($r[0] !== $file && $r[1] !== $widget) { - $n[] = $r; - } - } - self::set($n); - } - } + public static function unregister_by_file($file) + { + $rt = self::get(); + if ($rt) { + $n = []; + foreach ($rt as $r) { + if ($r[0] !== $file) { + $n[] = $r; + } + } + self::set($n); + } + } - static function unregister_by_file($file) { - $rt = self::get(); - if ($rt) { - $n = []; - foreach ($rt as $r) { - if ($r[0] !== $file) { - $n[] = $r; - } - } - self::set($n); - } - } + public static function get() + { + return get_config('system', 'widgets', []); + } - static function get() { - return get_config('system','widgets',[]); - } - - static function set($r) { - return set_config('system','widgets',$r); - } + public static function set($r) + { + return set_config('system', 'widgets', $r); + } } diff --git a/Zotlabs/Identity/OAuth2Server.php b/Zotlabs/Identity/OAuth2Server.php index 9d1763dce..93628d89c 100644 --- a/Zotlabs/Identity/OAuth2Server.php +++ b/Zotlabs/Identity/OAuth2Server.php @@ -8,37 +8,38 @@ use OAuth2\Storage\Memory; use OAuth2\GrantType\ClientCredentials; use OAuth2\OpenID\GrantType\AuthorizationCode; -class OAuth2Server extends Server { +class OAuth2Server extends Server +{ - public function __construct(OAuth2Storage $storage, $config = null) { + public function __construct(OAuth2Storage $storage, $config = null) + { - if (! is_array($config)) { - $config = [ -// 'use_openid_connect' => true, - 'issuer' => System::get_site_name(), -// 'use_jwt_access_tokens' => true, -// 'enforce_state' => false - ]; - } + if (! is_array($config)) { + $config = [ +// 'use_openid_connect' => true, + 'issuer' => System::get_site_name(), +// 'use_jwt_access_tokens' => true, +// 'enforce_state' => false + ]; + } - parent::__construct($storage, $config); + parent::__construct($storage, $config); - // Add the "Client Credentials" grant type (it is the simplest of the grant types) - $this->addGrantType(new ClientCredentials($storage)); + // Add the "Client Credentials" grant type (it is the simplest of the grant types) + $this->addGrantType(new ClientCredentials($storage)); - // Add the "Authorization Code" grant type (this is where the oauth magic happens) - // Need to use OpenID\GrantType to return id_token - // (see:https://github.com/bshaffer/oauth2-server-php/issues/443) - $this->addGrantType(new AuthorizationCode($storage)); + // Add the "Authorization Code" grant type (this is where the oauth magic happens) + // Need to use OpenID\GrantType to return id_token + // (see:https://github.com/bshaffer/oauth2-server-php/issues/443) + $this->addGrantType(new AuthorizationCode($storage)); - $keyStorage = new Memory( [ - 'keys' => [ - 'public_key' => get_config('system', 'pubkey'), - 'private_key' => get_config('system', 'prvkey') - ] - ]); - - $this->addStorage($keyStorage, 'public_key'); - } + $keyStorage = new Memory([ + 'keys' => [ + 'public_key' => get_config('system', 'pubkey'), + 'private_key' => get_config('system', 'prvkey') + ] + ]); + $this->addStorage($keyStorage, 'public_key'); + } } diff --git a/Zotlabs/Identity/OAuth2Storage.php b/Zotlabs/Identity/OAuth2Storage.php index cf229b2eb..bd76a8704 100644 --- a/Zotlabs/Identity/OAuth2Storage.php +++ b/Zotlabs/Identity/OAuth2Storage.php @@ -2,8 +2,10 @@ namespace Zotlabs\Identity; +use OAuth2\Storage\Pdo; -class OAuth2Storage extends \OAuth2\Storage\Pdo { +class OAuth2Storage extends Pdo +{ /** * @param string $username @@ -38,9 +40,8 @@ class OAuth2Storage extends \OAuth2\Storage\Pdo { protected function checkPassword($user, $password) { - $x = account_verify_password($user,$password); - return((array_key_exists('channel',$x) && ! empty($x['channel'])) ? true : false); - + $x = account_verify_password($user, $password); + return((array_key_exists('channel', $x) && ! empty($x['channel'])) ? true : false); } /** @@ -50,77 +51,80 @@ class OAuth2Storage extends \OAuth2\Storage\Pdo { public function getUser($username) { - $x = channelx_by_n($username); - if (! $x) { - return false; - } + $x = channelx_by_n($username); + if (! $x) { + return false; + } - $a = q("select * from account where account_id = %d", - intval($x['channel_account_id']) - ); + $a = q( + "select * from account where account_id = %d", + intval($x['channel_account_id']) + ); - $n = explode(' ', $x['channel_name']); + $n = explode(' ', $x['channel_name']); - return( [ - 'webfinger' => channel_reddress($x), - 'portable_id' => $x['channel_hash'], - 'email' => $a[0]['account_email'], - 'username' => $x['channel_address'], - 'user_id' => $x['channel_id'], - 'name' => $x['channel_name'], - 'firstName' => ((count($n) > 1) ? $n[1] : $n[0]), - 'lastName' => ((count($n) > 2) ? $n[count($n) - 1] : ''), - 'picture' => $x['xchan_photo_l'] - ] ); + return( [ + 'webfinger' => channel_reddress($x), + 'portable_id' => $x['channel_hash'], + 'email' => $a[0]['account_email'], + 'username' => $x['channel_address'], + 'user_id' => $x['channel_id'], + 'name' => $x['channel_name'], + 'firstName' => ((count($n) > 1) ? $n[1] : $n[0]), + 'lastName' => ((count($n) > 2) ? $n[count($n) - 1] : ''), + 'picture' => $x['xchan_photo_l'] + ] ); } - public function scopeExists($scope) { + public function scopeExists($scope) + { // Report that the scope is valid even if it's not. // We will only return a very small subset no matter what. // @TODO: Truly validate the scope // see vendor/bshaffer/oauth2-server-php/src/OAuth2/Storage/ScopeInterface.php and // vendor/bshaffer/oauth2-server-php/src/OAuth2/Storage/Pdo.php // for more info. - return true; + return true; } - public function getDefaultScope($client_id=null) { + public function getDefaultScope($client_id = null) + { // Do not REQUIRE a scope // see vendor/bshaffer/oauth2-server-php/src/OAuth2/Storage/ScopeInterface.php and // for more info. - return null; + return null; } - public function getUserClaims ($user_id, $claims) { - // Populate the CLAIMS requested (if any). - // @TODO: create a more reasonable/comprehensive list. - // @TODO: present claims on the AUTHORIZATION screen + public function getUserClaims($user_id, $claims) + { + // Populate the CLAIMS requested (if any). + // @TODO: create a more reasonable/comprehensive list. + // @TODO: present claims on the AUTHORIZATION screen $userClaims = []; - $claims = explode (' ', trim($claims)); + $claims = explode(' ', trim($claims)); $validclaims = [ "name", "preferred_username", "webfinger", "portable_id", "email", "picture", "firstName", "lastName" ]; $claimsmap = [ - "webfinger" => 'webfinger', - "portable_id" => 'portable_id', - "name" => 'name', - "email" => 'email', - "preferred_username" => 'username', - "picture" => 'picture', - "given_name" => 'firstName', - "family_name" => 'lastName' - ]; + "webfinger" => 'webfinger', + "portable_id" => 'portable_id', + "name" => 'name', + "email" => 'email', + "preferred_username" => 'username', + "picture" => 'picture', + "given_name" => 'firstName', + "family_name" => 'lastName' + ]; $userinfo = $this->getUser($user_id); foreach ($validclaims as $validclaim) { - if (in_array($validclaim,$claims)) { - $claimkey = $claimsmap[$validclaim]; - $userClaims[$validclaim] = $userinfo[$claimkey]; - } - else { - $userClaims[$validclaim] = $validclaim; + if (in_array($validclaim, $claims)) { + $claimkey = $claimsmap[$validclaim]; + $userClaims[$validclaim] = $userinfo[$claimkey]; + } else { + $userClaims[$validclaim] = $validclaim; } } - $userClaims["sub"]=$user_id; - return $userClaims; + $userClaims["sub"] = $user_id; + return $userClaims; } /** @@ -163,5 +167,4 @@ class OAuth2Storage extends \OAuth2\Storage\Pdo { // if grant_types are not defined, then none are restricted return true; } - } diff --git a/Zotlabs/Import/Friendica.php b/Zotlabs/Import/Friendica.php index ec49fd6a4..2b4a181b5 100644 --- a/Zotlabs/Import/Friendica.php +++ b/Zotlabs/Import/Friendica.php @@ -1,4 +1,5 @@ data = $data; - $this->settings = $settings; - $this->extract(); - } - - function extract() { - - // channel stuff - - $channel = [ - 'channel_name' => escape_tags($this->data['user']['username']), - 'channel_address' => escape_tags($this->data['user']['nickname']), - 'channel_guid' => escape_tags($this->data['user']['guid']), - 'channel_guid_sig' => Libzot::sign($this->data['user']['guid'],$this->data['user']['prvkey']), - 'channel_hash' => Libzot::make_xchan_hash($this->data['user']['guid'],$this->data['user']['pubkey']), - 'channel_prvkey' => $this->data['user']['prvkey'], - 'channel_pubkey' => $this->data['user']['pubkey'], - 'channel_pageflags' => PAGE_NORMAL, - 'channel_expire_days' => intval($this->data['user']['expire']), - 'channel_timezone' => escape_tags($this->data['user']['timezone']), - 'channel_location' => escape_tags($this->data['user']['default-location']) - ]; - - $account_id = $this->settings['account_id']; - - $max_identities = account_service_class_fetch($account_id,'total_identities'); - - if ($max_identities !== false) { - $r = q("select channel_id from channel where channel_account_id = %d and channel_removed = 0 ", - intval($account_id) - ); - if ($r && count($r) > $max_identities) { - notice( sprintf( t('Your service plan only allows %d channels.'), $max_identities) . EOL); - return; - } - } - - // save channel or die + private $groups = null; + private $members = null; + private $contacts = null; - $channel = import_channel($channel,$this->settings['account_id'],$this->settings['sieze'],$this->settings['newname']); - if (! $channel) { - logger('no channel'); - return; - } + public function __construct($data, $settings) + { + $this->data = $data; + $this->settings = $settings; + $this->extract(); + } + + public function extract() + { + + // channel stuff + + $channel = [ + 'channel_name' => escape_tags($this->data['user']['username']), + 'channel_address' => escape_tags($this->data['user']['nickname']), + 'channel_guid' => escape_tags($this->data['user']['guid']), + 'channel_guid_sig' => Libzot::sign($this->data['user']['guid'], $this->data['user']['prvkey']), + 'channel_hash' => Libzot::make_xchan_hash($this->data['user']['guid'], $this->data['user']['pubkey']), + 'channel_prvkey' => $this->data['user']['prvkey'], + 'channel_pubkey' => $this->data['user']['pubkey'], + 'channel_pageflags' => PAGE_NORMAL, + 'channel_expire_days' => intval($this->data['user']['expire']), + 'channel_timezone' => escape_tags($this->data['user']['timezone']), + 'channel_location' => escape_tags($this->data['user']['default-location']) + ]; + + $account_id = $this->settings['account_id']; + + $max_identities = account_service_class_fetch($account_id, 'total_identities'); + + if ($max_identities !== false) { + $r = q( + "select channel_id from channel where channel_account_id = %d and channel_removed = 0 ", + intval($account_id) + ); + if ($r && count($r) > $max_identities) { + notice(sprintf(t('Your service plan only allows %d channels.'), $max_identities) . EOL); + return; + } + } + + // save channel or die - // figure out channel permission roles - - $permissions_role = 'social'; - - $pageflags = ((isset($this->data['user']['page-flags'])) ? intval($this->data['user']['page-flags']) : 0); - - if ($pageflags === 2) { - $permissions_role = 'forum'; - } - if ($pageflags === 5) { - $permissions_role = 'forum_restricted'; - } - - if ($pageflags === 0 && isset($this->data['user']['allow_gid']) && $this->data['user']['allow_gid']) { - $permissions_role = 'social_restricted'; - } - - // Friendica folks only have PERMS_AUTHED and "just me" - - $post_comments = (($pageflags === 1) ? 0 : PERMS_AUTHED); - PermissionLimits::Set(local_channel(),'post_comments',$post_comments); - - PConfig::Set($channel['channel_id'],'system','permissions_role',$permissions_role); - PConfig::Set($channel['channel_id'],'system','use_browser_location', (string) intval($this->data['user']['allow_location'])); - - // find the self contact - - $self_contact = null; - - if (isset($this->data['contact']) && is_array($this->data['contact'])) { - foreach ($this->data['contact'] as $contact) { - if (isset($contact['self']) && intval($contact['self'])) { - $self_contact = $contact; - break; - } - } - } - - if (! is_array($self_contact)) { - logger('self contact not found.'); - return; - } - - // Create a verified hub location pointing to this site. - - $r = hubloc_store_lowlevel( - [ - 'hubloc_guid' => $channel['channel_guid'], - 'hubloc_guid_sig' => $channel['channel_guid_sig'], - 'hubloc_id_url' => channel_url($channel), - 'hubloc_hash' => $channel['channel_hash'], - 'hubloc_addr' => channel_reddress($channel), - 'hubloc_primary' => 1, - 'hubloc_url' => z_root(), - 'hubloc_url_sig' => Libzot::sign(z_root(),$channel['channel_prvkey']), - 'hubloc_site_id' => Libzot::make_xchan_hash(z_root(),get_config('system','pubkey')), - 'hubloc_host' => App::get_hostname(), - 'hubloc_callback' => z_root() . '/zot', - 'hubloc_sitekey' => get_config('system','pubkey'), - 'hubloc_network' => 'nomad', - 'hubloc_updated' => datetime_convert() - ] - ); - if (! $r) { - logger('Unable to store hub location'); - } + $channel = import_channel($channel, $this->settings['account_id'], $this->settings['sieze'], $this->settings['newname']); + if (!$channel) { + logger('no channel'); + return; + } - if ($self_contact['avatar']) { - $p = z_fetch_url($self_contact['avatar'],true); - if ($p['success']) { - $h = explode("\n",$p['header']); - foreach ($h as $l) { - list($k,$v) = array_map("trim", explode(":", trim($l), 2)); - $hdrs[strtolower($k)] = $v; - } - if (array_key_exists('content-type', $hdrs)) { - $phototype = $hdrs['content-type']; - } - else { - $phototype = 'image/jpeg'; - } + // figure out channel permission roles - import_channel_photo($p['body'],$phototype,$account_id,$channel['channel_id']); - } - } + $permissions_role = 'social'; - $newuid = $channel['channel_id']; + $pageflags = ((isset($this->data['user']['page-flags'])) ? intval($this->data['user']['page-flags']) : 0); - $r = xchan_store_lowlevel( - [ - 'xchan_hash' => $channel['channel_hash'], - 'xchan_guid' => $channel['channel_guid'], - 'xchan_guid_sig' => $channel['channel_guid_sig'], - 'xchan_pubkey' => $channel['channel_pubkey'], - 'xchan_photo_mimetype' => (($photo_type) ? $photo_type : 'image/png'), - 'xchan_photo_l' => z_root() . "/photo/profile/l/{$newuid}", - 'xchan_photo_m' => z_root() . "/photo/profile/m/{$newuid}", - 'xchan_photo_s' => z_root() . "/photo/profile/s/{$newuid}", - 'xchan_addr' => channel_reddress($channel), - 'xchan_url' => channel_url($channel), - 'xchan_follow' => z_root() . '/follow?f=&url=%s', - 'xchan_connurl' => z_root() . '/poco/' . $channel['channel_address'], - 'xchan_name' => $channel['channel_name'], - 'xchan_network' => 'nomad', - 'xchan_updated' => datetime_convert(), - 'xchan_photo_date' => datetime_convert(), - 'xchan_name_date' => datetime_convert(), - 'xchan_system' => 0 - ] - ); + if ($pageflags === 2) { + $permissions_role = 'forum'; + } + if ($pageflags === 5) { + $permissions_role = 'forum_restricted'; + } - $r = profile_store_lowlevel( - [ - 'aid' => intval($channel['channel_account_id']), - 'uid' => intval($newuid), - 'profile_guid' => new_uuid(), - 'profile_name' => t('Default Profile'), - 'is_default' => 1, - 'publish' => ((isset($this->data['profile']['publish'])) ? $this->data['profile']['publish'] : 1), - 'fullname' => $channel['channel_name'], - 'photo' => z_root() . "/photo/profile/l/{$newuid}", - 'thumb' => z_root() . "/photo/profile/m/{$newuid}", - 'homepage' => ((isset($this->data['profile']['homepage'])) ? $this->data['profile']['homepage'] : EMPTY_STR), - ] - ); + if ($pageflags === 0 && isset($this->data['user']['allow_gid']) && $this->data['user']['allow_gid']) { + $permissions_role = 'social_restricted'; + } - if($role_permissions) { - $myperms = ((array_key_exists('perms_connect',$role_permissions)) ? $role_permissions['perms_connect'] : [] ); - } - else { - $x = PermissionRoles::role_perms('social'); - $myperms = $x['perms_connect']; - } + // Friendica folks only have PERMS_AUTHED and "just me" - $r = abook_store_lowlevel( - [ - 'abook_account' => intval($channel['channel_account_id']), - 'abook_channel' => intval($newuid), - 'abook_xchan' => $channel['channel_hash'], - 'abook_closeness' => 0, - 'abook_created' => datetime_convert(), - 'abook_updated' => datetime_convert(), - 'abook_self' => 1 - ] - ); + $post_comments = (($pageflags === 1) ? 0 : PERMS_AUTHED); + PermissionLimits::Set(local_channel(), 'post_comments', $post_comments); + + PConfig::Set($channel['channel_id'], 'system', 'permissions_role', $permissions_role); + PConfig::Set($channel['channel_id'], 'system', 'use_browser_location', (string)intval($this->data['user']['allow_location'])); + + // find the self contact + + $self_contact = null; + + if (isset($this->data['contact']) && is_array($this->data['contact'])) { + foreach ($this->data['contact'] as $contact) { + if (isset($contact['self']) && intval($contact['self'])) { + $self_contact = $contact; + break; + } + } + } + + if (!is_array($self_contact)) { + logger('self contact not found.'); + return; + } + + // Create a verified hub location pointing to this site. + + $r = hubloc_store_lowlevel( + [ + 'hubloc_guid' => $channel['channel_guid'], + 'hubloc_guid_sig' => $channel['channel_guid_sig'], + 'hubloc_id_url' => channel_url($channel), + 'hubloc_hash' => $channel['channel_hash'], + 'hubloc_addr' => channel_reddress($channel), + 'hubloc_primary' => 1, + 'hubloc_url' => z_root(), + 'hubloc_url_sig' => Libzot::sign(z_root(), $channel['channel_prvkey']), + 'hubloc_site_id' => Libzot::make_xchan_hash(z_root(), get_config('system', 'pubkey')), + 'hubloc_host' => App::get_hostname(), + 'hubloc_callback' => z_root() . '/zot', + 'hubloc_sitekey' => get_config('system', 'pubkey'), + 'hubloc_network' => 'nomad', + 'hubloc_updated' => datetime_convert() + ] + ); + if (!$r) { + logger('Unable to store hub location'); + } - $x = Permissions::serialise(Permissions::FilledPerms($myperms)); - set_abconfig($newuid,$channel['channel_hash'],'system','my_perms',$x); + if ($self_contact['avatar']) { + $p = z_fetch_url($self_contact['avatar'], true); + if ($p['success']) { + $h = explode("\n", $p['header']); + foreach ($h as $l) { + list($k, $v) = array_map("trim", explode(":", trim($l), 2)); + $hdrs[strtolower($k)] = $v; + } + if (array_key_exists('content-type', $hdrs)) { + $phototype = $hdrs['content-type']; + } else { + $phototype = 'image/jpeg'; + } - if(intval($channel['channel_account_id'])) { + import_channel_photo($p['body'], $phototype, $account_id, $channel['channel_id']); + } + } - // Save our permissions role so we can perhaps call it up and modify it later. + $newuid = $channel['channel_id']; - if($role_permissions) { - if(array_key_exists('online',$role_permissions)) - set_pconfig($newuid,'system','hide_presence',1-intval($role_permissions['online'])); - if(array_key_exists('perms_auto',$role_permissions)) { - $autoperms = intval($role_permissions['perms_auto']); - set_pconfig($newuid,'system','autoperms',$autoperms); - } - } + $r = xchan_store_lowlevel( + [ + 'xchan_hash' => $channel['channel_hash'], + 'xchan_guid' => $channel['channel_guid'], + 'xchan_guid_sig' => $channel['channel_guid_sig'], + 'xchan_pubkey' => $channel['channel_pubkey'], + 'xchan_photo_mimetype' => (($photo_type) ? $photo_type : 'image/png'), + 'xchan_photo_l' => z_root() . "/photo/profile/l/{$newuid}", + 'xchan_photo_m' => z_root() . "/photo/profile/m/{$newuid}", + 'xchan_photo_s' => z_root() . "/photo/profile/s/{$newuid}", + 'xchan_addr' => channel_reddress($channel), + 'xchan_url' => channel_url($channel), + 'xchan_follow' => z_root() . '/follow?f=&url=%s', + 'xchan_connurl' => z_root() . '/poco/' . $channel['channel_address'], + 'xchan_name' => $channel['channel_name'], + 'xchan_network' => 'nomad', + 'xchan_updated' => datetime_convert(), + 'xchan_photo_date' => datetime_convert(), + 'xchan_name_date' => datetime_convert(), + 'xchan_system' => 0 + ] + ); - // Create a group with yourself as a member. This allows somebody to use it - // right away as a default group for new contacts. + $r = profile_store_lowlevel( + [ + 'aid' => intval($channel['channel_account_id']), + 'uid' => intval($newuid), + 'profile_guid' => new_uuid(), + 'profile_name' => t('Default Profile'), + 'is_default' => 1, + 'publish' => ((isset($this->data['profile']['publish'])) ? $this->data['profile']['publish'] : 1), + 'fullname' => $channel['channel_name'], + 'photo' => z_root() . "/photo/profile/l/{$newuid}", + 'thumb' => z_root() . "/photo/profile/m/{$newuid}", + 'homepage' => ((isset($this->data['profile']['homepage'])) ? $this->data['profile']['homepage'] : EMPTY_STR), + ] + ); - AccessList::add($newuid, t('Friends')); - AccessList::member_add($newuid,t('Friends'),$ret['channel']['channel_hash']); + if ($role_permissions) { + $myperms = ((array_key_exists('perms_connect', $role_permissions)) ? $role_permissions['perms_connect'] : []); + } else { + $x = PermissionRoles::role_perms('social'); + $myperms = $x['perms_connect']; + } - // if our role_permissions indicate that we're using a default collection ACL, add it. - - if(is_array($role_permissions) && $role_permissions['default_collection']) { - $r = q("select hash from pgrp where uid = %d and gname = '%s' limit 1", - intval($newuid), - dbesc( t('Friends') ) - ); - if($r) { - q("update channel set channel_default_group = '%s', channel_allow_gid = '%s' where channel_id = %d", - dbesc($r[0]['hash']), - dbesc('<' . $r[0]['hash'] . '>'), - intval($newuid) - ); - } - } - - set_pconfig($channel['channel_id'],'system','photo_path', '%Y/%Y-%m'); - set_pconfig($channel['channel_id'],'system','attach_path','%Y/%Y-%m'); + $r = abook_store_lowlevel( + [ + 'abook_account' => intval($channel['channel_account_id']), + 'abook_channel' => intval($newuid), + 'abook_xchan' => $channel['channel_hash'], + 'abook_closeness' => 0, + 'abook_created' => datetime_convert(), + 'abook_updated' => datetime_convert(), + 'abook_self' => 1 + ] + ); - // auto-follow any of the hub's pre-configured channel choices. - // Only do this if it's the first channel for this account; - // otherwise it could get annoying. Don't make this list too big - // or it will impact registration time. + $x = Permissions::serialise(Permissions::FilledPerms($myperms)); + set_abconfig($newuid, $channel['channel_hash'], 'system', 'my_perms', $x); - $accts = get_config('system','auto_follow'); - if(($accts) && (! $total_identities)) { - if(! is_array($accts)) - $accts = array($accts); + if (intval($channel['channel_account_id'])) { + // Save our permissions role so we can perhaps call it up and modify it later. - foreach($accts as $acct) { - if(trim($acct)) { - $f = connect_and_sync($channel,trim($acct)); - if($f['success']) { + if ($role_permissions) { + if (array_key_exists('online', $role_permissions)) { + set_pconfig($newuid, 'system', 'hide_presence', 1 - intval($role_permissions['online'])); + } + if (array_key_exists('perms_auto', $role_permissions)) { + $autoperms = intval($role_permissions['perms_auto']); + set_pconfig($newuid, 'system', 'autoperms', $autoperms); + } + } - $can_view_stream = their_perms_contains($channel['channel_id'],$f['abook']['abook_xchan'],'view_stream'); + // Create a group with yourself as a member. This allows somebody to use it + // right away as a default group for new contacts. - // If we can view their stream, pull in some posts + AccessList::add($newuid, t('Friends')); + AccessList::member_add($newuid, t('Friends'), $ret['channel']['channel_hash']); - if(($can_view_stream) || ($f['abook']['xchan_network'] === 'rss')) { - Run::Summon([ 'Onepoll',$f['abook']['abook_id'] ]); - } - } - } - } - } + // if our role_permissions indicate that we're using a default collection ACL, add it. - call_hooks('create_identity', $newuid); - } + if (is_array($role_permissions) && $role_permissions['default_collection']) { + $r = q( + "select hash from pgrp where uid = %d and gname = '%s' limit 1", + intval($newuid), + dbesc(t('Friends')) + ); + if ($r) { + q( + "update channel set channel_default_group = '%s', channel_allow_gid = '%s' where channel_id = %d", + dbesc($r[0]['hash']), + dbesc('<' . $r[0]['hash'] . '>'), + intval($newuid) + ); + } + } - $this->groups = ((isset($this->data['group'])) ? $this->data['group'] : null); - $this->members = ((isset($this->data['group_member'])) ? $this->data['group_member'] : null); - - // import contacts - - if (isset($this->data['contact']) && is_array($this->data['contact'])) { - foreach ($this->data['contact'] as $contact) { - if (isset($contact['self']) && intval($contact['self'])) { - continue; - } - logger('connecting: ' . $contact['url'], LOGGER_DEBUG); - $result = Connect::connect($channel,(($contact['addr']) ? $contact['addr'] : $contact['url'])); - if ($result['success'] && isset($result['abook'])) { - $contact['xchan_hash'] = $result['abook']['abook_xchan']; - $this->contacts[] = $contact; - } - } - } - - // import pconfig - // it is unlikely we can make use of these unless we recongise them. - - if (isset($this->data['pconfig']) && is_array($this->data['pconfig'])) { - foreach ($this->data['pconfig'] as $pc) { - $entry = [ - 'cat' => escape_tags(str_replace('.','__',$pc['cat'])), - 'k' => escape_tags(str_replace('.','__',$pc['k'])), - 'v' => ((preg_match('|^a:[0-9]+:{.*}$|s', $pc['v'])) ? serialise(unserialize($pc['v'])) : $pc['v']), - ]; - PConfig::Set($channel['channel_id'],$entry['cat'],$entry['k'],$entry['v']); - } - } - - // The default 'Friends' group is already created and possibly populated. - // So some of the following code is redundant in that regard. - // Mostly this is used to create and populate any other groups. - - if ($this->groups) { - foreach ($this->groups as $group) { - if (! intval($group['deleted'])) { - AccessList::add($channel['channel_id'], $group['name'], intval($group['visible'])); - if ($this->members) { - foreach ($this->members as $member) { - if (intval($member['gid']) === intval(AccessList::byname($channel['channel_id'],$group['name']))) { - $contact_id = $member['contact-id']; - if ($this->contacts) { - foreach ($this->contacts as $contact) { - if (intval($contact['id']) === intval($contact_id)) { - AccessList::member_add($channel['channel_id'],$group['name'],$contact['xchan_hash']); - break; - } - } - } - } - } - } - } - } - } - - change_channel($channel['channel_id']); - notice( t('Import complete.') . EOL); - - goaway(z_root() . '/stream' ); - - } + set_pconfig($channel['channel_id'], 'system', 'photo_path', '%Y/%Y-%m'); + set_pconfig($channel['channel_id'], 'system', 'attach_path', '%Y/%Y-%m'); -} \ No newline at end of file + // auto-follow any of the hub's pre-configured channel choices. + // Only do this if it's the first channel for this account; + // otherwise it could get annoying. Don't make this list too big + // or it will impact registration time. + + $accts = get_config('system', 'auto_follow'); + if (($accts) && (!$total_identities)) { + if (!is_array($accts)) { + $accts = array($accts); + } + + foreach ($accts as $acct) { + if (trim($acct)) { + $f = connect_and_sync($channel, trim($acct)); + if ($f['success']) { + $can_view_stream = their_perms_contains($channel['channel_id'], $f['abook']['abook_xchan'], 'view_stream'); + + // If we can view their stream, pull in some posts + + if (($can_view_stream) || ($f['abook']['xchan_network'] === 'rss')) { + Run::Summon(['Onepoll', $f['abook']['abook_id']]); + } + } + } + } + } + + call_hooks('create_identity', $newuid); + } + + $this->groups = ((isset($this->data['group'])) ? $this->data['group'] : null); + $this->members = ((isset($this->data['group_member'])) ? $this->data['group_member'] : null); + + // import contacts + + if (isset($this->data['contact']) && is_array($this->data['contact'])) { + foreach ($this->data['contact'] as $contact) { + if (isset($contact['self']) && intval($contact['self'])) { + continue; + } + logger('connecting: ' . $contact['url'], LOGGER_DEBUG); + $result = Connect::connect($channel, (($contact['addr']) ? $contact['addr'] : $contact['url'])); + if ($result['success'] && isset($result['abook'])) { + $contact['xchan_hash'] = $result['abook']['abook_xchan']; + $this->contacts[] = $contact; + } + } + } + + // import pconfig + // it is unlikely we can make use of these unless we recongise them. + + if (isset($this->data['pconfig']) && is_array($this->data['pconfig'])) { + foreach ($this->data['pconfig'] as $pc) { + $entry = [ + 'cat' => escape_tags(str_replace('.', '__', $pc['cat'])), + 'k' => escape_tags(str_replace('.', '__', $pc['k'])), + 'v' => ((preg_match('|^a:[0-9]+:{.*}$|s', $pc['v'])) ? serialise(unserialize($pc['v'])) : $pc['v']), + ]; + PConfig::Set($channel['channel_id'], $entry['cat'], $entry['k'], $entry['v']); + } + } + + // The default 'Friends' group is already created and possibly populated. + // So some of the following code is redundant in that regard. + // Mostly this is used to create and populate any other groups. + + if ($this->groups) { + foreach ($this->groups as $group) { + if (!intval($group['deleted'])) { + AccessList::add($channel['channel_id'], $group['name'], intval($group['visible'])); + if ($this->members) { + foreach ($this->members as $member) { + if (intval($member['gid']) === intval(AccessList::byname($channel['channel_id'], $group['name']))) { + $contact_id = $member['contact-id']; + if ($this->contacts) { + foreach ($this->contacts as $contact) { + if (intval($contact['id']) === intval($contact_id)) { + AccessList::member_add($channel['channel_id'], $group['name'], $contact['xchan_hash']); + break; + } + } + } + } + } + } + } + } + } + + change_channel($channel['channel_id']); + notice(t('Import complete.') . EOL); + + goaway(z_root() . '/stream'); + } +} + diff --git a/Zotlabs/Lib/AConfig.php b/Zotlabs/Lib/AConfig.php index 4e7c5483f..417f3034d 100644 --- a/Zotlabs/Lib/AConfig.php +++ b/Zotlabs/Lib/AConfig.php @@ -4,22 +4,26 @@ namespace Zotlabs\Lib; // account configuration storage is built on top of the under-utilised xconfig -class AConfig { +class AConfig +{ - static public function Load($account_id) { - return XConfig::Load('a_' . $account_id); - } + public static function Load($account_id) + { + return XConfig::Load('a_' . $account_id); + } - static public function Get($account_id,$family,$key,$default = false) { - return XConfig::Get('a_' . $account_id,$family,$key, $default); - } + public static 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) { - return XConfig::Set('a_' . $account_id,$family,$key,$value); - } - - static public function Delete($account_id,$family,$key) { - return XConfig::Delete('a_' . $account_id,$family,$key); - } + public static function Set($account_id, $family, $key, $value) + { + return XConfig::Set('a_' . $account_id, $family, $key, $value); + } + public static function Delete($account_id, $family, $key) + { + return XConfig::Delete('a_' . $account_id, $family, $key); + } } diff --git a/Zotlabs/Lib/ASCollection.php b/Zotlabs/Lib/ASCollection.php index 2865625be..ac328504e 100644 --- a/Zotlabs/Lib/ASCollection.php +++ b/Zotlabs/Lib/ASCollection.php @@ -9,150 +9,143 @@ use Zotlabs\Lib\Activity; * Class for dealing with fetching ActivityStreams collections (ordered or unordered, normal or paged). * Construct with either an existing object or url and an optional channel to sign requests. * $direction is 0 (default) to fetch from the beginning, and 1 to fetch from the end and reverse order the resultant array. - * An optional limit to the number of records returned may also be specified. + * An optional limit to the number of records returned may also be specified. * Use $class->get() to return an array of collection members. */ - +class ASCollection +{ + + private $channel = null; + private $nextpage = null; + private $limit = 0; + private $direction = 0; // 0 = forward, 1 = reverse + private $data = []; + private $history = []; + public function __construct($obj, $channel = null, $direction = 0, $limit = 0) + { -class ASCollection { + $this->channel = $channel; + $this->direction = $direction; + $this->limit = $limit; - private $channel = null; - private $nextpage = null; - private $limit = 0; - private $direction = 0; // 0 = forward, 1 = reverse - private $data = []; - private $history = []; + if (is_array($obj)) { + $data = $obj; + } + if (is_string($obj)) { + $data = Activity::fetch($obj, $channel); + $this->history[] = $obj; + } - function __construct($obj, $channel = null, $direction = 0, $limit = 0) { + if (!is_array($data)) { + return; + } - $this->channel = $channel; - $this->direction = $direction; - $this->limit = $limit; - - if (is_array($obj)) { - $data = $obj; - } + if (!in_array($data['type'], ['Collection', 'OrderedCollection'])) { + return false; + } - if (is_string($obj)) { - $data = Activity::fetch($obj,$channel); - $this->history[] = $obj; - } - - if (! is_array($data)) { - return; - } + if ($this->direction) { + if (array_key_exists('last', $data) && $data['last']) { + $this->nextpage = $data['last']; + } + } else { + if (array_key_exists('first', $data) && $data['first']) { + $this->nextpage = $data['first']; + } + } - if (! in_array($data['type'], ['Collection','OrderedCollection'])) { - return false; - } + if (isset($data['items']) && is_array($data['items'])) { + $this->data = (($this->direction) ? array_reverse($data['items']) : $data['items']); + } elseif (isset($data['orderedItems']) && is_array($data['orderedItems'])) { + $this->data = (($this->direction) ? array_reverse($data['orderedItems']) : $data['orderedItems']); + } - if ($this->direction) { - if (array_key_exists('last',$data) && $data['last']) { - $this->nextpage = $data['last']; - } - } - else { - if (array_key_exists('first',$data) && $data['first']) { - $this->nextpage = $data['first']; - } - } + if ($limit) { + if (count($this->data) > $limit) { + $this->data = array_slice($this->data, 0, $limit); + return; + } + } - if (isset($data['items']) && is_array($data['items'])) { - $this->data = (($this->direction) ? array_reverse($data['items']) : $data['items']); - } - elseif (isset($data['orderedItems']) && is_array($data['orderedItems'])) { - $this->data = (($this->direction) ? array_reverse($data['orderedItems']) : $data['orderedItems']); - } + do { + $x = $this->next(); + } while ($x); + } - if ($limit) { - if (count($this->data) > $limit) { - $this->data = array_slice($this->data,0,$limit); - return; - } - } + public function get() + { + return $this->data; + } - do { - $x = $this->next(); - } while ($x); - } + public function next() + { - function get() { - return $this->data; - } + if (!$this->nextpage) { + return false; + } - function next() { + if (is_array($this->nextpage)) { + $data = $this->nextpage; + } - if (! $this->nextpage) { - return false; - } - - if (is_array($this->nextpage)) { - $data = $this->nextpage; - } - - if (is_string($this->nextpage)) { - if (in_array($this->nextpage,$this->history)) { - // recursion detected - return false; - } - $data = Activity::fetch($this->nextpage,$this->channel); - $this->history[] = $this->nextpage; - } + if (is_string($this->nextpage)) { + if (in_array($this->nextpage, $this->history)) { + // recursion detected + return false; + } + $data = Activity::fetch($this->nextpage, $this->channel); + $this->history[] = $this->nextpage; + } - if (! is_array($data)) { - return false; - } - - if (! in_array($data['type'], ['CollectionPage','OrderedCollectionPage'])) { - return false; - } + if (!is_array($data)) { + return false; + } - $this->setnext($data); + if (!in_array($data['type'], ['CollectionPage', 'OrderedCollectionPage'])) { + return false; + } - if (isset($data['items']) && is_array($data['items'])) { - $this->data = array_merge($this->data,(($this->direction) ? array_reverse($data['items']) : $data['items'])); - } - elseif (isset($data['orderedItems']) && is_array($data['orderedItems'])) { - $this->data = array_merge($this->data,(($this->direction) ? array_reverse($data['orderedItems']) : $data['orderedItems'])); - } + $this->setnext($data); - if ($limit) { - if (count($this->data) > $limit) { - $this->data = array_slice($this->data,0,$limit); - $this->nextpage = false; - return true; - } - } + if (isset($data['items']) && is_array($data['items'])) { + $this->data = array_merge($this->data, (($this->direction) ? array_reverse($data['items']) : $data['items'])); + } elseif (isset($data['orderedItems']) && is_array($data['orderedItems'])) { + $this->data = array_merge($this->data, (($this->direction) ? array_reverse($data['orderedItems']) : $data['orderedItems'])); + } - return true; - } + if ($limit) { + if (count($this->data) > $limit) { + $this->data = array_slice($this->data, 0, $limit); + $this->nextpage = false; + return true; + } + } - function setnext($data) { - if ($this->direction) { - if (array_key_exists('prev',$data) && $data['prev']) { - $this->nextpage = $data['prev']; - } - elseif (array_key_exists('first',$data) && $data['first']) { - $this->nextpage = $data['first']; - } - else { - $this->nextpage = false; - } - } - else { - if (array_key_exists('next',$data) && $data['next']) { - $this->nextpage = $data['next']; - } - elseif (array_key_exists('last',$data) && $data['last']) { - $this->nextpage = $data['last']; - } - else { - $this->nextpage = false; - } - } - logger('nextpage: ' . $this->nextpage, LOGGER_DEBUG); - } + return true; + } + + public function setnext($data) + { + if ($this->direction) { + if (array_key_exists('prev', $data) && $data['prev']) { + $this->nextpage = $data['prev']; + } elseif (array_key_exists('first', $data) && $data['first']) { + $this->nextpage = $data['first']; + } else { + $this->nextpage = false; + } + } else { + if (array_key_exists('next', $data) && $data['next']) { + $this->nextpage = $data['next']; + } elseif (array_key_exists('last', $data) && $data['last']) { + $this->nextpage = $data['last']; + } else { + $this->nextpage = false; + } + } + logger('nextpage: ' . $this->nextpage, LOGGER_DEBUG); + } } diff --git a/Zotlabs/Lib/AbConfig.php b/Zotlabs/Lib/AbConfig.php index d205071f8..391ae1af9 100644 --- a/Zotlabs/Lib/AbConfig.php +++ b/Zotlabs/Lib/AbConfig.php @@ -2,74 +2,83 @@ namespace Zotlabs\Lib; +class AbConfig +{ -class AbConfig { - - static public function Load($chan,$xhash,$family = '') { - if($family) - $where = sprintf(" and cat = '%s' ",dbesc($family)); - $r = q("select * from abconfig where chan = %d and xchan = '%s' $where", - intval($chan), - dbesc($xhash) - ); - return $r; - } + public static function Load($chan, $xhash, $family = '') + { + if ($family) { + $where = sprintf(" and cat = '%s' ", dbesc($family)); + } + $r = q( + "select * from abconfig where chan = %d and xchan = '%s' $where", + intval($chan), + dbesc($xhash) + ); + return $r; + } - 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), - dbesc($family), - dbesc($key) - ); - if($r) { - return unserialise($r[0]['v']); - } - return $default; - } + public static 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), + dbesc($family), + dbesc($key) + ); + if ($r) { + return unserialise($r[0]['v']); + } + return $default; + } - static public function Set($chan,$xhash,$family,$key,$value) { + public static function Set($chan, $xhash, $family, $key, $value) + { - $dbvalue = ((is_array($value)) ? serialise($value) : $value); - $dbvalue = ((is_bool($dbvalue)) ? intval($dbvalue) : $dbvalue); + $dbvalue = ((is_array($value)) ? serialise($value) : $value); + $dbvalue = ((is_bool($dbvalue)) ? intval($dbvalue) : $dbvalue); - if(self::Get($chan,$xhash,$family,$key) === false) { - $r = q("insert into abconfig ( chan, xchan, cat, k, v ) values ( %d, '%s', '%s', '%s', '%s' ) ", - intval($chan), - dbesc($xhash), - dbesc($family), - dbesc($key), - dbesc($dbvalue) - ); - } - else { - $r = q("update abconfig set v = '%s' where chan = %d and xchan = '%s' and cat = '%s' and k = '%s' ", - dbesc($dbvalue), - dbesc($chan), - dbesc($xhash), - dbesc($family), - dbesc($key) - ); - } - - if($r) - return $value; - return false; - } + if (self::Get($chan, $xhash, $family, $key) === false) { + $r = q( + "insert into abconfig ( chan, xchan, cat, k, v ) values ( %d, '%s', '%s', '%s', '%s' ) ", + intval($chan), + dbesc($xhash), + dbesc($family), + dbesc($key), + dbesc($dbvalue) + ); + } else { + $r = q( + "update abconfig set v = '%s' where chan = %d and xchan = '%s' and cat = '%s' and k = '%s' ", + dbesc($dbvalue), + dbesc($chan), + dbesc($xhash), + dbesc($family), + dbesc($key) + ); + } + + if ($r) { + return $value; + } + return false; + } - static public function Delete($chan,$xhash,$family,$key) { + public static function Delete($chan, $xhash, $family, $key) + { - $r = q("delete from abconfig where chan = %d and xchan = '%s' and cat = '%s' and k = '%s' ", - intval($chan), - dbesc($xhash), - dbesc($family), - dbesc($key) - ); - - return $r; - } + $r = q( + "delete from abconfig where chan = %d and xchan = '%s' and cat = '%s' and k = '%s' ", + intval($chan), + dbesc($xhash), + dbesc($family), + dbesc($key) + ); + return $r; + } } diff --git a/Zotlabs/Lib/AccessList.php b/Zotlabs/Lib/AccessList.php index c238e5cfd..eabdc6a10 100644 --- a/Zotlabs/Lib/AccessList.php +++ b/Zotlabs/Lib/AccessList.php @@ -1,464 +1,492 @@ -may apply to this list and any future members. If this is not what you intended, please create another list with a different name.') . EOL); + } + $hash = self::by_id($uid, $r); + return $hash; + } - $z = q("SELECT * FROM pgrp WHERE id = %d LIMIT 1", - intval($r) - ); - if(($z) && $z[0]['deleted']) { - q('UPDATE pgrp SET deleted = 0 WHERE id = %d', intval($z[0]['id'])); - notice( t('A deleted list with this name was revived. Existing item permissions may apply to this list and any future members. If this is not what you intended, please create another list with a different name.') . EOL); - } - $hash = self::by_id($uid,$r); - return $hash; - } + $hash = new_uuid(); - $hash = new_uuid(); - - $r = q("INSERT INTO pgrp ( hash, uid, visible, gname, rule ) + $r = q( + "INSERT INTO pgrp ( hash, uid, visible, gname, rule ) VALUES( '%s', %d, %d, '%s', '' ) ", - dbesc($hash), - intval($uid), - intval($public), - dbesc($name) - ); - $ret = $r; - } + dbesc($hash), + intval($uid), + intval($public), + dbesc($name) + ); + $ret = $r; + } - Libsync::build_sync_packet($uid,null,true); - - return (($ret) ? $hash : $ret); - } + Libsync::build_sync_packet($uid, null, true); + + return (($ret) ? $hash : $ret); + } - static function remove($uid,$name) { - $ret = false; - if ($uid && $name) { - $r = q("SELECT id, hash FROM pgrp WHERE uid = %d AND gname = '%s' LIMIT 1", - intval($uid), - dbesc($name) - ); - if ($r) { - $group_id = $r[0]['id']; - $group_hash = $r[0]['hash']; - } - else { - return false; - } - - // remove group from default posting lists - $r = q("SELECT channel_default_group, channel_allow_gid, channel_deny_gid FROM channel WHERE channel_id = %d LIMIT 1", - intval($uid) - ); - if ($r) { - $user_info = array_shift($r); - $change = false; + public static function remove($uid, $name) + { + $ret = false; + if ($uid && $name) { + $r = q( + "SELECT id, hash FROM pgrp WHERE uid = %d AND gname = '%s' LIMIT 1", + intval($uid), + dbesc($name) + ); + if ($r) { + $group_id = $r[0]['id']; + $group_hash = $r[0]['hash']; + } else { + return false; + } - if ($user_info['channel_default_group'] == $group_hash) { - $user_info['channel_default_group'] = ''; - $change = true; - } - if (strpos($user_info['channel_allow_gid'], '<' . $group_hash . '>') !== false) { - $user_info['channel_allow_gid'] = str_replace('<' . $group_hash . '>', '', $user_info['channel_allow_gid']); - $change = true; - } - if (strpos($user_info['channel_deny_gid'], '<' . $group_hash . '>') !== false) { - $user_info['channel_deny_gid'] = str_replace('<' . $group_hash . '>', '', $user_info['channel_deny_gid']); - $change = true; - } + // remove group from default posting lists + $r = q( + "SELECT channel_default_group, channel_allow_gid, channel_deny_gid FROM channel WHERE channel_id = %d LIMIT 1", + intval($uid) + ); + if ($r) { + $user_info = array_shift($r); + $change = false; - if ($change) { - q("UPDATE channel SET channel_default_group = '%s', channel_allow_gid = '%s', channel_deny_gid = '%s' + if ($user_info['channel_default_group'] == $group_hash) { + $user_info['channel_default_group'] = ''; + $change = true; + } + if (strpos($user_info['channel_allow_gid'], '<' . $group_hash . '>') !== false) { + $user_info['channel_allow_gid'] = str_replace('<' . $group_hash . '>', '', $user_info['channel_allow_gid']); + $change = true; + } + if (strpos($user_info['channel_deny_gid'], '<' . $group_hash . '>') !== false) { + $user_info['channel_deny_gid'] = str_replace('<' . $group_hash . '>', '', $user_info['channel_deny_gid']); + $change = true; + } + + if ($change) { + q( + "UPDATE channel SET channel_default_group = '%s', channel_allow_gid = '%s', channel_deny_gid = '%s' WHERE channel_id = %d", - intval($user_info['channel_default_group']), - dbesc($user_info['channel_allow_gid']), - dbesc($user_info['channel_deny_gid']), - intval($uid) - ); - } - } + intval($user_info['channel_default_group']), + dbesc($user_info['channel_allow_gid']), + dbesc($user_info['channel_deny_gid']), + intval($uid) + ); + } + } - // remove all members - $r = q("DELETE FROM pgrp_member WHERE uid = %d AND gid = %d ", - intval($uid), - intval($group_id) - ); + // remove all members + $r = q( + "DELETE FROM pgrp_member WHERE uid = %d AND gid = %d ", + intval($uid), + intval($group_id) + ); - // remove group - $r = q("UPDATE pgrp SET deleted = 1 WHERE uid = %d AND gname = '%s'", - intval($uid), - dbesc($name) - ); + // remove group + $r = q( + "UPDATE pgrp SET deleted = 1 WHERE uid = %d AND gname = '%s'", + intval($uid), + dbesc($name) + ); - $ret = $r; + $ret = $r; + } - } + Libsync::build_sync_packet($uid, null, true); - Libsync::build_sync_packet($uid,null,true); + return $ret; + } - return $ret; - } + // returns the integer id of an access group owned by $uid and named $name + // or false. - // returns the integer id of an access group owned by $uid and named $name - // or false. - - static function byname($uid,$name) { - if (! ($uid && $name)) { - return false; - } - $r = q("SELECT id FROM pgrp WHERE uid = %d AND gname = '%s' LIMIT 1", - intval($uid), - dbesc($name) - ); - if ($r) { - return $r[0]['id']; - } - return false; - } + public static function byname($uid, $name) + { + if (!($uid && $name)) { + return false; + } + $r = q( + "SELECT id FROM pgrp WHERE uid = %d AND gname = '%s' LIMIT 1", + intval($uid), + dbesc($name) + ); + if ($r) { + return $r[0]['id']; + } + return false; + } - static function by_id($uid,$id) { - if (! ($uid && $id)) { - return false; - } - - $r = q("SELECT * FROM pgrp WHERE uid = %d AND id = %d and deleted = 0", - intval($uid), - intval($id) - ); - if ($r) { - return array_shift($r); - } - return false; - } + public static function by_id($uid, $id) + { + if (!($uid && $id)) { + return false; + } + + $r = q( + "SELECT * FROM pgrp WHERE uid = %d AND id = %d and deleted = 0", + intval($uid), + intval($id) + ); + if ($r) { + return array_shift($r); + } + return false; + } - - static function rec_byhash($uid,$hash) { - if (! ( $uid && $hash)) { - return false; - } - $r = q("SELECT * FROM pgrp WHERE uid = %d AND hash = '%s' LIMIT 1", - intval($uid), - dbesc($hash) - ); - if ($r) { - return array_shift($r); - } - return false; - } + public static function rec_byhash($uid, $hash) + { + if (!($uid && $hash)) { + return false; + } + $r = q( + "SELECT * FROM pgrp WHERE uid = %d AND hash = '%s' LIMIT 1", + intval($uid), + dbesc($hash) + ); + if ($r) { + return array_shift($r); + } + return false; + } - static function member_remove($uid,$name,$member) { - $gid = self::byname($uid,$name); - if (! $gid) { - return false; - } - if (! ($uid && $gid && $member)) { - return false; - } - $r = q("DELETE FROM pgrp_member WHERE uid = %d AND gid = %d AND xchan = '%s' ", - intval($uid), - intval($gid), - dbesc($member) - ); + public static function member_remove($uid, $name, $member) + { + $gid = self::byname($uid, $name); + if (!$gid) { + return false; + } + if (!($uid && $gid && $member)) { + return false; + } + $r = q( + "DELETE FROM pgrp_member WHERE uid = %d AND gid = %d AND xchan = '%s' ", + intval($uid), + intval($gid), + dbesc($member) + ); - Libsync::build_sync_packet($uid,null,true); + Libsync::build_sync_packet($uid, null, true); - return $r; - } + return $r; + } - static function member_add($uid,$name,$member,$gid = 0) { - if (! $gid) { - $gid = self::byname($uid,$name); - } - if (! ($gid && $uid && $member)) { - return false; - } + public static function member_add($uid, $name, $member, $gid = 0) + { + if (!$gid) { + $gid = self::byname($uid, $name); + } + if (!($gid && $uid && $member)) { + return false; + } - $r = q("SELECT * FROM pgrp_member WHERE uid = %d AND gid = %d AND xchan = '%s' LIMIT 1", - intval($uid), - intval($gid), - dbesc($member) - ); - if ($r) { - return true; // You might question this, but - // we indicate success because the group member was in fact created - // -- It was just created at another time - } - else { - $r = q("INSERT INTO pgrp_member (uid, gid, xchan) + $r = q( + "SELECT * FROM pgrp_member WHERE uid = %d AND gid = %d AND xchan = '%s' LIMIT 1", + intval($uid), + intval($gid), + dbesc($member) + ); + if ($r) { + return true; // You might question this, but + // we indicate success because the group member was in fact created + // -- It was just created at another time + } else { + $r = q( + "INSERT INTO pgrp_member (uid, gid, xchan) VALUES( %d, %d, '%s' ) ", - intval($uid), - intval($gid), - dbesc($member) - ); - } - Libsync::build_sync_packet($uid,null,true); - return $r; - } + intval($uid), + intval($gid), + dbesc($member) + ); + } + Libsync::build_sync_packet($uid, null, true); + return $r; + } - static function members($uid, $gid, $total = false, $start = 0, $records = 0) { - $ret = []; - if ($records) { - $pager_sql = sprintf(" LIMIT %d OFFSET %d ", intval($records), intval($start)); - } + public static function members($uid, $gid, $total = false, $start = 0, $records = 0) + { + $ret = []; + if ($records) { + $pager_sql = sprintf(" LIMIT %d OFFSET %d ", intval($records), intval($start)); + } - // process virtual groups - if (strpos($gid,':') === 0) { - $vg = substr($gid,1); - switch ($vg) { - case '1': - $sql_extra = EMPTY_STR; - break; - case '2': + // process virtual groups + if (strpos($gid, ':') === 0) { + $vg = substr($gid, 1); + switch ($vg) { + case '1': + $sql_extra = EMPTY_STR; + break; + case '2': $sql_extra = " and xchan_network in ('nomad','zot6') "; - break; - case '3': - $sql_extra = " and xchan_network = 'activitypub' "; - break; - default: - break; - } - if ($total) { - $r = q("SELECT count(*) FROM abook left join xchan on xchan_hash = abook_xchan WHERE abook_channel = %d and xchan_deleted = 0 and abook_self = 0 and abook_blocked = 0 and abook_pending = 0 $sql_extra ORDER BY xchan_name ASC $pager_sql", - intval($uid) - ); - return ($r) ? $r[0]['total'] : false; - } + break; + case '3': + $sql_extra = " and xchan_network = 'activitypub' "; + break; + default: + break; + } + if ($total) { + $r = q( + "SELECT count(*) FROM abook left join xchan on xchan_hash = abook_xchan WHERE abook_channel = %d and xchan_deleted = 0 and abook_self = 0 and abook_blocked = 0 and abook_pending = 0 $sql_extra ORDER BY xchan_name ASC $pager_sql", + intval($uid) + ); + return ($r) ? $r[0]['total'] : false; + } - $r = q("SELECT * FROM abook left join xchan on xchan_hash = abook_xchan + $r = q( + "SELECT * FROM abook left join xchan on xchan_hash = abook_xchan WHERE abook_channel = %d and xchan_deleted = 0 and abook_self = 0 and abook_blocked = 0 and abook_pending = 0 $sql_extra ORDER BY xchan_name ASC $pager_sql", - intval($uid) - ); - if ($r) { - for($x = 0; $x < count($r); $x ++) { - $r[$x]['xchan'] = $r[$x]['abook_xchan']; - } - } - return $r; - } - - if (intval($gid)) { - if ($total) { - $r = q("SELECT count(xchan) as total FROM pgrp_member + intval($uid) + ); + if ($r) { + for ($x = 0; $x < count($r); $x++) { + $r[$x]['xchan'] = $r[$x]['abook_xchan']; + } + } + return $r; + } + + if (intval($gid)) { + if ($total) { + $r = q( + "SELECT count(xchan) as total FROM pgrp_member LEFT JOIN abook ON abook_xchan = pgrp_member.xchan left join xchan on xchan_hash = abook_xchan WHERE gid = %d AND abook_channel = %d and pgrp_member.uid = %d and xchan_deleted = 0 and abook_self = 0 and abook_blocked = 0 and abook_pending = 0", - intval($gid), - intval($uid), - intval($uid) - ); - if ($r) { - return $r[0]['total']; - } - } - - $r = q("SELECT * FROM pgrp_member + intval($gid), + intval($uid), + intval($uid) + ); + if ($r) { + return $r[0]['total']; + } + } + + $r = q( + "SELECT * FROM pgrp_member LEFT JOIN abook ON abook_xchan = pgrp_member.xchan left join xchan on xchan_hash = abook_xchan WHERE gid = %d AND abook_channel = %d and pgrp_member.uid = %d and xchan_deleted = 0 and abook_self = 0 and abook_blocked = 0 and abook_pending = 0 ORDER BY xchan_name ASC $pager_sql", - intval($gid), - intval($uid), - intval($uid) - ); - if ($r) { - $ret = $r; - } - } - return $ret; - } + intval($gid), + intval($uid), + intval($uid) + ); + if ($r) { + $ret = $r; + } + } + return $ret; + } - static function members_xchan($uid,$gid) { - $ret = []; - if (intval($gid)) { - $r = q("SELECT xchan FROM pgrp_member WHERE gid = %d AND uid = %d", - intval($gid), - intval($uid) - ); - if ($r) { - foreach ($r as $rv) { - $ret[] = $rv['xchan']; - } - } - } - return $ret; - } + public static function members_xchan($uid, $gid) + { + $ret = []; + if (intval($gid)) { + $r = q( + "SELECT xchan FROM pgrp_member WHERE gid = %d AND uid = %d", + intval($gid), + intval($uid) + ); + if ($r) { + foreach ($r as $rv) { + $ret[] = $rv['xchan']; + } + } + } + return $ret; + } - static function select($uid,$group = '') { - - $grps = []; + public static function select($uid, $group = '') + { - $r = q("SELECT * FROM pgrp WHERE deleted = 0 AND uid = %d ORDER BY gname ASC", - intval($uid) - ); - $grps[] = [ 'name' => '', 'hash' => '0', 'selected' => '' ]; - if ($r) { - foreach ($r as $rr) { - $grps[] = [ 'name' => $rr['gname'], 'id' => $rr['hash'], 'selected' => (($group == $rr['hash']) ? 'true' : '') ]; - } + $grps = []; - } - - return replace_macros(get_markup_template('group_selection.tpl'), [ - '$label' => t('Add new connections to this access list'), - '$groups' => $grps - ]); - } + $r = q( + "SELECT * FROM pgrp WHERE deleted = 0 AND uid = %d ORDER BY gname ASC", + intval($uid) + ); + $grps[] = ['name' => '', 'hash' => '0', 'selected' => '']; + if ($r) { + foreach ($r as $rr) { + $grps[] = ['name' => $rr['gname'], 'id' => $rr['hash'], 'selected' => (($group == $rr['hash']) ? 'true' : '')]; + } + } + + return replace_macros(get_markup_template('group_selection.tpl'), [ + '$label' => t('Add new connections to this access list'), + '$groups' => $grps + ]); + } - static function widget($every="connections",$each="lists",$edit = false, $group_id = 0, $cid = '',$mode = 1) { + public static function widget($every = "connections", $each = "lists", $edit = false, $group_id = 0, $cid = '', $mode = 1) + { - $o = ''; + $o = ''; - $groups = []; + $groups = []; - $r = q("SELECT * FROM pgrp WHERE deleted = 0 AND uid = %d ORDER BY gname ASC", - intval($_SESSION['uid']) - ); - $member_of = []; - if ($cid) { - $member_of = self::containing(local_channel(),$cid); - } + $r = q( + "SELECT * FROM pgrp WHERE deleted = 0 AND uid = %d ORDER BY gname ASC", + intval($_SESSION['uid']) + ); + $member_of = []; + if ($cid) { + $member_of = self::containing(local_channel(), $cid); + } - if ($r) { - foreach ($r as $rr) { - $selected = (($group_id == $rr['id']) ? ' group-selected' : ''); - - if ($edit) { - $groupedit = [ 'href' => "lists/".$rr['id'], 'title' => t('edit') ]; - } - else { - $groupedit = null; - } - - $groups[] = [ - 'id' => $rr['id'], - 'enc_cid' => base64url_encode($cid), - 'cid' => $cid, - 'text' => $rr['gname'], - 'selected' => $selected, - 'href' => (($mode == 0) ? $each.'?f=&gid='.$rr['id'] : $each."/".$rr['id']) . ((x($_GET,'new')) ? '&new=' . $_GET['new'] : '') . ((x($_GET,'order')) ? '&order=' . $_GET['order'] : ''), - 'edit' => $groupedit, - 'ismember' => in_array($rr['id'],$member_of), - ]; - } - } - - return replace_macros(get_markup_template('group_side.tpl'), [ - '$title' => t('Lists'), - '$edittext' => t('Edit list'), - '$createtext' => t('Create new list'), - '$ungrouped' => (($every === 'contacts') ? t('Channels not in any access list') : ''), - '$groups' => $groups, - '$add' => t('add'), - ]); + if ($r) { + foreach ($r as $rr) { + $selected = (($group_id == $rr['id']) ? ' group-selected' : ''); - } + if ($edit) { + $groupedit = ['href' => "lists/" . $rr['id'], 'title' => t('edit')]; + } else { + $groupedit = null; + } + + $groups[] = [ + 'id' => $rr['id'], + 'enc_cid' => base64url_encode($cid), + 'cid' => $cid, + 'text' => $rr['gname'], + 'selected' => $selected, + 'href' => (($mode == 0) ? $each . '?f=&gid=' . $rr['id'] : $each . "/" . $rr['id']) . ((x($_GET, 'new')) ? '&new=' . $_GET['new'] : '') . ((x($_GET, 'order')) ? '&order=' . $_GET['order'] : ''), + 'edit' => $groupedit, + 'ismember' => in_array($rr['id'], $member_of), + ]; + } + } + + return replace_macros(get_markup_template('group_side.tpl'), [ + '$title' => t('Lists'), + '$edittext' => t('Edit list'), + '$createtext' => t('Create new list'), + '$ungrouped' => (($every === 'contacts') ? t('Channels not in any access list') : ''), + '$groups' => $groups, + '$add' => t('add'), + ]); + } - static function expand($g) { - if (! (is_array($g) && count($g))) { - return []; - } + public static function expand($g) + { + if (!(is_array($g) && count($g))) { + return []; + } - $ret = []; - $x = []; + $ret = []; + $x = []; - foreach ($g as $gv) { + foreach ($g as $gv) { + // virtual access lists + // connections:abc is all the connection sof the channel with channel_hash abc + // zot:abc is all of abc's zot6 connections + // activitypub:abc is all of abc's activitypub connections - // virtual access lists - // connections:abc is all the connection sof the channel with channel_hash abc - // zot:abc is all of abc's zot6 connections - // activitypub:abc is all of abc's activitypub connections - - if (strpos($gv,'connections:') === 0 || strpos($gv,'zot:') === 0 || strpos($gv,'activitypub:') === 0) { - $sql_extra = EMPTY_STR; - $channel_hash = substr($gv,strpos($gv,':') + 1); - if (strpos($gv,'zot:') === 0) { + if (strpos($gv, 'connections:') === 0 || strpos($gv, 'zot:') === 0 || strpos($gv, 'activitypub:') === 0) { + $sql_extra = EMPTY_STR; + $channel_hash = substr($gv, strpos($gv, ':') + 1); + if (strpos($gv, 'zot:') === 0) { $sql_extra = " and xchan_network in ('nomad','zot6') "; - } - if (strpos($gv,'activitypub:') === 0) { - $sql_extra = " and xchan_network = 'activitypub' "; - } - $r = q("select channel_id from channel where channel_hash = '%s' ", - dbesc($channel_hash) - ); - if ($r) { - foreach ($r as $rv) { - $y = q("select abook_xchan from abook left join xchan on abook_xchan = xchan_hash where abook_channel = %d and abook_self = 0 and abook_pending = 0 and abook_archived = 0 $sql_extra", - intval($rv['channel_id']) - ); - if ($y) { - foreach ($y as $yv) { - $ret[] = $yv['abook_xchan']; - } - } - } - } - } - else { - $x[] = $gv; - } - } + } + if (strpos($gv, 'activitypub:') === 0) { + $sql_extra = " and xchan_network = 'activitypub' "; + } + $r = q( + "select channel_id from channel where channel_hash = '%s' ", + dbesc($channel_hash) + ); + if ($r) { + foreach ($r as $rv) { + $y = q( + "select abook_xchan from abook left join xchan on abook_xchan = xchan_hash where abook_channel = %d and abook_self = 0 and abook_pending = 0 and abook_archived = 0 $sql_extra", + intval($rv['channel_id']) + ); + if ($y) { + foreach ($y as $yv) { + $ret[] = $yv['abook_xchan']; + } + } + } + } + } else { + $x[] = $gv; + } + } - if ($x) { - stringify_array_elms($x,true); - $groups = implode(',', $x); - if ($groups) { - $r = q("SELECT xchan FROM pgrp_member WHERE gid IN ( select id from pgrp where hash in ( $groups ))"); - if ($r) { - foreach ($r as $rv) { - $ret[] = $rv['xchan']; - } - } - - } - } - return $ret; - } + if ($x) { + stringify_array_elms($x, true); + $groups = implode(',', $x); + if ($groups) { + $r = q("SELECT xchan FROM pgrp_member WHERE gid IN ( select id from pgrp where hash in ( $groups ))"); + if ($r) { + foreach ($r as $rv) { + $ret[] = $rv['xchan']; + } + } + } + } + return $ret; + } - static function member_of($c) { - $r = q("SELECT pgrp.gname, pgrp.id FROM pgrp LEFT JOIN pgrp_member ON pgrp_member.gid = pgrp.id + public static function member_of($c) + { + $r = q( + "SELECT pgrp.gname, pgrp.id FROM pgrp LEFT JOIN pgrp_member ON pgrp_member.gid = pgrp.id WHERE pgrp_member.xchan = '%s' AND pgrp.deleted = 0 ORDER BY pgrp.gname ASC ", - dbesc($c) - ); + dbesc($c) + ); - return $r; - } + return $r; + } - static function containing($uid,$c) { + public static function containing($uid, $c) + { - $r = q("SELECT gid FROM pgrp_member WHERE uid = %d AND pgrp_member.xchan = '%s' ", - intval($uid), - dbesc($c) - ); + $r = q( + "SELECT gid FROM pgrp_member WHERE uid = %d AND pgrp_member.xchan = '%s' ", + intval($uid), + dbesc($c) + ); - $ret = []; - if ($r) { - foreach ($r as $rv) - $ret[] = $rv['gid']; - } - - return $ret; - } -} \ No newline at end of file + $ret = []; + if ($r) { + foreach ($r as $rv) { + $ret[] = $rv['gid']; + } + } + + return $ret; + } +} diff --git a/Zotlabs/Lib/Activity.php b/Zotlabs/Lib/Activity.php index 61ea02388..253afd0f8 100644 --- a/Zotlabs/Lib/Activity.php +++ b/Zotlabs/Lib/Activity.php @@ -23,4314 +23,4321 @@ require_once('include/html2bbcode.php'); require_once('include/html2plain.php'); require_once('include/event.php'); -class Activity { +class Activity +{ - static $ACTOR_CACHE_DAYS = 3; + public static $ACTOR_CACHE_DAYS = 3; - // $x (string|array) - // if json string, decode it - // returns activitystreams object as an array except if it is a URL - // which returns the URL as string - - static function encode_object($x) { + // $x (string|array) + // if json string, decode it + // returns activitystreams object as an array except if it is a URL + // which returns the URL as string - if ($x) { - if (is_string($x)) { - $tmp = json_decode($x,true); - if ($tmp !== NULL) { - $x = $tmp; - } - } - } + public static function encode_object($x) + { - if (is_string($x)) { - return ($x); - } + if ($x) { + if (is_string($x)) { + $tmp = json_decode($x, true); + if ($tmp !== null) { + $x = $tmp; + } + } + } - if ($x['type'] === ACTIVITY_OBJ_PERSON) { - return self::fetch_person($x); - } - if ($x['type'] === ACTIVITY_OBJ_PROFILE) { - return self::fetch_profile($x); - } - if (in_array($x['type'], [ ACTIVITY_OBJ_NOTE, ACTIVITY_OBJ_ARTICLE ] )) { + if (is_string($x)) { + return ($x); + } - // Use Mastodon-specific note and media hacks if nomadic. Else HTML. - // Eventually this needs to be passed in much further up the stack - // and base the decision on whether or not we are encoding for + if ($x['type'] === ACTIVITY_OBJ_PERSON) { + return self::fetch_person($x); + } + if ($x['type'] === ACTIVITY_OBJ_PROFILE) { + return self::fetch_profile($x); + } + if (in_array($x['type'], [ACTIVITY_OBJ_NOTE, ACTIVITY_OBJ_ARTICLE])) { + // Use Mastodon-specific note and media hacks if nomadic. Else HTML. + // Eventually this needs to be passed in much further up the stack + // and base the decision on whether or not we are encoding for // ActivityPub or Zot6 or Nomad - return self::fetch_item($x,((get_config('system','activitypub', ACTIVITYPUB_ENABLED)) ? true : false)); - } - if ($x['type'] === ACTIVITY_OBJ_THING) { - return self::fetch_thing($x); - } - - call_hooks('encode_object',$x); - - return $x; - - } - - - - static function fetch($url,$channel = null,$hub = null,$debug = false) { - $redirects = 0; - if (! check_siteallowed($url)) { - logger('denied: ' . $url); - return null; - } - if (! $channel) { - $channel = get_sys_channel(); - } - - $parsed = parse_url($url); - - // perform IDN substitution - - if ($parsed['host'] !== punify($parsed['host'])) { - $url = str_replace($parsed['host'],punify($parsed['host']),$url); - } - - logger('fetch: ' . $url, LOGGER_DEBUG); - - if (isset($parsed['scheme']) && $parsed['scheme'] === 'x-zot') { - $x = ZotURL::fetch($url,$channel,$hub); - } - else { - // handle bearcaps - if (isset($parsed['scheme']) && isset($parsed['query']) && $parsed['scheme'] === 'bear' && $parsed['query'] !== EMPTY_STR) { - $params = explode('&', $parsed['query']); - if ($params) { - foreach ($params as $p) { - if (substr($p,0,2) === 'u=') { - $url = substr($p,2); - } - if (substr($p,0,2) === 't=') { - $token = substr($p,2); - } - } - // the entire URL just changed so parse it again - $parsed = parse_url($url); - } - } - - // Ignore fragments; as we are not in a browser and some platforms (e.g. Django or at least funkwhale) don't handle them well - unset($parsed['fragment']); - - // rebuild the url - $url = unparse_url($parsed); - - logger('fetch_actual: ' . $url, LOGGER_DEBUG); - - $headers = [ - 'Accept' => 'application/activity+json, application/x-zot-activity+json, application/ld+json; profile="https://www.w3.org/ns/activitystreams"', - 'Host' => $parsed['host'], - 'Date' => datetime_convert('UTC','UTC', 'now', 'D, d M Y H:i:s \\G\\M\\T'), - '(request-target)' => 'get ' . get_request_string($url) - ]; - - if (isset($token)) { - $headers['Authorization'] = 'Bearer ' . $token; - } - $h = HTTPSig::create_sig($headers,$channel['channel_prvkey'],channel_url($channel),false); - $x = z_fetch_url($url, true, $redirects, [ 'headers' => $h ] ); - } - - if ($x['success']) { - - $y = json_decode($x['body'],true); - logger('returned: ' . json_encode($y,JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES)); - - $site_url = unparse_url( ['scheme' => $parsed['scheme'], 'host' => $parsed['host'], 'port' => ((array_key_exists('port',$parsed) && intval($parsed['port'])) ? $parsed['port'] : 0) ] ); - q("update site set site_update = '%s' where site_url = '%s' and site_update < %s - INTERVAL %s", - dbesc(datetime_convert()), - dbesc($site_url), - db_utcnow(), db_quoteinterval('1 DAY') - ); - - // check for a valid signature, but only if this is not an actor object. If it is signed, it must be valid. - // Ignore actors because of the potential for infinite recursion if we perform this step while - // fetching an actor key to validate a signature elsewhere. This should validate relayed activities - // over litepub which arrived at our inbox that do not use LD signatures - - if (($y['type']) && (! ActivityStreams::is_an_actor($y['type']))) { - $sigblock = HTTPSig::verify($x); - - if (($sigblock['header_signed']) && (! $sigblock['header_valid'])) { - return null; - } - } - - return json_decode($x['body'], true); - } - else { - logger('fetch failed: ' . $url); - if ($debug) { - return $x; - } - } - return null; - } - - - - static function fetch_person($x) { - return self::fetch_profile($x); - } - - static function fetch_profile($x) { - $r = q("select * from xchan left join hubloc on xchan_hash = hubloc_hash where hubloc_id_url = '%s' limit 1", - dbesc($x['id']) - ); - if (! $r) { - $r = q("select * from xchan where xchan_hash = '%s' limit 1", - dbesc($x['id']) - ); - - } - if (! $r) { - return []; - } - - return self::encode_person($r[0],false); - } - - static function fetch_thing($x) { - - $r = q("select * from obj where obj_type = %d and obj_obj = '%s' limit 1", - intval(TERM_OBJ_THING), - dbesc($x['id']) - ); - - if (! $r) { - return []; - } - - $x = [ - 'type' => 'Object', - 'id' => z_root() . '/thing/' . $r[0]['obj_obj'], - 'name' => $r[0]['obj_term'] - ]; - - if ($r[0]['obj_image']) { - $x['image'] = $r[0]['obj_image']; - } - return $x; - - } - - static function fetch_item($x,$activitypub = false) { - - if (array_key_exists('source',$x)) { - // This item is already processed and encoded - return $x; - } - - $r = q("select * from item where mid = '%s' limit 1", - dbesc($x['id']) - ); - if ($r) { - xchan_query($r,true); - $r = fetch_post_tags($r,true); - if ($r[0]['verb'] === 'Invite') { - return self::encode_activity($r[0],$activitypub); - } - return self::encode_item($r[0],$activitypub); - } - } - - static function paged_collection_init($total,$id, $type = 'OrderedCollection') { - - $ret = [ - 'id' => z_root() . '/' . $id, - 'type' => $type, - 'totalItems' => $total, - ]; - - $numpages = $total / App::$pager['itemspage']; - $lastpage = (($numpages > intval($numpages)) ? intval($numpages) + 1 : $numpages); - - $ret['first'] = z_root() . '/' . App::$query_string . '?page=1'; - $ret['last'] = z_root() . '/' . App::$query_string . '?page=' . $lastpage; - - return $ret; - - } - - - static function encode_item_collection($items,$id,$type,$activitypub = false,$total = 0) { - - if ($total > 100) { - $ret = [ - 'id' => z_root() . '/' . $id, - 'type' => $type . 'Page', - ]; - - $numpages = $total / App::$pager['itemspage']; - $lastpage = (($numpages > intval($numpages)) ? intval($numpages) + 1 : $numpages); - - $url_parts = parse_url($id); - - $ret['partOf'] = z_root() . '/' . $url_parts['path']; - - $extra_query_args = ''; - $query_args = null; - if (isset($url_parts['query'])) { - parse_str($url_parts['query'], $query_args); - } - - if (is_array($query_args)) { - unset($query_args['page']); - foreach ($query_args as $k => $v) { - $extra_query_args .= '&' . urlencode($k) . '=' . urlencode($v); - } - } - - if (App::$pager['page'] < $lastpage) { - $ret['next'] = z_root() . '/' . $url_parts['path'] . '?page=' . (intval(App::$pager['page']) + 1) . $extra_query_args; - } - if (App::$pager['page'] > 1) { - $ret['prev'] = z_root() . '/' . $url_parts['path'] . '?page=' . (intval(App::$pager['page']) - 1) . $extra_query_args; - } - } - else { - $ret = [ - 'id' => z_root() . '/' . $id, - 'type' => $type, - 'totalItems' => $total, - ]; - } - - - if ($items) { - $x = []; - foreach ($items as $i) { - $m = get_iconfig($i['id'],'activitypub','rawmsg'); - if ($m) { - $t = json_decode($m,true); - } - else { - $t = self::encode_activity($i,$activitypub); - } - if ($t) { - $x[] = $t; - } - } - if ($type === 'OrderedCollection') { - $ret['orderedItems'] = $x; - } - else { - $ret['items'] = $x; - } - } - - return $ret; - } - - static function encode_follow_collection($items,$id,$type,$total = 0,$extra = null) { - - if ($total > 100) { - $ret = [ - 'id' => z_root() . '/' . $id, - 'type' => $type . 'Page', - ]; - - $numpages = $total / App::$pager['itemspage']; - $lastpage = (($numpages > intval($numpages)) ? intval($numpages) + 1 : $numpages); - - $stripped = preg_replace('/([&|\?]page=[0-9]*)/','',$id); - $stripped = rtrim($stripped,'/'); - - $ret['partOf'] = z_root() . '/' . $stripped; - - if (App::$pager['page'] < $lastpage) { - $ret['next'] = z_root() . '/' . $stripped . '?page=' . (intval(App::$pager['page']) + 1); - } - if (App::$pager['page'] > 1) { - $ret['prev'] = z_root() . '/' . $stripped . '?page=' . (intval(App::$pager['page']) - 1); - } - } - else { - $ret = [ - 'id' => z_root() . '/' . $id, - 'type' => $type, - 'totalItems' => $total, - ]; - } - - if ($extra) { - $ret = array_merge($ret,$extra); - } - - if ($items) { - $x = []; - foreach ($items as $i) { - if ($i['xchan_network'] === 'activitypub') { - $x[] = $i['xchan_hash']; - } - else { - $x[] = $i['xchan_url']; - } - } - - if ($type === 'OrderedCollection') { - $ret['orderedItems'] = $x; - } - else { - $ret['items'] = $x; - } - } - - return $ret; - } - - - - static function encode_simple_collection($items,$id,$type,$total = 0,$extra = null) { - - $ret = [ - 'id' => z_root() . '/' . $id, - 'type' => $type, - 'totalItems' => $total, - ]; - - if ($extra) { - $ret = array_merge($ret,$extra); - } - - if ($items) { - if ($type === 'OrderedCollection') { - $ret['orderedItems'] = $items; - } - else { - $ret['items'] = $items; - } - } - - return $ret; - } - - - - - - static function decode_taxonomy($item) { - - $ret = []; - - if (array_key_exists('tag',$item) && is_array($item['tag'])) { - $ptr = $item['tag']; - if (! array_key_exists(0,$ptr)) { - $ptr = [ $ptr ]; - } - foreach ($ptr as $t) { - if (! is_array($t)) { - continue; - } - if (! array_key_exists('type',$t)) { - $t['type'] = 'Hashtag'; - } - if (! (array_key_exists('name',$t))) { - continue; - } - if (! (array_path_exists('icon/url',$t) || array_key_exists('href',$t))) { - continue; - } - - switch($t['type']) { - case 'Hashtag': - $ret[] = [ 'ttype' => TERM_HASHTAG, 'url' => $t['href'], 'term' => escape_tags((substr($t['name'],0,1) === '#') ? substr($t['name'],1) : $t['name']) ]; - break; - - case 'topicalCollection': - $ret[] = [ 'ttype' => TERM_PCATEGORY, 'url' => $t['href'], 'term' => escape_tags($t['name']) ]; - break; - - case 'Category': - $ret[] = [ 'ttype' => TERM_CATEGORY, 'url' => $t['href'], 'term' => escape_tags($t['name']) ]; - break; - - case 'Mention': - $mention_type = substr($t['name'],0,1); - if ($mention_type === '!') { - $ret[] = [ 'ttype' => TERM_FORUM, 'url' => $t['href'], 'term' => escape_tags(substr($t['name'],1)) ]; - } - else { - $ret[] = [ 'ttype' => TERM_MENTION, 'url' => $t['href'], 'term' => escape_tags((substr($t['name'],0,1) === '@') ? substr($t['name'],1) : $t['name']) ]; - } - break; - - case 'Emoji': - $ret[] = [ 'ttype' => TERM_EMOJI, 'url' => $t['icon']['url'], 'term' => escape_tags($t['name']) ]; - break; - - default: - break; - } - } - } - - return $ret; - } - - - static function encode_taxonomy($item) { - - $ret = []; - - if (isset($item['term']) && is_array($item['term']) && $item['term']) { - foreach ($item['term'] as $t) { - switch($t['ttype']) { - case TERM_HASHTAG: - // An id is required so if we don't have a url in the taxonomy, ignore it and keep going. - if ($t['url']) { - $ret[] = [ 'id' => $t['url'], 'name' => '#' . $t['term'] ]; - } - break; - - case TERM_PCATEGORY: - if ($t['url'] && $t['term']) { - $ret[] = [ 'type' => 'topicalCollection', 'href' => $t['url'], 'name' => $t['term'] ]; - } - break; - - case TERM_CATEGORY: - if ($t['url'] && $t['term']) { - $ret[] = [ 'type' => 'Category', 'href' => $t['url'], 'name' => $t['term'] ]; - } - break; - - case TERM_FORUM: - $term = self::lookup_term_addr($t['url'],$t['term']); - $ret[] = [ 'type' => 'Mention', 'href' => $t['url'], 'name' => '!' . (($term) ? $term : $t['term']) ]; - break; - - case TERM_MENTION: - $term = self::lookup_term_addr($t['url'],$t['term']); - $ret[] = [ 'type' => 'Mention', 'href' => $t['url'], 'name' => '@' . (($term) ? $term : $t['term']) ]; - break; - - default: - break; - } - } - } - - return $ret; - } - - - static function lookup_term_addr($url,$name) { - - // The visible mention in our activities is always the full name. - // In the object taxonomy change this to the webfinger handle in case - // platforms expect the Mastodon form in order to generate notifications - // Try a couple of different things in case the url provided isn't the canonical id. - // If all else fails, try to match the name. - - $r = false; - - if ($url) { - $r = q("select xchan_addr from xchan where ( xchan_url = '%s' OR xchan_hash = '%s' ) limit 1", - dbesc($url), - dbesc($url) - ); - - if ($r) { - return $r[0]['xchan_addr']; - } - } - if ($name) { - $r = q("select xchan_addr from xchan where xchan_name = '%s' limit 1", - dbesc($name) - ); - if ($r) { - return $r[0]['xchan_addr']; - } - - } - - return EMPTY_STR; - } - - - - static function lookup_term_url($url) { - - // The xchan_url for mastodon is a text/html rendering. This is called from map_mentions where we need - // to convert the mention url to an ActivityPub id. If this fails for any reason, return the url we have - - $r = q("select * from hubloc where hubloc_id_url = '%s' or hubloc_hash = '%s' limit 1", - dbesc($url), - dbesc($url) - ); - - if ($r) { - if ($r[0]['hubloc_network'] === 'activitypub') { - return $r[0]['hubloc_hash']; - } - return $r[0]['hubloc_id_url']; - } - - return $url; - } - - - static function encode_attachment($item) { - - $ret = []; - - if (array_key_exists('attach',$item)) { - $atts = ((is_array($item['attach'])) ? $item['attach'] : json_decode($item['attach'],true)); - if ($atts) { - foreach ($atts as $att) { - if (strpos($att['type'],'image')) { - $ret[] = [ 'type' => 'Image', 'url' => $att['href'] ]; - } - else { - $ret[] = [ 'type' => 'Link', 'mediaType' => $att['type'], 'href' => $att['href'] ]; - } - } - } - } - if (array_key_exists('iconfig',$item) && is_array($item['iconfig'])) { - foreach ($item['iconfig'] as $att) { - if ($att['sharing']) { - $ret[] = [ 'type' => 'PropertyValue', 'name' => 'zot.' . $att['cat'] . '.' . $att['k'], 'value' => unserialise($att['v']) ]; - } - } - } - - return $ret; - } - - - static function decode_iconfig($item) { - - $ret = []; - - if (is_array($item['attachment']) && $item['attachment']) { - $ptr = $item['attachment']; - if (! array_key_exists(0,$ptr)) { - $ptr = [ $ptr ]; - } - foreach ($ptr as $att) { - $entry = []; - if ($att['type'] === 'PropertyValue') { - if (array_key_exists('name',$att) && $att['name']) { - $key = explode('.',$att['name']); - if (count($key) === 3 && $key[0] === 'zot') { - $entry['cat'] = $key[1]; - $entry['k'] = $key[2]; - $entry['v'] = $att['value']; - $entry['sharing'] = '1'; - $ret[] = $entry; - } - } - } - } - } - return $ret; - } - - - - - static function decode_attachment($item) { - - $ret = []; - - if (array_key_exists('attachment',$item) && is_array($item['attachment'])) { - $ptr = $item['attachment']; - if (! array_key_exists(0,$ptr)) { - $ptr = [ $ptr ]; - } - foreach ($ptr as $att) { - $entry = []; - if (array_key_exists('href',$att) && $att['href']) - $entry['href'] = $att['href']; - elseif (array_key_exists('url',$att) && $att['url']) - $entry['href'] = $att['url']; - if (array_key_exists('mediaType',$att) && $att['mediaType']) - $entry['type'] = $att['mediaType']; - elseif (array_key_exists('type',$att) && $att['type'] === 'Image') - $entry['type'] = 'image/jpeg'; - if (array_key_exists('name',$att) && $att['name']) { - $entry['name'] = html2plain(purify_html($att['name']),256); - } - if ($entry) - $ret[] = $entry; - } - } - elseif (is_string($item['attachment'])) { - btlogger('not an array: ' . $item['attachment']); - } - - return $ret; - } - - - // the $recurse flag encodes the original non-deleted object of a deleted activity - - static function encode_activity($i,$activitypub = false,$recurse = false) { - - $ret = []; - $reply = false; - - if (intval($i['item_deleted']) && (! $recurse)) { - - $is_response = ActivityStreams::is_response_activity($i['verb']); - - if ($is_response) { - $ret['type'] = 'Undo'; - $fragment = '#undo'; - } - else { - $ret['type'] = 'Delete'; - $fragment = '#delete'; - } - - $ret['id'] = str_replace('/item/','/activity/',$i['mid']) . $fragment; - $actor = self::encode_person($i['author'],false); - if ($actor) - $ret['actor'] = $actor; - else - return []; - - $obj = (($is_response) ? self::encode_activity($i,$activitypub,true) : self::encode_item($i,$activitypub)); - if ($obj) { - if (array_path_exists('object/id',$obj)) { - $obj['object'] = $obj['object']['id']; - } - if ($obj) { - $ret['object'] = $obj; - } + return self::fetch_item($x, ((get_config('system', 'activitypub', ACTIVITYPUB_ENABLED)) ? true : false)); + } + if ($x['type'] === ACTIVITY_OBJ_THING) { + return self::fetch_thing($x); + } + + call_hooks('encode_object', $x); + + return $x; + } + + + public static function fetch($url, $channel = null, $hub = null, $debug = false) + { + $redirects = 0; + if (!check_siteallowed($url)) { + logger('denied: ' . $url); + return null; + } + if (!$channel) { + $channel = get_sys_channel(); + } + + $parsed = parse_url($url); + + // perform IDN substitution + + if ($parsed['host'] !== punify($parsed['host'])) { + $url = str_replace($parsed['host'], punify($parsed['host']), $url); + } + + logger('fetch: ' . $url, LOGGER_DEBUG); + + if (isset($parsed['scheme']) && $parsed['scheme'] === 'x-zot') { + $x = ZotURL::fetch($url, $channel, $hub); + } else { + // handle bearcaps + if (isset($parsed['scheme']) && isset($parsed['query']) && $parsed['scheme'] === 'bear' && $parsed['query'] !== EMPTY_STR) { + $params = explode('&', $parsed['query']); + if ($params) { + foreach ($params as $p) { + if (substr($p, 0, 2) === 'u=') { + $url = substr($p, 2); + } + if (substr($p, 0, 2) === 't=') { + $token = substr($p, 2); + } + } + // the entire URL just changed so parse it again + $parsed = parse_url($url); + } } - else { + + // Ignore fragments; as we are not in a browser and some platforms (e.g. Django or at least funkwhale) don't handle them well + unset($parsed['fragment']); + + // rebuild the url + $url = unparse_url($parsed); + + logger('fetch_actual: ' . $url, LOGGER_DEBUG); + + $headers = [ + 'Accept' => 'application/activity+json, application/x-zot-activity+json, application/ld+json; profile="https://www.w3.org/ns/activitystreams"', + 'Host' => $parsed['host'], + 'Date' => datetime_convert('UTC', 'UTC', 'now', 'D, d M Y H:i:s \\G\\M\\T'), + '(request-target)' => 'get ' . get_request_string($url) + ]; + + if (isset($token)) { + $headers['Authorization'] = 'Bearer ' . $token; + } + $h = HTTPSig::create_sig($headers, $channel['channel_prvkey'], channel_url($channel), false); + $x = z_fetch_url($url, true, $redirects, ['headers' => $h]); + } + + if ($x['success']) { + $y = json_decode($x['body'], true); + logger('returned: ' . json_encode($y, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)); + + $site_url = unparse_url(['scheme' => $parsed['scheme'], 'host' => $parsed['host'], 'port' => ((array_key_exists('port', $parsed) && intval($parsed['port'])) ? $parsed['port'] : 0)]); + q( + "update site set site_update = '%s' where site_url = '%s' and site_update < %s - INTERVAL %s", + dbesc(datetime_convert()), + dbesc($site_url), + db_utcnow(), + db_quoteinterval('1 DAY') + ); + + // check for a valid signature, but only if this is not an actor object. If it is signed, it must be valid. + // Ignore actors because of the potential for infinite recursion if we perform this step while + // fetching an actor key to validate a signature elsewhere. This should validate relayed activities + // over litepub which arrived at our inbox that do not use LD signatures + + if (($y['type']) && (!ActivityStreams::is_an_actor($y['type']))) { + $sigblock = HTTPSig::verify($x); + + if (($sigblock['header_signed']) && (!$sigblock['header_valid'])) { + return null; + } + } + + return json_decode($x['body'], true); + } else { + logger('fetch failed: ' . $url); + if ($debug) { + return $x; + } + } + return null; + } + + + public static function fetch_person($x) + { + return self::fetch_profile($x); + } + + public static function fetch_profile($x) + { + $r = q( + "select * from xchan left join hubloc on xchan_hash = hubloc_hash where hubloc_id_url = '%s' limit 1", + dbesc($x['id']) + ); + if (!$r) { + $r = q( + "select * from xchan where xchan_hash = '%s' limit 1", + dbesc($x['id']) + ); + } + if (!$r) { + return []; + } + + return self::encode_person($r[0], false); + } + + public static function fetch_thing($x) + { + + $r = q( + "select * from obj where obj_type = %d and obj_obj = '%s' limit 1", + intval(TERM_OBJ_THING), + dbesc($x['id']) + ); + + if (!$r) { + return []; + } + + $x = [ + 'type' => 'Object', + 'id' => z_root() . '/thing/' . $r[0]['obj_obj'], + 'name' => $r[0]['obj_term'] + ]; + + if ($r[0]['obj_image']) { + $x['image'] = $r[0]['obj_image']; + } + return $x; + } + + public static function fetch_item($x, $activitypub = false) + { + + if (array_key_exists('source', $x)) { + // This item is already processed and encoded + return $x; + } + + $r = q( + "select * from item where mid = '%s' limit 1", + dbesc($x['id']) + ); + if ($r) { + xchan_query($r, true); + $r = fetch_post_tags($r, true); + if ($r[0]['verb'] === 'Invite') { + return self::encode_activity($r[0], $activitypub); + } + return self::encode_item($r[0], $activitypub); + } + } + + public static function paged_collection_init($total, $id, $type = 'OrderedCollection') + { + + $ret = [ + 'id' => z_root() . '/' . $id, + 'type' => $type, + 'totalItems' => $total, + ]; + + $numpages = $total / App::$pager['itemspage']; + $lastpage = (($numpages > intval($numpages)) ? intval($numpages) + 1 : $numpages); + + $ret['first'] = z_root() . '/' . App::$query_string . '?page=1'; + $ret['last'] = z_root() . '/' . App::$query_string . '?page=' . $lastpage; + + return $ret; + } + + + public static function encode_item_collection($items, $id, $type, $activitypub = false, $total = 0) + { + + if ($total > 100) { + $ret = [ + 'id' => z_root() . '/' . $id, + 'type' => $type . 'Page', + ]; + + $numpages = $total / App::$pager['itemspage']; + $lastpage = (($numpages > intval($numpages)) ? intval($numpages) + 1 : $numpages); + + $url_parts = parse_url($id); + + $ret['partOf'] = z_root() . '/' . $url_parts['path']; + + $extra_query_args = ''; + $query_args = null; + if (isset($url_parts['query'])) { + parse_str($url_parts['query'], $query_args); + } + + if (is_array($query_args)) { + unset($query_args['page']); + foreach ($query_args as $k => $v) { + $extra_query_args .= '&' . urlencode($k) . '=' . urlencode($v); + } + } + + if (App::$pager['page'] < $lastpage) { + $ret['next'] = z_root() . '/' . $url_parts['path'] . '?page=' . (intval(App::$pager['page']) + 1) . $extra_query_args; + } + if (App::$pager['page'] > 1) { + $ret['prev'] = z_root() . '/' . $url_parts['path'] . '?page=' . (intval(App::$pager['page']) - 1) . $extra_query_args; + } + } else { + $ret = [ + 'id' => z_root() . '/' . $id, + 'type' => $type, + 'totalItems' => $total, + ]; + } + + + if ($items) { + $x = []; + foreach ($items as $i) { + $m = get_iconfig($i['id'], 'activitypub', 'rawmsg'); + if ($m) { + $t = json_decode($m, true); + } else { + $t = self::encode_activity($i, $activitypub); + } + if ($t) { + $x[] = $t; + } + } + if ($type === 'OrderedCollection') { + $ret['orderedItems'] = $x; + } else { + $ret['items'] = $x; + } + } + + return $ret; + } + + public static function encode_follow_collection($items, $id, $type, $total = 0, $extra = null) + { + + if ($total > 100) { + $ret = [ + 'id' => z_root() . '/' . $id, + 'type' => $type . 'Page', + ]; + + $numpages = $total / App::$pager['itemspage']; + $lastpage = (($numpages > intval($numpages)) ? intval($numpages) + 1 : $numpages); + + $stripped = preg_replace('/([&|\?]page=[0-9]*)/', '', $id); + $stripped = rtrim($stripped, '/'); + + $ret['partOf'] = z_root() . '/' . $stripped; + + if (App::$pager['page'] < $lastpage) { + $ret['next'] = z_root() . '/' . $stripped . '?page=' . (intval(App::$pager['page']) + 1); + } + if (App::$pager['page'] > 1) { + $ret['prev'] = z_root() . '/' . $stripped . '?page=' . (intval(App::$pager['page']) - 1); + } + } else { + $ret = [ + 'id' => z_root() . '/' . $id, + 'type' => $type, + 'totalItems' => $total, + ]; + } + + if ($extra) { + $ret = array_merge($ret, $extra); + } + + if ($items) { + $x = []; + foreach ($items as $i) { + if ($i['xchan_network'] === 'activitypub') { + $x[] = $i['xchan_hash']; + } else { + $x[] = $i['xchan_url']; + } + } + + if ($type === 'OrderedCollection') { + $ret['orderedItems'] = $x; + } else { + $ret['items'] = $x; + } + } + + return $ret; + } + + + public static function encode_simple_collection($items, $id, $type, $total = 0, $extra = null) + { + + $ret = [ + 'id' => z_root() . '/' . $id, + 'type' => $type, + 'totalItems' => $total, + ]; + + if ($extra) { + $ret = array_merge($ret, $extra); + } + + if ($items) { + if ($type === 'OrderedCollection') { + $ret['orderedItems'] = $items; + } else { + $ret['items'] = $items; + } + } + + return $ret; + } + + + public static function decode_taxonomy($item) + { + + $ret = []; + + if (array_key_exists('tag', $item) && is_array($item['tag'])) { + $ptr = $item['tag']; + if (!array_key_exists(0, $ptr)) { + $ptr = [$ptr]; + } + foreach ($ptr as $t) { + if (!is_array($t)) { + continue; + } + if (!array_key_exists('type', $t)) { + $t['type'] = 'Hashtag'; + } + if (!(array_key_exists('name', $t))) { + continue; + } + if (!(array_path_exists('icon/url', $t) || array_key_exists('href', $t))) { + continue; + } + + switch ($t['type']) { + case 'Hashtag': + $ret[] = ['ttype' => TERM_HASHTAG, 'url' => $t['href'], 'term' => escape_tags((substr($t['name'], 0, 1) === '#') ? substr($t['name'], 1) : $t['name'])]; + break; + + case 'topicalCollection': + $ret[] = ['ttype' => TERM_PCATEGORY, 'url' => $t['href'], 'term' => escape_tags($t['name'])]; + break; + + case 'Category': + $ret[] = ['ttype' => TERM_CATEGORY, 'url' => $t['href'], 'term' => escape_tags($t['name'])]; + break; + + case 'Mention': + $mention_type = substr($t['name'], 0, 1); + if ($mention_type === '!') { + $ret[] = ['ttype' => TERM_FORUM, 'url' => $t['href'], 'term' => escape_tags(substr($t['name'], 1))]; + } else { + $ret[] = ['ttype' => TERM_MENTION, 'url' => $t['href'], 'term' => escape_tags((substr($t['name'], 0, 1) === '@') ? substr($t['name'], 1) : $t['name'])]; + } + break; + + case 'Emoji': + $ret[] = ['ttype' => TERM_EMOJI, 'url' => $t['icon']['url'], 'term' => escape_tags($t['name'])]; + break; + + default: + break; + } + } + } + + return $ret; + } + + + public static function encode_taxonomy($item) + { + + $ret = []; + + if (isset($item['term']) && is_array($item['term']) && $item['term']) { + foreach ($item['term'] as $t) { + switch ($t['ttype']) { + case TERM_HASHTAG: + // An id is required so if we don't have a url in the taxonomy, ignore it and keep going. + if ($t['url']) { + $ret[] = ['id' => $t['url'], 'name' => '#' . $t['term']]; + } + break; + + case TERM_PCATEGORY: + if ($t['url'] && $t['term']) { + $ret[] = ['type' => 'topicalCollection', 'href' => $t['url'], 'name' => $t['term']]; + } + break; + + case TERM_CATEGORY: + if ($t['url'] && $t['term']) { + $ret[] = ['type' => 'Category', 'href' => $t['url'], 'name' => $t['term']]; + } + break; + + case TERM_FORUM: + $term = self::lookup_term_addr($t['url'], $t['term']); + $ret[] = ['type' => 'Mention', 'href' => $t['url'], 'name' => '!' . (($term) ? $term : $t['term'])]; + break; + + case TERM_MENTION: + $term = self::lookup_term_addr($t['url'], $t['term']); + $ret[] = ['type' => 'Mention', 'href' => $t['url'], 'name' => '@' . (($term) ? $term : $t['term'])]; + break; + + default: + break; + } + } + } + + return $ret; + } + + + public static function lookup_term_addr($url, $name) + { + + // The visible mention in our activities is always the full name. + // In the object taxonomy change this to the webfinger handle in case + // platforms expect the Mastodon form in order to generate notifications + // Try a couple of different things in case the url provided isn't the canonical id. + // If all else fails, try to match the name. + + $r = false; + + if ($url) { + $r = q( + "select xchan_addr from xchan where ( xchan_url = '%s' OR xchan_hash = '%s' ) limit 1", + dbesc($url), + dbesc($url) + ); + + if ($r) { + return $r[0]['xchan_addr']; + } + } + if ($name) { + $r = q( + "select xchan_addr from xchan where xchan_name = '%s' limit 1", + dbesc($name) + ); + if ($r) { + return $r[0]['xchan_addr']; + } + } + + return EMPTY_STR; + } + + + public static function lookup_term_url($url) + { + + // The xchan_url for mastodon is a text/html rendering. This is called from map_mentions where we need + // to convert the mention url to an ActivityPub id. If this fails for any reason, return the url we have + + $r = q( + "select * from hubloc where hubloc_id_url = '%s' or hubloc_hash = '%s' limit 1", + dbesc($url), + dbesc($url) + ); + + if ($r) { + if ($r[0]['hubloc_network'] === 'activitypub') { + return $r[0]['hubloc_hash']; + } + return $r[0]['hubloc_id_url']; + } + + return $url; + } + + + public static function encode_attachment($item) + { + + $ret = []; + + if (array_key_exists('attach', $item)) { + $atts = ((is_array($item['attach'])) ? $item['attach'] : json_decode($item['attach'], true)); + if ($atts) { + foreach ($atts as $att) { + if (strpos($att['type'], 'image')) { + $ret[] = ['type' => 'Image', 'url' => $att['href']]; + } else { + $ret[] = ['type' => 'Link', 'mediaType' => $att['type'], 'href' => $att['href']]; + } + } + } + } + if (array_key_exists('iconfig', $item) && is_array($item['iconfig'])) { + foreach ($item['iconfig'] as $att) { + if ($att['sharing']) { + $ret[] = ['type' => 'PropertyValue', 'name' => 'zot.' . $att['cat'] . '.' . $att['k'], 'value' => unserialise($att['v'])]; + } + } + } + + return $ret; + } + + + public static function decode_iconfig($item) + { + + $ret = []; + + if (is_array($item['attachment']) && $item['attachment']) { + $ptr = $item['attachment']; + if (!array_key_exists(0, $ptr)) { + $ptr = [$ptr]; + } + foreach ($ptr as $att) { + $entry = []; + if ($att['type'] === 'PropertyValue') { + if (array_key_exists('name', $att) && $att['name']) { + $key = explode('.', $att['name']); + if (count($key) === 3 && $key[0] === 'zot') { + $entry['cat'] = $key[1]; + $entry['k'] = $key[2]; + $entry['v'] = $att['value']; + $entry['sharing'] = '1'; + $ret[] = $entry; + } + } + } + } + } + return $ret; + } + + + public static function decode_attachment($item) + { + + $ret = []; + + if (array_key_exists('attachment', $item) && is_array($item['attachment'])) { + $ptr = $item['attachment']; + if (!array_key_exists(0, $ptr)) { + $ptr = [$ptr]; + } + foreach ($ptr as $att) { + $entry = []; + if (array_key_exists('href', $att) && $att['href']) { + $entry['href'] = $att['href']; + } elseif (array_key_exists('url', $att) && $att['url']) { + $entry['href'] = $att['url']; + } + if (array_key_exists('mediaType', $att) && $att['mediaType']) { + $entry['type'] = $att['mediaType']; + } elseif (array_key_exists('type', $att) && $att['type'] === 'Image') { + $entry['type'] = 'image/jpeg'; + } + if (array_key_exists('name', $att) && $att['name']) { + $entry['name'] = html2plain(purify_html($att['name']), 256); + } + if ($entry) { + $ret[] = $entry; + } + } + } elseif (is_string($item['attachment'])) { + btlogger('not an array: ' . $item['attachment']); + } + + return $ret; + } + + + // the $recurse flag encodes the original non-deleted object of a deleted activity + + public static function encode_activity($i, $activitypub = false, $recurse = false) + { + + $ret = []; + $reply = false; + + if (intval($i['item_deleted']) && (!$recurse)) { + $is_response = ActivityStreams::is_response_activity($i['verb']); + + if ($is_response) { + $ret['type'] = 'Undo'; + $fragment = '#undo'; + } else { + $ret['type'] = 'Delete'; + $fragment = '#delete'; + } + + $ret['id'] = str_replace('/item/', '/activity/', $i['mid']) . $fragment; + $actor = self::encode_person($i['author'], false); + if ($actor) { + $ret['actor'] = $actor; + } else { return []; - } - - $ret['to'] = [ ACTIVITY_PUBLIC_INBOX ]; - return $ret; - - } - - $ret['type'] = self::activity_mapper($i['verb']); - - if (strpos($i['mid'],z_root() . '/item/') !== false) { - $ret['id'] = str_replace('/item/','/activity/',$i['mid']); - } - elseif (strpos($i['mid'],z_root() . '/event/') !== false) { - $ret['id'] = str_replace('/event/','/activity/',$i['mid']); - } - else { - $ret['id'] = $i['mid']; - } - - if ($i['title']) { - $ret['name'] = $i['title']; - } - - if ($i['summary']) { - $ret['summary'] = bbcode($i['summary'], [ 'export' => true ]); - } - - if ($ret['type'] === 'Announce') { - $tmp = $i['body']; - $ret['content'] = bbcode($tmp, [ 'export' => true ]); - $ret['source'] = [ - 'content' => $i['body'], - 'mediaType' => 'text/x-multicode' - ]; - if ($i['summary']) { - $ret['source']['summary'] = $i['summary']; - } - } - - $ret['published'] = datetime_convert('UTC','UTC',$i['created'],ATOM_TIME); - if ($i['created'] !== $i['edited']) { - $ret['updated'] = datetime_convert('UTC','UTC',$i['edited'],ATOM_TIME); - if ($ret['type'] === 'Create') { - $ret['type'] = 'Update'; - } - } - if ($i['app']) { - $ret['generator'] = [ 'type' => 'Application', 'name' => $i['app'] ]; - } - if ($i['location'] || $i['coord']) { - $ret['location'] = [ 'type' => 'Place' ]; - if ($i['location']) { - $ret['location']['name'] = $i['location']; - } - if ($i['coord']) { - $l = explode(' ',$i['coord']); - $ret['location']['latitude'] = $l[0]; - $ret['location']['longitude'] = $l[1]; - } - } - - if ($i['mid'] !== $i['parent_mid']) { - $reply = true; - - // inReplyTo needs to be set in the activity for followup actions (Like, Dislike, Announce, etc.), - // but *not* for comments and RSVPs, where it should only be present in the object - - if (! in_array($ret['type'],[ 'Create','Update','Accept','Reject','TentativeAccept','TentativeReject' ])) { - $ret['inReplyTo'] = $i['thr_parent']; - $cnv = get_iconfig($i['parent'],'activitypub','context'); - if (! $cnv) { - $cnv = get_iconfig($i['parent'],'ostatus','conversation'); - } - if (! $cnv) { - $cnv = $ret['parent_mid']; - } - } - } - - if (! (isset($cnv) && $cnv)) { - $cnv = get_iconfig($i,'activitypub','context'); - if (! $cnv) { - $cnv = get_iconfig($i,'ostatus','conversation'); - } - if (! $cnv) { - $cnv = $i['parent_mid']; - } - } - if (isset($cnv) && $cnv) { - if (strpos($cnv,z_root()) === 0) { - $cnv = str_replace(['/item/','/activity/'],[ '/conversation/', '/conversation/' ], $cnv); - } - $ret['context'] = $cnv; - $ret['conversation'] = $cnv; - } - - if (intval($i['item_private']) === 2) { - $ret['directMessage'] = true; - } - - $actor = self::encode_person($i['author'],false); - if ($actor) - $ret['actor'] = $actor; - else - return []; - - - $replyto = unserialise($i['replyto']); - if ($replyto) { - $ret['replyTo'] = $replyto; - } - - - - - if (! isset($ret['url'])) { - $urls = []; - if (intval($i['item_wall'])) { - $locs = self::nomadic_locations($i); - if ($locs) { - foreach ($locs as $l) { - if (strpos($ret['id'],$l['hubloc_url']) !== false) { - continue; - } - $urls[] = [ - 'type' => 'Link', - 'href' => str_replace(z_root(),$l['hubloc_url'],$ret['id']), - 'rel' => 'alternate', - 'mediaType' => 'text/html' - ]; - $urls[] = [ - 'type' => 'Link', - 'href' => str_replace(z_root(),$l['hubloc_url'],$ret['id']), - 'rel' => 'alternate', - 'mediaType' => 'application/activity+json' - ]; - $urls[] = [ - 'type' => 'Link', - 'href' => str_replace(z_root(),$l['hubloc_url'],$ret['id']), - 'rel' => 'alternate', - 'mediaType' => 'application/x-nomad+json' - ]; - } - } - } - if ($urls) { - $curr[] = [ - 'type' => 'Link', - 'href' => $ret['id'], - 'rel' => 'alternate', - 'mediaType' => 'text/html' - ]; - $ret['url'] = array_merge($curr, $urls); - } - else { - $ret['url'] = $ret['id']; - } - } - - - if ($i['obj']) { - if (is_string($i['obj'])) { - $tmp = json_decode($i['obj'],true); - if ($tmp !== NULL) { - $i['obj'] = $tmp; - } - } - $obj = self::encode_object($i['obj']); - if ($obj) - $ret['object'] = $obj; - else - return []; - } - else { - $obj = self::encode_item($i,$activitypub); - if ($obj) { - $ret['object'] = $obj; - } - else { - return []; - } - } - - if ($i['target']) { - if (is_string($i['target'])) { - $tmp = json_decode($i['target'],true); - if ($tmp !== NULL) { - $i['target'] = $tmp; - } - } - $tgt = self::encode_object($i['target']); - if ($tgt) { - $ret['target'] = $tgt; - } - } - - $t = self::encode_taxonomy($i); - if ($t) { - $ret['tag'] = $t; - } - - // addressing madness - - if ($activitypub) { - - $parent_i = []; - $public = (($i['item_private']) ? false : true); - $top_level = (($reply) ? false : true); - $ret['to'] = []; - $ret['cc'] = []; - - if (! $top_level) { - $recips = get_iconfig($i['parent'], 'activitypub', 'recips'); - if ($recips) { - $parent_i['to'] = $recips['to']; - $parent_i['cc'] = $recips['cc']; - } - } - - if ($public) { - $ret['to'] = [ ACTIVITY_PUBLIC_INBOX ]; - if (isset($parent_i['to']) && is_array($parent_i['to'])) { - $ret['to'] = array_values(array_unique(array_merge($ret['to'],$parent_i['to']))); - } - if ($i['item_origin']) { - $ret['cc'] = [ z_root() . '/followers/' . substr($i['author']['xchan_addr'],0,strpos($i['author']['xchan_addr'],'@')) ]; - } - if (isset($parent_i['cc']) && is_array($parent_i['cc'])) { - $ret['cc'] = array_values(array_unique(array_merge($ret['cc'],$parent_i['cc']))); - } - } - else { - - // private activity - - if ($top_level) { - $ret['to'] = self::map_acl($i); - if (isset($parent_i['to']) && is_array($parent_i['to'])) { - $ret['to'] = array_values(array_unique(array_merge($ret['to'],$parent_i['to']))); - } - } - else { - $ret['cc'] = self::map_acl($i); - if (isset($parent_i['cc']) && is_array($parent_i['cc'])) { - $ret['cc'] = array_values(array_unique(array_merge($ret['cc'],$parent_i['cc']))); - } - - if ($ret['tag']) { - foreach ($ret['tag'] as $mention) { - if (is_array($mention) && array_key_exists('ttype',$mention) && in_array($mention['ttype'],[ TERM_FORUM, TERM_MENTION]) && array_key_exists('href',$mention) && $mention['href']) { - $h = q("select * from hubloc where hubloc_id_url = '%s' limit 1", - dbesc($mention['href']) - ); - if ($h) { - if ($h[0]['hubloc_network'] === 'activitypub') { - $addr = $h[0]['hubloc_hash']; - } - else { - $addr = $h[0]['hubloc_id_url']; - } - if (! in_array($addr,$ret['to'])) { - $ret['to'][] = $addr; - } - } - } - } - } - - $d = q("select hubloc.* from hubloc left join item on hubloc_hash = owner_xchan where item.parent_mid = '%s' and item.uid = %d limit 1", - dbesc($i['parent_mid']), - intval($i['uid']) - ); - if ($d) { - if ($d[0]['hubloc_network'] === 'activitypub') { - $addr = $d[0]['hubloc_hash']; - } - else { - $addr = $d[0]['hubloc_id_url']; - } - $ret['cc'][] = $addr; - } - } - } - - $mentions = self::map_mentions($i); - if (count($mentions) > 0) { - if (! $ret['to']) { - $ret['to'] = $mentions; - } - else { - $ret['to'] = array_values(array_unique(array_merge($ret['to'], $mentions))); - } - } - } - - $cc = []; - if ($ret['cc'] && is_array($ret['cc'])) { - foreach ($ret['cc'] as $e) { - if (! is_array($ret['to'])) { - $cc[] = $e; - } - elseif (! in_array($e,$ret['to'])) { - $cc[] = $e; - } - } - } - $ret['cc'] = $cc; - - return $ret; - } - - - static function nomadic_locations($item) { - $synchubs = []; - $h = q("select hubloc.*, site.site_crypto from hubloc left join site on site_url = hubloc_url - where hubloc_hash = '%s' and hubloc_network in ('nomad','zot6') and hubloc_deleted = 0", - dbesc($item['author_xchan']) - ); - - if (! $h) { - return []; - } - - foreach ($h as $x) { - $y = q("select site_dead from site where site_url = '%s' limit 1", - dbesc($x['hubloc_url']) - ); - - if ((! $y) || intval($y[0]['site_dead']) === 0) { - $synchubs[] = $x; - } - } - - return $synchubs; - } - - - static function encode_item($i, $activitypub = false) { - - $ret = []; - $reply = false; - $is_directmessage = false; - - $bbopts = (($activitypub) ? 'activitypub' : 'export'); - - $objtype = self::activity_obj_mapper($i['obj_type']); - - if (intval($i['item_deleted'])) { - $ret['type'] = 'Tombstone'; - $ret['formerType'] = $objtype; - $ret['id'] = $i['mid']; - $ret['to'] = [ ACTIVITY_PUBLIC_INBOX ]; - return $ret; - } - - if (isset($i['obj']) && $i['obj']) { - if (is_string($i['obj'])) { - $tmp = json_decode($i['obj'],true); - if ($tmp !== NULL) { - $i['obj'] = $tmp; - } - } - $ret = $i['obj']; - if (is_string($ret)) { - return $ret; - } - } - - - $ret['type'] = $objtype; - - if ($objtype === 'Question') { - if ($i['obj']) { - if (is_array($i['obj'])) { - $ret = $i['obj']; - } - else { - $ret = json_decode($i['obj'],true); - } - - if(array_path_exists('actor/id',$ret)) { - $ret['actor'] = $ret['actor']['id']; - } - } - } - - - $images = false; - $has_images = preg_match_all('/\[[zi]mg(.*?)\](.*?)\[/ism',$i['body'],$images,PREG_SET_ORDER); - - $ret['id'] = $i['mid']; - -// $token = IConfig::get($i,'ocap','relay'); -// if ($token) { -// if (defined('USE_BEARCAPS')) { -// $ret['id'] = 'bear:?u=' . $ret['id'] . '&t=' . $token; -// } -// else { -// $ret['id'] = $ret['id'] . '?token=' . $token; -// } -// } - - $ret['published'] = datetime_convert('UTC','UTC',$i['created'],ATOM_TIME); - if ($i['created'] !== $i['edited']) { - $ret['updated'] = datetime_convert('UTC','UTC',$i['edited'],ATOM_TIME); - } - if ($i['expires'] > NULL_DATE) { - $ret['expires'] = datetime_convert('UTC','UTC',$i['expires'],ATOM_TIME); - } - if ($i['app']) { - $ret['generator'] = [ 'type' => 'Application', 'name' => $i['app'] ]; - } - if ($i['location'] || $i['coord']) { - $ret['location'] = [ 'type' => 'Place' ]; - if ($i['location']) { - $ret['location']['name'] = $i['location']; - } - if ($i['coord']) { - $l = explode(' ',$i['coord']); - $ret['location']['latitude'] = $l[0]; - $ret['location']['longitude'] = $l[1]; - } - } - - if (intval($i['item_wall']) && $i['mid'] === $i['parent_mid']) { - $ret['commentPolicy'] = $i['comment_policy']; - } - - if (intval($i['item_private']) === 2) { - $ret['directMessage'] = true; - } - - if (intval($i['item_nocomment'])) { - if($ret['commentPolicy']) { - $ret['commentPolicy'] .= ' '; - } - $ret['commentPolicy'] .= 'until=' . datetime_convert('UTC','UTC',$i['created'],ATOM_TIME); - } - elseif (array_key_exists('comments_closed',$i) && $i['comments_closed'] !== EMPTY_STR && $i['comments_closed'] > NULL_DATE) { - if($ret['commentPolicy']) { - $ret['commentPolicy'] .= ' '; - } - $ret['commentPolicy'] .= 'until=' . datetime_convert('UTC','UTC',$i['comments_closed'],ATOM_TIME); - } - - $ret['attributedTo'] = (in_array($i['author']['xchan_network'],['nomad','zot6']) ? $i['author']['xchan_url'] : $i['author']['xchan_hash']); - - if ($i['mid'] !== $i['parent_mid']) { - $ret['inReplyTo'] = $i['thr_parent']; - $cnv = get_iconfig($i['parent'],'activitypub','context'); - if (! $cnv) { - $cnv = get_iconfig($i['parent'],'ostatus','conversation'); - } - if (! $cnv) { - $cnv = $ret['parent_mid']; - } - - $reply = true; - - if ($i['item_private']) { - $d = q("select xchan_url, xchan_addr, xchan_name from item left join xchan on xchan_hash = author_xchan where id = %d limit 1", - intval($i['parent']) - ); - if ($d) { - $recips = get_iconfig($i['parent'], 'activitypub', 'recips'); - - if (is_array($recips) && in_array($i['author']['xchan_url'], $recips['to'])) { - $reply_url = $d[0]['xchan_url']; - $is_directmessage = true; - } - else { - $reply_url = z_root() . '/followers/' . substr($i['author']['xchan_addr'],0,strpos($i['author']['xchan_addr'],'@')); - } - $reply_addr = (($d[0]['xchan_addr']) ? $d[0]['xchan_addr'] : $d[0]['xchan_name']); - } - } - } - if (! isset($cnv)) { - $cnv = get_iconfig($i,'activitypub','context'); - if (! $cnv) { - $cnv = get_iconfig($i,'ostatus','conversation'); - } - if (! $cnv) { - $cnv = $i['parent_mid']; - } - } - if (isset($cnv) && $cnv) { - if (strpos($cnv,z_root()) === 0) { - $cnv = str_replace(['/item/','/activity/'],[ '/conversation/', '/conversation/' ], $cnv); - } - $ret['context'] = $cnv; - $ret['conversation'] = $cnv; - } - - // provide ocap access token for private media. - // set this for descendants even if the current item is not private - // because it may have been relayed from a private item. - - $token = get_iconfig($i,'ocap','relay'); - if ($token && $has_images) { - for ($n = 0; $n < count($images); $n ++) { - $match = $images[$n]; - if (strpos($match[1],'=http') === 0 && strpos($match[1],'/photo/' !== false)) { - $i['body'] = str_replace($match[1],$match[1] . '?token=' . $token, $i['body']); - $images[$n][2] = substr($match[1],1) . '?token=' . $token; - } - elseif (strpos($match[2],z_root() . '/photo/') !== false) { - $i['body'] = str_replace($match[2],$match[2] . '?token=' . $token, $i['body']); - $images[$n][2] = $match[2] . '?token=' . $token; - } - } - } - - if ($i['title']) { - $ret['name'] = $i['title']; - } - - if ($i['mimetype'] === 'text/x-multicode') { - if ($i['summary']) { - $ret['summary'] = bbcode($i['summary'], [ $bbopts => true ]); - } - $opts = [ $bbopts => true ]; - $ret['content'] = bbcode($i['body'], $opts); - $ret['source'] = [ 'content' => $i['body'], 'mediaType' => 'text/x-multicode' ]; - if (isset($ret['summary'])) { - $ret['source']['summary'] = $i['summary']; - } - } - else { - $ret['mediaType'] = $i['mimetype']; - $ret['content'] = $i['body']; - } - - if (! (isset($ret['actor']) || isset($ret['attributedTo']))) { - $actor = self::encode_person($i['author'],false); - if ($actor) { - $ret['actor'] = $actor; - } - else { - return []; - } - } - - $replyto = unserialise($i['replyto']); - if ($replyto) { - $ret['replyTo'] = $replyto; - } - - if (! isset($ret['url'])) { - $urls = []; - if (intval($i['item_wall'])) { - $locs = self::nomadic_locations($i); - if ($locs) { - foreach ($locs as $l) { - if (strpos($i['mid'],$l['hubloc_url']) !== false) { - continue; - } - $urls[] = [ - 'type' => 'Link', - 'href' => str_replace(z_root(),$l['hubloc_url'],$ret['id']), - 'rel' => 'alternate', - 'mediaType' => 'text/html' - ]; - $urls[] = [ - 'type' => 'Link', - 'href' => str_replace(z_root(),$l['hubloc_url'],$ret['id']), - 'rel' => 'alternate', - 'mediaType' => 'application/activity+json' - ]; - $urls[] = [ - 'type' => 'Link', - 'href' => str_replace(z_root(),$l['hubloc_url'],$ret['id']), - 'rel' => 'alternate', - 'mediaType' => 'application/x-nomad+json' - ]; - } - } - } - if ($urls) { - $curr[] = [ - 'type' => 'Link', - 'href' => $ret['id'], - 'rel' => 'alternate', - 'mediaType' => 'text/html' - ]; - $ret['url'] = array_merge($curr, $urls); - } - else { - $ret['url'] = $ret['id']; - } - } - - $t = self::encode_taxonomy($i); - if ($t) { - $ret['tag'] = $t; - } - - $a = self::encode_attachment($i); - if ($a) { - $ret['attachment'] = $a; - } - - - if ($activitypub && $has_images && $ret['type'] === 'Note') { - foreach ($images as $match) { - $img = []; - // handle Friendica/Hubzilla style img links with [img=$url]$alttext[/img] - if (strpos($match[1],'=http') === 0) { - $img[] = [ 'type' => 'Image', 'url' => substr($match[1],1), 'name' => $match[2] ]; - } - // preferred mechanism for adding alt text - elseif (strpos($match[1],'alt=') !== false) { - $txt = str_replace('"','"',$match[1]); - $txt = substr($match[1],strpos($match[1],'alt="')+5,-1); - $img[] = [ 'type' => 'Image', 'url' => $match[2], 'name' => $txt ]; - } - else { - $img[] = [ 'type' => 'Image', 'url' => $match[2] ]; - } - - if (! $ret['attachment']) { - $ret['attachment'] = []; - } - $already_added = false; - if ($img) { - for ($pc = 0; $pc < count($ret['attachment']); $pc ++) { - // caution: image attachments use url and links use href, and our own links will be 'attach' links based on the image href - // We could alternatively supply the correct attachment info when item is saved, but by replacing here we will pick up - // any "per-post" or manual changes to the image alt-text before sending. - - if ((isset($ret['attachment'][$pc]['href']) && strpos($img[0]['url'],str_replace('/attach/','/photo/',$ret['attachment'][$pc]['href'])) !== false) || (isset($ret['attachment'][$pc]['url']) && $ret['attachment'][$pc]['url'] === $img[0]['url'])) { - // if it's already there, replace it with our alt-text aware version - $ret['attachment'][$pc] = $img[0]; - $already_added = true; - } - } - if (! $already_added) { - // add it - $ret['attachment'] = array_merge($img,$ret['attachment']); - } - } - } - } - - // addressing madness - - if ($activitypub) { - - $parent_i = []; - $ret['to'] = []; - $ret['cc'] = []; - - $public = (($i['item_private']) ? false : true); - $top_level = (($i['mid'] === $i['parent_mid']) ? true : false); - - if (! $top_level) { - - if (intval($i['parent'])) { - $recips = get_iconfig($i['parent'], 'activitypub', 'recips'); - } - else { - // if we are encoding this item for storage there won't be a parent. - $p = q("select parent from item where parent_mid = '%s' and uid = %d", - dbesc($i['parent_mid']), - intval($i['uid']) - ); - if ($p) { - $recips = get_iconfig($p[0]['parent'], 'activitypub', 'recips'); - } - } - if ($recips) { - $parent_i['to'] = $recips['to']; - $parent_i['cc'] = $recips['cc']; - } - } - - - if ($public) { - $ret['to'] = [ ACTIVITY_PUBLIC_INBOX ]; - if (isset($parent_i['to']) && is_array($parent_i['to'])) { - $ret['to'] = array_values(array_unique(array_merge($ret['to'],$parent_i['to']))); - } - if ($i['item_origin']) { - $ret['cc'] = [ z_root() . '/followers/' . substr($i['author']['xchan_addr'],0,strpos($i['author']['xchan_addr'],'@')) ]; - } - if (isset($parent_i['cc']) && is_array($parent_i['cc'])) { - $ret['cc'] = array_values(array_unique(array_merge($ret['cc'],$parent_i['cc']))); - } - - } - else { - - // private activity - - if ($top_level) { - $ret['to'] = self::map_acl($i); - if (isset($parent_i['to']) && is_array($parent_i['to'])) { - $ret['to'] = array_values(array_unique(array_merge($ret['to'],$parent_i['to']))); - } - } - else { - $ret['cc'] = self::map_acl($i); - if (isset($parent_i['cc']) && is_array($parent_i['cc'])) { - $ret['cc'] = array_values(array_unique(array_merge($ret['cc'],$parent_i['cc']))); - } - if ($ret['tag']) { - foreach ($ret['tag'] as $mention) { - if (is_array($mention) && array_key_exists('ttype',$mention) && in_array($mention['ttype'],[ TERM_FORUM, TERM_MENTION]) && array_key_exists('href',$mention) && $mention['href']) { - $h = q("select * from hubloc where hubloc_id_url = '%s' or hubloc_hash = '%s' limit 1", - dbesc($mention['href']), - dbesc($mention['href']) - ); - if ($h) { - if ($h[0]['hubloc_network'] === 'activitypub') { - $addr = $h[0]['hubloc_hash']; - } - else { - $addr = $h[0]['hubloc_id_url']; - } - if (! in_array($addr,$ret['to'])) { - $ret['to'][] = $addr; - } - } - } - } - } - - - $d = q("select hubloc.* from hubloc left join item on hubloc_hash = owner_xchan where item.parent_mid = '%s' and item.uid = %d limit 1", - dbesc($i['parent_mid']), - intval($i['uid']) - ); - - if ($d) { - if ($d[0]['hubloc_network'] === 'activitypub') { - $addr = $d[0]['hubloc_hash']; - } - else { - $addr = $d[0]['hubloc_id_url']; - } - $ret['cc'][] = $addr; - } - } - } - - $mentions = self::map_mentions($i); - if (count($mentions) > 0) { - if (! $ret['to']) { - $ret['to'] = $mentions; - } - else { - $ret['to'] = array_values(array_unique(array_merge($ret['to'], $mentions))); - } - } - } - - // remove any duplicates from 'cc' that are present in 'to' - // as this may indicate that mentions changed the audience from secondary to primary - - $cc = []; - if ($ret['cc'] && is_array($ret['cc'])) { - foreach ($ret['cc'] as $e) { - if (! is_array($ret['to'])) { - $cc[] = $e; - } - elseif (! in_array($e,$ret['to'])) { - $cc[] = $e; - } - } - } - $ret['cc'] = $cc; - - return $ret; - } - - - - - // Returns an array of URLS for any mention tags found in the item array $i. - - static function map_mentions($i) { - if (! (array_key_exists('term',$i) && is_array($i['term']))) { - return []; - } - - $list = []; - - foreach ($i['term'] as $t) { - if (! (array_key_exists('url',$t) && $t['url'])) { - continue; - } - if (array_key_exists('ttype',$t) && $t['ttype'] == TERM_MENTION) { - $url = self::lookup_term_url($t['url']); - $list[] = (($url) ? $url : $t['url']); - } - } - - return $list; - } - - // Returns an array of all recipients targeted by private item array $i. - - static function map_acl($i) { - $ret = []; - - if (! $i['item_private']) { - return $ret; - } - - if ($i['mid'] !== $i['parent_mid']) { - $i = q("select * from item where parent_mid = '%s' and uid = %d", - dbesc($i['parent_mid']), - intval($i['uid']) - ); - if ($i) { - $i = array_shift($i); - } - } - if ($i['allow_gid']) { - $tmp = expand_acl($i['allow_gid']); - if ($tmp) { - foreach ($tmp as $t) { - $ret[] = z_root() . '/lists/' . $t; - } - } - } - - if ($i['allow_cid']) { - $tmp = expand_acl($i['allow_cid']); - $list = stringify_array($tmp,true); - if ($list) { - $details = q("select hubloc_id_url, hubloc_hash, hubloc_network from hubloc where hubloc_hash in (" . $list . ") "); - if ($details) { - foreach ($details as $d) { - if ($d['hubloc_network'] === 'activitypub') { - $ret[] = $d['hubloc_hash']; - } - else { - $ret[] = $d['hubloc_id_url']; - } - } - } - } - } - - $x = get_iconfig($i['id'],'activitypub','recips'); - if ($x) { - foreach ([ 'to','cc' ] as $k) { - if (isset($x[$k])) { - if (is_string($x[$k])) { - $ret[] = $x[$k]; - } - else { - $ret = array_merge($ret,$x[$k]); - } - } - } - } - - return array_values(array_unique($ret)); - - } - - - static function encode_person($p, $extended = true, $activitypub = false) { - - $ret = []; - - if (! $p['xchan_url']) - return $ret; - - if (! $extended) { - return $p['xchan_url']; - } - - $c = ((array_key_exists('channel_id',$p)) ? $p : channelx_by_hash($p['xchan_hash'])); - - $ret['type'] = 'Person'; - $auto_follow = false; - - if ($c) { - $role = PConfig::Get($c['channel_id'],'system','permissions_role'); - if (strpos($role,'forum') !== false) { - $ret['type'] = 'Group'; - } - $role_permissions = PermissionRoles::role_perms($role); - if (is_array($role_permissions) && isset($role_permissions['perms_auto'])) { - $auto_follow = intval($role_permissions['perms_auto']); - } - } - - if ($c) { - $ret['id'] = channel_url($c); - } - else { - $ret['id'] = ((strpos($p['xchan_hash'],'http') === 0) ? $p['xchan_hash'] : $p['xchan_url']); - } - if ($p['xchan_addr'] && strpos($p['xchan_addr'],'@')) - $ret['preferredUsername'] = substr($p['xchan_addr'],0,strpos($p['xchan_addr'],'@')); - $ret['name'] = $p['xchan_name']; - $ret['updated'] = datetime_convert('UTC','UTC',$p['xchan_name_date'],ATOM_TIME); - $ret['icon'] = [ - 'type' => 'Image', - 'mediaType' => (($p['xchan_photo_mimetype']) ? $p['xchan_photo_mimetype'] : 'image/png' ), - 'updated' => datetime_convert('UTC','UTC',$p['xchan_photo_date'],ATOM_TIME), - 'url' => $p['xchan_photo_l'], - 'height' => 300, - 'width' => 300, - ]; - $ret['url'] = $p['xchan_url']; - if (isset($p['channel_location']) && $p['channel_location']) { - $ret['location'] = [ 'type' => 'Place', 'name' => $p['channel_location'] ]; - } - - $ret['tag'] = [ [ 'type' => 'PropertyValue','name' => 'Protocol','value' => 'zot6'] ]; - $ret['tag'][] = [ 'type' => 'PropertyValue','name' => 'Protocol','value' => 'nomad'] ; - - if ($activitypub && get_config('system','activitypub', ACTIVITYPUB_ENABLED)) { - - if ($c) { - if (get_pconfig($c['channel_id'],'system','activitypub', ACTIVITYPUB_ENABLED)) { - $ret['inbox'] = z_root() . '/inbox/' . $c['channel_address']; - $ret['tag'][] = [ 'type' => 'PropertyValue','name' => 'Protocol','value' => 'activitypub']; - } - else { - $ret['inbox'] = null; - } - - $ret['outbox'] = z_root() . '/outbox/' . $c['channel_address']; - $ret['followers'] = z_root() . '/followers/' . $c['channel_address']; - $ret['following'] = z_root() . '/following/' . $c['channel_address']; - - $ret['endpoints'] = [ - 'sharedInbox' => z_root() . '/inbox', - 'oauthRegistrationEndpoint' => z_root() . '/api/client/register', - 'oauthAuthorizationEndpoint' => z_root() . '/authorize', - 'oauthTokenEndpoint' => z_root() . '/token' - ]; - - $ret['discoverable'] = ((1 - intval($p['xchan_hidden'])) ? true : false); - $ret['publicKey'] = [ - 'id' => $p['xchan_url'] . '?operation=getkey', - 'owner' => $p['xchan_url'], - 'signatureAlgorithm' => 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256', - 'publicKeyPem' => $p['xchan_pubkey'] - ]; - - $ret['manuallyApprovesFollowers'] = (($auto_follow) ? false : true); - if ($ret['type'] === 'Group') { - $ret['capabilities'] = [ 'acceptsJoins' => true ]; - } - // map other nomadic identities linked with this channel - - $locations = []; - $locs = Libzot::encode_locations($c); - if ($locs) { - foreach ($locs as $loc) { - if ($loc['url'] !== z_root()) { - $locations[] = $loc['id_url']; - } - } - } - - if ($locations) { - if (count($locations) === 1) { - $locations = array_shift($locations); - } - $ret['copiedTo'] = $locations; - $ret['alsoKnownAs'] = $locations; - } - - $cp = get_cover_photo($c['channel_id'],'array'); - if ($cp) { - $ret['image'] = [ - 'type' => 'Image', - 'mediaType' => $cp['type'], - 'url' => $cp['url'] - ]; - } - // only fill in profile information if the profile is publicly visible - if (perm_is_allowed($c['channel_id'],EMPTY_STR,'view_profile')) { - $dp = q("select * from profile where uid = %d and is_default = 1", - intval($c['channel_id']) - ); - if ($dp) { - if ($dp[0]['about']) { - $ret['summary'] = bbcode($dp[0]['about'],['export' => true ]); - } - foreach ( [ 'pdesc', 'address', 'locality', 'region', 'postal_code', 'country_name', - 'hometown', 'gender', 'marital', 'sexual', 'politic', 'religion', 'pronouns', - 'homepage', 'contact', 'dob' ] as $k ) { - if ($dp[0][$k]) { - $key = $k; - if ($key === 'pdesc') { - $key = 'description'; - } - if ($key == 'politic') { - $key = 'political'; - } - if ($key === 'dob') { - $key = 'birthday'; - } - $ret['attachment'][] = [ 'type' => 'PropertyValue', 'name' => $key, 'value' => $dp[0][$k] ]; - } - } - if ($dp[0]['keywords']) { - $kw = explode(' ', $dp[0]['keywords']); - if ($kw) { - foreach ($kw as $k) { - $k = trim($k); - $k = trim($k,'#,'); - $ret['tag'][] = [ 'id' => z_root() . '/search?tag=' . urlencode($k), 'name' => '#' . urlencode($k) ]; - } - } - } - } - } - } - else { - $collections = get_xconfig($p['xchan_hash'],'activitypub','collections',[]); - if ($collections) { - $ret = array_merge($ret,$collections); - } - else { - $ret['inbox'] = null; - $ret['outbox'] = null; - } - } - } - else { - $ret['publicKey'] = [ - 'id' => $p['xchan_url'], - 'owner' => $p['xchan_url'], - 'publicKeyPem' => $p['xchan_pubkey'] - ]; - } - - $arr = [ 'xchan' => $p, 'encoded' => $ret, 'activitypub' => $activitypub ]; - call_hooks('encode_person', $arr); - $ret = $arr['encoded']; - - - return $ret; - } - - - - static function encode_site() { - - - $sys = get_sys_channel(); - - // encode the sys channel information and over-ride with site - // information - $ret = self::encode_person($sys,true,true); - - $ret['type'] = ((is_group($sys['channel_id'])) ? 'Group' : 'Service'); - $ret['id'] = z_root(); - $ret['alsoKnownAs'] = z_root() . '/channel/sys'; - $auto_follow = false; - - $ret['preferredUsername'] = 'sys'; - $ret['name'] = System::get_site_name(); - - $ret['icon'] = [ - 'type' => 'Image', - 'url' => System::get_site_icon(), - ]; - - $ret['generator'] = [ 'type' => 'Application', 'name' => System::get_platform_name() ]; - - $ret['url'] = z_root(); - - $ret['manuallyApprovesFollowers'] = ((get_config('system','allowed_sites')) ? true : false); - - $cp = get_cover_photo($sys['channel_id'],'array'); - if ($cp) { - $ret['image'] = [ - 'type' => 'Image', - 'mediaType' => $cp['type'], - 'url' => $cp['url'] - ]; - } - - $ret['summary'] = bbcode(get_config('system','siteinfo',''),[ 'export' => true ]); - $ret['source'] = [ - 'mediaType' => 'text/x-multicode', - 'summary' => get_config('system','siteinfo','') - ]; - - $ret['publicKey'] = [ - 'id' => z_root() . '?operation=getkey', - 'owner' => z_root(), - 'publicKeyPem' => get_config('system','pubkey') - ]; - - return $ret; - } - - - - static function activity_mapper($verb) { - - if (strpos($verb,'/') === false) { - return $verb; - } - - $acts = [ - 'http://activitystrea.ms/schema/1.0/post' => 'Create', - 'http://activitystrea.ms/schema/1.0/share' => 'Announce', - 'http://activitystrea.ms/schema/1.0/update' => 'Update', - 'http://activitystrea.ms/schema/1.0/like' => 'Like', - 'http://activitystrea.ms/schema/1.0/favorite' => 'Like', - 'http://purl.org/zot/activity/dislike' => 'Dislike', - 'http://activitystrea.ms/schema/1.0/tag' => 'Add', - 'http://activitystrea.ms/schema/1.0/follow' => 'Follow', - 'http://activitystrea.ms/schema/1.0/unfollow' => 'Ignore', - ]; + } + + $obj = (($is_response) ? self::encode_activity($i, $activitypub, true) : self::encode_item($i, $activitypub)); + if ($obj) { + if (array_path_exists('object/id', $obj)) { + $obj['object'] = $obj['object']['id']; + } + if ($obj) { + $ret['object'] = $obj; + } + } else { + return []; + } + + $ret['to'] = [ACTIVITY_PUBLIC_INBOX]; + return $ret; + } + + $ret['type'] = self::activity_mapper($i['verb']); + + if (strpos($i['mid'], z_root() . '/item/') !== false) { + $ret['id'] = str_replace('/item/', '/activity/', $i['mid']); + } elseif (strpos($i['mid'], z_root() . '/event/') !== false) { + $ret['id'] = str_replace('/event/', '/activity/', $i['mid']); + } else { + $ret['id'] = $i['mid']; + } + + if ($i['title']) { + $ret['name'] = $i['title']; + } + + if ($i['summary']) { + $ret['summary'] = bbcode($i['summary'], ['export' => true]); + } + + if ($ret['type'] === 'Announce') { + $tmp = $i['body']; + $ret['content'] = bbcode($tmp, ['export' => true]); + $ret['source'] = [ + 'content' => $i['body'], + 'mediaType' => 'text/x-multicode' + ]; + if ($i['summary']) { + $ret['source']['summary'] = $i['summary']; + } + } + + $ret['published'] = datetime_convert('UTC', 'UTC', $i['created'], ATOM_TIME); + if ($i['created'] !== $i['edited']) { + $ret['updated'] = datetime_convert('UTC', 'UTC', $i['edited'], ATOM_TIME); + if ($ret['type'] === 'Create') { + $ret['type'] = 'Update'; + } + } + if ($i['app']) { + $ret['generator'] = ['type' => 'Application', 'name' => $i['app']]; + } + if ($i['location'] || $i['coord']) { + $ret['location'] = ['type' => 'Place']; + if ($i['location']) { + $ret['location']['name'] = $i['location']; + } + if ($i['coord']) { + $l = explode(' ', $i['coord']); + $ret['location']['latitude'] = $l[0]; + $ret['location']['longitude'] = $l[1]; + } + } + + if ($i['mid'] !== $i['parent_mid']) { + $reply = true; + + // inReplyTo needs to be set in the activity for followup actions (Like, Dislike, Announce, etc.), + // but *not* for comments and RSVPs, where it should only be present in the object + + if (!in_array($ret['type'], ['Create', 'Update', 'Accept', 'Reject', 'TentativeAccept', 'TentativeReject'])) { + $ret['inReplyTo'] = $i['thr_parent']; + $cnv = get_iconfig($i['parent'], 'activitypub', 'context'); + if (!$cnv) { + $cnv = get_iconfig($i['parent'], 'ostatus', 'conversation'); + } + if (!$cnv) { + $cnv = $ret['parent_mid']; + } + } + } + + if (!(isset($cnv) && $cnv)) { + $cnv = get_iconfig($i, 'activitypub', 'context'); + if (!$cnv) { + $cnv = get_iconfig($i, 'ostatus', 'conversation'); + } + if (!$cnv) { + $cnv = $i['parent_mid']; + } + } + if (isset($cnv) && $cnv) { + if (strpos($cnv, z_root()) === 0) { + $cnv = str_replace(['/item/', '/activity/'], ['/conversation/', '/conversation/'], $cnv); + } + $ret['context'] = $cnv; + $ret['conversation'] = $cnv; + } + + if (intval($i['item_private']) === 2) { + $ret['directMessage'] = true; + } + + $actor = self::encode_person($i['author'], false); + if ($actor) { + $ret['actor'] = $actor; + } else { + return []; + } + + + $replyto = unserialise($i['replyto']); + if ($replyto) { + $ret['replyTo'] = $replyto; + } + + + if (!isset($ret['url'])) { + $urls = []; + if (intval($i['item_wall'])) { + $locs = self::nomadic_locations($i); + if ($locs) { + foreach ($locs as $l) { + if (strpos($ret['id'], $l['hubloc_url']) !== false) { + continue; + } + $urls[] = [ + 'type' => 'Link', + 'href' => str_replace(z_root(), $l['hubloc_url'], $ret['id']), + 'rel' => 'alternate', + 'mediaType' => 'text/html' + ]; + $urls[] = [ + 'type' => 'Link', + 'href' => str_replace(z_root(), $l['hubloc_url'], $ret['id']), + 'rel' => 'alternate', + 'mediaType' => 'application/activity+json' + ]; + $urls[] = [ + 'type' => 'Link', + 'href' => str_replace(z_root(), $l['hubloc_url'], $ret['id']), + 'rel' => 'alternate', + 'mediaType' => 'application/x-zot+json' + ]; + $urls[] = [ + 'type' => 'Link', + 'href' => str_replace(z_root(), $l['hubloc_url'], $ret['id']), + 'rel' => 'alternate', + 'mediaType' => 'application/x-nomad+json' + ]; + } + } + } + if ($urls) { + $curr[] = [ + 'type' => 'Link', + 'href' => $ret['id'], + 'rel' => 'alternate', + 'mediaType' => 'text/html' + ]; + $ret['url'] = array_merge($curr, $urls); + } else { + $ret['url'] = $ret['id']; + } + } + + + if ($i['obj']) { + if (is_string($i['obj'])) { + $tmp = json_decode($i['obj'], true); + if ($tmp !== null) { + $i['obj'] = $tmp; + } + } + $obj = self::encode_object($i['obj']); + if ($obj) { + $ret['object'] = $obj; + } else { + return []; + } + } else { + $obj = self::encode_item($i, $activitypub); + if ($obj) { + $ret['object'] = $obj; + } else { + return []; + } + } + + if ($i['target']) { + if (is_string($i['target'])) { + $tmp = json_decode($i['target'], true); + if ($tmp !== null) { + $i['target'] = $tmp; + } + } + $tgt = self::encode_object($i['target']); + if ($tgt) { + $ret['target'] = $tgt; + } + } + + $t = self::encode_taxonomy($i); + if ($t) { + $ret['tag'] = $t; + } + + // addressing madness + + if ($activitypub) { + $parent_i = []; + $public = (($i['item_private']) ? false : true); + $top_level = (($reply) ? false : true); + $ret['to'] = []; + $ret['cc'] = []; + + if (!$top_level) { + $recips = get_iconfig($i['parent'], 'activitypub', 'recips'); + if ($recips) { + $parent_i['to'] = $recips['to']; + $parent_i['cc'] = $recips['cc']; + } + } + + if ($public) { + $ret['to'] = [ACTIVITY_PUBLIC_INBOX]; + if (isset($parent_i['to']) && is_array($parent_i['to'])) { + $ret['to'] = array_values(array_unique(array_merge($ret['to'], $parent_i['to']))); + } + if ($i['item_origin']) { + $ret['cc'] = [z_root() . '/followers/' . substr($i['author']['xchan_addr'], 0, strpos($i['author']['xchan_addr'], '@'))]; + } + if (isset($parent_i['cc']) && is_array($parent_i['cc'])) { + $ret['cc'] = array_values(array_unique(array_merge($ret['cc'], $parent_i['cc']))); + } + } else { + // private activity + + if ($top_level) { + $ret['to'] = self::map_acl($i); + if (isset($parent_i['to']) && is_array($parent_i['to'])) { + $ret['to'] = array_values(array_unique(array_merge($ret['to'], $parent_i['to']))); + } + } else { + $ret['cc'] = self::map_acl($i); + if (isset($parent_i['cc']) && is_array($parent_i['cc'])) { + $ret['cc'] = array_values(array_unique(array_merge($ret['cc'], $parent_i['cc']))); + } + + if ($ret['tag']) { + foreach ($ret['tag'] as $mention) { + if (is_array($mention) && array_key_exists('ttype', $mention) && in_array($mention['ttype'], [TERM_FORUM, TERM_MENTION]) && array_key_exists('href', $mention) && $mention['href']) { + $h = q( + "select * from hubloc where hubloc_id_url = '%s' limit 1", + dbesc($mention['href']) + ); + if ($h) { + if ($h[0]['hubloc_network'] === 'activitypub') { + $addr = $h[0]['hubloc_hash']; + } else { + $addr = $h[0]['hubloc_id_url']; + } + if (!in_array($addr, $ret['to'])) { + $ret['to'][] = $addr; + } + } + } + } + } + + $d = q( + "select hubloc.* from hubloc left join item on hubloc_hash = owner_xchan where item.parent_mid = '%s' and item.uid = %d limit 1", + dbesc($i['parent_mid']), + intval($i['uid']) + ); + if ($d) { + if ($d[0]['hubloc_network'] === 'activitypub') { + $addr = $d[0]['hubloc_hash']; + } else { + $addr = $d[0]['hubloc_id_url']; + } + $ret['cc'][] = $addr; + } + } + } + + $mentions = self::map_mentions($i); + if (count($mentions) > 0) { + if (!$ret['to']) { + $ret['to'] = $mentions; + } else { + $ret['to'] = array_values(array_unique(array_merge($ret['to'], $mentions))); + } + } + } + + $cc = []; + if ($ret['cc'] && is_array($ret['cc'])) { + foreach ($ret['cc'] as $e) { + if (!is_array($ret['to'])) { + $cc[] = $e; + } elseif (!in_array($e, $ret['to'])) { + $cc[] = $e; + } + } + } + $ret['cc'] = $cc; + + return $ret; + } + + + public static function nomadic_locations($item) + { + $synchubs = []; + $h = q( + "select hubloc.*, site.site_crypto from hubloc left join site on site_url = hubloc_url + where hubloc_hash = '%s' and hubloc_network in ('zot6','nomad') and hubloc_deleted = 0", + dbesc($item['author_xchan']) + ); + + if (!$h) { + return []; + } + + foreach ($h as $x) { + $y = q( + "select site_dead from site where site_url = '%s' limit 1", + dbesc($x['hubloc_url']) + ); + + if ((!$y) || intval($y[0]['site_dead']) === 0) { + $synchubs[] = $x; + } + } + + return $synchubs; + } + + + public static function encode_item($i, $activitypub = false) + { + + $ret = []; + $reply = false; + $is_directmessage = false; + + $bbopts = (($activitypub) ? 'activitypub' : 'export'); + + $objtype = self::activity_obj_mapper($i['obj_type']); + + if (intval($i['item_deleted'])) { + $ret['type'] = 'Tombstone'; + $ret['formerType'] = $objtype; + $ret['id'] = $i['mid']; + $ret['to'] = [ACTIVITY_PUBLIC_INBOX]; + return $ret; + } + + if (isset($i['obj']) && $i['obj']) { + if (is_string($i['obj'])) { + $tmp = json_decode($i['obj'], true); + if ($tmp !== null) { + $i['obj'] = $tmp; + } + } + $ret = $i['obj']; + if (is_string($ret)) { + return $ret; + } + } + + + $ret['type'] = $objtype; + + if ($objtype === 'Question') { + if ($i['obj']) { + if (is_array($i['obj'])) { + $ret = $i['obj']; + } else { + $ret = json_decode($i['obj'], true); + } + + if (array_path_exists('actor/id', $ret)) { + $ret['actor'] = $ret['actor']['id']; + } + } + } + + + $images = false; + $has_images = preg_match_all('/\[[zi]mg(.*?)\](.*?)\[/ism', $i['body'], $images, PREG_SET_ORDER); + + $ret['id'] = $i['mid']; + +// $token = IConfig::get($i,'ocap','relay'); +// if ($token) { +// if (defined('USE_BEARCAPS')) { +// $ret['id'] = 'bear:?u=' . $ret['id'] . '&t=' . $token; +// } +// else { +// $ret['id'] = $ret['id'] . '?token=' . $token; +// } +// } + + $ret['published'] = datetime_convert('UTC', 'UTC', $i['created'], ATOM_TIME); + if ($i['created'] !== $i['edited']) { + $ret['updated'] = datetime_convert('UTC', 'UTC', $i['edited'], ATOM_TIME); + } + if ($i['expires'] > NULL_DATE) { + $ret['expires'] = datetime_convert('UTC', 'UTC', $i['expires'], ATOM_TIME); + } + if ($i['app']) { + $ret['generator'] = ['type' => 'Application', 'name' => $i['app']]; + } + if ($i['location'] || $i['coord']) { + $ret['location'] = ['type' => 'Place']; + if ($i['location']) { + $ret['location']['name'] = $i['location']; + } + if ($i['coord']) { + $l = explode(' ', $i['coord']); + $ret['location']['latitude'] = $l[0]; + $ret['location']['longitude'] = $l[1]; + } + } + + if (intval($i['item_wall']) && $i['mid'] === $i['parent_mid']) { + $ret['commentPolicy'] = $i['comment_policy']; + } + + if (intval($i['item_private']) === 2) { + $ret['directMessage'] = true; + } + + if (intval($i['item_nocomment'])) { + if ($ret['commentPolicy']) { + $ret['commentPolicy'] .= ' '; + } + $ret['commentPolicy'] .= 'until=' . datetime_convert('UTC', 'UTC', $i['created'], ATOM_TIME); + } elseif (array_key_exists('comments_closed', $i) && $i['comments_closed'] !== EMPTY_STR && $i['comments_closed'] > NULL_DATE) { + if ($ret['commentPolicy']) { + $ret['commentPolicy'] .= ' '; + } + $ret['commentPolicy'] .= 'until=' . datetime_convert('UTC', 'UTC', $i['comments_closed'], ATOM_TIME); + } + + $ret['attributedTo'] = ((in_array($i['author']['xchan_network'],['zot6','nomad'])) ? $i['author']['xchan_url'] : $i['author']['xchan_hash']); + + if ($i['mid'] !== $i['parent_mid']) { + $ret['inReplyTo'] = $i['thr_parent']; + $cnv = get_iconfig($i['parent'], 'activitypub', 'context'); + if (!$cnv) { + $cnv = get_iconfig($i['parent'], 'ostatus', 'conversation'); + } + if (!$cnv) { + $cnv = $ret['parent_mid']; + } + + $reply = true; + + if ($i['item_private']) { + $d = q( + "select xchan_url, xchan_addr, xchan_name from item left join xchan on xchan_hash = author_xchan where id = %d limit 1", + intval($i['parent']) + ); + if ($d) { + $recips = get_iconfig($i['parent'], 'activitypub', 'recips'); + + if (is_array($recips) && in_array($i['author']['xchan_url'], $recips['to'])) { + $reply_url = $d[0]['xchan_url']; + $is_directmessage = true; + } else { + $reply_url = z_root() . '/followers/' . substr($i['author']['xchan_addr'], 0, strpos($i['author']['xchan_addr'], '@')); + } + $reply_addr = (($d[0]['xchan_addr']) ? $d[0]['xchan_addr'] : $d[0]['xchan_name']); + } + } + } + if (!isset($cnv)) { + $cnv = get_iconfig($i, 'activitypub', 'context'); + if (!$cnv) { + $cnv = get_iconfig($i, 'ostatus', 'conversation'); + } + if (!$cnv) { + $cnv = $i['parent_mid']; + } + } + if (isset($cnv) && $cnv) { + if (strpos($cnv, z_root()) === 0) { + $cnv = str_replace(['/item/', '/activity/'], ['/conversation/', '/conversation/'], $cnv); + } + $ret['context'] = $cnv; + $ret['conversation'] = $cnv; + } + + // provide ocap access token for private media. + // set this for descendants even if the current item is not private + // because it may have been relayed from a private item. + + $token = get_iconfig($i, 'ocap', 'relay'); + if ($token && $has_images) { + for ($n = 0; $n < count($images); $n++) { + $match = $images[$n]; + if (strpos($match[1], '=http') === 0 && strpos($match[1], '/photo/' !== false)) { + $i['body'] = str_replace($match[1], $match[1] . '?token=' . $token, $i['body']); + $images[$n][2] = substr($match[1], 1) . '?token=' . $token; + } elseif (strpos($match[2], z_root() . '/photo/') !== false) { + $i['body'] = str_replace($match[2], $match[2] . '?token=' . $token, $i['body']); + $images[$n][2] = $match[2] . '?token=' . $token; + } + } + } + + if ($i['title']) { + $ret['name'] = $i['title']; + } + + if (in_array($i['mimetype'], [ 'text/bbcode', 'text/x-multicode' ])) { + if ($i['summary']) { + $ret['summary'] = bbcode($i['summary'], [$bbopts => true]); + } + $opts = [$bbopts => true]; + $ret['content'] = bbcode($i['body'], $opts); + $ret['source'] = ['content' => $i['body'], 'mediaType' => 'text/x-multicode']; + if (isset($ret['summary'])) { + $ret['source']['summary'] = $i['summary']; + } + } else { + $ret['mediaType'] = $i['mimetype']; + $ret['content'] = $i['body']; + } + + if (!(isset($ret['actor']) || isset($ret['attributedTo']))) { + $actor = self::encode_person($i['author'], false); + if ($actor) { + $ret['actor'] = $actor; + } else { + return []; + } + } + + $replyto = unserialise($i['replyto']); + if ($replyto) { + $ret['replyTo'] = $replyto; + } + + if (!isset($ret['url'])) { + $urls = []; + if (intval($i['item_wall'])) { + $locs = self::nomadic_locations($i); + if ($locs) { + foreach ($locs as $l) { + if (strpos($i['mid'], $l['hubloc_url']) !== false) { + continue; + } + $urls[] = [ + 'type' => 'Link', + 'href' => str_replace(z_root(), $l['hubloc_url'], $ret['id']), + 'rel' => 'alternate', + 'mediaType' => 'text/html' + ]; + $urls[] = [ + 'type' => 'Link', + 'href' => str_replace(z_root(), $l['hubloc_url'], $ret['id']), + 'rel' => 'alternate', + 'mediaType' => 'application/activity+json' + ]; + $urls[] = [ + 'type' => 'Link', + 'href' => str_replace(z_root(), $l['hubloc_url'], $ret['id']), + 'rel' => 'alternate', + 'mediaType' => 'application/x-nomad+json' + ]; + } + } + } + if ($urls) { + $curr[] = [ + 'type' => 'Link', + 'href' => $ret['id'], + 'rel' => 'alternate', + 'mediaType' => 'text/html' + ]; + $ret['url'] = array_merge($curr, $urls); + } else { + $ret['url'] = $ret['id']; + } + } + + $t = self::encode_taxonomy($i); + if ($t) { + $ret['tag'] = $t; + } + + $a = self::encode_attachment($i); + if ($a) { + $ret['attachment'] = $a; + } + + + if ($activitypub && $has_images && $ret['type'] === 'Note') { + foreach ($images as $match) { + $img = []; + // handle Friendica/Hubzilla style img links with [img=$url]$alttext[/img] + if (strpos($match[1], '=http') === 0) { + $img[] = ['type' => 'Image', 'url' => substr($match[1], 1), 'name' => $match[2]]; + } // preferred mechanism for adding alt text + elseif (strpos($match[1], 'alt=') !== false) { + $txt = str_replace('"', '"', $match[1]); + $txt = substr($match[1], strpos($match[1], 'alt="') + 5, -1); + $img[] = ['type' => 'Image', 'url' => $match[2], 'name' => $txt]; + } else { + $img[] = ['type' => 'Image', 'url' => $match[2]]; + } + + if (!$ret['attachment']) { + $ret['attachment'] = []; + } + $already_added = false; + if ($img) { + for ($pc = 0; $pc < count($ret['attachment']); $pc++) { + // caution: image attachments use url and links use href, and our own links will be 'attach' links based on the image href + // We could alternatively supply the correct attachment info when item is saved, but by replacing here we will pick up + // any "per-post" or manual changes to the image alt-text before sending. + + if ((isset($ret['attachment'][$pc]['href']) && strpos($img[0]['url'], str_replace('/attach/', '/photo/', $ret['attachment'][$pc]['href'])) !== false) || (isset($ret['attachment'][$pc]['url']) && $ret['attachment'][$pc]['url'] === $img[0]['url'])) { + // if it's already there, replace it with our alt-text aware version + $ret['attachment'][$pc] = $img[0]; + $already_added = true; + } + } + if (!$already_added) { + // add it + $ret['attachment'] = array_merge($img, $ret['attachment']); + } + } + } + } + + // addressing madness + + if ($activitypub) { + $parent_i = []; + $ret['to'] = []; + $ret['cc'] = []; + + $public = (($i['item_private']) ? false : true); + $top_level = (($i['mid'] === $i['parent_mid']) ? true : false); + + if (!$top_level) { + if (intval($i['parent'])) { + $recips = get_iconfig($i['parent'], 'activitypub', 'recips'); + } else { + // if we are encoding this item for storage there won't be a parent. + $p = q( + "select parent from item where parent_mid = '%s' and uid = %d", + dbesc($i['parent_mid']), + intval($i['uid']) + ); + if ($p) { + $recips = get_iconfig($p[0]['parent'], 'activitypub', 'recips'); + } + } + if ($recips) { + $parent_i['to'] = $recips['to']; + $parent_i['cc'] = $recips['cc']; + } + } + + + if ($public) { + $ret['to'] = [ACTIVITY_PUBLIC_INBOX]; + if (isset($parent_i['to']) && is_array($parent_i['to'])) { + $ret['to'] = array_values(array_unique(array_merge($ret['to'], $parent_i['to']))); + } + if ($i['item_origin']) { + $ret['cc'] = [z_root() . '/followers/' . substr($i['author']['xchan_addr'], 0, strpos($i['author']['xchan_addr'], '@'))]; + } + if (isset($parent_i['cc']) && is_array($parent_i['cc'])) { + $ret['cc'] = array_values(array_unique(array_merge($ret['cc'], $parent_i['cc']))); + } + } else { + // private activity + + if ($top_level) { + $ret['to'] = self::map_acl($i); + if (isset($parent_i['to']) && is_array($parent_i['to'])) { + $ret['to'] = array_values(array_unique(array_merge($ret['to'], $parent_i['to']))); + } + } else { + $ret['cc'] = self::map_acl($i); + if (isset($parent_i['cc']) && is_array($parent_i['cc'])) { + $ret['cc'] = array_values(array_unique(array_merge($ret['cc'], $parent_i['cc']))); + } + if ($ret['tag']) { + foreach ($ret['tag'] as $mention) { + if (is_array($mention) && array_key_exists('ttype', $mention) && in_array($mention['ttype'], [TERM_FORUM, TERM_MENTION]) && array_key_exists('href', $mention) && $mention['href']) { + $h = q( + "select * from hubloc where hubloc_id_url = '%s' or hubloc_hash = '%s' limit 1", + dbesc($mention['href']), + dbesc($mention['href']) + ); + if ($h) { + if ($h[0]['hubloc_network'] === 'activitypub') { + $addr = $h[0]['hubloc_hash']; + } else { + $addr = $h[0]['hubloc_id_url']; + } + if (!in_array($addr, $ret['to'])) { + $ret['to'][] = $addr; + } + } + } + } + } + + + $d = q( + "select hubloc.* from hubloc left join item on hubloc_hash = owner_xchan where item.parent_mid = '%s' and item.uid = %d limit 1", + dbesc($i['parent_mid']), + intval($i['uid']) + ); + + if ($d) { + if ($d[0]['hubloc_network'] === 'activitypub') { + $addr = $d[0]['hubloc_hash']; + } else { + $addr = $d[0]['hubloc_id_url']; + } + $ret['cc'][] = $addr; + } + } + } + + $mentions = self::map_mentions($i); + if (count($mentions) > 0) { + if (!$ret['to']) { + $ret['to'] = $mentions; + } else { + $ret['to'] = array_values(array_unique(array_merge($ret['to'], $mentions))); + } + } + } + + // remove any duplicates from 'cc' that are present in 'to' + // as this may indicate that mentions changed the audience from secondary to primary + + $cc = []; + if ($ret['cc'] && is_array($ret['cc'])) { + foreach ($ret['cc'] as $e) { + if (!is_array($ret['to'])) { + $cc[] = $e; + } elseif (!in_array($e, $ret['to'])) { + $cc[] = $e; + } + } + } + $ret['cc'] = $cc; + + return $ret; + } + + + // Returns an array of URLS for any mention tags found in the item array $i. + + public static function map_mentions($i) + { + if (!(array_key_exists('term', $i) && is_array($i['term']))) { + return []; + } + + $list = []; + + foreach ($i['term'] as $t) { + if (!(array_key_exists('url', $t) && $t['url'])) { + continue; + } + if (array_key_exists('ttype', $t) && $t['ttype'] == TERM_MENTION) { + $url = self::lookup_term_url($t['url']); + $list[] = (($url) ? $url : $t['url']); + } + } + + return $list; + } + + // Returns an array of all recipients targeted by private item array $i. + + public static function map_acl($i) + { + $ret = []; + + if (!$i['item_private']) { + return $ret; + } + + if ($i['mid'] !== $i['parent_mid']) { + $i = q( + "select * from item where parent_mid = '%s' and uid = %d", + dbesc($i['parent_mid']), + intval($i['uid']) + ); + if ($i) { + $i = array_shift($i); + } + } + if ($i['allow_gid']) { + $tmp = expand_acl($i['allow_gid']); + if ($tmp) { + foreach ($tmp as $t) { + $ret[] = z_root() . '/lists/' . $t; + } + } + } + + if ($i['allow_cid']) { + $tmp = expand_acl($i['allow_cid']); + $list = stringify_array($tmp, true); + if ($list) { + $details = q("select hubloc_id_url, hubloc_hash, hubloc_network from hubloc where hubloc_hash in (" . $list . ") "); + if ($details) { + foreach ($details as $d) { + if ($d['hubloc_network'] === 'activitypub') { + $ret[] = $d['hubloc_hash']; + } else { + $ret[] = $d['hubloc_id_url']; + } + } + } + } + } + + $x = get_iconfig($i['id'], 'activitypub', 'recips'); + if ($x) { + foreach (['to', 'cc'] as $k) { + if (isset($x[$k])) { + if (is_string($x[$k])) { + $ret[] = $x[$k]; + } else { + $ret = array_merge($ret, $x[$k]); + } + } + } + } + + return array_values(array_unique($ret)); + } + + + public static function encode_person($p, $extended = true, $activitypub = false) + { + + $ret = []; + + if (!$p['xchan_url']) { + return $ret; + } + + if (!$extended) { + return $p['xchan_url']; + } + + $c = ((array_key_exists('channel_id', $p)) ? $p : channelx_by_hash($p['xchan_hash'])); + + $ret['type'] = 'Person'; + $auto_follow = false; + + if ($c) { + $role = PConfig::Get($c['channel_id'], 'system', 'permissions_role'); + if (strpos($role, 'forum') !== false) { + $ret['type'] = 'Group'; + } + $role_permissions = PermissionRoles::role_perms($role); + if (is_array($role_permissions) && isset($role_permissions['perms_auto'])) { + $auto_follow = intval($role_permissions['perms_auto']); + } + } + + if ($c) { + $ret['id'] = channel_url($c); + } else { + $ret['id'] = ((strpos($p['xchan_hash'], 'http') === 0) ? $p['xchan_hash'] : $p['xchan_url']); + } + if ($p['xchan_addr'] && strpos($p['xchan_addr'], '@')) { + $ret['preferredUsername'] = substr($p['xchan_addr'], 0, strpos($p['xchan_addr'], '@')); + } + $ret['name'] = $p['xchan_name']; + $ret['updated'] = datetime_convert('UTC', 'UTC', $p['xchan_name_date'], ATOM_TIME); + $ret['icon'] = [ + 'type' => 'Image', + 'mediaType' => (($p['xchan_photo_mimetype']) ? $p['xchan_photo_mimetype'] : 'image/png'), + 'updated' => datetime_convert('UTC', 'UTC', $p['xchan_photo_date'], ATOM_TIME), + 'url' => $p['xchan_photo_l'], + 'height' => 300, + 'width' => 300, + ]; + $ret['url'] = $p['xchan_url']; + if (isset($p['channel_location']) && $p['channel_location']) { + $ret['location'] = ['type' => 'Place', 'name' => $p['channel_location']]; + } + + $ret['tag'] = [['type' => 'PropertyValue', 'name' => 'Protocol', 'value' => 'zot6']]; + $ret['tag'][] = ['type' => 'PropertyValue', 'name' => 'Protocol', 'value' => 'nomad']; + + if ($activitypub && get_config('system', 'activitypub', ACTIVITYPUB_ENABLED)) { + if ($c) { + if (get_pconfig($c['channel_id'], 'system', 'activitypub', ACTIVITYPUB_ENABLED)) { + $ret['inbox'] = z_root() . '/inbox/' . $c['channel_address']; + $ret['tag'][] = ['type' => 'PropertyValue', 'name' => 'Protocol', 'value' => 'activitypub']; + } else { + $ret['inbox'] = null; + } + + $ret['outbox'] = z_root() . '/outbox/' . $c['channel_address']; + $ret['followers'] = z_root() . '/followers/' . $c['channel_address']; + $ret['following'] = z_root() . '/following/' . $c['channel_address']; + + $ret['endpoints'] = [ + 'sharedInbox' => z_root() . '/inbox', + 'oauthRegistrationEndpoint' => z_root() . '/api/client/register', + 'oauthAuthorizationEndpoint' => z_root() . '/authorize', + 'oauthTokenEndpoint' => z_root() . '/token' + ]; + + $ret['discoverable'] = ((1 - intval($p['xchan_hidden'])) ? true : false); + $ret['publicKey'] = [ + 'id' => $p['xchan_url'] . '?operation=getkey', + 'owner' => $p['xchan_url'], + 'signatureAlgorithm' => 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256', + 'publicKeyPem' => $p['xchan_pubkey'] + ]; + + $ret['manuallyApprovesFollowers'] = (($auto_follow) ? false : true); + if ($ret['type'] === 'Group') { + $ret['capabilities'] = ['acceptsJoins' => true]; + } + // map other nomadic identities linked with this channel + + $locations = []; + $locs = Libzot::encode_locations($c); + if ($locs) { + foreach ($locs as $loc) { + if ($loc['url'] !== z_root()) { + $locations[] = $loc['id_url']; + } + } + } + + if ($locations) { + if (count($locations) === 1) { + $locations = array_shift($locations); + } + $ret['copiedTo'] = $locations; + $ret['alsoKnownAs'] = $locations; + } + + $cp = get_cover_photo($c['channel_id'], 'array'); + if ($cp) { + $ret['image'] = [ + 'type' => 'Image', + 'mediaType' => $cp['type'], + 'url' => $cp['url'] + ]; + } + // only fill in profile information if the profile is publicly visible + if (perm_is_allowed($c['channel_id'], EMPTY_STR, 'view_profile')) { + $dp = q( + "select * from profile where uid = %d and is_default = 1", + intval($c['channel_id']) + ); + if ($dp) { + if ($dp[0]['about']) { + $ret['summary'] = bbcode($dp[0]['about'], ['export' => true]); + } + foreach ( + ['pdesc', 'address', 'locality', 'region', 'postal_code', 'country_name', + 'hometown', 'gender', 'marital', 'sexual', 'politic', 'religion', 'pronouns', + 'homepage', 'contact', 'dob'] as $k + ) { + if ($dp[0][$k]) { + $key = $k; + if ($key === 'pdesc') { + $key = 'description'; + } + if ($key == 'politic') { + $key = 'political'; + } + if ($key === 'dob') { + $key = 'birthday'; + } + $ret['attachment'][] = ['type' => 'PropertyValue', 'name' => $key, 'value' => $dp[0][$k]]; + } + } + if ($dp[0]['keywords']) { + $kw = explode(' ', $dp[0]['keywords']); + if ($kw) { + foreach ($kw as $k) { + $k = trim($k); + $k = trim($k, '#,'); + $ret['tag'][] = ['id' => z_root() . '/search?tag=' . urlencode($k), 'name' => '#' . urlencode($k)]; + } + } + } + } + } + } else { + $collections = get_xconfig($p['xchan_hash'], 'activitypub', 'collections', []); + if ($collections) { + $ret = array_merge($ret, $collections); + } else { + $ret['inbox'] = null; + $ret['outbox'] = null; + } + } + } else { + $ret['publicKey'] = [ + 'id' => $p['xchan_url'], + 'owner' => $p['xchan_url'], + 'publicKeyPem' => $p['xchan_pubkey'] + ]; + } + + $arr = ['xchan' => $p, 'encoded' => $ret, 'activitypub' => $activitypub]; + call_hooks('encode_person', $arr); + $ret = $arr['encoded']; + + + return $ret; + } + + + public static function encode_site() + { + + + $sys = get_sys_channel(); + + // encode the sys channel information and over-ride with site + // information + $ret = self::encode_person($sys, true, true); + + $ret['type'] = ((is_group($sys['channel_id'])) ? 'Group' : 'Service'); + $ret['id'] = z_root(); + $ret['alsoKnownAs'] = z_root() . '/channel/sys'; + $auto_follow = false; + + $ret['preferredUsername'] = 'sys'; + $ret['name'] = System::get_site_name(); + + $ret['icon'] = [ + 'type' => 'Image', + 'url' => System::get_site_icon(), + ]; + + $ret['generator'] = ['type' => 'Application', 'name' => System::get_platform_name()]; + + $ret['url'] = z_root(); + + $ret['manuallyApprovesFollowers'] = ((get_config('system', 'allowed_sites')) ? true : false); + + $cp = get_cover_photo($sys['channel_id'], 'array'); + if ($cp) { + $ret['image'] = [ + 'type' => 'Image', + 'mediaType' => $cp['type'], + 'url' => $cp['url'] + ]; + } + + $ret['summary'] = bbcode(get_config('system', 'siteinfo', ''), ['export' => true]); + $ret['source'] = [ + 'mediaType' => 'text/x-multicode', + 'summary' => get_config('system', 'siteinfo', '') + ]; + + $ret['publicKey'] = [ + 'id' => z_root() . '?operation=getkey', + 'owner' => z_root(), + 'publicKeyPem' => get_config('system', 'pubkey') + ]; + + return $ret; + } + + + public static function activity_mapper($verb) + { + + if (strpos($verb, '/') === false) { + return $verb; + } + + $acts = [ + 'http://activitystrea.ms/schema/1.0/post' => 'Create', + 'http://activitystrea.ms/schema/1.0/share' => 'Announce', + 'http://activitystrea.ms/schema/1.0/update' => 'Update', + 'http://activitystrea.ms/schema/1.0/like' => 'Like', + 'http://activitystrea.ms/schema/1.0/favorite' => 'Like', + 'http://purl.org/zot/activity/dislike' => 'Dislike', + 'http://activitystrea.ms/schema/1.0/tag' => 'Add', + 'http://activitystrea.ms/schema/1.0/follow' => 'Follow', + 'http://activitystrea.ms/schema/1.0/unfollow' => 'Ignore', + ]; + + call_hooks('activity_mapper', $acts); - call_hooks('activity_mapper',$acts); + if (array_key_exists($verb, $acts) && $acts[$verb]) { + return $acts[$verb]; + } - if (array_key_exists($verb,$acts) && $acts[$verb]) { - return $acts[$verb]; - } + // Reactions will just map to normal activities - // Reactions will just map to normal activities + if (strpos($verb, ACTIVITY_REACT) !== false) { + return 'Create'; + } + if (strpos($verb, ACTIVITY_MOOD) !== false) { + return 'Create'; + } - if (strpos($verb,ACTIVITY_REACT) !== false) - return 'Create'; - if (strpos($verb,ACTIVITY_MOOD) !== false) - return 'Create'; + if (strpos($verb, ACTIVITY_POKE) !== false) { + return 'Activity'; + } - if (strpos($verb,ACTIVITY_POKE) !== false) - return 'Activity'; + // We should return false, however this will trigger an uncaught exception and crash + // the delivery system if encountered by the JSON-LDSignature library - // We should return false, however this will trigger an uncaught exception and crash - // the delivery system if encountered by the JSON-LDSignature library - - logger('Unmapped activity: ' . $verb); - return 'Create'; - // return false; - } + logger('Unmapped activity: ' . $verb); + return 'Create'; + // return false; + } - static function activity_obj_mapper($obj) { + public static function activity_obj_mapper($obj) + { - $objs = [ - 'http://activitystrea.ms/schema/1.0/note' => 'Note', - 'http://activitystrea.ms/schema/1.0/comment' => 'Note', - 'http://activitystrea.ms/schema/1.0/person' => 'Person', - 'http://purl.org/zot/activity/profile' => 'Profile', - 'http://activitystrea.ms/schema/1.0/photo' => 'Image', - 'http://activitystrea.ms/schema/1.0/profile-photo' => 'Icon', - 'http://activitystrea.ms/schema/1.0/event' => 'Event', - 'http://activitystrea.ms/schema/1.0/wiki' => 'Document', - 'http://purl.org/zot/activity/location' => 'Place', - 'http://purl.org/zot/activity/chessgame' => 'Game', - 'http://purl.org/zot/activity/tagterm' => 'zot:Tag', - 'http://purl.org/zot/activity/thing' => 'Object', - 'http://purl.org/zot/activity/file' => 'zot:File', - 'http://purl.org/zot/activity/mood' => 'zot:Mood', - - ]; + $objs = [ + 'http://activitystrea.ms/schema/1.0/note' => 'Note', + 'http://activitystrea.ms/schema/1.0/comment' => 'Note', + 'http://activitystrea.ms/schema/1.0/person' => 'Person', + 'http://purl.org/zot/activity/profile' => 'Profile', + 'http://activitystrea.ms/schema/1.0/photo' => 'Image', + 'http://activitystrea.ms/schema/1.0/profile-photo' => 'Icon', + 'http://activitystrea.ms/schema/1.0/event' => 'Event', + 'http://activitystrea.ms/schema/1.0/wiki' => 'Document', + 'http://purl.org/zot/activity/location' => 'Place', + 'http://purl.org/zot/activity/chessgame' => 'Game', + 'http://purl.org/zot/activity/tagterm' => 'zot:Tag', + 'http://purl.org/zot/activity/thing' => 'Object', + 'http://purl.org/zot/activity/file' => 'zot:File', + 'http://purl.org/zot/activity/mood' => 'zot:Mood', - call_hooks('activity_obj_mapper',$objs); + ]; - if ($obj === 'Answer') { - return 'Note'; - } + call_hooks('activity_obj_mapper', $objs); - if (strpos($obj,'/') === false) { - return $obj; - } + if ($obj === 'Answer') { + return 'Note'; + } - if (array_key_exists($obj,$objs)) { - return $objs[$obj]; - } + if (strpos($obj, '/') === false) { + return $obj; + } - logger('Unmapped activity object: ' . $obj); - return 'Note'; + if (array_key_exists($obj, $objs)) { + return $objs[$obj]; + } - // return false; + logger('Unmapped activity object: ' . $obj); + return 'Note'; - } + // return false; + } - static function follow($channel,$act) { + public static function follow($channel, $act) + { - $contact = null; - $their_follow_id = null; + $contact = null; + $their_follow_id = null; - if (intval($channel['channel_system'])) { - // The system channel ignores all follow requests - return; - } + if (intval($channel['channel_system'])) { + // The system channel ignores all follow requests + return; + } - /* - * - * if $act->type === 'Follow', actor is now following $channel - * if $act->type === 'Accept', actor has approved a follow request from $channel - * - */ + /* + * + * if $act->type === 'Follow', actor is now following $channel + * if $act->type === 'Accept', actor has approved a follow request from $channel + * + */ - $person_obj = $act->actor; + $person_obj = $act->actor; - if (in_array($act->type, [ 'Follow', 'Invite', 'Join'])) { - $their_follow_id = $act->id; - } - elseif ($act->type === 'Accept') { - $my_follow_id = z_root() . '/follow/' . $contact['id']; - } - - if (is_array($person_obj)) { + if (in_array($act->type, ['Follow', 'Invite', 'Join'])) { + $their_follow_id = $act->id; + } elseif ($act->type === 'Accept') { + $my_follow_id = z_root() . '/follow/' . $contact['id']; + } - // store their xchan and hubloc + if (is_array($person_obj)) { + // store their xchan and hubloc - self::actor_store($person_obj['id'],$person_obj); + self::actor_store($person_obj['id'], $person_obj); - // Find any existing abook record + // Find any existing abook record - $r = q("select * from abook left join xchan on abook_xchan = xchan_hash where abook_xchan = '%s' and abook_channel = %d limit 1", - dbesc($person_obj['id']), - intval($channel['channel_id']) - ); - if ($r) { - $contact = $r[0]; - } - } + $r = q( + "select * from abook left join xchan on abook_xchan = xchan_hash where abook_xchan = '%s' and abook_channel = %d limit 1", + dbesc($person_obj['id']), + intval($channel['channel_id']) + ); + if ($r) { + $contact = $r[0]; + } + } - $x = PermissionRoles::role_perms('social'); - $p = Permissions::FilledPerms($x['perms_connect']); - - // add tag_deliver permissions to remote groups + $x = PermissionRoles::role_perms('social'); + $p = Permissions::FilledPerms($x['perms_connect']); - if (is_array($person_obj) && $person_obj['type'] === 'Group') { - $p['tag_deliver'] = 1; - } + // add tag_deliver permissions to remote groups - $their_perms = Permissions::serialise($p); + if (is_array($person_obj) && $person_obj['type'] === 'Group') { + $p['tag_deliver'] = 1; + } + $their_perms = Permissions::serialise($p); - if ($contact && $contact['abook_id']) { - // A relationship of some form already exists on this site. + if ($contact && $contact['abook_id']) { + // A relationship of some form already exists on this site. - switch($act->type) { + switch ($act->type) { + case 'Follow': + case 'Invite': + case 'Join': + // A second Follow request, but we haven't approved the first one - case 'Follow': - case 'Invite': - case 'Join': + if ($contact['abook_pending']) { + return; + } - // A second Follow request, but we haven't approved the first one + // We've already approved them or followed them first + // Send an Accept back to them - if ($contact['abook_pending']) { - return; - } + set_abconfig($channel['channel_id'], $person_obj['id'], 'activitypub', 'their_follow_id', $their_follow_id); + set_abconfig($channel['channel_id'], $person_obj['id'], 'activitypub', 'their_follow_type', $act->type); + Run::Summon(['Notifier', 'permissions_accept', $contact['abook_id']]); + return; - // We've already approved them or followed them first - // Send an Accept back to them + case 'Accept': + // They accepted our Follow request - set default permissions - set_abconfig($channel['channel_id'],$person_obj['id'],'activitypub','their_follow_id', $their_follow_id); - set_abconfig($channel['channel_id'],$person_obj['id'],'activitypub','their_follow_type', $act->type); - Run::Summon([ 'Notifier', 'permissions_accept', $contact['abook_id'] ]); - return; + set_abconfig($channel['channel_id'], $contact['abook_xchan'], 'system', 'their_perms', $their_perms); - case 'Accept': + $abook_instance = $contact['abook_instance']; - // They accepted our Follow request - set default permissions - - set_abconfig($channel['channel_id'],$contact['abook_xchan'],'system','their_perms',$their_perms); + if (strpos($abook_instance, z_root()) === false) { + if ($abook_instance) { + $abook_instance .= ','; + } + $abook_instance .= z_root(); - $abook_instance = $contact['abook_instance']; - - if (strpos($abook_instance,z_root()) === false) { - if ($abook_instance) - $abook_instance .= ','; - $abook_instance .= z_root(); - - $r = q("update abook set abook_instance = '%s', abook_not_here = 0 + $r = q( + "update abook set abook_instance = '%s', abook_not_here = 0 where abook_id = %d and abook_channel = %d", - dbesc($abook_instance), - intval($contact['abook_id']), - intval($channel['channel_id']) - ); - } - - return; - default: - return; - - } - } - - // No previous relationship exists. - - if ($act->type === 'Accept') { - // This should not happen unless we deleted the connection before it was accepted. - return; - } - - // From here on out we assume a Follow activity to somebody we have no existing relationship with - - set_abconfig($channel['channel_id'],$person_obj['id'],'activitypub','their_follow_id', $their_follow_id); - set_abconfig($channel['channel_id'],$person_obj['id'],'activitypub','their_follow_type', $act->type); - - // The xchan should have been created by actor_store() above - - $r = q("select * from xchan where xchan_hash = '%s' and xchan_network = 'activitypub' limit 1", - dbesc($person_obj['id']) - ); - - if (! $r) { - logger('xchan not found for ' . $person_obj['id']); - return; - } - $ret = $r[0]; - - $blocked = LibBlock::fetch($channel['channel_id'],BLOCKTYPE_SERVER); - if ($blocked) { - foreach($blocked as $b) { - if (strpos($ret['xchan_url'],$b['block_entity']) !== false) { - logger('siteblock - follower denied'); - return; - } - } - } - if (LibBlock::fetch_by_entity($channel['channel_id'],$ret['xchan_hash'])) { - logger('actorblock - follower denied'); - return; - } - - $p = Permissions::connect_perms($channel['channel_id']); - $my_perms = Permissions::serialise($p['perms']); - $automatic = $p['automatic']; - - $closeness = PConfig::Get($channel['channel_id'],'system','new_abook_closeness',80); - - $r = abook_store_lowlevel( - [ - 'abook_account' => intval($channel['channel_account_id']), - 'abook_channel' => intval($channel['channel_id']), - 'abook_xchan' => $ret['xchan_hash'], - 'abook_closeness' => intval($closeness), - 'abook_created' => datetime_convert(), - 'abook_updated' => datetime_convert(), - 'abook_connected' => datetime_convert(), - 'abook_dob' => NULL_DATE, - 'abook_pending' => intval(($automatic) ? 0 : 1), - 'abook_instance' => z_root() - ] - ); - - if ($my_perms) - AbConfig::Set($channel['channel_id'],$ret['xchan_hash'],'system','my_perms',$my_perms); - - if ($their_perms) - AbConfig::Set($channel['channel_id'],$ret['xchan_hash'],'system','their_perms',$their_perms); - - - if ($r) { - logger("New ActivityPub follower for {$channel['channel_name']}"); - - $new_connection = q("select * from abook left join xchan on abook_xchan = xchan_hash left join hubloc on hubloc_hash = xchan_hash where abook_channel = %d and abook_xchan = '%s' order by abook_created desc limit 1", - intval($channel['channel_id']), - dbesc($ret['xchan_hash']) - ); - if ($new_connection) { - Enotify::submit( - [ - 'type' => NOTIFY_INTRO, - 'from_xchan' => $ret['xchan_hash'], - 'to_xchan' => $channel['channel_hash'], - 'link' => z_root() . '/connedit/' . $new_connection[0]['abook_id'], - ] - ); - - if ($my_perms && $automatic) { - // send an Accept for this Follow activity - Run::Summon([ 'Notifier', 'permissions_accept', $new_connection[0]['abook_id'] ]); - // Send back a Follow notification to them - Run::Summon([ 'Notifier', 'permissions_create', $new_connection[0]['abook_id'] ]); - } - - $clone = []; - foreach ($new_connection[0] as $k => $v) { - if (strpos($k,'abook_') === 0) { - $clone[$k] = $v; - } - } - unset($clone['abook_id']); - unset($clone['abook_account']); - unset($clone['abook_channel']); - - $abconfig = load_abconfig($channel['channel_id'],$clone['abook_xchan']); - - if ($abconfig) { - $clone['abconfig'] = $abconfig; - } - Libsync::build_sync_packet($channel['channel_id'], [ 'abook' => [ $clone ] ] ); - } - } - - - /* If there is a default group for this channel and permissions are automatic, add this member to it */ - - if ($channel['channel_default_group'] && $automatic) { - $g = AccessList::rec_byhash($channel['channel_id'],$channel['channel_default_group']); - if ($g) { - AccessList::member_add($channel['channel_id'],'',$ret['xchan_hash'],$g['id']); - } - } - - return; - - } - - - static function unfollow($channel,$act) { - - $contact = null; - - /* actor is unfollowing $channel */ - - $person_obj = $act->actor; - - if (is_array($person_obj)) { - - $r = q("select * from abook left join xchan on abook_xchan = xchan_hash where abook_xchan = '%s' and abook_channel = %d limit 1", - dbesc($person_obj['id']), - intval($channel['channel_id']) - ); - if ($r) { - // remove all permissions they provided - del_abconfig($channel['channel_id'],$r[0]['xchan_hash'],'system','their_perms',EMPTY_STR); - } - } - - return; - } - - - - - static function actor_store($url, $person_obj, $force = false) { - - if (! is_array($person_obj)) { - return; - } - -// logger('person_obj: ' . print_r($person_obj,true)); - - if (array_key_exists('movedTo',$person_obj) && $person_obj['movedTo'] && ! is_array($person_obj['movedTo'])) { - $tgt = self::fetch($person_obj['movedTo']); - if (is_array($tgt)) { - self::actor_store($person_obj['movedTo'],$tgt); - ActivityPub::move($person_obj['id'],$tgt); - } - return; - } - - - $ap_hubloc = null; - - $hublocs = self::get_actor_hublocs($url); - if ($hublocs) { - foreach ($hublocs as $hub) { - if ($hub['hubloc_network'] === 'activitypub') { - $ap_hubloc = $hub; - } - if (in_array($hub['hubloc_network'],['nomad','zot6'])) { - Libzot::update_cached_hubloc($hub); - } - } - } - - if ($ap_hubloc) { - // we already have a stored record. Determine if it needs updating. - if ($ap_hubloc['hubloc_updated'] < datetime_convert('UTC','UTC',' now - ' . self::$ACTOR_CACHE_DAYS . ' days') || $force) { - $person_obj = self::fetch($url); - // ensure we received something - if (! is_array($person_obj)) { - return; - } - } - else { - return; - } - } - - - - if (isset($person_obj['id'])) { - $url = $person_obj['id']; - } - - if (! $url) { - return; - } - - // store the actor record in XConfig - XConfig::Set($url,'system','actor_record',$person_obj); - - $name = escape_tags($person_obj['name']); - if (! $name) - $name = escape_tags($person_obj['preferredUsername']); - if (! $name) - $name = escape_tags( t('Unknown')); - - $username = escape_tags($person_obj['preferredUsername']); - $h = parse_url($url); - if ($h && $h['host']) { - $username .= '@' . $h['host']; - } - - if ($person_obj['icon']) { - if (is_array($person_obj['icon'])) { - if (array_key_exists('url',$person_obj['icon'])) - $icon = $person_obj['icon']['url']; - else { - if (is_string($person_obj['icon'][0])) { - $icon = $person_obj['icon'][0]; - } - elseif (array_key_exists('url',$person_obj['icon'][0])) { - $icon = $person_obj['icon'][0]['url']; - } - } - } - else { - $icon = $person_obj['icon']; - } - } - if (! (isset($icon) && $icon)) { - $icon = z_root() . '/' . get_default_profile_photo(); - } - - $cover_photo = false; - - if (isset($person_obj['image'])) { - if (is_string($person_obj['image'])) { - $cover_photo = $person_obj['image']; - } - if (isset($person_obj['image']['url'])) { - $cover_photo = $person_obj['image']['url']; - } - } - - $hidden = false; - if (array_key_exists('discoverable',$person_obj) && (! intval($person_obj['discoverable']))) { - $hidden = true; - } - - $links = false; - $profile = false; - - if (is_array($person_obj['url'])) { - if (! array_key_exists(0,$person_obj['url'])) { - $links = [ $person_obj['url'] ]; - } - else { - $links = $person_obj['url']; - } - } - - if (is_array($links) && $links) { - foreach ($links as $link) { - if (is_array($link) && array_key_exists('mediaType',$link) && $link['mediaType'] === 'text/html') { - $profile = $link['href']; - } - } - if (! $profile) { - $profile = $links[0]['href']; - } - } - elseif (isset($person_obj['url']) && is_string($person_obj['url'])) { - $profile = $person_obj['url']; - } - - if (! $profile) { - $profile = $url; - } - - $inbox = ((array_key_exists('inbox',$person_obj)) ? $person_obj['inbox'] : null); - - // either an invalid identity or a cached entry of some kind which didn't get caught above - - if ((! $inbox) || strpos($inbox,z_root()) !== false) { - return; - } - - - $collections = []; - - if ($inbox) { - $collections['inbox'] = $inbox; - if (array_key_exists('outbox',$person_obj) && is_string($person_obj['outbox'])) { - $collections['outbox'] = $person_obj['outbox']; - } - if (array_key_exists('followers',$person_obj) && is_string($person_obj['followers'])) { - $collections['followers'] = $person_obj['followers']; - } - if (array_key_exists('following',$person_obj) && is_string($person_obj['following'])) { - $collections['following'] = $person_obj['following']; - } - if (array_key_exists('wall',$person_obj) && is_string($person_obj['wall'])) { - $collections['wall'] = $person_obj['wall']; - } - if (array_path_exists('endpoints/sharedInbox',$person_obj) && is_string($person_obj['endpoints']['sharedInbox'])) { - $collections['sharedInbox'] = $person_obj['endpoints']['sharedInbox']; - } - } - - if (isset($person_obj['publicKey']['publicKeyPem'])) { - if ($person_obj['id'] === $person_obj['publicKey']['owner']) { - $pubkey = $person_obj['publicKey']['publicKeyPem']; - if (strstr($pubkey,'RSA ')) { - $pubkey = Keyutils::rsatopem($pubkey); - } - } - } - - $keywords = []; - - if (isset($person_obj['tag']) && is_array($person_obj['tag'])) { - foreach ($person_obj['tag'] as $t) { - if (is_array($t) && isset($t['type']) && $t['type'] === 'Hashtag') { - if (isset($t['name'])) { - $tag = escape_tags((substr($t['name'],0,1) === '#') ? substr($t['name'],1) : $t['name']); - if ($tag) { - $keywords[] = $tag; - } - } - } - if (is_array($t) && isset($t['type']) && $t['type'] === 'PropertyValue') { - if (isset($t['name']) && isset($t['value']) && $t['name'] === 'Protocol') { - self::update_protocols($url,trim($t['value'])); - } - } - } - } - - $xchan_type = self::get_xchan_type($person_obj['type']); - $about = ((isset($person_obj['summary'])) ? html2bbcode(purify_html($person_obj['summary'])) : EMPTY_STR); - - $p = q("select * from xchan where xchan_url = '%s' and xchan_network in ('nomad','zot6') limit 1", - dbesc($url) - ); - if ($p) { - set_xconfig($url,'system','protocols','zot6,activitypub'); - } - - // there is no standard way to represent an 'instance actor' but this will at least subdue the multiple - // pages of Mastodon and Pleroma instance actors in the directory. - // @TODO - (2021-08-27) remove this if they provide a non-person xchan_type - // once extended xchan_type directory filtering is implemented. - $censored = ((strpos($profile,'instance_actor') || strpos($profile,'/internal/fetch')) ? 1 : 0); - - $r = q("select * from xchan where xchan_hash = '%s' limit 1", - dbesc($url) - ); - if (! $r) { - // create a new record - $r = xchan_store_lowlevel( - [ - 'xchan_hash' => $url, - 'xchan_guid' => $url, - 'xchan_pubkey' => $pubkey, - 'xchan_addr' => ((strpos($username,'@')) ? $username : ''), - 'xchan_url' => $profile, - 'xchan_name' => $name, - 'xchan_hidden' => intval($hidden), - 'xchan_updated' => datetime_convert(), - 'xchan_name_date' => datetime_convert(), - 'xchan_network' => 'activitypub', - 'xchan_type' => $xchan_type, - 'xchan_photo_date' => datetime_convert('UTC','UTC','1968-01-01'), - 'xchan_photo_l' => z_root() . '/' . get_default_profile_photo(), - 'xchan_photo_m' => z_root() . '/' . get_default_profile_photo(80), - 'xchan_photo_s' => z_root() . '/' . get_default_profile_photo(48), - 'xchan_photo_mimetype' => 'image/png', - 'xchan_censored' => $censored - - ] - ); - } - else { - - // Record exists. Cache existing records for a set number of days - // then refetch to catch updated profile photos, names, etc. - - if ($r[0]['xchan_name_date'] >= datetime_convert('UTC','UTC','now - ' . self::$ACTOR_CACHE_DAYS . ' days') && (! $force)) { - return; - } - - // update existing record - $u = q("update xchan set xchan_updated = '%s', xchan_name = '%s', xchan_pubkey = '%s', xchan_network = '%s', xchan_name_date = '%s', xchan_hidden = %d, xchan_type = %d, xchan_censored = %d where xchan_hash = '%s'", - dbesc(datetime_convert()), - dbesc($name), - dbesc($pubkey), - dbesc('activitypub'), - dbesc(datetime_convert()), - intval($hidden), - intval($xchan_type), - intval($censored), - dbesc($url) - ); - - if (strpos($username,'@') && ($r[0]['xchan_addr'] !== $username)) { - $r = q("update xchan set xchan_addr = '%s' where xchan_hash = '%s'", - dbesc($username), - dbesc($url) - ); - } - } - - if ($cover_photo) { - set_xconfig($url,'system','cover_photo',$cover_photo); - } - - - $m = parse_url($url); - if ($m['scheme'] && $m['host']) { - $site_url = $m['scheme'] . '://' . $m['host']; - $ni = Nodeinfo::fetch($site_url); - if ($ni && is_array($ni)) { - $software = ((array_path_exists('software/name',$ni)) ? $ni['software']['name'] : ''); - $version = ((array_path_exists('software/version',$ni)) ? $ni['software']['version'] : ''); - $register = $ni['openRegistrations']; - - $site = q("select * from site where site_url = '%s'", - dbesc($site_url) - ); - if ($site) { - q("update site set site_project = '%s', site_update = '%s', site_version = '%s' where site_url = '%s'", - dbesc($software), - dbesc(datetime_convert()), - dbesc($version), - dbesc($site_url) - ); - // it may have been saved originally as an unknown type, but we now know what it is - if (intval($site[0]['site_type']) === SITE_TYPE_UNKNOWN) { - q("update site set site_type = %d where site_url = '%s'", - intval(SITE_TYPE_NOTZOT), - dbesc($site_url) - ); - } - } - else { - site_store_lowlevel( - [ - 'site_url' => $site_url, - 'site_update' => datetime_convert(), - 'site_dead' => 0, - 'site_type' => SITE_TYPE_NOTZOT, - 'site_project' => $software, - 'site_version' => $version, - 'site_access' => (($register) ? ACCESS_FREE : ACCESS_PRIVATE), - 'site_register' => (($register) ? REGISTER_OPEN : REGISTER_CLOSED) - ] - ); - } - } - } - - Libzotdir::import_directory_profile($url,[ 'about' => $about, 'keywords' => $keywords, 'dob' => '0000-00-00' ], null,0,true); - - if ($collections) { - set_xconfig($url,'activitypub','collections',$collections); - } - - $h = q("select * from hubloc where hubloc_hash = '%s' limit 1", - dbesc($url) - ); - - - $m = parse_url($url); - if ($m) { - $hostname = $m['host']; - $baseurl = $m['scheme'] . '://' . $m['host'] . ((isset($m['port']) && intval($m['port'])) ? ':' . $m['port'] : ''); - } - - if (! $h) { - $r = hubloc_store_lowlevel( - [ - 'hubloc_guid' => $url, - 'hubloc_hash' => $url, - 'hubloc_id_url' => $profile, - 'hubloc_addr' => ((strpos($username,'@')) ? $username : ''), - 'hubloc_network' => 'activitypub', - 'hubloc_url' => $baseurl, - 'hubloc_host' => $hostname, - 'hubloc_callback' => $inbox, - 'hubloc_updated' => datetime_convert(), - 'hubloc_primary' => 1 - ] - ); - } - else { - if (strpos($username,'@') && ($h[0]['hubloc_addr'] !== $username)) { - $r = q("update hubloc set hubloc_addr = '%s' where hubloc_hash = '%s'", - dbesc($username), - dbesc($url) - ); - } - if ($inbox !== $h[0]['hubloc_callback']) { - $r = q("update hubloc set hubloc_callback = '%s' where hubloc_hash = '%s'", - dbesc($inbox), - dbesc($url) - ); - } - if ($profile !== $h[0]['hubloc_id_url']) { - $r = q("update hubloc set hubloc_id_url = '%s' where hubloc_hash = '%s'", - dbesc($profile), - dbesc($url) - ); - } - $r = q("update hubloc set hubloc_updated = '%s' where hubloc_hash = '%s'", - dbesc(datetime_convert()), - dbesc($url) - ); - } - - if (! $icon) { - $icon = z_root() . '/' . get_default_profile_photo(300); - } - - // We store all ActivityPub actors we can resolve. Some of them may be able to communicate over Zot6. Find them. - // Only probe if it looks like it looks something like a zot6 URL as there isn't anything in the actor record which we can reliably use for this purpose - // and adding zot discovery urls to the actor record will cause federation to fail with the 20-30 projects which don't accept arrays in the url field. - - if (strpos($url,'/channel/') !== false) { - $zx = q("select * from hubloc where hubloc_id_url = '%s' and hubloc_network in ('nomad','zot6')", - dbesc($url) - ); - if (($username) && strpos($username,'@') && (! $zx)) { - Run::Summon( [ 'Gprobe', bin2hex($username) ] ); - } - } - - Run::Summon( [ 'Xchan_photo', bin2hex($icon), bin2hex($url) ] ); - - } - - static function update_protocols($xchan,$str) { - $existing = explode(',',get_xconfig($xchan,'system','protocols',EMPTY_STR)); - if (! in_array($str,$existing)) { - $existing[] = $str; - set_xconfig($xchan,'system','protocols', implode(',',$existing)); - } - } - - - static function drop($channel,$observer,$act) { - $r = q("select * from item where mid = '%s' and uid = %d limit 1", - dbesc((is_array($act->obj)) ? $act->obj['id'] : $act->obj), - intval($channel['channel_id']) - ); - - if (! $r) { - return; - } - - if (in_array($observer,[ $r[0]['author_xchan'], $r[0]['owner_xchan'] ])) { - drop_item($r[0]['id'],false); - } - elseif (in_array($act->actor['id'],[ $r[0]['author_xchan'], $r[0]['owner_xchan'] ])) { - drop_item($r[0]['id'],false); - } - - } - - - // sort function width decreasing - - static function vid_sort($a,$b) { - if ($a['width'] === $b['width']) - return 0; - return (($a['width'] > $b['width']) ? -1 : 1); - } - - static function share_bb($obj) { - // @fixme - error check and set defaults - - $name = urlencode($obj['actor']['name']); - $profile = $obj['actor']['id']; - $photo = $obj['icon']['url']; - - $s = "\r\n[share author='" . $name . - "' profile='" . $profile . - "' avatar='" . $photo . - "' link='" . $act->obj['id'] . - "' auth='" . ((is_matrix_url($act->obj['id'])) ? 'true' : 'false' ) . - "' posted='" . $act->obj['published'] . - "' message_id='" . $act->obj['id'] . - "']"; - - return $s; - } - - static function get_actor_bbmention($id) { - - $x = q("select * from hubloc left join xchan on hubloc_hash = xchan_hash where hubloc_hash = '%s' or hubloc_id_url = '%s' limit 1", - dbesc($id), - dbesc($id) - ); - - if ($x) { - // a name starting with a left paren can trick the markdown parser into creating a link so insert a zero-width space - if (substr($x[0]['xchan_name'],0,1) === '(') { - $x[0]['xchan_name'] = htmlspecialchars_decode('​',ENT_QUOTES) . $x[0]['xchan_name']; - } - - return sprintf('@[zrl=%s]%s[/zrl]',$x[0]['xchan_url'],$x[0]['xchan_name']); - } - return '@{' . $id . '}'; - - } - - static function update_poll($item,$post) { - - logger('updating poll'); - - $multi = false; - $mid = $post['mid']; - $content = $post['title']; - - if (! $item) { - return false; - } - - $o = json_decode($item['obj'],true); - if ($o && array_key_exists('anyOf',$o)) { - $multi = true; - } - - $r = q("select mid, title from item where parent_mid = '%s' and author_xchan = '%s' and mid != parent_mid ", - dbesc($item['mid']), - dbesc($post['author_xchan']) - ); - - // prevent any duplicate votes by same author for oneOf and duplicate votes with same author and same answer for anyOf - - if ($r) { - if ($multi) { - foreach ($r as $rv) { - if ($rv['title'] === $content && $rv['mid'] !== $mid) { - return false; - } - } - } - else { - foreach ($r as $rv) { - if ($rv['mid'] !== $mid) { - return false; - } - } - } - } - - $answer_found = false; - $found = false; - if ($multi) { - for ($c = 0; $c < count($o['anyOf']); $c ++) { - if ($o['anyOf'][$c]['name'] === $content) { - $answer_found = true; - if (is_array($o['anyOf'][$c]['replies'])) { - foreach($o['anyOf'][$c]['replies'] as $reply) { - if(is_array($reply) && array_key_exists('id',$reply) && $reply['id'] === $mid) { - $found = true; - } - } - } - - if (! $found) { - $o['anyOf'][$c]['replies']['totalItems'] ++; - $o['anyOf'][$c]['replies']['items'][] = [ 'id' => $mid, 'type' => 'Note' ]; - } - } - } - } - else { - for ($c = 0; $c < count($o['oneOf']); $c ++) { - if ($o['oneOf'][$c]['name'] === $content) { - $answer_found = true; - if (is_array($o['oneOf'][$c]['replies'])) { - foreach($o['oneOf'][$c]['replies'] as $reply) { - if(is_array($reply) && array_key_exists('id',$reply) && $reply['id'] === $mid) { - $found = true; - } - } - } - - if (! $found) { - $o['oneOf'][$c]['replies']['totalItems'] ++; - $o['oneOf'][$c]['replies']['items'][] = [ 'id' => $mid, 'type' => 'Note' ]; - } - } - } - } - - if ($item['comments_closed'] > NULL_DATE) { - if ($item['comments_closed'] > datetime_convert()) { - $o['closed'] = datetime_convert('UTC','UTC',$item['comments_closed'], ATOM_TIME); - // set this to force an update - $answer_found = true; - } - } - - logger('updated_poll: ' . print_r($o,true),LOGGER_DATA); - if ($answer_found && ! $found) { - $x = q("update item set obj = '%s', edited = '%s' where id = %d", - dbesc(json_encode($o)), - dbesc(datetime_convert()), - intval($item['id']) - ); - Run::Summon( [ 'Notifier', 'wall-new', $item['id'] ] ); - return true; - } - - return false; - } - - - static function decode_note($act, $cacheable = false) { - - $response_activity = false; - $poll_handled = false; - - $s = []; - - if (is_array($act->obj)) { - $binary = false; - $markdown = false; - - if (array_key_exists('mediaType',$act->obj) && $act->obj['mediaType'] !== 'text/html') { - if ($act->obj['mediaType'] === 'text/markdown') { - $markdown = true; - } - else { - $s['mimetype'] = escape_tags($act->obj['mediaType']); - $binary = true; - } - } - - $content = self::get_content($act->obj,$binary); - - if ($cacheable) { - // Zot6 activities will all be rendered from bbcode source in order to generate dynamic content. - // If the activity came from ActivityPub (hence $cacheable is set), use the HTML rendering - // and discard the bbcode source since it is unlikely that it is compatible with our implementation. - // Friendica for example. - - unset($content['bbcode']); - } - - // handle markdown conversion inline (peertube) - - if ($markdown) { - foreach ( [ 'summary', 'content' ] as $t) { - $content[$t] = Markdown::to_bbcode($content[$t],true, [ 'preserve_lf' => true ]); - } - } - } - - // These activities should have been handled separately in the Inbox module and should not be turned into posts - - if (in_array($act->type, ['Follow', 'Accept', 'Reject', 'Create', 'Update']) && is_array($act->obj) && array_key_exists('type',$act->obj) - && ($act->obj['type'] === 'Follow' || ActivityStreams::is_an_actor($act->obj['type']))) { - return false; - } - - // Within our family of projects, Follow/Unfollow of a thread is an internal activity which should not be transmitted, - // hence if we receive it - ignore or reject it. - // This may have to be revisited if AP projects start using Follow for objects other than actors. - - if (in_array($act->type, [ ACTIVITY_FOLLOW, ACTIVITY_IGNORE ])) { - return false; - } - - // Do not proceed further if there is no actor. - - if (! isset($act->actor['id'])) { - logger('No actor!'); - return false; - } - - $s['owner_xchan'] = $act->actor['id']; - $s['author_xchan'] = $act->actor['id']; - - // ensure we store the original actor - self::actor_store($act->actor['id'],$act->actor); - - $s['mid'] = ((is_array($act->obj) && isset($act->obj['id'])) ? $act->obj['id'] : $act->obj); - - if (! $s['mid']) { - return false; - } - - $s['parent_mid'] = $act->parent_id; - - if (array_key_exists('published',$act->data) && $act->data['published']) { - $s['created'] = datetime_convert('UTC','UTC',$act->data['published']); - } - elseif (array_key_exists('published',$act->obj) && $act->obj['published']) { - $s['created'] = datetime_convert('UTC','UTC',$act->obj['published']); - } - if (array_key_exists('updated',$act->data) && $act->data['updated']) { - $s['edited'] = datetime_convert('UTC','UTC',$act->data['updated']); - } - elseif (array_key_exists('updated',$act->obj) && $act->obj['updated']) { - $s['edited'] = datetime_convert('UTC','UTC',$act->obj['updated']); - } - if (array_key_exists('expires',$act->data) && $act->data['expires']) { - $s['expires'] = datetime_convert('UTC','UTC',$act->data['expires']); - } - elseif (array_key_exists('expires',$act->obj) && $act->obj['expires']) { - $s['expires'] = datetime_convert('UTC','UTC',$act->obj['expires']); - } - - if ($act->type === 'Invite' && is_array($act->obj) && array_key_exists('type',$act->obj) && $act->obj['type'] === 'Event') { - $s['mid'] = $s['parent_mid'] = $act->id; - } - - if (isset($act->replyto) && ! empty($act->replyto)) { - if (is_array($act->replyto) && isset($act->replyto['id'])) { - $s['replyto'] = $act->replyto['id']; - } - else { - $s['replyto'] = $act->replyto; - } - } - - if (ActivityStreams::is_response_activity($act->type)) { - - $response_activity = true; - - $s['mid'] = $act->id; - $s['parent_mid'] = ((is_array($act->obj) && isset($act->obj['id'])) ? $act->obj['id'] : $act->obj); - - - // over-ride the object timestamp with the activity - - if (isset($act->data['published']) && $act->data['published']) { - $s['created'] = datetime_convert('UTC','UTC',$act->data['published']); - } - - if (isset($act->data['updated']) && $act->data['updated']) { - $s['edited'] = datetime_convert('UTC','UTC',$act->data['updated']); - } - - $obj_actor = ((isset($act->obj['actor'])) ? $act->obj['actor'] : $act->get_actor('attributedTo', $act->obj)); - - // Actor records themselves do not have an actor or attributedTo - if ((! $obj_actor) && isset($act->obj['type']) && Activitystreams::is_an_actor($act->obj['type'])) { - $obj_actor = $act->obj; - } - - // We already check for admin blocks of third-party objects when fetching them explicitly. - // Repeat here just in case the entire object was supplied inline and did not require fetching - - if ($obj_actor && array_key_exists('id',$obj_actor)) { - $m = parse_url($obj_actor['id']); - if ($m && $m['scheme'] && $m['host']) { - if (! check_siteallowed($m['scheme'] . '://' . $m['host'])) { - return; - } - } - if (! check_channelallowed($obj_actor['id'])) { - return; - } - } - - // if the object is an actor, it is not really a response activity, so reset it to a top level post - - if (ActivityStreams::is_an_actor($act->obj['type'])) { - $s['parent_mid'] = $s['mid']; - } - - - // ensure we store the original actor of the associated (parent) object - self::actor_store($obj_actor['id'],$obj_actor); - - $mention = self::get_actor_bbmention($obj_actor['id']); - - $quoted_content = '[quote]' . $content['content'] . '[/quote]'; - - if ($act->type === 'Like') { - $content['content'] = sprintf( t('Likes %1$s\'s %2$s'),$mention, ((ActivityStreams::is_an_actor($act->obj['type'])) ? t('Profile') : $act->obj['type'])) . EOL . EOL . $quoted_content; - } - if ($act->type === 'Dislike') { - $content['content'] = sprintf( t('Doesn\'t like %1$s\'s %2$s'),$mention, ((ActivityStreams::is_an_actor($act->obj['type'])) ? t('Profile') : $act->obj['type'])) . EOL . EOL . $quoted_content; - } - - // handle event RSVPs - if (($act->obj['type'] === 'Event') || ($act->obj['type'] === 'Invite' && array_path_exists('object/type',$act->obj) && $act->obj['object']['type'] === 'Event')) { - if ($act->type === 'Accept') { - $content['content'] = sprintf( t('Will attend %s\'s event'),$mention) . EOL . EOL . $quoted_content; - } - if ($act->type === 'Reject') { - $content['content'] = sprintf( t('Will not attend %s\'s event'),$mention) . EOL . EOL . $quoted_content; - } - if ($act->type === 'TentativeAccept') { - $content['content'] = sprintf( t('May attend %s\'s event'),$mention) . EOL . EOL . $quoted_content; - } - if ($act->type === 'TentativeReject') { - $content['content'] = sprintf( t('May not attend %s\'s event'),$mention) . EOL . EOL . $quoted_content; - } - } - - if ($act->type === 'Announce') { - $content['content'] = sprintf( t('🔁 Repeated %1$s\'s %2$s'), $mention, ((ActivityStreams::is_an_actor($act->obj['type'])) ? t('Profile') : $act->obj['type'])); - } - - if ($act->type === 'emojiReaction') { - // Hubzilla reactions - $content['content'] = (($act->tgt && $act->tgt['type'] === 'Image') ? '[img=32x32]' . $act->tgt['url'] . '[/img]' : '&#x' . $act->tgt['name'] . ';'); - } - - if (in_array($act->type,[ 'EmojiReaction', 'EmojiReact' ])) { - // Pleroma reactions - $t = trim(self::get_textfield($act->data,'content')); - $e = Emoji\is_single_emoji($t) || mb_strlen($t) === 1; - if ($e) { - $content['content'] = $t; - } - } - } - - $s['comment_policy'] = 'authenticated'; - - if ($s['mid'] === $s['parent_mid']) { - // it is a parent node - decode the comment policy info if present - if (isset($act->obj['commentPolicy'])) { - $until = strpos($act->obj['commentPolicy'],'until='); - if ($until !== false) { - $s['comments_closed'] = datetime_convert('UTC','UTC',substr($act->obj['commentPolicy'],$until + 6)); - if ($s['comments_closed'] < datetime_convert()) { - $s['nocomment'] = true; - } - } - $remainder = substr($act->obj['commentPolicy'],0,(($until) ? $until : strlen($act->obj['commentPolicy']))); - if ($remainder) { - $s['comment_policy'] = $remainder; - } - if (! (isset($item['comment_policy']) && strlen($item['comment_policy']))) { - $s['comment_policy'] = 'contacts'; - } - } - } - - if (! (array_key_exists('created',$s) && $s['created'])) { - $s['created'] = datetime_convert(); - } - if (! (array_key_exists('edited',$s) && $s['edited'])) { - $s['edited'] = $s['created']; - } - $s['title'] = (($response_activity) ? EMPTY_STR : self::bb_content($content,'name')); - $s['summary'] = self::bb_content($content,'summary'); - - if (array_key_exists('mimetype',$s) && (! in_array($s['mimetype'], [ 'text/bbcode', 'text/x-multicode' ]))) { - $s['body'] = $content['content']; - } - else { - $s['body'] = ((self::bb_content($content,'bbcode') && (! $response_activity)) ? self::bb_content($content,'bbcode') : self::bb_content($content,'content')); - } - - - // handle some of the more widely used of the numerous and varied ways of deleting something - - if (in_array($act->type, [ 'Delete', 'Undo', 'Tombstone' ])) { - $s['item_deleted'] = 1; - } - - if ($act->type === 'Create' && $act->obj['type'] === 'Tombstone') { - $s['item_deleted'] = 1; - } - - if ($act->obj && array_key_exists('sensitive',$act->obj) && boolval($act->obj['sensitive'])) { - $s['item_nsfw'] = 1; - } - - $s['verb'] = self::activity_mapper($act->type); - - // Mastodon does not provide update timestamps when updating poll tallies which means race conditions may occur here. - if ($act->type === 'Update' && $act->obj['type'] === 'Question' && $s['edited'] === $s['created']) { - $s['edited'] = datetime_convert(); - } - - - $s['obj_type'] = self::activity_obj_mapper($act->obj['type']); - $s['obj'] = $act->obj; - if (is_array($s['obj']) && array_path_exists('actor/id',$s['obj'])) { - $s['obj']['actor'] = $s['obj']['actor']['id']; - } - - if (is_array($act->tgt) && $act->tgt) { - if (array_key_exists('type',$act->tgt)) { - $s['tgt_type'] = self::activity_obj_mapper($act->tgt['type']); - } - // We shouldn't need to store collection contents which could be large. We will often only require the meta-data - if (isset($s['tgt_type']) && strpos($s['tgt_type'],'Collection') !== false) { - $s['target'] = [ 'id' => $act->tgt['id'], 'type' => $s['tgt_type'], 'attributedTo' => ((isset($act->tgt['attributedTo'])) ? $act->tgt['attributedTo'] : $act->tgt['actor']) ]; - } - } - - $generator = $act->get_property_obj('generator'); - if ((! $generator) && (! $response_activity)) { - $generator = $act->get_property_obj('generator',$act->obj); - } - - if ($generator && array_key_exists('type',$generator) - && in_array($generator['type'], [ 'Application','Service' ] ) && array_key_exists('name',$generator)) { - $s['app'] = escape_tags($generator['name']); - } - - $location = $act->get_property_obj('location'); - if (is_array($location) && array_key_exists('type',$location) && $location['type'] === 'Place') { - if (array_key_exists('name',$location)) { - $s['location'] = escape_tags($location['name']); - } - if (array_key_exists('content',$location)) { - $s['location'] = html2plain(purify_html($location['content']),256); - } - - if (array_key_exists('latitude',$location) && array_key_exists('longitude',$location)) { - $s['coord'] = escape_tags($location['latitude']) . ' ' . escape_tags($location['longitude']); - } - } - - if (! $response_activity) { - $a = self::decode_taxonomy($act->obj); - if ($a) { - $s['term'] = $a; - foreach ($a as $b) { - if ($b['ttype'] === TERM_EMOJI) { - $s['summary'] = str_replace($b['term'],'[img=16x16]' . $b['url'] . '[/img]',$s['summary']); - - // @todo - @bug - // The emoji reference in the body might be inside a code block. In that case we shouldn't replace it. - // Currently we do. - - $s['body'] = str_replace($b['term'],'[img=16x16]' . $b['url'] . '[/img]',$s['body']); - } - } - } - - $a = self::decode_attachment($act->obj); - if ($a) { - $s['attach'] = $a; - } - - $a = self::decode_iconfig($act->obj); - if ($a) { - $s['iconfig'] = $a; - } - } - - // Objects that might have media attachments which aren't already provided in the content element. - // We'll check specific media objects separately. - - if (in_array($act->obj['type'], [ 'Article', 'Document', 'Event', 'Note', 'Page', 'Place', 'Question' ]) && isset($s['attach']) && $s['attach']) { - $s['body'] .= self::bb_attach($s['attach'],$s['body']); - } - - if ($act->obj['type'] === 'Question' && in_array($act->type,['Create','Update'])) { - if ($act->obj['endTime']) { - $s['comments_closed'] = datetime_convert('UTC','UTC', $act->obj['endTime']); - } - } - - if (array_key_exists('closed',$act->obj) && $act->obj['closed']) { - $s['comments_closed'] = datetime_convert('UTC','UTC', $act->obj['closed']); - } - - - // we will need a hook here to extract magnet links e.g. peertube - // right now just link to the largest mp4 we find that will fit in our - // standard content region - - if (! $response_activity) { - if ($act->obj['type'] === 'Video') { - - $vtypes = [ - 'video/mp4', - 'video/ogg', - 'video/webm' - ]; - - $mps = []; - $poster = null; - $ptr = null; - - // try to find a poster to display on the video element - - if (array_key_exists('icon',$act->obj)) { - if (is_array($act->obj['icon'])) { - if (array_key_exists(0,$act->obj['icon'])) { - $ptr = $act->obj['icon']; - } - else { - $ptr = [ $act->obj['icon'] ]; - } - } - if ($ptr) { - foreach ($ptr as $foo) { - if (is_array($foo) && array_key_exists('type',$foo) && $foo['type'] === 'Image' && is_string($foo['url'])) { - $poster = $foo['url']; - } - } - } - } - - $tag = (($poster) ? '[video poster="' . $poster . '"]' : '[video]' ); - $ptr = null; - - if (array_key_exists('url',$act->obj)) { - if (is_array($act->obj['url'])) { - if (array_key_exists(0,$act->obj['url'])) { - $ptr = $act->obj['url']; - } - else { - $ptr = [ $act->obj['url'] ]; - } - // handle peertube's weird url link tree if we find it here - // 0 => html link, 1 => application/x-mpegURL with 'tag' set to an array of actual media links - foreach ($ptr as $idex) { - if (is_array($idex) && array_key_exists('mediaType',$idex)) { - if ($idex['mediaType'] === 'application/x-mpegURL' && isset($idex['tag']) && is_array($idex['tag'])) { - $ptr = $idex['tag']; - break; - } - } - } - foreach ($ptr as $vurl) { - if (array_key_exists('mediaType',$vurl)) { - if (in_array($vurl['mediaType'], $vtypes)) { - if (! array_key_exists('width',$vurl)) { - $vurl['width'] = 0; - } - $mps[] = $vurl; - } - } - } - } - if ($mps) { - usort($mps,[ __CLASS__, 'vid_sort' ]); - foreach ($mps as $m) { - if (intval($m['width']) < 500 && self::media_not_in_body($m['href'],$s['body'])) { - $s['body'] .= "\n\n" . $tag . $m['href'] . '[/video]'; - break; - } - } - } - elseif (is_string($act->obj['url']) && self::media_not_in_body($act->obj['url'],$s['body'])) { - $s['body'] .= "\n\n" . $tag . $act->obj['url'] . '[/video]'; - } - } - } - - if ($act->obj['type'] === 'Audio') { - - $atypes = [ - 'audio/mpeg', - 'audio/ogg', - 'audio/wav' - ]; - - $ptr = null; - - if (array_key_exists('url',$act->obj)) { - if (is_array($act->obj['url'])) { - if (array_key_exists(0,$act->obj['url'])) { - $ptr = $act->obj['url']; - } - else { - $ptr = [ $act->obj['url'] ]; - } - foreach ($ptr as $vurl) { - if (isset($vurl['mediaType']) && in_array($vurl['mediaType'], $atypes) && self::media_not_in_body($vurl['href'],$s['body'])) { - $s['body'] .= "\n\n" . '[audio]' . $vurl['href'] . '[/audio]'; - break; - } - } - } - elseif (is_string($act->obj['url']) && self::media_not_in_body($act->obj['url'],$s['body'])) { - $s['body'] .= "\n\n" . '[audio]' . $act->obj['url'] . '[/audio]'; - } - } - // Pleroma audio scrobbler - elseif ($act->type === 'Listen' && array_key_exists('artist', $act->obj) && array_key_exists('title',$act->obj) && $s['body'] === EMPTY_STR) { - $s['body'] .= "\n\n" . sprintf('Listening to \"%1$s\" by %2$s', escape_tags($act->obj['title']), escape_tags($act->obj['artist'])); - if(isset($act->obj['album'])) { - $s['body'] .= "\n" . sprintf('(%s)', escape_tags($act->obj['album'])); - } - } - } - - if ($act->obj['type'] === 'Image' && strpos($s['body'],'zrl=') === false) { - - $ptr = null; - - if (array_key_exists('url',$act->obj)) { - if (is_array($act->obj['url'])) { - if (array_key_exists(0,$act->obj['url'])) { - $ptr = $act->obj['url']; - } - else { - $ptr = [ $act->obj['url'] ]; - } - foreach ($ptr as $vurl) { - if (is_array($vurl) && isset($vurl['href']) && strpos($s['body'],$vurl['href']) === false) { - $s['body'] .= "\n\n" . '[zmg]' . $vurl['href'] . '[/zmg]'; - break; - } - } - } - elseif (is_string($act->obj['url'])) { - if (strpos($s['body'],$act->obj['url']) === false) { - $s['body'] .= "\n\n" . '[zmg]' . $act->obj['url'] . '[/zmg]'; - } - } - } - } - - - if ($act->obj['type'] === 'Page' && ! $s['body']) { - - $ptr = null; - $purl = EMPTY_STR; - - if (array_key_exists('url',$act->obj)) { - if (is_array($act->obj['url'])) { - if (array_key_exists(0,$act->obj['url'])) { - $ptr = $act->obj['url']; - } - else { - $ptr = [ $act->obj['url'] ]; - } - foreach ($ptr as $vurl) { - if (is_array($vurl) && array_key_exists('mediaType',$vurl) && $vurl['mediaType'] === 'text/html') { - $purl = $vurl['href']; - break; - } - elseif (array_key_exists('mimeType',$vurl) && $vurl['mimeType'] === 'text/html') { - $purl = $vurl['href']; - break; - } - } - } - elseif (is_string($act->obj['url'])) { - $purl = $act->obj['url']; - } - if ($purl) { - $li = z_fetch_url(z_root() . '/linkinfo?binurl=' . bin2hex($purl)); - if ($li['success'] && $li['body']) { - $s['body'] .= "\n" . $li['body']; - } - else { - $s['body'] .= "\n\n" . $purl; - } - } - } - } - } - - - - if (in_array($act->obj['type'],[ 'Note','Article','Page' ])) { - $ptr = null; - - if (array_key_exists('url',$act->obj)) { - if (is_array($act->obj['url'])) { - if (array_key_exists(0,$act->obj['url'])) { - $ptr = $act->obj['url']; - } - else { - $ptr = [ $act->obj['url'] ]; - } - foreach ($ptr as $vurl) { - if (is_array($vurl) && array_key_exists('mediaType',$vurl) && $vurl['mediaType'] === 'text/html') { - $s['plink'] = $vurl['href']; - break; - } - } - } - elseif (is_string($act->obj['url'])) { - $s['plink'] = $act->obj['url']; - } - } - } - - if (! (isset($s['plink']) && $s['plink'])) { - $s['plink'] = $s['mid']; - } - - // assume this is private unless specifically told otherwise. - - $s['item_private'] = 1; - - if ($act->recips && (in_array(ACTIVITY_PUBLIC_INBOX,$act->recips) || in_array('Public',$act->recips) || in_array('as:Public',$act->recips))) { - $s['item_private'] = 0; - } - - if (is_array($act->obj)) { - if (array_key_exists('directMessage',$act->obj) && intval($act->obj['directMessage'])) { - $s['item_private'] = 2; - } - } - - set_iconfig($s,'activitypub','recips',$act->raw_recips); - - if (array_key_exists('directMessage',$act->data) && intval($act->data['directMessage'])) { - $s['item_private'] = 2; - } - - - set_iconfig($s,'activitypub','rawmsg',$act->raw,1); - - // Restrict html caching to ActivityPub senders. - // Zot has dynamic content and this library is used by both. - - if ($cacheable) { - if ((! array_key_exists('mimetype',$s)) || (in_array($s['mimetype'], [ 'text/bbcode', 'text/x-multicode' ]))) { - - // preserve the original purified HTML content *unless* we've modified $s['body'] - // within this function (to add attachments or reaction descriptions or mention rewrites). - // This avoids/bypasses some markdown rendering issues which can occur when - // converting to our markdown-enhanced bbcode and then back to HTML again. - // Also if we do need bbcode, use the 'bbonly' flag to ignore markdown and only - // interpret bbcode; which is much less susceptible to false positives in the - // conversion regexes. - - if ($s['body'] === self::bb_content($content,'content')) { - $s['html'] = $content['content']; - } - else { - $s['html'] = bbcode($s['body'], [ 'bbonly' => true ]); - } - } - } - - $hookinfo = [ - 'act' => $act, - 's' => $s - ]; - - call_hooks('decode_note',$hookinfo); - - $s = $hookinfo['s']; - - return $s; - - } - - static function rewrite_mentions_sub(&$s, $pref, &$obj = null) { - - if (isset($s['term']) && is_array($s['term'])) { - foreach ($s['term'] as $tag) { - $txt = EMPTY_STR; - if (intval($tag['ttype']) === TERM_MENTION) { - // some platforms put the identity url into href rather than the profile url. Accept either form. - $x = q("select * from xchan where xchan_url = '%s' or xchan_hash = '%s' limit 1", - dbesc($tag['url']), - dbesc($tag['url']) - ); - if ($x) { - switch ($pref) { - case 0: - $txt = $x[0]['xchan_name']; - break; - case 1: - $txt = (($x[0]['xchan_addr']) ? $x[0]['xchan_addr'] : $x[0]['xchan_name']); - break; - case 2: - default; - if ($x[0]['xchan_addr']) { - $txt = sprintf( t('%1$s (%2$s)'), $x[0]['xchan_name'], $x[0]['xchan_addr']); - } - else { - $txt = $x[0]['xchan_name']; - } - break; - } - } - } - - if ($txt) { - - // the Markdown filter will get tripped up and think this is a markdown link - // if $txt begins with parens so put it behind a zero-width space - if (substr($txt,0,1) === '(') { - $txt = htmlspecialchars_decode('​',ENT_QUOTES) . $txt; - } - $s['body'] = preg_replace('/\@\[zrl\=' . preg_quote($x[0]['xchan_url'],'/') . '\](.*?)\[\/zrl\]/ism', - '@[zrl=' . $x[0]['xchan_url'] . ']' . $txt . '[/zrl]',$s['body']); - $s['body'] = preg_replace('/\@\[url\=' . preg_quote($x[0]['xchan_url'],'/') . '\](.*?)\[\/url\]/ism', - '@[url=' . $x[0]['xchan_url'] . ']' . $txt . '[/url]',$s['body']); - $s['body'] = preg_replace('/\[zrl\=' . preg_quote($x[0]['xchan_url'],'/') . '\]@(.*?)\[\/zrl\]/ism', - '@[zrl=' . $x[0]['xchan_url'] . ']' . $txt . '[/zrl]',$s['body']); - $s['body'] = preg_replace('/\[url\=' . preg_quote($x[0]['xchan_url'],'/') . '\]@(.*?)\[\/url\]/ism', - '@[url=' . $x[0]['xchan_url'] . ']' . $txt . '[/url]',$s['body']); - - // replace these just in case the sender (in this case Friendica) got it wrong - $s['body'] = preg_replace('/\@\[zrl\=' . preg_quote($x[0]['xchan_hash'],'/') . '\](.*?)\[\/zrl\]/ism', - '@[zrl=' . $x[0]['xchan_url'] . ']' . $txt . '[/zrl]',$s['body']); - $s['body'] = preg_replace('/\@\[url\=' . preg_quote($x[0]['xchan_hash'],'/') . '\](.*?)\[\/url\]/ism', - '@[url=' . $x[0]['xchan_url'] . ']' . $txt . '[/url]',$s['body']); - $s['body'] = preg_replace('/\[zrl\=' . preg_quote($x[0]['xchan_hash'],'/') . '\]@(.*?)\[\/zrl\]/ism', - '@[zrl=' . $x[0]['xchan_url'] . ']' . $txt . '[/zrl]',$s['body']); - $s['body'] = preg_replace('/\[url\=' . preg_quote($x[0]['xchan_hash'],'/') . '\]@(.*?)\[\/url\]/ism', - '@[url=' . $x[0]['xchan_url'] . ']' . $txt . '[/url]',$s['body']); - - if ($obj && $txt) { - if (! is_array($obj)) { - $obj = json_decode($obj,true); - } - if (array_path_exists('source/content',$obj)) { - $obj['source']['content'] = preg_replace('/\@\[zrl\=' . preg_quote($x[0]['xchan_url'],'/') . '\](.*?)\[\/zrl\]/ism', - '@[zrl=' . $x[0]['xchan_url'] . ']' . $txt . '[/zrl]',$obj['source']['content']); - $obj['source']['content'] = preg_replace('/\@\[url\=' . preg_quote($x[0]['xchan_url'],'/') . '\](.*?)\[\/url\]/ism', - '@[url=' . $x[0]['xchan_url'] . ']' . $txt . '[/url]',$obj['source']['content']); - } - $obj['content'] = preg_replace('/\@(.*?)\(.*?)\<\/a\>/ism', - '@$1' . $txt . '', $obj['content']); - } - } - } - } - - // $s['html'] will be populated if caching was enabled. - // This is usually the case for ActivityPub sourced content, while Zot6 content is not cached. - - if (isset($s['html']) && $s['html']) { - $s['html'] = bbcode($s['body'], [ 'bbonly' => true ] ); - } - - return; - } - - static function rewrite_mentions(&$s) { - // rewrite incoming mentions in accordance with system.tag_username setting - // 0 - displayname - // 1 - username - // 2 - displayname (username) - // 127 - default - - $pref = intval(PConfig::Get($s['uid'],'system','tag_username',Config::Get('system','tag_username',false))); - - if ($pref === 127) { - return; - } - - self::rewrite_mentions_sub($s,$pref); - - - return; - } - - // $force is used when manually fetching a remote item - it assumes you are granting one-time - // permission for the selected item/conversation regardless of your relationship with the author and - // assumes that you are in fact the sender. Please do not use it for anything else. The only permission - // checking that is performed is that the author isn't blocked by the site admin. - - static function store($channel,$observer_hash,$act,$item,$fetch_parents = true, $force = false) { - - if ($act && $act->implied_create && ! $force) { - // This is originally a S2S object with no associated activity - logger('Not storing implied create activity!'); - return; - } - - $is_sys_channel = is_sys_channel($channel['channel_id']); - $is_child_node = false; - - // Pleroma scrobbles can be really noisy and contain lots of duplicate activities. Disable them by default. - - if (($act->type === 'Listen') && ($is_sys_channel || get_pconfig($channel['channel_id'],'system','allow_scrobbles',false))) { - return; - } - - // Mastodon only allows visibility in public timelines if the public inbox is listed in the 'to' field. - // They are hidden in the public timeline if the public inbox is listed in the 'cc' field. - // This is not part of the activitypub protocol - we might change this to show all public posts in pubstream at some point. - - $pubstream = ((is_array($act->obj) && array_key_exists('to', $act->obj) && is_array($act->obj['to']) && (in_array(ACTIVITY_PUBLIC_INBOX, $act->obj['to']) || in_array('Public',$act->obj['to']) || in_array('as:Public',$act->obj['to']))) ? true : false); - - // very unpleasant and imperfect way of determining a Mastodon DM - - if ($act->raw_recips && array_key_exists('to',$act->raw_recips) && is_array($act->raw_recips['to']) && count($act->raw_recips['to']) === 1 && $act->raw_recips['to'][0] === channel_url($channel) && ! $act->raw_recips['cc']) { - $item['item_private'] = 2; - } - - - - if ($item['parent_mid'] && $item['parent_mid'] !== $item['mid']) { - $is_child_node = true; - } - - $allowed = false; - $reason = [ 'init' ]; - $permit_mentions = intval(PConfig::Get($channel['channel_id'], 'system','permit_all_mentions') && i_am_mentioned($channel,$item)); - - if ($is_child_node) { - $p = q("select * from item where mid = '%s' and uid = %d and item_wall = 1", - dbesc($item['parent_mid']), - intval($channel['channel_id']) - ); - if ($p) { - // set the owner to the owner of the parent - $item['owner_xchan'] = $p[0]['owner_xchan']; - - // quietly reject group comment boosts by group owner - // (usually only sent via ActivityPub so groups will work on microblog platforms) - // This catches those activities if they slipped in via a conversation fetch - - if ($p[0]['parent_mid'] !== $item['parent_mid']) { - if ($item['verb'] === 'Announce' && $item['author_xchan'] === $item['owner_xchan']) { - logger('group boost activity by group owner rejected'); - return; - } - } - - // check permissions against the author, not the sender - $allowed = perm_is_allowed($channel['channel_id'],$item['author_xchan'],'post_comments'); - if (! $allowed) { - $reason[] = 'post_comments perm'; - } - if ((! $allowed) && $permit_mentions) { - if ($p[0]['owner_xchan'] === $channel['channel_hash']) { - $allowed = false; - $reason[] = 'ownership'; - } - else { - $allowed = true; - } - } - if (absolutely_no_comments($p[0])) { - $allowed = false; - $reason[] = 'absolutely'; - } - - if (! $allowed) { - logger('rejected comment from ' . $item['author_xchan'] . ' for ' . $channel['channel_address']); - logger('rejected reason ' . print_r($reason,true)); - logger('rejected: ' . print_r($item,true), LOGGER_DATA); - // let the sender know we received their comment but we don't permit spam here. - self::send_rejection_activity($channel,$item['author_xchan'],$item); - return; - } - - if (perm_is_allowed($channel['channel_id'],$item['author_xchan'],'moderated')) { - $item['item_blocked'] = ITEM_MODERATED; - } - } - else { - - // By default if we allow you to send_stream and comments and this is a comment, it is allowed. - // A side effect of this action is that if you take away send_stream permission, comments to those - // posts you previously allowed will still be accepted. It is possible but might be difficult to fix this. - - $allowed = true; - - // reject public stream comments that weren't sent by the conversation owner - // but only on remote message deliveries to our site ($fetch_parents === true) - - if ($is_sys_channel && $pubstream && $item['owner_xchan'] !== $observer_hash && ! $fetch_parents) { - $allowed = false; - $reason[] = 'sender ' . $observer_hash . ' not owner ' . $item['owner_xchan']; - } - } - - if ($p && $p[0]['obj_type'] === 'Question') { - if ($item['obj_type'] === 'Note' && $item['title'] && (! $item['content'])) { - $item['obj_type'] = 'Answer'; - } - } - } - else { - if (perm_is_allowed($channel['channel_id'],$observer_hash,'send_stream') || ($is_sys_channel && $pubstream)) { - logger('allowed: permission allowed', LOGGER_DATA); - $allowed = true; - } - if ($permit_mentions) { - logger('allowed: permitted mention', LOGGER_DATA); - $allowed = true; - } - } - - if (tgroup_check($channel['channel_id'],$item) && (! $is_child_node)) { - // for forum deliveries, make sure we keep a copy of the signed original - set_iconfig($item,'activitypub','rawmsg',$act->raw,1); - logger('allowed: tgroup'); - $allowed = true; - } - - if (get_abconfig($channel['channel_id'],$observer_hash,'system','block_announce', false)) { - if ($item['verb'] === 'Announce' || strpos($item['body'],'[/share]')) { - $allowed = false; - } - } - - if (intval($item['item_private']) === 2) { - if (! perm_is_allowed($channel['channel_id'],$observer_hash,'post_mail')) { - $allowed = false; - } - } - - if ($is_sys_channel) { - - if (! check_pubstream_channelallowed($observer_hash)) { - $allowed = false; - $reason[] = 'pubstream channel blocked'; - } - - // don't allow pubstream posts if the sender even has a clone on a pubstream denied site - - $h = q("select hubloc_url from hubloc where hubloc_hash = '%s'", - dbesc($observer_hash) - ); - if ($h) { - foreach ($h as $hub) { - if (! check_pubstream_siteallowed($hub['hubloc_url'])) { - $allowed = false; - $reason = 'pubstream site blocked'; - break; - } - } - } - if (intval($item['item_private'])) { - $allowed = false; - $reason[] = 'private item'; - } - } - - $blocked = LibBlock::fetch($channel['channel_id'],BLOCKTYPE_SERVER); - if ($blocked) { - foreach($blocked as $b) { - if (strpos($observer_hash,$b['block_entity']) !== false) { - $allowed = false; - $reason[] = 'blocked'; - } - } - } - - if (! $allowed && ! $force) { - logger('no permission: channel ' . $channel['channel_address'] . ', id = ' . $item['mid']); - logger('no permission: reason ' . print_r($reason,true)); - return; - } - - $item['aid'] = $channel['channel_account_id']; - $item['uid'] = $channel['channel_id']; - - - // Some authors may be zot6 authors in which case we want to store their nomadic identity - // instead of their ActivityPub identity - - $item['author_xchan'] = self::find_best_identity($item['author_xchan']); - $item['owner_xchan'] = self::find_best_identity($item['owner_xchan']); - - if (! ( $item['author_xchan'] && $item['owner_xchan'])) { - logger('owner or author missing.'); - return; - } - - if ($channel['channel_system']) { - if (! MessageFilter::evaluate($item,get_config('system','pubstream_incl'),get_config('system','pubstream_excl'))) { - logger('post is filtered'); - return; - } - } - - // fetch allow/deny lists for the sender, author, or both - // if you have them. post_is_importable() assumes true - // and only fails if there was intentional rejection - // due to this channel's filtering rules for content - // provided by either of these entities. - - $abook = q("select * from abook where ( abook_xchan = '%s' OR abook_xchan = '%s') and abook_channel = %d ", - dbesc($item['author_xchan']), - dbesc($item['owner_xchan']), - intval($channel['channel_id']) - ); - - - if (! post_is_importable($channel['channel_id'],$item,$abook)) { - logger('post is filtered'); - return; - } - - $maxlen = get_max_import_size(); - - if($maxlen && mb_strlen($item['body']) > $maxlen) { - $item['body'] = mb_substr($item['body'],0,$maxlen,'UTF-8'); - logger('message length exceeds max_import_size: truncated'); - } - - if($maxlen && mb_strlen($item['summary']) > $maxlen) { - $item['summary'] = mb_substr($item['summary'],0,$maxlen,'UTF-8'); - logger('message summary length exceeds max_import_size: truncated'); - } - - if ($act->obj['context']) { - set_iconfig($item,'activitypub','context',$act->obj['context'],1); - } - - if ($act->obj['conversation']) { - set_iconfig($item,'ostatus','conversation',$act->obj['conversation'],1); - } - - set_iconfig($item,'activitypub','recips',$act->raw_recips); - - if (intval($act->sigok)) { - $item['item_verified'] = 1; - } - - $parent = null; - - if ($is_child_node) { - - $parent = q("select * from item where mid = '%s' and uid = %d limit 1", - dbesc($item['parent_mid']), - intval($item['uid']) - ); - if (! $parent) { - if (! get_config('system','activitypub', ACTIVITYPUB_ENABLED)) { - return; - } - else { - $fetch = false; - if (intval($channel['channel_system']) || (perm_is_allowed($channel['channel_id'],$observer_hash,'send_stream') && (PConfig::Get($channel['channel_id'],'system','hyperdrive',true) || $act->type === 'Announce'))) { - $fetch = (($fetch_parents) ? self::fetch_and_store_parents($channel,$observer_hash,$act,$item) : false); - } - if ($fetch) { - $parent = q("select * from item where mid = '%s' and uid = %d limit 1", - dbesc($item['parent_mid']), - intval($item['uid']) - ); - } - else { - logger('no parent'); - return; - } - } - } - - $item['comment_policy'] = $parent[0]['comment_policy']; - $item['item_nocomment'] = $parent[0]['item_nocomment']; - $item['comments_closed'] = $parent[0]['comments_closed']; - - if ($parent[0]['parent_mid'] !== $item['parent_mid']) { - $item['thr_parent'] = $item['parent_mid']; - } - else { - $item['thr_parent'] = $parent[0]['parent_mid']; - } - $item['parent_mid'] = $parent[0]['parent_mid']; - - /* - * - * Check for conversation privacy mismatches - * We can only do this if we have a channel and we have fetched the parent - * - */ - - // public conversation, but this comment went rogue and was published privately - // hide it from everybody except the channel owner - - if (intval($parent[0]['item_private']) === 0) { - if (intval($item['item_private'])) { - $item['item_restrict'] = $item['item_restrict'] | 1; - $item['allow_cid'] = '<' . $channel['channel_hash'] . '>'; - $item['allow_gid'] = $item['deny_cid'] = $item['deny_gid'] = ''; - } - } - - // Private conversation, but this comment went rogue and was published publicly - // Set item_restrict to indicate this condition so we can flag it in the UI - - if (intval($parent[0]['item_private']) !== 0 && $act->recips && (in_array(ACTIVITY_PUBLIC_INBOX,$act->recips) || in_array('Public',$act->recips) || in_array('as:Public',$act->recips))) { - $item['item_restrict'] = $item['item_restrict'] | 2; - } - - } - - self::rewrite_mentions($item); - - $r = q("select id, created, edited from item where mid = '%s' and uid = %d limit 1", - dbesc($item['mid']), - intval($item['uid']) - ); - if ($r) { - if ($item['edited'] > $r[0]['edited']) { - $item['id'] = $r[0]['id']; - $x = item_store_update($item); - } - else { - return; - } - } - else { - $x = item_store($item); - } + dbesc($abook_instance), + intval($contact['abook_id']), + intval($channel['channel_id']) + ); + } + + return; + default: + return; + } + } + + // No previous relationship exists. + + if ($act->type === 'Accept') { + // This should not happen unless we deleted the connection before it was accepted. + return; + } + + // From here on out we assume a Follow activity to somebody we have no existing relationship with + + set_abconfig($channel['channel_id'], $person_obj['id'], 'activitypub', 'their_follow_id', $their_follow_id); + set_abconfig($channel['channel_id'], $person_obj['id'], 'activitypub', 'their_follow_type', $act->type); + + // The xchan should have been created by actor_store() above + + $r = q( + "select * from xchan where xchan_hash = '%s' and xchan_network = 'activitypub' limit 1", + dbesc($person_obj['id']) + ); + + if (!$r) { + logger('xchan not found for ' . $person_obj['id']); + return; + } + $ret = $r[0]; + + $blocked = LibBlock::fetch($channel['channel_id'], BLOCKTYPE_SERVER); + if ($blocked) { + foreach ($blocked as $b) { + if (strpos($ret['xchan_url'], $b['block_entity']) !== false) { + logger('siteblock - follower denied'); + return; + } + } + } + if (LibBlock::fetch_by_entity($channel['channel_id'], $ret['xchan_hash'])) { + logger('actorblock - follower denied'); + return; + } + + $p = Permissions::connect_perms($channel['channel_id']); + $my_perms = Permissions::serialise($p['perms']); + $automatic = $p['automatic']; + + $closeness = PConfig::Get($channel['channel_id'], 'system', 'new_abook_closeness', 80); + + $r = abook_store_lowlevel( + [ + 'abook_account' => intval($channel['channel_account_id']), + 'abook_channel' => intval($channel['channel_id']), + 'abook_xchan' => $ret['xchan_hash'], + 'abook_closeness' => intval($closeness), + 'abook_created' => datetime_convert(), + 'abook_updated' => datetime_convert(), + 'abook_connected' => datetime_convert(), + 'abook_dob' => NULL_DATE, + 'abook_pending' => intval(($automatic) ? 0 : 1), + 'abook_instance' => z_root() + ] + ); + + if ($my_perms) { + AbConfig::Set($channel['channel_id'], $ret['xchan_hash'], 'system', 'my_perms', $my_perms); + } + + if ($their_perms) { + AbConfig::Set($channel['channel_id'], $ret['xchan_hash'], 'system', 'their_perms', $their_perms); + } + + + if ($r) { + logger("New ActivityPub follower for {$channel['channel_name']}"); + + $new_connection = q( + "select * from abook left join xchan on abook_xchan = xchan_hash left join hubloc on hubloc_hash = xchan_hash where abook_channel = %d and abook_xchan = '%s' order by abook_created desc limit 1", + intval($channel['channel_id']), + dbesc($ret['xchan_hash']) + ); + if ($new_connection) { + Enotify::submit( + [ + 'type' => NOTIFY_INTRO, + 'from_xchan' => $ret['xchan_hash'], + 'to_xchan' => $channel['channel_hash'], + 'link' => z_root() . '/connedit/' . $new_connection[0]['abook_id'], + ] + ); + + if ($my_perms && $automatic) { + // send an Accept for this Follow activity + Run::Summon(['Notifier', 'permissions_accept', $new_connection[0]['abook_id']]); + // Send back a Follow notification to them + Run::Summon(['Notifier', 'permissions_create', $new_connection[0]['abook_id']]); + } + + $clone = []; + foreach ($new_connection[0] as $k => $v) { + if (strpos($k, 'abook_') === 0) { + $clone[$k] = $v; + } + } + unset($clone['abook_id']); + unset($clone['abook_account']); + unset($clone['abook_channel']); + + $abconfig = load_abconfig($channel['channel_id'], $clone['abook_xchan']); + + if ($abconfig) { + $clone['abconfig'] = $abconfig; + } + Libsync::build_sync_packet($channel['channel_id'], ['abook' => [$clone]]); + } + } + + + /* If there is a default group for this channel and permissions are automatic, add this member to it */ + + if ($channel['channel_default_group'] && $automatic) { + $g = AccessList::rec_byhash($channel['channel_id'], $channel['channel_default_group']); + if ($g) { + AccessList::member_add($channel['channel_id'], '', $ret['xchan_hash'], $g['id']); + } + } + + return; + } + + + public static function unfollow($channel, $act) + { + + $contact = null; + + /* actor is unfollowing $channel */ + + $person_obj = $act->actor; + + if (is_array($person_obj)) { + $r = q( + "select * from abook left join xchan on abook_xchan = xchan_hash where abook_xchan = '%s' and abook_channel = %d limit 1", + dbesc($person_obj['id']), + intval($channel['channel_id']) + ); + if ($r) { + // remove all permissions they provided + del_abconfig($channel['channel_id'], $r[0]['xchan_hash'], 'system', 'their_perms', EMPTY_STR); + } + } + + return; + } + + + public static function actor_store($url, $person_obj, $force = false) + { + + if (!is_array($person_obj)) { + return; + } + +// logger('person_obj: ' . print_r($person_obj,true)); + + if (array_key_exists('movedTo', $person_obj) && $person_obj['movedTo'] && !is_array($person_obj['movedTo'])) { + $tgt = self::fetch($person_obj['movedTo']); + if (is_array($tgt)) { + self::actor_store($person_obj['movedTo'], $tgt); + ActivityPub::move($person_obj['id'], $tgt); + } + return; + } + + + $ap_hubloc = null; + + $hublocs = self::get_actor_hublocs($url); + if ($hublocs) { + foreach ($hublocs as $hub) { + if ($hub['hubloc_network'] === 'activitypub') { + $ap_hubloc = $hub; + } + if (in_array($hub['hubloc_network'],['zot6','nomad'])) { + Libzot::update_cached_hubloc($hub); + } + } + } + + if ($ap_hubloc) { + // we already have a stored record. Determine if it needs updating. + if ($ap_hubloc['hubloc_updated'] < datetime_convert('UTC', 'UTC', ' now - ' . self::$ACTOR_CACHE_DAYS . ' days') || $force) { + $person_obj = self::fetch($url); + // ensure we received something + if (!is_array($person_obj)) { + return; + } + } else { + return; + } + } + + + if (isset($person_obj['id'])) { + $url = $person_obj['id']; + } + + if (!$url) { + return; + } + + // store the actor record in XConfig + XConfig::Set($url, 'system', 'actor_record', $person_obj); + + $name = escape_tags($person_obj['name']); + if (!$name) { + $name = escape_tags($person_obj['preferredUsername']); + } + if (!$name) { + $name = escape_tags(t('Unknown')); + } + + $username = escape_tags($person_obj['preferredUsername']); + $h = parse_url($url); + if ($h && $h['host']) { + $username .= '@' . $h['host']; + } + + if ($person_obj['icon']) { + if (is_array($person_obj['icon'])) { + if (array_key_exists('url', $person_obj['icon'])) { + $icon = $person_obj['icon']['url']; + } else { + if (is_string($person_obj['icon'][0])) { + $icon = $person_obj['icon'][0]; + } elseif (array_key_exists('url', $person_obj['icon'][0])) { + $icon = $person_obj['icon'][0]['url']; + } + } + } else { + $icon = $person_obj['icon']; + } + } + if (!(isset($icon) && $icon)) { + $icon = z_root() . '/' . get_default_profile_photo(); + } + + $cover_photo = false; + + if (isset($person_obj['image'])) { + if (is_string($person_obj['image'])) { + $cover_photo = $person_obj['image']; + } + if (isset($person_obj['image']['url'])) { + $cover_photo = $person_obj['image']['url']; + } + } + + $hidden = false; + if (array_key_exists('discoverable', $person_obj) && (!intval($person_obj['discoverable']))) { + $hidden = true; + } + + $links = false; + $profile = false; + + if (is_array($person_obj['url'])) { + if (!array_key_exists(0, $person_obj['url'])) { + $links = [$person_obj['url']]; + } else { + $links = $person_obj['url']; + } + } + + if (is_array($links) && $links) { + foreach ($links as $link) { + if (is_array($link) && array_key_exists('mediaType', $link) && $link['mediaType'] === 'text/html') { + $profile = $link['href']; + } + } + if (!$profile) { + $profile = $links[0]['href']; + } + } elseif (isset($person_obj['url']) && is_string($person_obj['url'])) { + $profile = $person_obj['url']; + } + + if (!$profile) { + $profile = $url; + } + + $inbox = ((array_key_exists('inbox', $person_obj)) ? $person_obj['inbox'] : null); + + // either an invalid identity or a cached entry of some kind which didn't get caught above + + if ((!$inbox) || strpos($inbox, z_root()) !== false) { + return; + } + + + $collections = []; + + if ($inbox) { + $collections['inbox'] = $inbox; + if (array_key_exists('outbox', $person_obj) && is_string($person_obj['outbox'])) { + $collections['outbox'] = $person_obj['outbox']; + } + if (array_key_exists('followers', $person_obj) && is_string($person_obj['followers'])) { + $collections['followers'] = $person_obj['followers']; + } + if (array_key_exists('following', $person_obj) && is_string($person_obj['following'])) { + $collections['following'] = $person_obj['following']; + } + if (array_key_exists('wall', $person_obj) && is_string($person_obj['wall'])) { + $collections['wall'] = $person_obj['wall']; + } + if (array_path_exists('endpoints/sharedInbox', $person_obj) && is_string($person_obj['endpoints']['sharedInbox'])) { + $collections['sharedInbox'] = $person_obj['endpoints']['sharedInbox']; + } + } + + if (isset($person_obj['publicKey']['publicKeyPem'])) { + if ($person_obj['id'] === $person_obj['publicKey']['owner']) { + $pubkey = $person_obj['publicKey']['publicKeyPem']; + if (strstr($pubkey, 'RSA ')) { + $pubkey = Keyutils::rsatopem($pubkey); + } + } + } + + $keywords = []; + + if (isset($person_obj['tag']) && is_array($person_obj['tag'])) { + foreach ($person_obj['tag'] as $t) { + if (is_array($t) && isset($t['type']) && $t['type'] === 'Hashtag') { + if (isset($t['name'])) { + $tag = escape_tags((substr($t['name'], 0, 1) === '#') ? substr($t['name'], 1) : $t['name']); + if ($tag) { + $keywords[] = $tag; + } + } + } + if (is_array($t) && isset($t['type']) && $t['type'] === 'PropertyValue') { + if (isset($t['name']) && isset($t['value']) && $t['name'] === 'Protocol') { + self::update_protocols($url, trim($t['value'])); + } + } + } + } + + $xchan_type = self::get_xchan_type($person_obj['type']); + $about = ((isset($person_obj['summary'])) ? html2bbcode(purify_html($person_obj['summary'])) : EMPTY_STR); + + $p = q( + "select * from xchan where xchan_url = '%s' and xchan_network in ('zot6','nomad') limit 1", + dbesc($url) + ); + if ($p) { + set_xconfig($url, 'system', 'protocols', 'nomad,zot6,activitypub'); + } + + // there is no standard way to represent an 'instance actor' but this will at least subdue the multiple + // pages of Mastodon and Pleroma instance actors in the directory. + // @TODO - (2021-08-27) remove this if they provide a non-person xchan_type + // once extended xchan_type directory filtering is implemented. + $censored = ((strpos($profile, 'instance_actor') || strpos($profile, '/internal/fetch')) ? 1 : 0); + + $r = q( + "select * from xchan where xchan_hash = '%s' limit 1", + dbesc($url) + ); + if (!$r) { + // create a new record + $r = xchan_store_lowlevel( + [ + 'xchan_hash' => $url, + 'xchan_guid' => $url, + 'xchan_pubkey' => $pubkey, + 'xchan_addr' => ((strpos($username, '@')) ? $username : ''), + 'xchan_url' => $profile, + 'xchan_name' => $name, + 'xchan_hidden' => intval($hidden), + 'xchan_updated' => datetime_convert(), + 'xchan_name_date' => datetime_convert(), + 'xchan_network' => 'activitypub', + 'xchan_type' => $xchan_type, + 'xchan_photo_date' => datetime_convert('UTC', 'UTC', '1968-01-01'), + 'xchan_photo_l' => z_root() . '/' . get_default_profile_photo(), + 'xchan_photo_m' => z_root() . '/' . get_default_profile_photo(80), + 'xchan_photo_s' => z_root() . '/' . get_default_profile_photo(48), + 'xchan_photo_mimetype' => 'image/png', + 'xchan_censored' => $censored + + ] + ); + } else { + // Record exists. Cache existing records for a set number of days + // then refetch to catch updated profile photos, names, etc. + + if ($r[0]['xchan_name_date'] >= datetime_convert('UTC', 'UTC', 'now - ' . self::$ACTOR_CACHE_DAYS . ' days') && (!$force)) { + return; + } + + // update existing record + $u = q( + "update xchan set xchan_updated = '%s', xchan_name = '%s', xchan_pubkey = '%s', xchan_network = '%s', xchan_name_date = '%s', xchan_hidden = %d, xchan_type = %d, xchan_censored = %d where xchan_hash = '%s'", + dbesc(datetime_convert()), + dbesc($name), + dbesc($pubkey), + dbesc('activitypub'), + dbesc(datetime_convert()), + intval($hidden), + intval($xchan_type), + intval($censored), + dbesc($url) + ); + + if (strpos($username, '@') && ($r[0]['xchan_addr'] !== $username)) { + $r = q( + "update xchan set xchan_addr = '%s' where xchan_hash = '%s'", + dbesc($username), + dbesc($url) + ); + } + } + + if ($cover_photo) { + set_xconfig($url, 'system', 'cover_photo', $cover_photo); + } + + + $m = parse_url($url); + if ($m['scheme'] && $m['host']) { + $site_url = $m['scheme'] . '://' . $m['host']; + $ni = Nodeinfo::fetch($site_url); + if ($ni && is_array($ni)) { + $software = ((array_path_exists('software/name', $ni)) ? $ni['software']['name'] : ''); + $version = ((array_path_exists('software/version', $ni)) ? $ni['software']['version'] : ''); + $register = $ni['openRegistrations']; + + $site = q( + "select * from site where site_url = '%s'", + dbesc($site_url) + ); + if ($site) { + q( + "update site set site_project = '%s', site_update = '%s', site_version = '%s' where site_url = '%s'", + dbesc($software), + dbesc(datetime_convert()), + dbesc($version), + dbesc($site_url) + ); + // it may have been saved originally as an unknown type, but we now know what it is + if (intval($site[0]['site_type']) === SITE_TYPE_UNKNOWN) { + q( + "update site set site_type = %d where site_url = '%s'", + intval(SITE_TYPE_NOTZOT), + dbesc($site_url) + ); + } + } else { + site_store_lowlevel( + [ + 'site_url' => $site_url, + 'site_update' => datetime_convert(), + 'site_dead' => 0, + 'site_type' => SITE_TYPE_NOTZOT, + 'site_project' => $software, + 'site_version' => $version, + 'site_access' => (($register) ? ACCESS_FREE : ACCESS_PRIVATE), + 'site_register' => (($register) ? REGISTER_OPEN : REGISTER_CLOSED) + ] + ); + } + } + } + + Libzotdir::import_directory_profile($url, ['about' => $about, 'keywords' => $keywords, 'dob' => '0000-00-00'], null, 0, true); + + if ($collections) { + set_xconfig($url, 'activitypub', 'collections', $collections); + } + + $h = q( + "select * from hubloc where hubloc_hash = '%s' limit 1", + dbesc($url) + ); + + + $m = parse_url($url); + if ($m) { + $hostname = $m['host']; + $baseurl = $m['scheme'] . '://' . $m['host'] . ((isset($m['port']) && intval($m['port'])) ? ':' . $m['port'] : ''); + } + + if (!$h) { + $r = hubloc_store_lowlevel( + [ + 'hubloc_guid' => $url, + 'hubloc_hash' => $url, + 'hubloc_id_url' => $profile, + 'hubloc_addr' => ((strpos($username, '@')) ? $username : ''), + 'hubloc_network' => 'activitypub', + 'hubloc_url' => $baseurl, + 'hubloc_host' => $hostname, + 'hubloc_callback' => $inbox, + 'hubloc_updated' => datetime_convert(), + 'hubloc_primary' => 1 + ] + ); + } else { + if (strpos($username, '@') && ($h[0]['hubloc_addr'] !== $username)) { + $r = q( + "update hubloc set hubloc_addr = '%s' where hubloc_hash = '%s'", + dbesc($username), + dbesc($url) + ); + } + if ($inbox !== $h[0]['hubloc_callback']) { + $r = q( + "update hubloc set hubloc_callback = '%s' where hubloc_hash = '%s'", + dbesc($inbox), + dbesc($url) + ); + } + if ($profile !== $h[0]['hubloc_id_url']) { + $r = q( + "update hubloc set hubloc_id_url = '%s' where hubloc_hash = '%s'", + dbesc($profile), + dbesc($url) + ); + } + $r = q( + "update hubloc set hubloc_updated = '%s' where hubloc_hash = '%s'", + dbesc(datetime_convert()), + dbesc($url) + ); + } + + if (!$icon) { + $icon = z_root() . '/' . get_default_profile_photo(300); + } + + // We store all ActivityPub actors we can resolve. Some of them may be able to communicate over Zot6. Find them. + // Only probe if it looks like it looks something like a zot6 URL as there isn't anything in the actor record which we can reliably use for this purpose + // and adding zot discovery urls to the actor record will cause federation to fail with the 20-30 projects which don't accept arrays in the url field. + + if (strpos($url, '/channel/') !== false) { + $zx = q( + "select * from hubloc where hubloc_id_url = '%s' and hubloc_network in ('zot6','nomad')", + dbesc($url) + ); + if (($username) && strpos($username, '@') && (!$zx)) { + Run::Summon(['Gprobe', bin2hex($username)]); + } + } + + Run::Summon(['Xchan_photo', bin2hex($icon), bin2hex($url)]); + } + + public static function update_protocols($xchan, $str) + { + $existing = explode(',', get_xconfig($xchan, 'system', 'protocols', EMPTY_STR)); + if (!in_array($str, $existing)) { + $existing[] = $str; + set_xconfig($xchan, 'system', 'protocols', implode(',', $existing)); + } + } + + + public static function drop($channel, $observer, $act) + { + $r = q( + "select * from item where mid = '%s' and uid = %d limit 1", + dbesc((is_array($act->obj)) ? $act->obj['id'] : $act->obj), + intval($channel['channel_id']) + ); + + if (!$r) { + return; + } + + if (in_array($observer, [$r[0]['author_xchan'], $r[0]['owner_xchan']])) { + drop_item($r[0]['id'], false); + } elseif (in_array($act->actor['id'], [$r[0]['author_xchan'], $r[0]['owner_xchan']])) { + drop_item($r[0]['id'], false); + } + } + + + // sort function width decreasing + + public static function vid_sort($a, $b) + { + if ($a['width'] === $b['width']) { + return 0; + } + return (($a['width'] > $b['width']) ? -1 : 1); + } + + public static function share_bb($obj) + { + // @fixme - error check and set defaults + + $name = urlencode($obj['actor']['name']); + $profile = $obj['actor']['id']; + $photo = $obj['icon']['url']; + + $s = "\r\n[share author='" . $name . + "' profile='" . $profile . + "' avatar='" . $photo . + "' link='" . $act->obj['id'] . + "' auth='" . ((is_matrix_url($act->obj['id'])) ? 'true' : 'false') . + "' posted='" . $act->obj['published'] . + "' message_id='" . $act->obj['id'] . + "']"; + + return $s; + } + + public static function get_actor_bbmention($id) + { + + $x = q( + "select * from hubloc left join xchan on hubloc_hash = xchan_hash where hubloc_hash = '%s' or hubloc_id_url = '%s' limit 1", + dbesc($id), + dbesc($id) + ); + + if ($x) { + // a name starting with a left paren can trick the markdown parser into creating a link so insert a zero-width space + if (substr($x[0]['xchan_name'], 0, 1) === '(') { + $x[0]['xchan_name'] = htmlspecialchars_decode('​', ENT_QUOTES) . $x[0]['xchan_name']; + } + + return sprintf('@[zrl=%s]%s[/zrl]', $x[0]['xchan_url'], $x[0]['xchan_name']); + } + return '@{' . $id . '}'; + } + + public static function update_poll($item, $post) + { + + logger('updating poll'); + + $multi = false; + $mid = $post['mid']; + $content = $post['title']; + + if (!$item) { + return false; + } + + $o = json_decode($item['obj'], true); + if ($o && array_key_exists('anyOf', $o)) { + $multi = true; + } + + $r = q( + "select mid, title from item where parent_mid = '%s' and author_xchan = '%s' and mid != parent_mid ", + dbesc($item['mid']), + dbesc($post['author_xchan']) + ); + + // prevent any duplicate votes by same author for oneOf and duplicate votes with same author and same answer for anyOf + + if ($r) { + if ($multi) { + foreach ($r as $rv) { + if ($rv['title'] === $content && $rv['mid'] !== $mid) { + return false; + } + } + } else { + foreach ($r as $rv) { + if ($rv['mid'] !== $mid) { + return false; + } + } + } + } + + $answer_found = false; + $found = false; + if ($multi) { + for ($c = 0; $c < count($o['anyOf']); $c++) { + if ($o['anyOf'][$c]['name'] === $content) { + $answer_found = true; + if (is_array($o['anyOf'][$c]['replies'])) { + foreach ($o['anyOf'][$c]['replies'] as $reply) { + if (is_array($reply) && array_key_exists('id', $reply) && $reply['id'] === $mid) { + $found = true; + } + } + } + + if (!$found) { + $o['anyOf'][$c]['replies']['totalItems']++; + $o['anyOf'][$c]['replies']['items'][] = ['id' => $mid, 'type' => 'Note']; + } + } + } + } else { + for ($c = 0; $c < count($o['oneOf']); $c++) { + if ($o['oneOf'][$c]['name'] === $content) { + $answer_found = true; + if (is_array($o['oneOf'][$c]['replies'])) { + foreach ($o['oneOf'][$c]['replies'] as $reply) { + if (is_array($reply) && array_key_exists('id', $reply) && $reply['id'] === $mid) { + $found = true; + } + } + } + + if (!$found) { + $o['oneOf'][$c]['replies']['totalItems']++; + $o['oneOf'][$c]['replies']['items'][] = ['id' => $mid, 'type' => 'Note']; + } + } + } + } + + if ($item['comments_closed'] > NULL_DATE) { + if ($item['comments_closed'] > datetime_convert()) { + $o['closed'] = datetime_convert('UTC', 'UTC', $item['comments_closed'], ATOM_TIME); + // set this to force an update + $answer_found = true; + } + } + + logger('updated_poll: ' . print_r($o, true), LOGGER_DATA); + if ($answer_found && !$found) { + $x = q( + "update item set obj = '%s', edited = '%s' where id = %d", + dbesc(json_encode($o)), + dbesc(datetime_convert()), + intval($item['id']) + ); + Run::Summon(['Notifier', 'wall-new', $item['id']]); + return true; + } + + return false; + } + + + public static function decode_note($act, $cacheable = false) + { + + $response_activity = false; + $poll_handled = false; + + $s = []; + + if (is_array($act->obj)) { + $binary = false; + $markdown = false; + + if (array_key_exists('mediaType', $act->obj) && $act->obj['mediaType'] !== 'text/html') { + if ($act->obj['mediaType'] === 'text/markdown') { + $markdown = true; + } else { + $s['mimetype'] = escape_tags($act->obj['mediaType']); + $binary = true; + } + } + + $content = self::get_content($act->obj, $binary); + + if ($cacheable) { + // Zot6 activities will all be rendered from bbcode source in order to generate dynamic content. + // If the activity came from ActivityPub (hence $cacheable is set), use the HTML rendering + // and discard the bbcode source since it is unlikely that it is compatible with our implementation. + // Friendica for example. + + unset($content['bbcode']); + } + + // handle markdown conversion inline (peertube) + + if ($markdown) { + foreach (['summary', 'content'] as $t) { + $content[$t] = Markdown::to_bbcode($content[$t], true, ['preserve_lf' => true]); + } + } + } + + // These activities should have been handled separately in the Inbox module and should not be turned into posts + + if ( + in_array($act->type, ['Follow', 'Accept', 'Reject', 'Create', 'Update']) && is_array($act->obj) && array_key_exists('type', $act->obj) + && ($act->obj['type'] === 'Follow' || ActivityStreams::is_an_actor($act->obj['type'])) + ) { + return false; + } + + // Within our family of projects, Follow/Unfollow of a thread is an internal activity which should not be transmitted, + // hence if we receive it - ignore or reject it. + // This may have to be revisited if AP projects start using Follow for objects other than actors. + + if (in_array($act->type, [ACTIVITY_FOLLOW, ACTIVITY_IGNORE])) { + return false; + } + + // Do not proceed further if there is no actor. + + if (!isset($act->actor['id'])) { + logger('No actor!'); + return false; + } + + $s['owner_xchan'] = $act->actor['id']; + $s['author_xchan'] = $act->actor['id']; + + // ensure we store the original actor + self::actor_store($act->actor['id'], $act->actor); + + $s['mid'] = ((is_array($act->obj) && isset($act->obj['id'])) ? $act->obj['id'] : $act->obj); + + if (!$s['mid']) { + return false; + } + + $s['parent_mid'] = $act->parent_id; + + if (array_key_exists('published', $act->data) && $act->data['published']) { + $s['created'] = datetime_convert('UTC', 'UTC', $act->data['published']); + } elseif (array_key_exists('published', $act->obj) && $act->obj['published']) { + $s['created'] = datetime_convert('UTC', 'UTC', $act->obj['published']); + } + if (array_key_exists('updated', $act->data) && $act->data['updated']) { + $s['edited'] = datetime_convert('UTC', 'UTC', $act->data['updated']); + } elseif (array_key_exists('updated', $act->obj) && $act->obj['updated']) { + $s['edited'] = datetime_convert('UTC', 'UTC', $act->obj['updated']); + } + if (array_key_exists('expires', $act->data) && $act->data['expires']) { + $s['expires'] = datetime_convert('UTC', 'UTC', $act->data['expires']); + } elseif (array_key_exists('expires', $act->obj) && $act->obj['expires']) { + $s['expires'] = datetime_convert('UTC', 'UTC', $act->obj['expires']); + } + + if ($act->type === 'Invite' && is_array($act->obj) && array_key_exists('type', $act->obj) && $act->obj['type'] === 'Event') { + $s['mid'] = $s['parent_mid'] = $act->id; + } + + if (isset($act->replyto) && !empty($act->replyto)) { + if (is_array($act->replyto) && isset($act->replyto['id'])) { + $s['replyto'] = $act->replyto['id']; + } else { + $s['replyto'] = $act->replyto; + } + } + + if (ActivityStreams::is_response_activity($act->type)) { + $response_activity = true; + + $s['mid'] = $act->id; + $s['parent_mid'] = ((is_array($act->obj) && isset($act->obj['id'])) ? $act->obj['id'] : $act->obj); + + + // over-ride the object timestamp with the activity + + if (isset($act->data['published']) && $act->data['published']) { + $s['created'] = datetime_convert('UTC', 'UTC', $act->data['published']); + } + + if (isset($act->data['updated']) && $act->data['updated']) { + $s['edited'] = datetime_convert('UTC', 'UTC', $act->data['updated']); + } + + $obj_actor = ((isset($act->obj['actor'])) ? $act->obj['actor'] : $act->get_actor('attributedTo', $act->obj)); + + // Actor records themselves do not have an actor or attributedTo + if ((!$obj_actor) && isset($act->obj['type']) && Activitystreams::is_an_actor($act->obj['type'])) { + $obj_actor = $act->obj; + } + + // We already check for admin blocks of third-party objects when fetching them explicitly. + // Repeat here just in case the entire object was supplied inline and did not require fetching + + if ($obj_actor && array_key_exists('id', $obj_actor)) { + $m = parse_url($obj_actor['id']); + if ($m && $m['scheme'] && $m['host']) { + if (!check_siteallowed($m['scheme'] . '://' . $m['host'])) { + return; + } + } + if (!check_channelallowed($obj_actor['id'])) { + return; + } + } + + // if the object is an actor, it is not really a response activity, so reset it to a top level post + + if (ActivityStreams::is_an_actor($act->obj['type'])) { + $s['parent_mid'] = $s['mid']; + } + + + // ensure we store the original actor of the associated (parent) object + self::actor_store($obj_actor['id'], $obj_actor); + + $mention = self::get_actor_bbmention($obj_actor['id']); + + $quoted_content = '[quote]' . $content['content'] . '[/quote]'; + + if ($act->type === 'Like') { + $content['content'] = sprintf(t('Likes %1$s\'s %2$s'), $mention, ((ActivityStreams::is_an_actor($act->obj['type'])) ? t('Profile') : $act->obj['type'])) . EOL . EOL . $quoted_content; + } + if ($act->type === 'Dislike') { + $content['content'] = sprintf(t('Doesn\'t like %1$s\'s %2$s'), $mention, ((ActivityStreams::is_an_actor($act->obj['type'])) ? t('Profile') : $act->obj['type'])) . EOL . EOL . $quoted_content; + } + + // handle event RSVPs + if (($act->obj['type'] === 'Event') || ($act->obj['type'] === 'Invite' && array_path_exists('object/type', $act->obj) && $act->obj['object']['type'] === 'Event')) { + if ($act->type === 'Accept') { + $content['content'] = sprintf(t('Will attend %s\'s event'), $mention) . EOL . EOL . $quoted_content; + } + if ($act->type === 'Reject') { + $content['content'] = sprintf(t('Will not attend %s\'s event'), $mention) . EOL . EOL . $quoted_content; + } + if ($act->type === 'TentativeAccept') { + $content['content'] = sprintf(t('May attend %s\'s event'), $mention) . EOL . EOL . $quoted_content; + } + if ($act->type === 'TentativeReject') { + $content['content'] = sprintf(t('May not attend %s\'s event'), $mention) . EOL . EOL . $quoted_content; + } + } + + if ($act->type === 'Announce') { + $content['content'] = sprintf(t('🔁 Repeated %1$s\'s %2$s'), $mention, ((ActivityStreams::is_an_actor($act->obj['type'])) ? t('Profile') : $act->obj['type'])); + } + + if ($act->type === 'emojiReaction') { + // Hubzilla reactions + $content['content'] = (($act->tgt && $act->tgt['type'] === 'Image') ? '[img=32x32]' . $act->tgt['url'] . '[/img]' : '&#x' . $act->tgt['name'] . ';'); + } + + if (in_array($act->type, ['EmojiReaction', 'EmojiReact'])) { + // Pleroma reactions + $t = trim(self::get_textfield($act->data, 'content')); + $e = Emoji\is_single_emoji($t) || mb_strlen($t) === 1; + if ($e) { + $content['content'] = $t; + } + } + } + + $s['comment_policy'] = 'authenticated'; + + if ($s['mid'] === $s['parent_mid']) { + // it is a parent node - decode the comment policy info if present + if (isset($act->obj['commentPolicy'])) { + $until = strpos($act->obj['commentPolicy'], 'until='); + if ($until !== false) { + $s['comments_closed'] = datetime_convert('UTC', 'UTC', substr($act->obj['commentPolicy'], $until + 6)); + if ($s['comments_closed'] < datetime_convert()) { + $s['nocomment'] = true; + } + } + $remainder = substr($act->obj['commentPolicy'], 0, (($until) ? $until : strlen($act->obj['commentPolicy']))); + if ($remainder) { + $s['comment_policy'] = $remainder; + } + if (!(isset($item['comment_policy']) && strlen($item['comment_policy']))) { + $s['comment_policy'] = 'contacts'; + } + } + } + + if (!(array_key_exists('created', $s) && $s['created'])) { + $s['created'] = datetime_convert(); + } + if (!(array_key_exists('edited', $s) && $s['edited'])) { + $s['edited'] = $s['created']; + } + $s['title'] = (($response_activity) ? EMPTY_STR : self::bb_content($content, 'name')); + $s['summary'] = self::bb_content($content, 'summary'); + + if (array_key_exists('mimetype', $s) && (!in_array($s['mimetype'], ['text/bbcode', 'text/x-multicode']))) { + $s['body'] = $content['content']; + } else { + $s['body'] = ((self::bb_content($content, 'bbcode') && (!$response_activity)) ? self::bb_content($content, 'bbcode') : self::bb_content($content, 'content')); + } + + + // handle some of the more widely used of the numerous and varied ways of deleting something + + if (in_array($act->type, ['Delete', 'Undo', 'Tombstone'])) { + $s['item_deleted'] = 1; + } + + if ($act->type === 'Create' && $act->obj['type'] === 'Tombstone') { + $s['item_deleted'] = 1; + } + + if ($act->obj && array_key_exists('sensitive', $act->obj) && boolval($act->obj['sensitive'])) { + $s['item_nsfw'] = 1; + } + + $s['verb'] = self::activity_mapper($act->type); + + // Mastodon does not provide update timestamps when updating poll tallies which means race conditions may occur here. + if ($act->type === 'Update' && $act->obj['type'] === 'Question' && $s['edited'] === $s['created']) { + $s['edited'] = datetime_convert(); + } + + + $s['obj_type'] = self::activity_obj_mapper($act->obj['type']); + $s['obj'] = $act->obj; + if (is_array($s['obj']) && array_path_exists('actor/id', $s['obj'])) { + $s['obj']['actor'] = $s['obj']['actor']['id']; + } + + if (is_array($act->tgt) && $act->tgt) { + if (array_key_exists('type', $act->tgt)) { + $s['tgt_type'] = self::activity_obj_mapper($act->tgt['type']); + } + // We shouldn't need to store collection contents which could be large. We will often only require the meta-data + if (isset($s['tgt_type']) && strpos($s['tgt_type'], 'Collection') !== false) { + $s['target'] = ['id' => $act->tgt['id'], 'type' => $s['tgt_type'], 'attributedTo' => ((isset($act->tgt['attributedTo'])) ? $act->tgt['attributedTo'] : $act->tgt['actor'])]; + } + } + + $generator = $act->get_property_obj('generator'); + if ((!$generator) && (!$response_activity)) { + $generator = $act->get_property_obj('generator', $act->obj); + } + + if ( + $generator && array_key_exists('type', $generator) + && in_array($generator['type'], ['Application', 'Service']) && array_key_exists('name', $generator) + ) { + $s['app'] = escape_tags($generator['name']); + } + + $location = $act->get_property_obj('location'); + if (is_array($location) && array_key_exists('type', $location) && $location['type'] === 'Place') { + if (array_key_exists('name', $location)) { + $s['location'] = escape_tags($location['name']); + } + if (array_key_exists('content', $location)) { + $s['location'] = html2plain(purify_html($location['content']), 256); + } + + if (array_key_exists('latitude', $location) && array_key_exists('longitude', $location)) { + $s['coord'] = escape_tags($location['latitude']) . ' ' . escape_tags($location['longitude']); + } + } + + if (!$response_activity) { + $a = self::decode_taxonomy($act->obj); + if ($a) { + $s['term'] = $a; + foreach ($a as $b) { + if ($b['ttype'] === TERM_EMOJI) { + $s['summary'] = str_replace($b['term'], '[img=16x16]' . $b['url'] . '[/img]', $s['summary']); + + // @todo - @bug + // The emoji reference in the body might be inside a code block. In that case we shouldn't replace it. + // Currently we do. + + $s['body'] = str_replace($b['term'], '[img=16x16]' . $b['url'] . '[/img]', $s['body']); + } + } + } + + $a = self::decode_attachment($act->obj); + if ($a) { + $s['attach'] = $a; + } + + $a = self::decode_iconfig($act->obj); + if ($a) { + $s['iconfig'] = $a; + } + } + + // Objects that might have media attachments which aren't already provided in the content element. + // We'll check specific media objects separately. + + if (in_array($act->obj['type'], ['Article', 'Document', 'Event', 'Note', 'Page', 'Place', 'Question']) && isset($s['attach']) && $s['attach']) { + $s['body'] .= self::bb_attach($s['attach'], $s['body']); + } + + if ($act->obj['type'] === 'Question' && in_array($act->type, ['Create', 'Update'])) { + if ($act->obj['endTime']) { + $s['comments_closed'] = datetime_convert('UTC', 'UTC', $act->obj['endTime']); + } + } + + if (array_key_exists('closed', $act->obj) && $act->obj['closed']) { + $s['comments_closed'] = datetime_convert('UTC', 'UTC', $act->obj['closed']); + } + + + // we will need a hook here to extract magnet links e.g. peertube + // right now just link to the largest mp4 we find that will fit in our + // standard content region + + if (!$response_activity) { + if ($act->obj['type'] === 'Video') { + $vtypes = [ + 'video/mp4', + 'video/ogg', + 'video/webm' + ]; + + $mps = []; + $poster = null; + $ptr = null; + + // try to find a poster to display on the video element + + if (array_key_exists('icon', $act->obj)) { + if (is_array($act->obj['icon'])) { + if (array_key_exists(0, $act->obj['icon'])) { + $ptr = $act->obj['icon']; + } else { + $ptr = [$act->obj['icon']]; + } + } + if ($ptr) { + foreach ($ptr as $foo) { + if (is_array($foo) && array_key_exists('type', $foo) && $foo['type'] === 'Image' && is_string($foo['url'])) { + $poster = $foo['url']; + } + } + } + } + + $tag = (($poster) ? '[video poster="' . $poster . '"]' : '[video]'); + $ptr = null; + + if (array_key_exists('url', $act->obj)) { + if (is_array($act->obj['url'])) { + if (array_key_exists(0, $act->obj['url'])) { + $ptr = $act->obj['url']; + } else { + $ptr = [$act->obj['url']]; + } + // handle peertube's weird url link tree if we find it here + // 0 => html link, 1 => application/x-mpegURL with 'tag' set to an array of actual media links + foreach ($ptr as $idex) { + if (is_array($idex) && array_key_exists('mediaType', $idex)) { + if ($idex['mediaType'] === 'application/x-mpegURL' && isset($idex['tag']) && is_array($idex['tag'])) { + $ptr = $idex['tag']; + break; + } + } + } + foreach ($ptr as $vurl) { + if (array_key_exists('mediaType', $vurl)) { + if (in_array($vurl['mediaType'], $vtypes)) { + if (!array_key_exists('width', $vurl)) { + $vurl['width'] = 0; + } + $mps[] = $vurl; + } + } + } + } + if ($mps) { + usort($mps, [__CLASS__, 'vid_sort']); + foreach ($mps as $m) { + if (intval($m['width']) < 500 && self::media_not_in_body($m['href'], $s['body'])) { + $s['body'] .= "\n\n" . $tag . $m['href'] . '[/video]'; + break; + } + } + } elseif (is_string($act->obj['url']) && self::media_not_in_body($act->obj['url'], $s['body'])) { + $s['body'] .= "\n\n" . $tag . $act->obj['url'] . '[/video]'; + } + } + } + + if ($act->obj['type'] === 'Audio') { + $atypes = [ + 'audio/mpeg', + 'audio/ogg', + 'audio/wav' + ]; + + $ptr = null; + + if (array_key_exists('url', $act->obj)) { + if (is_array($act->obj['url'])) { + if (array_key_exists(0, $act->obj['url'])) { + $ptr = $act->obj['url']; + } else { + $ptr = [$act->obj['url']]; + } + foreach ($ptr as $vurl) { + if (isset($vurl['mediaType']) && in_array($vurl['mediaType'], $atypes) && self::media_not_in_body($vurl['href'], $s['body'])) { + $s['body'] .= "\n\n" . '[audio]' . $vurl['href'] . '[/audio]'; + break; + } + } + } elseif (is_string($act->obj['url']) && self::media_not_in_body($act->obj['url'], $s['body'])) { + $s['body'] .= "\n\n" . '[audio]' . $act->obj['url'] . '[/audio]'; + } + } // Pleroma audio scrobbler + elseif ($act->type === 'Listen' && array_key_exists('artist', $act->obj) && array_key_exists('title', $act->obj) && $s['body'] === EMPTY_STR) { + $s['body'] .= "\n\n" . sprintf('Listening to \"%1$s\" by %2$s', escape_tags($act->obj['title']), escape_tags($act->obj['artist'])); + if (isset($act->obj['album'])) { + $s['body'] .= "\n" . sprintf('(%s)', escape_tags($act->obj['album'])); + } + } + } + + if ($act->obj['type'] === 'Image' && strpos($s['body'], 'zrl=') === false) { + $ptr = null; + + if (array_key_exists('url', $act->obj)) { + if (is_array($act->obj['url'])) { + if (array_key_exists(0, $act->obj['url'])) { + $ptr = $act->obj['url']; + } else { + $ptr = [$act->obj['url']]; + } + foreach ($ptr as $vurl) { + if (is_array($vurl) && isset($vurl['href']) && strpos($s['body'], $vurl['href']) === false) { + $s['body'] .= "\n\n" . '[zmg]' . $vurl['href'] . '[/zmg]'; + break; + } + } + } elseif (is_string($act->obj['url'])) { + if (strpos($s['body'], $act->obj['url']) === false) { + $s['body'] .= "\n\n" . '[zmg]' . $act->obj['url'] . '[/zmg]'; + } + } + } + } + + + if ($act->obj['type'] === 'Page' && !$s['body']) { + $ptr = null; + $purl = EMPTY_STR; + + if (array_key_exists('url', $act->obj)) { + if (is_array($act->obj['url'])) { + if (array_key_exists(0, $act->obj['url'])) { + $ptr = $act->obj['url']; + } else { + $ptr = [$act->obj['url']]; + } + foreach ($ptr as $vurl) { + if (is_array($vurl) && array_key_exists('mediaType', $vurl) && $vurl['mediaType'] === 'text/html') { + $purl = $vurl['href']; + break; + } elseif (array_key_exists('mimeType', $vurl) && $vurl['mimeType'] === 'text/html') { + $purl = $vurl['href']; + break; + } + } + } elseif (is_string($act->obj['url'])) { + $purl = $act->obj['url']; + } + if ($purl) { + $li = z_fetch_url(z_root() . '/linkinfo?binurl=' . bin2hex($purl)); + if ($li['success'] && $li['body']) { + $s['body'] .= "\n" . $li['body']; + } else { + $s['body'] .= "\n\n" . $purl; + } + } + } + } + } + + + if (in_array($act->obj['type'], ['Note', 'Article', 'Page'])) { + $ptr = null; + + if (array_key_exists('url', $act->obj)) { + if (is_array($act->obj['url'])) { + if (array_key_exists(0, $act->obj['url'])) { + $ptr = $act->obj['url']; + } else { + $ptr = [$act->obj['url']]; + } + foreach ($ptr as $vurl) { + if (is_array($vurl) && array_key_exists('mediaType', $vurl) && $vurl['mediaType'] === 'text/html') { + $s['plink'] = $vurl['href']; + break; + } + } + } elseif (is_string($act->obj['url'])) { + $s['plink'] = $act->obj['url']; + } + } + } + + if (!(isset($s['plink']) && $s['plink'])) { + $s['plink'] = $s['mid']; + } + + // assume this is private unless specifically told otherwise. + + $s['item_private'] = 1; + + if ($act->recips && (in_array(ACTIVITY_PUBLIC_INBOX, $act->recips) || in_array('Public', $act->recips) || in_array('as:Public', $act->recips))) { + $s['item_private'] = 0; + } + + if (is_array($act->obj)) { + if (array_key_exists('directMessage', $act->obj) && intval($act->obj['directMessage'])) { + $s['item_private'] = 2; + } + } + + set_iconfig($s, 'activitypub', 'recips', $act->raw_recips); + + if (array_key_exists('directMessage', $act->data) && intval($act->data['directMessage'])) { + $s['item_private'] = 2; + } + + + set_iconfig($s, 'activitypub', 'rawmsg', $act->raw, 1); + + // Restrict html caching to ActivityPub senders. + // Zot has dynamic content and this library is used by both. + + if ($cacheable) { + if ((!array_key_exists('mimetype', $s)) || (in_array($s['mimetype'], ['text/bbcode', 'text/x-multicode']))) { + // preserve the original purified HTML content *unless* we've modified $s['body'] + // within this function (to add attachments or reaction descriptions or mention rewrites). + // This avoids/bypasses some markdown rendering issues which can occur when + // converting to our markdown-enhanced bbcode and then back to HTML again. + // Also if we do need bbcode, use the 'bbonly' flag to ignore markdown and only + // interpret bbcode; which is much less susceptible to false positives in the + // conversion regexes. + + if ($s['body'] === self::bb_content($content, 'content')) { + $s['html'] = $content['content']; + } else { + $s['html'] = bbcode($s['body'], ['bbonly' => true]); + } + } + } + + $hookinfo = [ + 'act' => $act, + 's' => $s + ]; + + call_hooks('decode_note', $hookinfo); + + $s = $hookinfo['s']; + + return $s; + } + + public static function rewrite_mentions_sub(&$s, $pref, &$obj = null) + { + + if (isset($s['term']) && is_array($s['term'])) { + foreach ($s['term'] as $tag) { + $txt = EMPTY_STR; + if (intval($tag['ttype']) === TERM_MENTION) { + // some platforms put the identity url into href rather than the profile url. Accept either form. + $x = q( + "select * from xchan where xchan_url = '%s' or xchan_hash = '%s' limit 1", + dbesc($tag['url']), + dbesc($tag['url']) + ); + if ($x) { + switch ($pref) { + case 0: + $txt = $x[0]['xchan_name']; + break; + case 1: + $txt = (($x[0]['xchan_addr']) ? $x[0]['xchan_addr'] : $x[0]['xchan_name']); + break; + case 2: + default; + if ($x[0]['xchan_addr']) { + $txt = sprintf(t('%1$s (%2$s)'), $x[0]['xchan_name'], $x[0]['xchan_addr']); + } else { + $txt = $x[0]['xchan_name']; + } + break; + } + } + } + + if ($txt) { + // the Markdown filter will get tripped up and think this is a markdown link + // if $txt begins with parens so put it behind a zero-width space + if (substr($txt, 0, 1) === '(') { + $txt = htmlspecialchars_decode('​', ENT_QUOTES) . $txt; + } + $s['body'] = preg_replace( + '/\@\[zrl\=' . preg_quote($x[0]['xchan_url'], '/') . '\](.*?)\[\/zrl\]/ism', + '@[zrl=' . $x[0]['xchan_url'] . ']' . $txt . '[/zrl]', + $s['body'] + ); + $s['body'] = preg_replace( + '/\@\[url\=' . preg_quote($x[0]['xchan_url'], '/') . '\](.*?)\[\/url\]/ism', + '@[url=' . $x[0]['xchan_url'] . ']' . $txt . '[/url]', + $s['body'] + ); + $s['body'] = preg_replace( + '/\[zrl\=' . preg_quote($x[0]['xchan_url'], '/') . '\]@(.*?)\[\/zrl\]/ism', + '@[zrl=' . $x[0]['xchan_url'] . ']' . $txt . '[/zrl]', + $s['body'] + ); + $s['body'] = preg_replace( + '/\[url\=' . preg_quote($x[0]['xchan_url'], '/') . '\]@(.*?)\[\/url\]/ism', + '@[url=' . $x[0]['xchan_url'] . ']' . $txt . '[/url]', + $s['body'] + ); + + // replace these just in case the sender (in this case Friendica) got it wrong + $s['body'] = preg_replace( + '/\@\[zrl\=' . preg_quote($x[0]['xchan_hash'], '/') . '\](.*?)\[\/zrl\]/ism', + '@[zrl=' . $x[0]['xchan_url'] . ']' . $txt . '[/zrl]', + $s['body'] + ); + $s['body'] = preg_replace( + '/\@\[url\=' . preg_quote($x[0]['xchan_hash'], '/') . '\](.*?)\[\/url\]/ism', + '@[url=' . $x[0]['xchan_url'] . ']' . $txt . '[/url]', + $s['body'] + ); + $s['body'] = preg_replace( + '/\[zrl\=' . preg_quote($x[0]['xchan_hash'], '/') . '\]@(.*?)\[\/zrl\]/ism', + '@[zrl=' . $x[0]['xchan_url'] . ']' . $txt . '[/zrl]', + $s['body'] + ); + $s['body'] = preg_replace( + '/\[url\=' . preg_quote($x[0]['xchan_hash'], '/') . '\]@(.*?)\[\/url\]/ism', + '@[url=' . $x[0]['xchan_url'] . ']' . $txt . '[/url]', + $s['body'] + ); + + if ($obj && $txt) { + if (!is_array($obj)) { + $obj = json_decode($obj, true); + } + if (array_path_exists('source/content', $obj)) { + $obj['source']['content'] = preg_replace( + '/\@\[zrl\=' . preg_quote($x[0]['xchan_url'], '/') . '\](.*?)\[\/zrl\]/ism', + '@[zrl=' . $x[0]['xchan_url'] . ']' . $txt . '[/zrl]', + $obj['source']['content'] + ); + $obj['source']['content'] = preg_replace( + '/\@\[url\=' . preg_quote($x[0]['xchan_url'], '/') . '\](.*?)\[\/url\]/ism', + '@[url=' . $x[0]['xchan_url'] . ']' . $txt . '[/url]', + $obj['source']['content'] + ); + } + $obj['content'] = preg_replace( + '/\@(.*?)\(.*?)\<\/a\>/ism', + '@$1' . $txt . '', + $obj['content'] + ); + } + } + } + } + + // $s['html'] will be populated if caching was enabled. + // This is usually the case for ActivityPub sourced content, while Zot6 content is not cached. + + if (isset($s['html']) && $s['html']) { + $s['html'] = bbcode($s['body'], ['bbonly' => true]); + } + + return; + } + + public static function rewrite_mentions(&$s) + { + // rewrite incoming mentions in accordance with system.tag_username setting + // 0 - displayname + // 1 - username + // 2 - displayname (username) + // 127 - default + + $pref = intval(PConfig::Get($s['uid'], 'system', 'tag_username', Config::Get('system', 'tag_username', false))); + + if ($pref === 127) { + return; + } + + self::rewrite_mentions_sub($s, $pref); + + + return; + } + + // $force is used when manually fetching a remote item - it assumes you are granting one-time + // permission for the selected item/conversation regardless of your relationship with the author and + // assumes that you are in fact the sender. Please do not use it for anything else. The only permission + // checking that is performed is that the author isn't blocked by the site admin. + + public static function store($channel, $observer_hash, $act, $item, $fetch_parents = true, $force = false) + { + + if ($act && $act->implied_create && !$force) { + // This is originally a S2S object with no associated activity + logger('Not storing implied create activity!'); + return; + } + + $is_sys_channel = is_sys_channel($channel['channel_id']); + $is_child_node = false; + + // Pleroma scrobbles can be really noisy and contain lots of duplicate activities. Disable them by default. + + if (($act->type === 'Listen') && ($is_sys_channel || get_pconfig($channel['channel_id'], 'system', 'allow_scrobbles', false))) { + return; + } + + // Mastodon only allows visibility in public timelines if the public inbox is listed in the 'to' field. + // They are hidden in the public timeline if the public inbox is listed in the 'cc' field. + // This is not part of the activitypub protocol - we might change this to show all public posts in pubstream at some point. + + $pubstream = ((is_array($act->obj) && array_key_exists('to', $act->obj) && is_array($act->obj['to']) && (in_array(ACTIVITY_PUBLIC_INBOX, $act->obj['to']) || in_array('Public', $act->obj['to']) || in_array('as:Public', $act->obj['to']))) ? true : false); + + // very unpleasant and imperfect way of determining a Mastodon DM + + if ($act->raw_recips && array_key_exists('to', $act->raw_recips) && is_array($act->raw_recips['to']) && count($act->raw_recips['to']) === 1 && $act->raw_recips['to'][0] === channel_url($channel) && !$act->raw_recips['cc']) { + $item['item_private'] = 2; + } + + + if ($item['parent_mid'] && $item['parent_mid'] !== $item['mid']) { + $is_child_node = true; + } + + $allowed = false; + $reason = ['init']; + $permit_mentions = intval(PConfig::Get($channel['channel_id'], 'system', 'permit_all_mentions') && i_am_mentioned($channel, $item)); + + if ($is_child_node) { + $p = q( + "select * from item where mid = '%s' and uid = %d and item_wall = 1", + dbesc($item['parent_mid']), + intval($channel['channel_id']) + ); + if ($p) { + // set the owner to the owner of the parent + $item['owner_xchan'] = $p[0]['owner_xchan']; + + // quietly reject group comment boosts by group owner + // (usually only sent via ActivityPub so groups will work on microblog platforms) + // This catches those activities if they slipped in via a conversation fetch + + if ($p[0]['parent_mid'] !== $item['parent_mid']) { + if ($item['verb'] === 'Announce' && $item['author_xchan'] === $item['owner_xchan']) { + logger('group boost activity by group owner rejected'); + return; + } + } + + // check permissions against the author, not the sender + $allowed = perm_is_allowed($channel['channel_id'], $item['author_xchan'], 'post_comments'); + if (!$allowed) { + $reason[] = 'post_comments perm'; + } + if ((!$allowed) && $permit_mentions) { + if ($p[0]['owner_xchan'] === $channel['channel_hash']) { + $allowed = false; + $reason[] = 'ownership'; + } else { + $allowed = true; + } + } + if (absolutely_no_comments($p[0])) { + $allowed = false; + $reason[] = 'absolutely'; + } + + if (!$allowed) { + logger('rejected comment from ' . $item['author_xchan'] . ' for ' . $channel['channel_address']); + logger('rejected reason ' . print_r($reason, true)); + logger('rejected: ' . print_r($item, true), LOGGER_DATA); + // let the sender know we received their comment but we don't permit spam here. + self::send_rejection_activity($channel, $item['author_xchan'], $item); + return; + } + + if (perm_is_allowed($channel['channel_id'], $item['author_xchan'], 'moderated')) { + $item['item_blocked'] = ITEM_MODERATED; + } + } else { + // By default if we allow you to send_stream and comments and this is a comment, it is allowed. + // A side effect of this action is that if you take away send_stream permission, comments to those + // posts you previously allowed will still be accepted. It is possible but might be difficult to fix this. + + $allowed = true; + + // reject public stream comments that weren't sent by the conversation owner + // but only on remote message deliveries to our site ($fetch_parents === true) + + if ($is_sys_channel && $pubstream && $item['owner_xchan'] !== $observer_hash && !$fetch_parents) { + $allowed = false; + $reason[] = 'sender ' . $observer_hash . ' not owner ' . $item['owner_xchan']; + } + } + + if ($p && $p[0]['obj_type'] === 'Question') { + if ($item['obj_type'] === 'Note' && $item['title'] && (!$item['content'])) { + $item['obj_type'] = 'Answer'; + } + } + } else { + if (perm_is_allowed($channel['channel_id'], $observer_hash, 'send_stream') || ($is_sys_channel && $pubstream)) { + logger('allowed: permission allowed', LOGGER_DATA); + $allowed = true; + } + if ($permit_mentions) { + logger('allowed: permitted mention', LOGGER_DATA); + $allowed = true; + } + } + + if (tgroup_check($channel['channel_id'], $item) && (!$is_child_node)) { + // for forum deliveries, make sure we keep a copy of the signed original + set_iconfig($item, 'activitypub', 'rawmsg', $act->raw, 1); + logger('allowed: tgroup'); + $allowed = true; + } + + if (get_abconfig($channel['channel_id'], $observer_hash, 'system', 'block_announce', false)) { + if ($item['verb'] === 'Announce' || strpos($item['body'], '[/share]')) { + $allowed = false; + } + } + + if (intval($item['item_private']) === 2) { + if (!perm_is_allowed($channel['channel_id'], $observer_hash, 'post_mail')) { + $allowed = false; + } + } + + if ($is_sys_channel) { + if (!check_pubstream_channelallowed($observer_hash)) { + $allowed = false; + $reason[] = 'pubstream channel blocked'; + } + + // don't allow pubstream posts if the sender even has a clone on a pubstream denied site + + $h = q( + "select hubloc_url from hubloc where hubloc_hash = '%s'", + dbesc($observer_hash) + ); + if ($h) { + foreach ($h as $hub) { + if (!check_pubstream_siteallowed($hub['hubloc_url'])) { + $allowed = false; + $reason = 'pubstream site blocked'; + break; + } + } + } + if (intval($item['item_private'])) { + $allowed = false; + $reason[] = 'private item'; + } + } + + $blocked = LibBlock::fetch($channel['channel_id'], BLOCKTYPE_SERVER); + if ($blocked) { + foreach ($blocked as $b) { + if (strpos($observer_hash, $b['block_entity']) !== false) { + $allowed = false; + $reason[] = 'blocked'; + } + } + } + + if (!$allowed && !$force) { + logger('no permission: channel ' . $channel['channel_address'] . ', id = ' . $item['mid']); + logger('no permission: reason ' . print_r($reason, true)); + return; + } + + $item['aid'] = $channel['channel_account_id']; + $item['uid'] = $channel['channel_id']; + + + // Some authors may be zot6 authors in which case we want to store their nomadic identity + // instead of their ActivityPub identity + + $item['author_xchan'] = self::find_best_identity($item['author_xchan']); + $item['owner_xchan'] = self::find_best_identity($item['owner_xchan']); + + if (!($item['author_xchan'] && $item['owner_xchan'])) { + logger('owner or author missing.'); + return; + } + + if ($channel['channel_system']) { + if (!MessageFilter::evaluate($item, get_config('system', 'pubstream_incl'), get_config('system', 'pubstream_excl'))) { + logger('post is filtered'); + return; + } + } + + // fetch allow/deny lists for the sender, author, or both + // if you have them. post_is_importable() assumes true + // and only fails if there was intentional rejection + // due to this channel's filtering rules for content + // provided by either of these entities. + + $abook = q( + "select * from abook where ( abook_xchan = '%s' OR abook_xchan = '%s') and abook_channel = %d ", + dbesc($item['author_xchan']), + dbesc($item['owner_xchan']), + intval($channel['channel_id']) + ); + + + if (!post_is_importable($channel['channel_id'], $item, $abook)) { + logger('post is filtered'); + return; + } + + $maxlen = get_max_import_size(); + + if ($maxlen && mb_strlen($item['body']) > $maxlen) { + $item['body'] = mb_substr($item['body'], 0, $maxlen, 'UTF-8'); + logger('message length exceeds max_import_size: truncated'); + } + + if ($maxlen && mb_strlen($item['summary']) > $maxlen) { + $item['summary'] = mb_substr($item['summary'], 0, $maxlen, 'UTF-8'); + logger('message summary length exceeds max_import_size: truncated'); + } + + if ($act->obj['context']) { + set_iconfig($item, 'activitypub', 'context', $act->obj['context'], 1); + } + + if ($act->obj['conversation']) { + set_iconfig($item, 'ostatus', 'conversation', $act->obj['conversation'], 1); + } + + set_iconfig($item, 'activitypub', 'recips', $act->raw_recips); + + if (intval($act->sigok)) { + $item['item_verified'] = 1; + } + + $parent = null; + + if ($is_child_node) { + $parent = q( + "select * from item where mid = '%s' and uid = %d limit 1", + dbesc($item['parent_mid']), + intval($item['uid']) + ); + if (!$parent) { + if (!get_config('system', 'activitypub', ACTIVITYPUB_ENABLED)) { + return; + } else { + $fetch = false; + if (intval($channel['channel_system']) || (perm_is_allowed($channel['channel_id'], $observer_hash, 'send_stream') && (PConfig::Get($channel['channel_id'], 'system', 'hyperdrive', true) || $act->type === 'Announce'))) { + $fetch = (($fetch_parents) ? self::fetch_and_store_parents($channel, $observer_hash, $act, $item) : false); + } + if ($fetch) { + $parent = q( + "select * from item where mid = '%s' and uid = %d limit 1", + dbesc($item['parent_mid']), + intval($item['uid']) + ); + } else { + logger('no parent'); + return; + } + } + } + + $item['comment_policy'] = $parent[0]['comment_policy']; + $item['item_nocomment'] = $parent[0]['item_nocomment']; + $item['comments_closed'] = $parent[0]['comments_closed']; + + if ($parent[0]['parent_mid'] !== $item['parent_mid']) { + $item['thr_parent'] = $item['parent_mid']; + } else { + $item['thr_parent'] = $parent[0]['parent_mid']; + } + $item['parent_mid'] = $parent[0]['parent_mid']; + + /* + * + * Check for conversation privacy mismatches + * We can only do this if we have a channel and we have fetched the parent + * + */ + + // public conversation, but this comment went rogue and was published privately + // hide it from everybody except the channel owner + + if (intval($parent[0]['item_private']) === 0) { + if (intval($item['item_private'])) { + $item['item_restrict'] = $item['item_restrict'] | 1; + $item['allow_cid'] = '<' . $channel['channel_hash'] . '>'; + $item['allow_gid'] = $item['deny_cid'] = $item['deny_gid'] = ''; + } + } + + // Private conversation, but this comment went rogue and was published publicly + // Set item_restrict to indicate this condition so we can flag it in the UI + + if (intval($parent[0]['item_private']) !== 0 && $act->recips && (in_array(ACTIVITY_PUBLIC_INBOX, $act->recips) || in_array('Public', $act->recips) || in_array('as:Public', $act->recips))) { + $item['item_restrict'] = $item['item_restrict'] | 2; + } + } + + self::rewrite_mentions($item); + + $r = q( + "select id, created, edited from item where mid = '%s' and uid = %d limit 1", + dbesc($item['mid']), + intval($item['uid']) + ); + if ($r) { + if ($item['edited'] > $r[0]['edited']) { + $item['id'] = $r[0]['id']; + $x = item_store_update($item); + } else { + return; + } + } else { + $x = item_store($item); + } + // experimental code that needs more work. What this did was once we fetched a conversation to find the root node, // start at that root node and fetch children so you get all the branches and not just the branch related to the current node. // Unfortunately there is no standard method for achieving this. Mastodon provides a 'replies' collection and Nomad projects // can fetch the 'context'. For other platforms it's a wild guess. Additionally when we tested this, it started an infinite -// recursion and has been disabled until the recursive behaviour is tracked down and fixed. - -// if ($fetch_parents && $parent && ! intval($parent[0]['item_private'])) { -// logger('topfetch', LOGGER_DEBUG); -// // if the thread owner is a connnection, we will already receive any additional comments to their posts -// // but if they are not we can try to fetch others in the background -// $x = q("SELECT abook.*, xchan.* FROM abook left join xchan on abook_xchan = xchan_hash -// WHERE abook_channel = %d and abook_xchan = '%s' LIMIT 1", -// intval($channel['channel_id']), -// dbesc($parent[0]['owner_xchan']) -// ); -// if (! $x) { -// // determine if the top-level post provides a replies collection -// if ($parent[0]['obj']) { -// $parent[0]['obj'] = json_decode($parent[0]['obj'],true); -// } -// logger('topfetch: ' . print_r($parent[0],true), LOGGER_ALL); -// $id = ((array_path_exists('obj/replies/id',$parent[0])) ? $parent[0]['obj']['replies']['id'] : false); -// if (! $id) { -// $id = ((array_path_exists('obj/replies',$parent[0]) && is_string($parent[0]['obj']['replies'])) ? $parent[0]['obj']['replies'] : false); -// } -// if ($id) { -// Run::Summon( [ 'Convo', $id, $channel['channel_id'], $observer_hash ] ); -// } -// } -// } - - if (is_array($x) && $x['item_id']) { - if ($is_child_node) { - if ($item['owner_xchan'] === $channel['channel_hash']) { - // We are the owner of this conversation, so send all received comments back downstream - Run::Summon( [ 'Notifier','comment-import',$x['item_id'] ] ); - } - $r = q("select * from item where id = %d limit 1", - intval($x['item_id']) - ); - if ($r) { - send_status_notifications($x['item_id'],$r[0]); - } - } - sync_an_item($channel['channel_id'],$x['item_id']); - } - - } - - - static public function find_best_identity($xchan) { - - $r = q("select hubloc_hash from hubloc where hubloc_id_url = '%s' limit 1", - dbesc($xchan) - ); - if ($r) { - return $r[0]['hubloc_hash']; - } - return $xchan; - } - - - static public function fetch_and_store_parents($channel,$observer_hash,$act,$item) { - - logger('fetching parents'); - - $p = []; - - $current_act = $act; - $current_item = $item; - - while ($current_item['parent_mid'] !== $current_item['mid']) { - $n = self::fetch($current_item['parent_mid']); - if (! $n) { - break; - } - // set client flag to convert objects to implied activities - $a = new ActivityStreams($n,null,true); - if ($a->type === 'Announce' && is_array($a->obj) - && array_key_exists('object',$a->obj) && array_key_exists('actor',$a->obj)) { - // This is a relayed/forwarded Activity (as opposed to a shared/boosted object) - // Reparse the encapsulated Activity and use that instead - logger('relayed activity',LOGGER_DEBUG); - $a = new ActivityStreams($a->obj,null,true); - } - - logger($a->debug(),LOGGER_DATA); - - if (! $a->is_valid()) { - logger('not a valid activity'); - break; - } - if (is_array($a->actor) && array_key_exists('id',$a->actor)) { - self::actor_store($a->actor['id'],$a->actor); - } - - // ActivityPub sourced items are cacheable - $item = self::decode_note($a,true); - - if (! $item) { - break; - } - - $hookinfo = [ - 'a' => $a, - 'item' => $item - ]; - - call_hooks('fetch_and_store',$hookinfo); - - $item = $hookinfo['item']; - - if ($item) { - - // don't leak any private conversations to the public stream - // even if they contain publicly addressed comments/reactions - - if (intval($channel['channel_system']) && intval($item['item_private'])) { - logger('private conversation ignored'); - $p = []; - break; - } - - if (count($p) > 100) { - logger('Conversation overflow'); - $p = []; - break; - } - - array_unshift($p,[ $a, $item ]); - - if ($item['parent_mid'] === $item['mid']) { - break; - } - } - - $current_act = $a; - $current_item = $item; - } - - - - if ($p) { - foreach ($p as $pv) { - if ($pv[0]->is_valid()) { - self::store($channel,$observer_hash,$pv[0],$pv[1],false); - } - } - return true; - } - - return false; - } - - - // This function is designed to work with Zot attachments and item body - - static function bb_attach($attach,$body) { - - $ret = false; - - if (! (is_array($attach) && $attach)) { - return EMPTY_STR; - } - - foreach ($attach as $a) { - if (array_key_exists('type',$a) && stripos($a['type'],'image') !== false) { - // don't add inline image if it's an svg and we already have an inline svg - if ($a['type'] === 'image/svg+xml' && strpos($body,'[/svg]')) { - continue; - } - if (self::media_not_in_body($a['href'],$body)) { - if (isset($a['name']) && $a['name']) { - $alt = htmlspecialchars($a['name'],ENT_QUOTES); - $ret .= "\n\n" . '[img alt="' . $alt . '"]' . $a['href'] . '[/img]'; - } - else { - $ret .= "\n\n" . '[img]' . $a['href'] . '[/img]'; - } - } - } - if (array_key_exists('type',$a) && stripos($a['type'], 'video') !== false) { - if (self::media_not_in_body($a['href'],$body)) { - $ret .= "\n\n" . '[video]' . $a['href'] . '[/video]'; - } - } - if (array_key_exists('type',$a) && stripos($a['type'], 'audio') !== false) { - if (self::media_not_in_body($a['href'],$body)) { - $ret .= "\n\n" . '[audio]' . $a['href'] . '[/audio]'; - } - } - } - - return $ret; - } - - - // check for the existence of existing media link in body - - static function media_not_in_body($s,$body) { - - $s_alt = htmlspecialchars($s, ENT_QUOTES, 'UTF-8'); - - if ((strpos($body,']' . $s . '[/img]') === false) && - (strpos($body,']' . $s . '[/zmg]') === false) && - (strpos($body,']' . $s . '[/video]') === false) && - (strpos($body,']' . $s . '[/zvideo]') === false) && - (strpos($body,']' . $s . '[/audio]') === false) && - (strpos($body,']' . $s . '[/zaudio]') === false) && - (strpos($body,']' . $s_alt . '[/img]') === false) && - (strpos($body,']' . $s_alt . '[/zmg]') === false) && - (strpos($body,']' . $s_alt . '[/video]') === false) && - (strpos($body,']' . $s_alt . '[/zvideo]') === false) && - (strpos($body,']' . $s_alt . '[/audio]') === false) && - (strpos($body,']' . $s_alt . '[/zaudio]') === false)) { - return true; - } - return false; - } - - - static function bb_content($content,$field) { - - $ret = false; - - if (! is_array($content)) { - btlogger('content not initialised'); - return $ret; - } - - if (array_key_exists($field,$content) && is_array($content[$field])) { - foreach ($content[$field] as $k => $v) { - $ret .= html2bbcode($v); - // save this for auto-translate or dynamic filtering - // $ret .= '[language=' . $k . ']' . html2bbcode($v) . '[/language]'; - } - } - elseif (isset($content[$field])) { - if ($field === 'bbcode' && array_key_exists('bbcode',$content)) { - $ret = $content[$field]; - } - else { - $ret = html2bbcode($content[$field]); - } - } - else { - $ret = EMPTY_STR; - } - if ($field === 'content' && isset($content['event']) && (! strpos($ret,'[event'))) { - $ret .= format_event_bbcode($content['event']); - } - - return $ret; - } - - - static function get_content($act,$binary = false) { - - $content = []; - $event = null; - - if ((! $act) || (! is_array($act))) { - return $content; - } - - - if ($act['type'] === 'Event') { - $adjust = false; - $event = []; - $event['event_hash'] = $act['id']; - if (array_key_exists('startTime',$act) && strpos($act['startTime'],-1,1) === 'Z') { - $adjust = true; - $event['adjust'] = 1; - $event['dtstart'] = datetime_convert('UTC','UTC',$event['startTime'] . (($adjust) ? '' : 'Z')); - } - if (array_key_exists('endTime',$act)) { - $event['dtend'] = datetime_convert('UTC','UTC',$event['endTime'] . (($adjust) ? '' : 'Z')); - } - else { - $event['nofinish'] = true; - } - - if (array_key_exists('eventRepeat',$act)) { - $event['event_repeat'] = $act['eventRepeat']; - } - } - - foreach ([ 'name', 'summary', 'content' ] as $a) { - if (($x = self::get_textfield($act,$a,$binary)) !== false) { - $content[$a] = $x; - } - if (isset($content['name'])) { - $content['name'] = html2plain(purify_html($content['name']),256); - } - } - - if ($event && ! $binary) { - $event['summary'] = html2plain(purify_html($content['summary']),256); - if (! $event['summary']) { - if ($content['name']) { - $event['summary'] = html2plain(purify_html($content['name']),256); - } - } - if (! $event['summary']) { - if ($content['content']) { - $event['summary'] = html2plain(purify_html($content['content']),256); - } - } - if ($event['summary']) { - $event['summary'] = substr($event['summary'],0,256); - } - $event['description'] = html2bbcode($content['content']); - if ($event['summary'] && $event['dtstart']) { - $content['event'] = $event; - } - } - - if (array_path_exists('source/mediaType',$act) && array_path_exists('source/content',$act)) { - if (in_array($act['source']['mediaType'], [ 'text/bbcode', 'text/x-multicode' ])) { - if (is_string($act['source']['content']) && (strpos($act['source']['content'],'<') !== false || strpos($act['source']['content'],'>') !== false)) { - $content['bbcode'] = multicode_purify($act['source']['content']); - } - else { - $content['bbcode'] = purify_html($act['source']['content'], [ 'escape' ] ); - } - } - } - - return $content; - } - - - static function get_textfield($act,$field,$binary = false) { - - $content = false; - - if (array_key_exists($field,$act) && $act[$field]) - $content = (($binary) ? $act[$field] : purify_html($act[$field])); - elseif (array_key_exists($field . 'Map',$act) && $act[$field . 'Map']) { - foreach ($act[$field . 'Map'] as $k => $v) { - $content[escape_tags($k)] = (($binary) ? $v : purify_html($v)); - } - } - return $content; - } - - static function send_rejection_activity($channel,$observer_hash,$item) { - - $recip = q("select * from hubloc where hubloc_hash = '%s' limit 1", - dbesc($observer_hash) - ); - if (! $recip) { - return; - } - - $arr = [ - 'id' => z_root() . '/bounces/' . new_uuid(), - 'to' => [ $observer_hash ], - 'type' => 'Reject', - 'actor' => channel_url($channel), - 'name' => 'Permission denied', - 'object' => $item['message_id'] - ]; - - $msg = array_merge(['@context' => [ - ACTIVITYSTREAMS_JSONLD_REV, - 'https://w3id.org/security/v1', - self::ap_schema() - ]], $arr); - - $queue_id = ActivityPub::queue_message(json_encode($msg, JSON_UNESCAPED_SLASHES),$channel,$recip[0]); - do_delivery( [ $queue_id ] ); - - } - - // Find either an Authorization: Bearer token or 'token' request variable - // in the current web request and return it - - static function token_from_request() { - - foreach ( [ 'REDIRECT_REMOTE_USER', 'HTTP_AUTHORIZATION' ] as $s ) { - $auth = ((array_key_exists($s,$_SERVER) && strpos($_SERVER[$s],'Bearer ') === 0) - ? str_replace('Bearer ', EMPTY_STR, $_SERVER[$s]) - : EMPTY_STR - ); - if ($auth) { - break; - } - } - - if (! $auth) { - if (array_key_exists('token',$_REQUEST) && $_REQUEST['token']) { - $auth = $_REQUEST['token']; - } - } - - return $auth; - } - - static function get_xchan_type($type) { - switch ($type) { - case 'Person': - return XCHAN_TYPE_PERSON; - case 'Group': - return XCHAN_TYPE_GROUP; - case 'Service': - return XCHAN_TYPE_SERVICE; - case 'Organization': - return XCHAN_TYPE_ORGANIZATION; - case 'Application': - return XCHAN_TYPE_APPLICATION; - default: - return XCHAN_TYPE_UNKNOWN; - } - } - - static function get_cached_actor($id) { - return (XConfig::Get($id,'system','actor_record')); - } - - - static function get_actor_hublocs($url, $options = 'all,not_deleted') { - - $hublocs = false; - $sql_options = EMPTY_STR; - - $options_arr = explode(',',$options); - if (count($options_arr) > 1) { - for ($x = 1; $x < count($options_arr); $x ++) { - switch (trim($options_arr[$x])) { - case 'not_deleted': - $sql_options .= ' and hubloc_deleted = 0 '; - break; - default: - break; - } - } - } - - switch (trim($options_arr[0])) { - case 'activitypub': - $hublocs = q("select * from hubloc left join xchan on hubloc_hash = xchan_hash where hubloc_hash = '%s' $sql_options ", - dbesc($url) - ); - break; - case 'zot6': +// recursion and has been disabled until the recursive behaviour is tracked down and fixed. + +// if ($fetch_parents && $parent && ! intval($parent[0]['item_private'])) { +// logger('topfetch', LOGGER_DEBUG); +// // if the thread owner is a connnection, we will already receive any additional comments to their posts +// // but if they are not we can try to fetch others in the background +// $x = q("SELECT abook.*, xchan.* FROM abook left join xchan on abook_xchan = xchan_hash +// WHERE abook_channel = %d and abook_xchan = '%s' LIMIT 1", +// intval($channel['channel_id']), +// dbesc($parent[0]['owner_xchan']) +// ); +// if (! $x) { +// // determine if the top-level post provides a replies collection +// if ($parent[0]['obj']) { +// $parent[0]['obj'] = json_decode($parent[0]['obj'],true); +// } +// logger('topfetch: ' . print_r($parent[0],true), LOGGER_ALL); +// $id = ((array_path_exists('obj/replies/id',$parent[0])) ? $parent[0]['obj']['replies']['id'] : false); +// if (! $id) { +// $id = ((array_path_exists('obj/replies',$parent[0]) && is_string($parent[0]['obj']['replies'])) ? $parent[0]['obj']['replies'] : false); +// } +// if ($id) { +// Run::Summon( [ 'Convo', $id, $channel['channel_id'], $observer_hash ] ); +// } +// } +// } + + if (is_array($x) && $x['item_id']) { + if ($is_child_node) { + if ($item['owner_xchan'] === $channel['channel_hash']) { + // We are the owner of this conversation, so send all received comments back downstream + Run::Summon(['Notifier', 'comment-import', $x['item_id']]); + } + $r = q( + "select * from item where id = %d limit 1", + intval($x['item_id']) + ); + if ($r) { + send_status_notifications($x['item_id'], $r[0]); + } + } + sync_an_item($channel['channel_id'], $x['item_id']); + } + } + + public static function find_best_identity($xchan) + { + + $r = q( + "select hubloc_hash from hubloc where hubloc_id_url = '%s' limit 1", + dbesc($xchan) + ); + if ($r) { + return $r[0]['hubloc_hash']; + } + return $xchan; + } + + + public static function fetch_and_store_parents($channel, $observer_hash, $act, $item) + { + + logger('fetching parents'); + + $p = []; + + $current_act = $act; + $current_item = $item; + + while ($current_item['parent_mid'] !== $current_item['mid']) { + $n = self::fetch($current_item['parent_mid']); + if (!$n) { + break; + } + // set client flag to convert objects to implied activities + $a = new ActivityStreams($n, null, true); + if ( + $a->type === 'Announce' && is_array($a->obj) + && array_key_exists('object', $a->obj) && array_key_exists('actor', $a->obj) + ) { + // This is a relayed/forwarded Activity (as opposed to a shared/boosted object) + // Reparse the encapsulated Activity and use that instead + logger('relayed activity', LOGGER_DEBUG); + $a = new ActivityStreams($a->obj, null, true); + } + + logger($a->debug(), LOGGER_DATA); + + if (!$a->is_valid()) { + logger('not a valid activity'); + break; + } + if (is_array($a->actor) && array_key_exists('id', $a->actor)) { + self::actor_store($a->actor['id'], $a->actor); + } + + // ActivityPub sourced items are cacheable + $item = self::decode_note($a, true); + + if (!$item) { + break; + } + + $hookinfo = [ + 'a' => $a, + 'item' => $item + ]; + + call_hooks('fetch_and_store', $hookinfo); + + $item = $hookinfo['item']; + + if ($item) { + // don't leak any private conversations to the public stream + // even if they contain publicly addressed comments/reactions + + if (intval($channel['channel_system']) && intval($item['item_private'])) { + logger('private conversation ignored'); + $p = []; + break; + } + + if (count($p) > 100) { + logger('Conversation overflow'); + $p = []; + break; + } + + array_unshift($p, [$a, $item]); + + if ($item['parent_mid'] === $item['mid']) { + break; + } + } + + $current_act = $a; + $current_item = $item; + } + + + if ($p) { + foreach ($p as $pv) { + if ($pv[0]->is_valid()) { + self::store($channel, $observer_hash, $pv[0], $pv[1], false); + } + } + return true; + } + + return false; + } + + + // This function is designed to work with Zot attachments and item body + + public static function bb_attach($attach, $body) + { + + $ret = false; + + if (!(is_array($attach) && $attach)) { + return EMPTY_STR; + } + + foreach ($attach as $a) { + if (array_key_exists('type', $a) && stripos($a['type'], 'image') !== false) { + // don't add inline image if it's an svg and we already have an inline svg + if ($a['type'] === 'image/svg+xml' && strpos($body, '[/svg]')) { + continue; + } + if (self::media_not_in_body($a['href'], $body)) { + if (isset($a['name']) && $a['name']) { + $alt = htmlspecialchars($a['name'], ENT_QUOTES); + $ret .= "\n\n" . '[img alt="' . $alt . '"]' . $a['href'] . '[/img]'; + } else { + $ret .= "\n\n" . '[img]' . $a['href'] . '[/img]'; + } + } + } + if (array_key_exists('type', $a) && stripos($a['type'], 'video') !== false) { + if (self::media_not_in_body($a['href'], $body)) { + $ret .= "\n\n" . '[video]' . $a['href'] . '[/video]'; + } + } + if (array_key_exists('type', $a) && stripos($a['type'], 'audio') !== false) { + if (self::media_not_in_body($a['href'], $body)) { + $ret .= "\n\n" . '[audio]' . $a['href'] . '[/audio]'; + } + } + } + + return $ret; + } + + + // check for the existence of existing media link in body + + public static function media_not_in_body($s, $body) + { + + $s_alt = htmlspecialchars($s, ENT_QUOTES, 'UTF-8'); + + if ( + (strpos($body, ']' . $s . '[/img]') === false) && + (strpos($body, ']' . $s . '[/zmg]') === false) && + (strpos($body, ']' . $s . '[/video]') === false) && + (strpos($body, ']' . $s . '[/zvideo]') === false) && + (strpos($body, ']' . $s . '[/audio]') === false) && + (strpos($body, ']' . $s . '[/zaudio]') === false) && + (strpos($body, ']' . $s_alt . '[/img]') === false) && + (strpos($body, ']' . $s_alt . '[/zmg]') === false) && + (strpos($body, ']' . $s_alt . '[/video]') === false) && + (strpos($body, ']' . $s_alt . '[/zvideo]') === false) && + (strpos($body, ']' . $s_alt . '[/audio]') === false) && + (strpos($body, ']' . $s_alt . '[/zaudio]') === false) + ) { + return true; + } + return false; + } + + + public static function bb_content($content, $field) + { + + $ret = false; + + if (!is_array($content)) { + btlogger('content not initialised'); + return $ret; + } + + if (array_key_exists($field, $content) && is_array($content[$field])) { + foreach ($content[$field] as $k => $v) { + $ret .= html2bbcode($v); + // save this for auto-translate or dynamic filtering + // $ret .= '[language=' . $k . ']' . html2bbcode($v) . '[/language]'; + } + } elseif (isset($content[$field])) { + if ($field === 'bbcode' && array_key_exists('bbcode', $content)) { + $ret = $content[$field]; + } else { + $ret = html2bbcode($content[$field]); + } + } else { + $ret = EMPTY_STR; + } + if ($field === 'content' && isset($content['event']) && (!strpos($ret, '[event'))) { + $ret .= format_event_bbcode($content['event']); + } + + return $ret; + } + + + public static function get_content($act, $binary = false) + { + + $content = []; + $event = null; + + if ((!$act) || (!is_array($act))) { + return $content; + } + + + if ($act['type'] === 'Event') { + $adjust = false; + $event = []; + $event['event_hash'] = $act['id']; + if (array_key_exists('startTime', $act) && strpos($act['startTime'], -1, 1) === 'Z') { + $adjust = true; + $event['adjust'] = 1; + $event['dtstart'] = datetime_convert('UTC', 'UTC', $event['startTime'] . (($adjust) ? '' : 'Z')); + } + if (array_key_exists('endTime', $act)) { + $event['dtend'] = datetime_convert('UTC', 'UTC', $event['endTime'] . (($adjust) ? '' : 'Z')); + } else { + $event['nofinish'] = true; + } + + if (array_key_exists('eventRepeat', $act)) { + $event['event_repeat'] = $act['eventRepeat']; + } + } + + foreach (['name', 'summary', 'content'] as $a) { + if (($x = self::get_textfield($act, $a, $binary)) !== false) { + $content[$a] = $x; + } + if (isset($content['name'])) { + $content['name'] = html2plain(purify_html($content['name']), 256); + } + } + + if ($event && !$binary) { + $event['summary'] = html2plain(purify_html($content['summary']), 256); + if (!$event['summary']) { + if ($content['name']) { + $event['summary'] = html2plain(purify_html($content['name']), 256); + } + } + if (!$event['summary']) { + if ($content['content']) { + $event['summary'] = html2plain(purify_html($content['content']), 256); + } + } + if ($event['summary']) { + $event['summary'] = substr($event['summary'], 0, 256); + } + $event['description'] = html2bbcode($content['content']); + if ($event['summary'] && $event['dtstart']) { + $content['event'] = $event; + } + } + + if (array_path_exists('source/mediaType', $act) && array_path_exists('source/content', $act)) { + if (in_array($act['source']['mediaType'], ['text/bbcode', 'text/x-multicode'])) { + if (is_string($act['source']['content']) && strpos($act['source']['content'], '<') !== false) { + $content['bbcode'] = multicode_purify($act['source']['content']); + } else { + $content['bbcode'] = purify_html($act['source']['content'], ['escape']); + } + } + } + + return $content; + } + + + public static function get_textfield($act, $field, $binary = false) + { + + $content = false; + + if (array_key_exists($field, $act) && $act[$field]) { + $content = (($binary) ? $act[$field] : purify_html($act[$field])); + } elseif (array_key_exists($field . 'Map', $act) && $act[$field . 'Map']) { + foreach ($act[$field . 'Map'] as $k => $v) { + $content[escape_tags($k)] = (($binary) ? $v : purify_html($v)); + } + } + return $content; + } + + public static function send_rejection_activity($channel, $observer_hash, $item) + { + + $recip = q( + "select * from hubloc where hubloc_hash = '%s' limit 1", + dbesc($observer_hash) + ); + if (!$recip) { + return; + } + + $arr = [ + 'id' => z_root() . '/bounces/' . new_uuid(), + 'to' => [$observer_hash], + 'type' => 'Reject', + 'actor' => channel_url($channel), + 'name' => 'Permission denied', + 'object' => $item['message_id'] + ]; + + $msg = array_merge(['@context' => [ + ACTIVITYSTREAMS_JSONLD_REV, + 'https://w3id.org/security/v1', + self::ap_schema() + ]], $arr); + + $queue_id = ActivityPub::queue_message(json_encode($msg, JSON_UNESCAPED_SLASHES), $channel, $recip[0]); + do_delivery([$queue_id]); + } + + // Find either an Authorization: Bearer token or 'token' request variable + // in the current web request and return it + + public static function token_from_request() + { + + foreach (['REDIRECT_REMOTE_USER', 'HTTP_AUTHORIZATION'] as $s) { + $auth = ((array_key_exists($s, $_SERVER) && strpos($_SERVER[$s], 'Bearer ') === 0) + ? str_replace('Bearer ', EMPTY_STR, $_SERVER[$s]) + : EMPTY_STR + ); + if ($auth) { + break; + } + } + + if (!$auth) { + if (array_key_exists('token', $_REQUEST) && $_REQUEST['token']) { + $auth = $_REQUEST['token']; + } + } + + return $auth; + } + + public static function get_xchan_type($type) + { + switch ($type) { + case 'Person': + return XCHAN_TYPE_PERSON; + case 'Group': + return XCHAN_TYPE_GROUP; + case 'Service': + return XCHAN_TYPE_SERVICE; + case 'Organization': + return XCHAN_TYPE_ORGANIZATION; + case 'Application': + return XCHAN_TYPE_APPLICATION; + default: + return XCHAN_TYPE_UNKNOWN; + } + } + + public static function get_cached_actor($id) + { + return (XConfig::Get($id, 'system', 'actor_record')); + } + + + public static function get_actor_hublocs($url, $options = 'all,not_deleted') + { + + $hublocs = false; + $sql_options = EMPTY_STR; + + $options_arr = explode(',', $options); + if (count($options_arr) > 1) { + for ($x = 1; $x < count($options_arr); $x++) { + switch (trim($options_arr[$x])) { + case 'not_deleted': + $sql_options .= ' and hubloc_deleted = 0 '; + break; + default: + break; + } + } + } + + switch (trim($options_arr[0])) { + case 'activitypub': + $hublocs = q( + "select * from hubloc left join xchan on hubloc_hash = xchan_hash where hubloc_hash = '%s' $sql_options ", + dbesc($url) + ); + break; + case 'zot6': case 'nomad': - $hublocs = q("select * from hubloc left join xchan on hubloc_hash = xchan_hash where hubloc_id_url = '%s' $sql_options ", - dbesc($url) - ); - break; - case 'all': - default: - $hublocs = q("select * from hubloc left join xchan on hubloc_hash = xchan_hash where ( hubloc_id_url = '%s' OR hubloc_hash = '%s' ) $sql_options ", - dbesc($url), - dbesc($url) - ); - break; - } + $hublocs = q( + "select * from hubloc left join xchan on hubloc_hash = xchan_hash where hubloc_id_url = '%s' $sql_options ", + dbesc($url) + ); + break; + case 'all': + default: + $hublocs = q( + "select * from hubloc left join xchan on hubloc_hash = xchan_hash where ( hubloc_id_url = '%s' OR hubloc_hash = '%s' ) $sql_options ", + dbesc($url), + dbesc($url) + ); + break; + } - return $hublocs; - } + return $hublocs; + } - static function get_actor_collections($url) { - $ret = []; - $actor_record = XConfig::Get($url,'system','actor_record'); - if (! $actor_record) { - return $ret; - } - - foreach ( [ 'inbox','outbox','followers','following' ] as $collection) { - if (isset($actor_record[$collection]) && $actor_record[$collection]) { - $ret[$collection] = $actor_record[$collection]; - } - } - if (array_path_exists('endpoints/sharedInbox',$actor_record) && $actor_record['endpoints']['sharedInbox']) { - $ret['sharedInbox'] = $actor_record['endpoints']['sharedInbox']; - } - - return $ret; - } - - static function ap_schema() { - - return [ - 'zot' => z_root() . '/apschema#', - 'toot' => 'http://joinmastodon.org/ns#', - 'ostatus' => 'http://ostatus.org#', - 'schema' => 'http://schema.org#', - 'litepub' => 'http://litepub.social/ns#', - 'sm' => 'http://smithereen.software/ns#', - 'conversation' => 'ostatus:conversation', - 'manuallyApprovesFollowers' => 'as:manuallyApprovesFollowers', - 'oauthRegistrationEndpoint' => 'litepub:oauthRegistrationEndpoint', - 'sensitive' => 'as:sensitive', - 'movedTo' => 'as:movedTo', - 'copiedTo' => 'as:copiedTo', - 'alsoKnownAs' => 'as:alsoKnownAs', - 'EmojiReact' => 'as:EmojiReact', - 'commentPolicy' => 'zot:commentPolicy', - 'topicalCollection' => 'zot:topicalCollection', - 'eventRepeat' => 'zot:eventRepeat', - 'emojiReaction' => 'zot:emojiReaction', - 'expires' => 'zot:expires', - 'directMessage' => 'zot:directMessage', - 'Category' => 'zot:Category', - 'replyTo' => 'zot:replyTo', - 'PropertyValue' => 'schema:PropertyValue', - 'value' => 'schema:value', - 'discoverable' => 'toot:discoverable', - 'wall' => 'sm:wall', - 'capabilities' => 'litepub:capabilities', - 'acceptsJoins' => 'litepub:acceptsJoins', - ]; - - } + public static function get_actor_collections($url) + { + $ret = []; + $actor_record = XConfig::Get($url, 'system', 'actor_record'); + if (!$actor_record) { + return $ret; + } + foreach (['inbox', 'outbox', 'followers', 'following'] as $collection) { + if (isset($actor_record[$collection]) && $actor_record[$collection]) { + $ret[$collection] = $actor_record[$collection]; + } + } + if (array_path_exists('endpoints/sharedInbox', $actor_record) && $actor_record['endpoints']['sharedInbox']) { + $ret['sharedInbox'] = $actor_record['endpoints']['sharedInbox']; + } + return $ret; + } + public static function ap_schema() + { + return [ + 'zot' => z_root() . '/apschema#', + 'toot' => 'http://joinmastodon.org/ns#', + 'ostatus' => 'http://ostatus.org#', + 'schema' => 'http://schema.org#', + 'litepub' => 'http://litepub.social/ns#', + 'sm' => 'http://smithereen.software/ns#', + 'conversation' => 'ostatus:conversation', + 'manuallyApprovesFollowers' => 'as:manuallyApprovesFollowers', + 'oauthRegistrationEndpoint' => 'litepub:oauthRegistrationEndpoint', + 'sensitive' => 'as:sensitive', + 'movedTo' => 'as:movedTo', + 'copiedTo' => 'as:copiedTo', + 'alsoKnownAs' => 'as:alsoKnownAs', + 'EmojiReact' => 'as:EmojiReact', + 'commentPolicy' => 'zot:commentPolicy', + 'topicalCollection' => 'zot:topicalCollection', + 'eventRepeat' => 'zot:eventRepeat', + 'emojiReaction' => 'zot:emojiReaction', + 'expires' => 'zot:expires', + 'directMessage' => 'zot:directMessage', + 'Category' => 'zot:Category', + 'replyTo' => 'zot:replyTo', + 'PropertyValue' => 'schema:PropertyValue', + 'value' => 'schema:value', + 'discoverable' => 'toot:discoverable', + 'wall' => 'sm:wall', + 'capabilities' => 'litepub:capabilities', + 'acceptsJoins' => 'litepub:acceptsJoins', + ]; + } } diff --git a/Zotlabs/Lib/ActivityPub.php b/Zotlabs/Lib/ActivityPub.php index e79f5d4f5..ee09c1ba9 100644 --- a/Zotlabs/Lib/ActivityPub.php +++ b/Zotlabs/Lib/ActivityPub.php @@ -1,4 +1,5 @@ channel_url($arr['channel']) . '?operation=delete', + 'actor' => channel_url($arr['channel']), + 'type' => 'Delete', + 'object' => channel_url($arr['channel']), + 'to' => ['https://www.w3.org/ns/activitystreams#Public'] + ]; - $ti = [ - 'id' => channel_url($arr['channel']) . '?operation=delete', - 'actor' => channel_url($arr['channel']), - 'type' => 'Delete', - 'object' => channel_url($arr['channel']), - 'to' => [ 'https://www.w3.org/ns/activitystreams#Public' ] - ]; + $msg = array_merge(['@context' => [ + ACTIVITYSTREAMS_JSONLD_REV, + 'https://w3id.org/security/v1', + Activity::ap_schema() + ]], $ti); - $msg = array_merge(['@context' => [ - ACTIVITYSTREAMS_JSONLD_REV, - 'https://w3id.org/security/v1', - Activity::ap_schema() - ]], $ti); + $msg['signature'] = LDSignatures::sign($msg, $arr['channel']); - $msg['signature'] = LDSignatures::sign($msg,$arr['channel']); + logger('ActivityPub_encoded (purge_all): ' . json_encode($msg, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT)); - logger('ActivityPub_encoded (purge_all): ' . json_encode($msg,JSON_UNESCAPED_SLASHES|JSON_PRETTY_PRINT)); - - $jmsg = json_encode($msg, JSON_UNESCAPED_SLASHES); - - } - else { - $target_item = $arr['target_item']; + $jmsg = json_encode($msg, JSON_UNESCAPED_SLASHES); + } else { + $target_item = $arr['target_item']; - if (! $target_item['mid']) { - return; - } + if (!$target_item['mid']) { + return; + } - $prv_recips = $arr['env_recips']; + $prv_recips = $arr['env_recips']; - if ($signed_msg) { - $jmsg = $signed_msg; - } - else { - - // Rewrite outbound mentions so they match the ActivityPub convention, which - // is to pretend that the preferred display name doesn't exist and instead use - // the username or webfinger address when displaying names. This is likely to - // only cause confusion on nomadic networks where there could be any number - // of applicable webfinger addresses for a given identity. + if ($signed_msg) { + $jmsg = $signed_msg; + } else { + // Rewrite outbound mentions so they match the ActivityPub convention, which + // is to pretend that the preferred display name doesn't exist and instead use + // the username or webfinger address when displaying names. This is likely to + // only cause confusion on nomadic networks where there could be any number + // of applicable webfinger addresses for a given identity. - Activity::rewrite_mentions_sub($target_item, 1, $target_item['obj']); + Activity::rewrite_mentions_sub($target_item, 1, $target_item['obj']); - $ti = Activity::encode_activity($target_item, true); + $ti = Activity::encode_activity($target_item, true); - if (! $ti) { - return; - } + if (!$ti) { + return; + } -// $token = IConfig::get($target_item['id'],'ocap','relay'); -// if ($token) { -// if (defined('USE_BEARCAPS')) { -// $ti['id'] = 'bear:?u=' . $ti['id'] . '&t=' . $token; -// } -// else { -// $ti['id'] = $ti['id'] . '?token=' . $token; -// } -// if ($ti['url'] && is_string($ti['url'])) { -// $ti['url'] .= '?token=' . $token; -// } -// } +// $token = IConfig::get($target_item['id'],'ocap','relay'); +// if ($token) { +// if (defined('USE_BEARCAPS')) { +// $ti['id'] = 'bear:?u=' . $ti['id'] . '&t=' . $token; +// } +// else { +// $ti['id'] = $ti['id'] . '?token=' . $token; +// } +// if ($ti['url'] && is_string($ti['url'])) { +// $ti['url'] .= '?token=' . $token; +// } +// } - $msg = array_merge(['@context' => [ - ACTIVITYSTREAMS_JSONLD_REV, - 'https://w3id.org/security/v1', - Activity::ap_schema() - ]], $ti); - - $msg['signature'] = LDSignatures::sign($msg,$arr['channel']); + $msg = array_merge(['@context' => [ + ACTIVITYSTREAMS_JSONLD_REV, + 'https://w3id.org/security/v1', + Activity::ap_schema() + ]], $ti); - logger('ActivityPub_encoded: ' . json_encode($msg,JSON_UNESCAPED_SLASHES|JSON_PRETTY_PRINT)); - - $jmsg = json_encode($msg, JSON_UNESCAPED_SLASHES); - } - } - if ($prv_recips) { - $hashes = []; + $msg['signature'] = LDSignatures::sign($msg, $arr['channel']); - // re-explode the recipients, but only for this hub/pod + logger('ActivityPub_encoded: ' . json_encode($msg, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT)); - foreach ($prv_recips as $recip) { - $hashes[] = "'" . $recip . "'"; - } + $jmsg = json_encode($msg, JSON_UNESCAPED_SLASHES); + } + } + if ($prv_recips) { + $hashes = []; - $r = q("select * from xchan left join hubloc on xchan_hash = hubloc_hash where hubloc_url = '%s' + // re-explode the recipients, but only for this hub/pod + + foreach ($prv_recips as $recip) { + $hashes[] = "'" . $recip . "'"; + } + + $r = q( + "select * from xchan left join hubloc on xchan_hash = hubloc_hash where hubloc_url = '%s' and xchan_hash in (" . implode(',', $hashes) . ") and xchan_network = 'activitypub' ", - dbesc($arr['hub']['hubloc_url']) - ); - - if (! $r) { - logger('activitypub_process_outbound: no recipients'); - return; - } - - foreach ($r as $contact) { - - // is $contact connected with this channel - and if the channel is cloned, also on this hub? - // 2018-10-19 this probably doesn't apply to activitypub anymore, just send the thing. - // They'll reject it if they don't like it. - // $single = deliverable_singleton($arr['channel']['channel_id'],$contact); - - if (! $arr['normal_mode']) { - continue; - } - - $qi = self::queue_message($jmsg,$arr['channel'],$contact,$target_item['mid']); - if ($qi) { - $arr['queued'][] = $qi; - } - continue; - } - - } - else { - - // public message - - // See if we can deliver all of them at once - - $x = get_xconfig($arr['hub']['hubloc_hash'],'activitypub','collections'); - if ($x && $x['sharedInbox']) { - logger('using publicInbox delivery for ' . $arr['hub']['hubloc_url'], LOGGER_DEBUG); - $contact['hubloc_callback'] = $x['sharedInbox']; - $qi = self::queue_message($jmsg,$arr['channel'],$contact,$target_item['mid']); - if ($qi) { - $arr['queued'][] = $qi; - } - } - else { - - $r = q("select * from xchan left join hubloc on xchan_hash = hubloc_hash where hubloc_url = '%s' and xchan_network = 'activitypub' ", - dbesc($arr['hub']['hubloc_url']) - ); - - if (! $r) { - logger('activitypub_process_outbound: no recipients'); - return; - } - - foreach ($r as $contact) { - - // $single = deliverable_singleton($arr['channel']['channel_id'],$contact); - - $qi = self::queue_message($jmsg,$arr['channel'],$contact,$target_item['mid']); - if ($qi) { - $arr['queued'][] = $qi; - } - } - } - } - - return; - - } - - - static function queue_message($msg,$sender,$recip,$message_id = '') { - - $dest_url = $recip['hubloc_callback']; - - logger('URL: ' . $dest_url, LOGGER_DEBUG); - logger('DATA: ' . jindent($msg), LOGGER_DATA); - - if (intval(get_config('system','activitypub_test')) || intval(get_pconfig($sender['channel_id'],'system','activitypub_test'))) { - logger('test mode - delivery disabled'); - return false; - } - - $hash = random_string(); - - logger('queue: ' . $hash . ' ' . $dest_url, LOGGER_DEBUG); - Queue::insert([ - 'hash' => $hash, - 'account_id' => $sender['channel_account_id'], - 'channel_id' => $sender['channel_id'], - 'driver' => 'activitypub', - 'posturl' => $dest_url, - 'notify' => '', - 'msg' => $msg - ]); - - if ($message_id && (! get_config('system','disable_dreport'))) { - q("insert into dreport ( dreport_mid, dreport_site, dreport_recip, dreport_result, dreport_time, dreport_xchan, dreport_queue, dreport_log ) values ( '%s','%s','%s','%s','%s','%s','%s','%s' ) ", - dbesc($message_id), - dbesc($dest_url), - dbesc($dest_url), - dbesc('queued'), - dbesc(datetime_convert()), - dbesc($sender['channel_hash']), - dbesc($hash), - dbesc(EMPTY_STR) - ); - } - - return $hash; - } - - - static function permissions_update(&$x) { - - if ($x['recipient']['xchan_network'] !== 'activitypub') { - return; - } - self::discover($x['recipient']['xchan_hash'],true); - $x['success'] = true; - } - - - static function permissions_create(&$x) { - - // send a follow activity to the followee's inbox - - if ($x['recipient']['xchan_network'] !== 'activitypub') { - return; - } - - $p = Activity::encode_person($x['sender'],false); - if (! $p) { - return; - } - - $orig_follow = get_abconfig($x['sender']['channel_id'],$x['recipient']['xchan_hash'],'activitypub','their_follow_id'); - $orig_follow_type = get_abconfig($x['sender']['channel_id'],$x['recipient']['xchan_hash'],'activitypub','their_follow_type'); - - $msg = array_merge(['@context' => [ - ACTIVITYSTREAMS_JSONLD_REV, - 'https://w3id.org/security/v1', - Activity::ap_schema() - ]], - [ - 'id' => z_root() . '/follow/' . $x['recipient']['abook_id'] . (($orig_follow) ? '/' . md5($orig_follow) : EMPTY_STR), - 'type' => (($orig_follow_type) ? $orig_follow_type : 'Follow'), - 'actor' => $p, - 'object' => $x['recipient']['xchan_hash'], - 'to' => [ $x['recipient']['xchan_hash'] ] - ]); - - // for Group actors, send both a Follow and a Join because some platforms only support one and there's - // no way of discovering/knowing in advance which type they support - - $join_msg = null; - - if (intval($x['recipient']['xchan_type']) === XCHAN_TYPE_GROUP) { - $join_msg = $msg; - $join_msg['type'] = 'Join'; - $join_msg['signature'] = LDSignatures::sign($join_msg,$x['sender']); - $jmsg2 = json_encode($join_msg, JSON_UNESCAPED_SLASHES); - } - - $msg['signature'] = LDSignatures::sign($msg,$x['sender']); - $jmsg = json_encode($msg, JSON_UNESCAPED_SLASHES); - - $h = q("select * from hubloc where hubloc_hash = '%s' limit 1", - dbesc($x['recipient']['xchan_hash']) - ); - - if ($h) { - $qi = self::queue_message($jmsg,$x['sender'],$h[0]); - if ($qi) { - $x['deliveries'] = $qi; - } - if ($join_msg) { - $qi = self::queue_message($jmsg2,$x['sender'],$h[0]); - if ($qi) { - $x['deliveries'] = $qi; - } - } - } - - $x['success'] = true; - } - - - static function permissions_accept(&$x) { - - // send an accept activity to the followee's inbox - - if ($x['recipient']['xchan_network'] !== 'activitypub') { - return; - } - - // we currently are not handling send of reject follow activities; this is permitted by protocol - - $accept = get_abconfig($x['recipient']['abook_channel'],$x['recipient']['xchan_hash'],'activitypub','their_follow_id'); - $follow_type = get_abconfig($x['recipient']['abook_channel'],$x['recipient']['xchan_hash'],'activitypub','their_follow_type'); - if (! $accept) { - return; - } - - $p = Activity::encode_person($x['sender'],false); - if (! $p) { - return; - } - - $msg = array_merge(['@context' => [ - ACTIVITYSTREAMS_JSONLD_REV, - 'https://w3id.org/security/v1', - Activity::ap_schema() - ]], - [ - 'id' => z_root() . '/follow/' . $x['recipient']['abook_id'] . '/' . md5($accept), - 'type' => 'Accept', - 'actor' => $p, - 'object' => [ - 'type' => (($follow_type) ? $follow_type : 'Follow'), - 'id' => $accept, - 'actor' => $x['recipient']['xchan_hash'], - 'object' => z_root() . '/channel/' . $x['sender']['channel_address'] - ], - 'to' => [ $x['recipient']['xchan_hash'] ] - ]); - - $msg['signature'] = LDSignatures::sign($msg,$x['sender']); - - $jmsg = json_encode($msg, JSON_UNESCAPED_SLASHES); - - $h = q("select * from hubloc where hubloc_hash = '%s' limit 1", - dbesc($x['recipient']['xchan_hash']) - ); - - if ($h) { - $qi = self::queue_message($jmsg,$x['sender'],$h[0]); - if ($qi) { - $x['deliveries'] = $qi; - } - } - - $x['success'] = true; - - } - - static function contact_remove($channel_id,$abook) { - - $recip = q("select * from abook left join xchan on abook_xchan = xchan_hash where abook_id = %d", - intval($abook['abook_id']) - ); - - if ((! $recip) || $recip[0]['xchan_network'] !== 'activitypub') - return; - - $channel = channelx_by_n($recip[0]['abook_channel']); - if (! $channel) { - return; - } - - $p = Activity::encode_person($channel,true,true); - if (! $p) { - return; - } - - // send an unfollow activity to the followee's inbox - - $orig_activity = get_abconfig($recip[0]['abook_channel'],$recip[0]['xchan_hash'],'activitypub','follow_id'); - - if ($orig_activity && $recip[0]['abook_pending']) { - - // was never approved - - $msg = array_merge(['@context' => [ - ACTIVITYSTREAMS_JSONLD_REV, - 'https://w3id.org/security/v1', - Activity::ap_schema() - ]], - [ - 'id' => z_root() . '/follow/' . $recip[0]['abook_id'] . '/' . md5($orig_activity) . '?operation=reject', - 'type' => 'Reject', - 'actor' => $p, - 'object' => [ - 'type' => 'Follow', - 'id' => $orig_activity, - 'actor' => $recip[0]['xchan_hash'], - 'object' => $p - ], - 'to' => [ $recip[0]['xchan_hash'] ] - ]); - del_abconfig($recip[0]['abook_channel'],$recip[0]['xchan_hash'],'activitypub','follow_id'); - - } - else { - - // send an unfollow - - $msg = array_merge(['@context' => [ - ACTIVITYSTREAMS_JSONLD_REV, - 'https://w3id.org/security/v1', - Activity::ap_schema() - ]], - [ - 'id' => z_root() . '/follow/' . $recip[0]['abook_id'] . (($orig_activity) ? '/' . md5($orig_activity) : EMPTY_STR) . '?operation=unfollow', - 'type' => 'Undo', - 'actor' => $p, - 'object' => [ - 'id' => z_root() . '/follow/' . $recip[0]['abook_id'] . (($orig_activity) ? '/' . md5($orig_activity) : EMPTY_STR), - 'type' => 'Follow', - 'actor' => $p, - 'object' => $recip[0]['xchan_hash'] - ], - 'to' => [ $recip[0]['xchan_hash'] ] - ]); - } - - $msg['signature'] = LDSignatures::sign($msg,$channel); - - $jmsg = json_encode($msg, JSON_UNESCAPED_SLASHES); - - $h = q("select * from hubloc where hubloc_hash = '%s' limit 1", - dbesc($recip[0]['xchan_hash']) - ); - - if ($h) { - $qi = self::queue_message($jmsg,$channel,$h[0]); - if ($qi) { - Run::Summon([ 'Deliver' , $qi ]); - } - } - } - - static function discover($apurl, $force = false) { - - $person_obj = null; - $ap = Activity::fetch($apurl); - if ($ap) { - $AS = new ActivityStreams($ap); - if ($AS->is_valid()) { - if (ActivityStreams::is_an_actor($AS->type)) { - $person_obj = $AS->data; - } - elseif ($AS->obj && ActivityStreams::is_an_actor($AS->obj['type'])) { - $person_obj = $AS->obj; - } - } - } - - if (isset($person_obj)) { - - Activity::actor_store($person_obj['id'],$person_obj, $force); - return $person_obj['id']; - } - return false; - } - - static public function move($src,$dst) { - - if (! ($src && $dst)) { - return; - } - - if ($src && ! is_array($src)) { - $src = Activity::fetch($src); - if (is_array($src)) { - $src_xchan = $src['id']; - } - } - - $approvals = null; - - if ($dst && ! is_array($dst)) { - $dst = Activity::fetch($dst); - if (is_array($dst)) { - $dst_xchan = $dst['id']; - if (array_key_exists('alsoKnownAs',$dst)) { - if(! is_array($dst['alsoKnownAs'])) { - $dst['alsoKnownAs'] = [ $dst['alsoKnownAs'] ]; - } - $approvals = $dst['alsoKnownAs']; - } - } - } - - if(! ($src_xchan && $dst_xchan)) { - return; - } - - if ($approvals) { - foreach($approvals as $approval) { - if($approval === $src_xchan) { - $abooks = q("select abook_channel from abook where abook_xchan = '%s'", - dbesc($src_xchan) - ); - if ($abooks) { - foreach ($abooks as $abook) { - // check to see if we already performed this action - $x = q("select * from abook where abook_xchan = '%s' and abook_channel = %d", - dbesc($dst_xchan), - intval($abook['abook_channel']) - ); - if ($x) { - continue; - } - // update the local abook - q("update abconfig set xchan = '%s' where chan = %d and xchan = '%s'", - dbesc($dst_xchan), - intval($abook['abook_channel']), - dbesc($src_xchan) - ); - q("update pgrp_member set xchan = '%s' where uid = %d and xchan = '%s'", - dbesc($dst_xchan), - intval($abook['abook_channel']), - dbesc($src_xchan) - ); - $r = q("update abook set abook_xchan = '%s' where abook_xchan = '%s' and abook_channel = %d ", - dbesc($dst_xchan), - dbesc($src_xchan), - intval($abook['abook_channel']) - ); - - $r = q("SELECT abook.*, xchan.* + dbesc($arr['hub']['hubloc_url']) + ); + + if (!$r) { + logger('activitypub_process_outbound: no recipients'); + return; + } + + foreach ($r as $contact) { + // is $contact connected with this channel - and if the channel is cloned, also on this hub? + // 2018-10-19 this probably doesn't apply to activitypub anymore, just send the thing. + // They'll reject it if they don't like it. + // $single = deliverable_singleton($arr['channel']['channel_id'],$contact); + + if (!$arr['normal_mode']) { + continue; + } + + $qi = self::queue_message($jmsg, $arr['channel'], $contact, $target_item['mid']); + if ($qi) { + $arr['queued'][] = $qi; + } + continue; + } + } else { + // public message + + // See if we can deliver all of them at once + + $x = get_xconfig($arr['hub']['hubloc_hash'], 'activitypub', 'collections'); + if ($x && $x['sharedInbox']) { + logger('using publicInbox delivery for ' . $arr['hub']['hubloc_url'], LOGGER_DEBUG); + $contact['hubloc_callback'] = $x['sharedInbox']; + $qi = self::queue_message($jmsg, $arr['channel'], $contact, $target_item['mid']); + if ($qi) { + $arr['queued'][] = $qi; + } + } else { + $r = q( + "select * from xchan left join hubloc on xchan_hash = hubloc_hash where hubloc_url = '%s' and xchan_network = 'activitypub' ", + dbesc($arr['hub']['hubloc_url']) + ); + + if (!$r) { + logger('activitypub_process_outbound: no recipients'); + return; + } + + foreach ($r as $contact) { + // $single = deliverable_singleton($arr['channel']['channel_id'],$contact); + + $qi = self::queue_message($jmsg, $arr['channel'], $contact, $target_item['mid']); + if ($qi) { + $arr['queued'][] = $qi; + } + } + } + } + + return; + } + + + public static function queue_message($msg, $sender, $recip, $message_id = '') + { + + $dest_url = $recip['hubloc_callback']; + + logger('URL: ' . $dest_url, LOGGER_DEBUG); + logger('DATA: ' . jindent($msg), LOGGER_DATA); + + if (intval(get_config('system', 'activitypub_test')) || intval(get_pconfig($sender['channel_id'], 'system', 'activitypub_test'))) { + logger('test mode - delivery disabled'); + return false; + } + + $hash = random_string(); + + logger('queue: ' . $hash . ' ' . $dest_url, LOGGER_DEBUG); + Queue::insert([ + 'hash' => $hash, + 'account_id' => $sender['channel_account_id'], + 'channel_id' => $sender['channel_id'], + 'driver' => 'activitypub', + 'posturl' => $dest_url, + 'notify' => '', + 'msg' => $msg + ]); + + if ($message_id && (!get_config('system', 'disable_dreport'))) { + q( + "insert into dreport ( dreport_mid, dreport_site, dreport_recip, dreport_result, dreport_time, dreport_xchan, dreport_queue, dreport_log ) values ( '%s','%s','%s','%s','%s','%s','%s','%s' ) ", + dbesc($message_id), + dbesc($dest_url), + dbesc($dest_url), + dbesc('queued'), + dbesc(datetime_convert()), + dbesc($sender['channel_hash']), + dbesc($hash), + dbesc(EMPTY_STR) + ); + } + + return $hash; + } + + + public static function permissions_update(&$x) + { + + if ($x['recipient']['xchan_network'] !== 'activitypub') { + return; + } + self::discover($x['recipient']['xchan_hash'], true); + $x['success'] = true; + } + + + public static function permissions_create(&$x) + { + + // send a follow activity to the followee's inbox + + if ($x['recipient']['xchan_network'] !== 'activitypub') { + return; + } + + $p = Activity::encode_person($x['sender'], false); + if (!$p) { + return; + } + + $orig_follow = get_abconfig($x['sender']['channel_id'], $x['recipient']['xchan_hash'], 'activitypub', 'their_follow_id'); + $orig_follow_type = get_abconfig($x['sender']['channel_id'], $x['recipient']['xchan_hash'], 'activitypub', 'their_follow_type'); + + $msg = array_merge( + ['@context' => [ + ACTIVITYSTREAMS_JSONLD_REV, + 'https://w3id.org/security/v1', + Activity::ap_schema() + ]], + [ + 'id' => z_root() . '/follow/' . $x['recipient']['abook_id'] . (($orig_follow) ? '/' . md5($orig_follow) : EMPTY_STR), + 'type' => (($orig_follow_type) ? $orig_follow_type : 'Follow'), + 'actor' => $p, + 'object' => $x['recipient']['xchan_hash'], + 'to' => [$x['recipient']['xchan_hash']] + ] + ); + + // for Group actors, send both a Follow and a Join because some platforms only support one and there's + // no way of discovering/knowing in advance which type they support + + $join_msg = null; + + if (intval($x['recipient']['xchan_type']) === XCHAN_TYPE_GROUP) { + $join_msg = $msg; + $join_msg['type'] = 'Join'; + $join_msg['signature'] = LDSignatures::sign($join_msg, $x['sender']); + $jmsg2 = json_encode($join_msg, JSON_UNESCAPED_SLASHES); + } + + $msg['signature'] = LDSignatures::sign($msg, $x['sender']); + $jmsg = json_encode($msg, JSON_UNESCAPED_SLASHES); + + $h = q( + "select * from hubloc where hubloc_hash = '%s' limit 1", + dbesc($x['recipient']['xchan_hash']) + ); + + if ($h) { + $qi = self::queue_message($jmsg, $x['sender'], $h[0]); + if ($qi) { + $x['deliveries'] = $qi; + } + if ($join_msg) { + $qi = self::queue_message($jmsg2, $x['sender'], $h[0]); + if ($qi) { + $x['deliveries'] = $qi; + } + } + } + + $x['success'] = true; + } + + + public static function permissions_accept(&$x) + { + + // send an accept activity to the followee's inbox + + if ($x['recipient']['xchan_network'] !== 'activitypub') { + return; + } + + // we currently are not handling send of reject follow activities; this is permitted by protocol + + $accept = get_abconfig($x['recipient']['abook_channel'], $x['recipient']['xchan_hash'], 'activitypub', 'their_follow_id'); + $follow_type = get_abconfig($x['recipient']['abook_channel'], $x['recipient']['xchan_hash'], 'activitypub', 'their_follow_type'); + if (!$accept) { + return; + } + + $p = Activity::encode_person($x['sender'], false); + if (!$p) { + return; + } + + $msg = array_merge( + ['@context' => [ + ACTIVITYSTREAMS_JSONLD_REV, + 'https://w3id.org/security/v1', + Activity::ap_schema() + ]], + [ + 'id' => z_root() . '/follow/' . $x['recipient']['abook_id'] . '/' . md5($accept), + 'type' => 'Accept', + 'actor' => $p, + 'object' => [ + 'type' => (($follow_type) ? $follow_type : 'Follow'), + 'id' => $accept, + 'actor' => $x['recipient']['xchan_hash'], + 'object' => z_root() . '/channel/' . $x['sender']['channel_address'] + ], + 'to' => [$x['recipient']['xchan_hash']] + ] + ); + + $msg['signature'] = LDSignatures::sign($msg, $x['sender']); + + $jmsg = json_encode($msg, JSON_UNESCAPED_SLASHES); + + $h = q( + "select * from hubloc where hubloc_hash = '%s' limit 1", + dbesc($x['recipient']['xchan_hash']) + ); + + if ($h) { + $qi = self::queue_message($jmsg, $x['sender'], $h[0]); + if ($qi) { + $x['deliveries'] = $qi; + } + } + + $x['success'] = true; + } + + public static function contact_remove($channel_id, $abook) + { + + $recip = q( + "select * from abook left join xchan on abook_xchan = xchan_hash where abook_id = %d", + intval($abook['abook_id']) + ); + + if ((!$recip) || $recip[0]['xchan_network'] !== 'activitypub') { + return; + } + + $channel = channelx_by_n($recip[0]['abook_channel']); + if (!$channel) { + return; + } + + $p = Activity::encode_person($channel, true, true); + if (!$p) { + return; + } + + // send an unfollow activity to the followee's inbox + + $orig_activity = get_abconfig($recip[0]['abook_channel'], $recip[0]['xchan_hash'], 'activitypub', 'follow_id'); + + if ($orig_activity && $recip[0]['abook_pending']) { + // was never approved + + $msg = array_merge( + ['@context' => [ + ACTIVITYSTREAMS_JSONLD_REV, + 'https://w3id.org/security/v1', + Activity::ap_schema() + ]], + [ + 'id' => z_root() . '/follow/' . $recip[0]['abook_id'] . '/' . md5($orig_activity) . '?operation=reject', + 'type' => 'Reject', + 'actor' => $p, + 'object' => [ + 'type' => 'Follow', + 'id' => $orig_activity, + 'actor' => $recip[0]['xchan_hash'], + 'object' => $p + ], + 'to' => [$recip[0]['xchan_hash']] + ] + ); + del_abconfig($recip[0]['abook_channel'], $recip[0]['xchan_hash'], 'activitypub', 'follow_id'); + } else { + // send an unfollow + + $msg = array_merge( + ['@context' => [ + ACTIVITYSTREAMS_JSONLD_REV, + 'https://w3id.org/security/v1', + Activity::ap_schema() + ]], + [ + 'id' => z_root() . '/follow/' . $recip[0]['abook_id'] . (($orig_activity) ? '/' . md5($orig_activity) : EMPTY_STR) . '?operation=unfollow', + 'type' => 'Undo', + 'actor' => $p, + 'object' => [ + 'id' => z_root() . '/follow/' . $recip[0]['abook_id'] . (($orig_activity) ? '/' . md5($orig_activity) : EMPTY_STR), + 'type' => 'Follow', + 'actor' => $p, + 'object' => $recip[0]['xchan_hash'] + ], + 'to' => [$recip[0]['xchan_hash']] + ] + ); + } + + $msg['signature'] = LDSignatures::sign($msg, $channel); + + $jmsg = json_encode($msg, JSON_UNESCAPED_SLASHES); + + $h = q( + "select * from hubloc where hubloc_hash = '%s' limit 1", + dbesc($recip[0]['xchan_hash']) + ); + + if ($h) { + $qi = self::queue_message($jmsg, $channel, $h[0]); + if ($qi) { + Run::Summon(['Deliver', $qi]); + } + } + } + + public static function discover($apurl, $force = false) + { + + $person_obj = null; + $ap = Activity::fetch($apurl); + if ($ap) { + $AS = new ActivityStreams($ap); + if ($AS->is_valid()) { + if (ActivityStreams::is_an_actor($AS->type)) { + $person_obj = $AS->data; + } elseif ($AS->obj && ActivityStreams::is_an_actor($AS->obj['type'])) { + $person_obj = $AS->obj; + } + } + } + + if (isset($person_obj)) { + Activity::actor_store($person_obj['id'], $person_obj, $force); + return $person_obj['id']; + } + return false; + } + + public static function move($src, $dst) + { + + if (!($src && $dst)) { + return; + } + + if ($src && !is_array($src)) { + $src = Activity::fetch($src); + if (is_array($src)) { + $src_xchan = $src['id']; + } + } + + $approvals = null; + + if ($dst && !is_array($dst)) { + $dst = Activity::fetch($dst); + if (is_array($dst)) { + $dst_xchan = $dst['id']; + if (array_key_exists('alsoKnownAs', $dst)) { + if (!is_array($dst['alsoKnownAs'])) { + $dst['alsoKnownAs'] = [$dst['alsoKnownAs']]; + } + $approvals = $dst['alsoKnownAs']; + } + } + } + + if (!($src_xchan && $dst_xchan)) { + return; + } + + if ($approvals) { + foreach ($approvals as $approval) { + if ($approval === $src_xchan) { + $abooks = q( + "select abook_channel from abook where abook_xchan = '%s'", + dbesc($src_xchan) + ); + if ($abooks) { + foreach ($abooks as $abook) { + // check to see if we already performed this action + $x = q( + "select * from abook where abook_xchan = '%s' and abook_channel = %d", + dbesc($dst_xchan), + intval($abook['abook_channel']) + ); + if ($x) { + continue; + } + // update the local abook + q( + "update abconfig set xchan = '%s' where chan = %d and xchan = '%s'", + dbesc($dst_xchan), + intval($abook['abook_channel']), + dbesc($src_xchan) + ); + q( + "update pgrp_member set xchan = '%s' where uid = %d and xchan = '%s'", + dbesc($dst_xchan), + intval($abook['abook_channel']), + dbesc($src_xchan) + ); + $r = q( + "update abook set abook_xchan = '%s' where abook_xchan = '%s' and abook_channel = %d ", + dbesc($dst_xchan), + dbesc($src_xchan), + intval($abook['abook_channel']) + ); + + $r = q( + "SELECT abook.*, xchan.* FROM abook left join xchan on abook_xchan = xchan_hash WHERE abook_channel = %d and abook_id = %d LIMIT 1", - intval(abook['abook_channel']), - intval($dst_xchan) - ); - if ($r) { - $clone = array_shift($r); - unset($clone['abook_id']); - unset($clone['abook_account']); - unset($clone['abook_channel']); - $abconfig = load_abconfig($abook['abook_channel'],$clone['abook_xchan']); - if ($abconfig) { - $clone['abconfig'] = $abconfig; - } - Libsync::build_sync_packet($abook['abook_channel'], [ 'abook' => [ $clone ] ] ); - } - } - } - } - } - } - } - -} \ No newline at end of file + intval(abook['abook_channel']), + intval($dst_xchan) + ); + if ($r) { + $clone = array_shift($r); + unset($clone['abook_id']); + unset($clone['abook_account']); + unset($clone['abook_channel']); + $abconfig = load_abconfig($abook['abook_channel'], $clone['abook_xchan']); + if ($abconfig) { + $clone['abconfig'] = $abconfig; + } + Libsync::build_sync_packet($abook['abook_channel'], ['abook' => [$clone]]); + } + } + } + } + } + } + } +} diff --git a/Zotlabs/Lib/ActivityStreams.php b/Zotlabs/Lib/ActivityStreams.php index 5eecd3938..15646e4b7 100644 --- a/Zotlabs/Lib/ActivityStreams.php +++ b/Zotlabs/Lib/ActivityStreams.php @@ -4,463 +4,489 @@ namespace Zotlabs\Lib; use Zotlabs\Web\HTTPSig; - /** * @brief ActivityStreams class. * * Parses an ActivityStream JSON string. */ +class ActivityStreams +{ -class ActivityStreams { + public $raw = null; + public $data = null; + public $hub = null; + public $valid = false; + public $deleted = false; + public $id = ''; + public $parent_id = ''; + public $type = ''; + public $actor = null; + public $obj = null; + public $tgt = null; + public $replyto = null; + public $origin = null; + public $owner = null; + public $signer = null; + public $ldsig = null; + public $sigok = false; + public $recips = null; + public $raw_recips = null; + public $implied_create = false; - public $raw = null; - public $data = null; - public $hub = null; - public $valid = false; - public $deleted = false; - public $id = ''; - public $parent_id = ''; - public $type = ''; - public $actor = null; - public $obj = null; - public $tgt = null; - public $replyto = null; - public $origin = null; - public $owner = null; - public $signer = null; - public $ldsig = null; - public $sigok = false; - public $recips = null; - public $raw_recips = null; - public $implied_create = false; - - /** - * @brief Constructor for ActivityStreams. - * - * Takes a JSON string or previously decode activity array as parameter, - * decodes it and sets up this object/activity, fetching any required attributes - * which were only referenced by @id/URI. - * - * @param string $string - */ - function __construct($string,$hub = null,$client = null) { + /** + * @brief Constructor for ActivityStreams. + * + * Takes a JSON string or previously decode activity array as parameter, + * decodes it and sets up this object/activity, fetching any required attributes + * which were only referenced by @id/URI. + * + * @param string $string + */ + public function __construct($string, $hub = null, $client = null) + { - $this->raw = $string; - $this->hub = $hub; - - if (is_array($string)) { - $this->data = $string; - $this->raw = json_encode($string,JSON_UNESCAPED_SLASHES); - } - else { - $this->data = json_decode($string, true); - } + $this->raw = $string; + $this->hub = $hub; - if ($this->data) { + if (is_array($string)) { + $this->data = $string; + $this->raw = json_encode($string, JSON_UNESCAPED_SLASHES); + } else { + $this->data = json_decode($string, true); + } - // verify and unpack JSalmon signature if present - // This will only be the case for Zot6 packets - - if (is_array($this->data) && array_key_exists('signed',$this->data)) { - $ret = JSalmon::verify($this->data); - $tmp = JSalmon::unpack($this->data['data']); - if ($ret && $ret['success']) { - if ($ret['signer']) { - logger('Unpacked: ' . json_encode($tmp,JSON_UNESCAPED_SLASHES|JSON_PRETTY_PRINT),LOGGER_DATA,LOG_DEBUG); - $saved = json_encode($this->data,JSON_UNESCAPED_SLASHES); - $this->data = $tmp; - $this->data['signer'] = $ret['signer']; - $this->data['signed_data'] = $saved; - if ($ret['hubloc']) { - $this->data['hubloc'] = $ret['hubloc']; - } - } - } - } + if ($this->data) { + // verify and unpack JSalmon signature if present + // This will only be the case for Zot6 packets - // This indicates only that we have sucessfully decoded JSON. - $this->valid = true; + if (is_array($this->data) && array_key_exists('signed', $this->data)) { + $ret = JSalmon::verify($this->data); + $tmp = JSalmon::unpack($this->data['data']); + if ($ret && $ret['success']) { + if ($ret['signer']) { + logger('Unpacked: ' . json_encode($tmp, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT), LOGGER_DATA, LOG_DEBUG); + $saved = json_encode($this->data, JSON_UNESCAPED_SLASHES); + $this->data = $tmp; + $this->data['signer'] = $ret['signer']; + $this->data['signed_data'] = $saved; + if ($ret['hubloc']) { + $this->data['hubloc'] = $ret['hubloc']; + } + } + } + } - // Special handling for Mastodon "delete actor" activities which will often fail to verify - // because the key cannot be fetched. We will catch this condition elsewhere. - - if (array_key_exists('type',$this->data) && array_key_exists('actor',$this->data) && array_key_exists('object',$this->data)) { - if ($this->data['type'] === 'Delete' && $this->data['actor'] === $this->data['object']) { - $this->deleted = $this->data['actor']; - $this->valid = false; - } - } + // This indicates only that we have sucessfully decoded JSON. + $this->valid = true; - } + // Special handling for Mastodon "delete actor" activities which will often fail to verify + // because the key cannot be fetched. We will catch this condition elsewhere. - // Attempt to assemble an Activity from what we were given. - - if ($this->is_valid()) { - $this->id = $this->get_property_obj('id'); - $this->type = $this->get_primary_type(); - $this->actor = $this->get_actor('actor','',''); - $this->obj = $this->get_compound_property('object'); - $this->tgt = $this->get_compound_property('target'); - $this->origin = $this->get_compound_property('origin'); - $this->recips = $this->collect_recips(); - $this->replyto = $this->get_property_obj('replyTo'); - - $this->ldsig = $this->get_compound_property('signature'); - if ($this->ldsig) { - $this->signer = $this->get_compound_property('creator',$this->ldsig); - if ($this->signer && is_array($this->signer) && array_key_exists('publicKey',$this->signer) - && is_array($this->signer['publicKey']) && $this->signer['publicKey']['publicKeyPem']) { - $this->sigok = LDSignatures::verify($this->data,$this->signer['publicKey']['publicKeyPem']); - } - } + if (array_key_exists('type', $this->data) && array_key_exists('actor', $this->data) && array_key_exists('object', $this->data)) { + if ($this->data['type'] === 'Delete' && $this->data['actor'] === $this->data['object']) { + $this->deleted = $this->data['actor']; + $this->valid = false; + } + } + } - // Implied create activity required by C2S specification if no object is present - - if (! $this->obj) { - if (! $client) { - $this->implied_create = true; - } - $this->obj = $this->data; - $this->type = 'Create'; - if (! $this->actor) { - $this->actor = $this->get_actor('attributedTo',$this->obj); - } - } + // Attempt to assemble an Activity from what we were given. - // fetch recursive or embedded activities - - if ($this->obj && is_array($this->obj) && array_key_exists('object',$this->obj)) { - $this->obj['object'] = $this->get_compound_property($this->obj['object']); - } - - // Enumerate and store actors in referenced objects - - if ($this->obj && is_array($this->obj) && $this->obj['actor']) { - $this->obj['actor'] = $this->get_actor('actor',$this->obj); - } - if ($this->tgt && is_array($this->tgt) && $this->tgt['actor']) { - $this->tgt['actor'] = $this->get_actor('actor',$this->tgt); - } + if ($this->is_valid()) { + $this->id = $this->get_property_obj('id'); + $this->type = $this->get_primary_type(); + $this->actor = $this->get_actor('actor', '', ''); + $this->obj = $this->get_compound_property('object'); + $this->tgt = $this->get_compound_property('target'); + $this->origin = $this->get_compound_property('origin'); + $this->recips = $this->collect_recips(); + $this->replyto = $this->get_property_obj('replyTo'); - // Determine if this is a followup or response activity - - $this->parent_id = $this->get_property_obj('inReplyTo'); + $this->ldsig = $this->get_compound_property('signature'); + if ($this->ldsig) { + $this->signer = $this->get_compound_property('creator', $this->ldsig); + if ( + $this->signer && is_array($this->signer) && array_key_exists('publicKey', $this->signer) + && is_array($this->signer['publicKey']) && $this->signer['publicKey']['publicKeyPem'] + ) { + $this->sigok = LDSignatures::verify($this->data, $this->signer['publicKey']['publicKeyPem']); + } + } - if ((! $this->parent_id) && is_array($this->obj)) { - $this->parent_id = $this->obj['inReplyTo']; - } - if ((! $this->parent_id) && is_array($this->obj)) { - $this->parent_id = $this->obj['id']; - } - } - } + // Implied create activity required by C2S specification if no object is present - /** - * @brief Return if instantiated ActivityStream is valid. - * - * @return boolean Return true if the JSON string could be decoded. - */ + if (!$this->obj) { + if (!$client) { + $this->implied_create = true; + } + $this->obj = $this->data; + $this->type = 'Create'; + if (!$this->actor) { + $this->actor = $this->get_actor('attributedTo', $this->obj); + } + } - function is_valid() { - return $this->valid; - } + // fetch recursive or embedded activities - function set_recips($arr) { - $this->saved_recips = $arr; - } + if ($this->obj && is_array($this->obj) && array_key_exists('object', $this->obj)) { + $this->obj['object'] = $this->get_compound_property($this->obj['object']); + } - /** - * @brief Collects all recipients. - * - * @param string $base - * @param string $namespace (optional) default empty - * @return array - */ - function collect_recips($base = '', $namespace = '') { - $x = []; + // Enumerate and store actors in referenced objects + + if ($this->obj && is_array($this->obj) && $this->obj['actor']) { + $this->obj['actor'] = $this->get_actor('actor', $this->obj); + } + if ($this->tgt && is_array($this->tgt) && $this->tgt['actor']) { + $this->tgt['actor'] = $this->get_actor('actor', $this->tgt); + } + + // Determine if this is a followup or response activity + + $this->parent_id = $this->get_property_obj('inReplyTo'); + + if ((!$this->parent_id) && is_array($this->obj)) { + $this->parent_id = $this->obj['inReplyTo']; + } + if ((!$this->parent_id) && is_array($this->obj)) { + $this->parent_id = $this->obj['id']; + } + } + } + + /** + * @brief Return if instantiated ActivityStream is valid. + * + * @return bool Return true if the JSON string could be decoded. + */ + + public function is_valid() + { + return $this->valid; + } + + public function set_recips($arr) + { + $this->saved_recips = $arr; + } + + /** + * @brief Collects all recipients. + * + * @param string $base + * @param string $namespace (optional) default empty + * @return array + */ + public function collect_recips($base = '', $namespace = '') + { + $x = []; + + $fields = ['to', 'cc', 'bto', 'bcc', 'audience']; + foreach ($fields as $f) { + // don't expand these yet + $y = $this->get_property_obj($f, $base, $namespace); + if ($y) { + if (!is_array($this->raw_recips)) { + $this->raw_recips = []; + } + if (!is_array($y)) { + $y = [$y]; + } + $this->raw_recips[$f] = $y; + $x = array_merge($x, $y); + } + } - $fields = [ 'to', 'cc', 'bto', 'bcc', 'audience']; - foreach ($fields as $f) { - // don't expand these yet - $y = $this->get_property_obj($f, $base, $namespace); - if ($y) { - if (! is_array($this->raw_recips)) { - $this->raw_recips = []; - } - if (! is_array($y)) { - $y = [ $y ]; - } - $this->raw_recips[$f] = $y; - $x = array_merge($x, $y); - } - } - // not yet ready for prime time -// $x = $this->expand($x,$base,$namespace); - return $x; - } +// $x = $this->expand($x,$base,$namespace); + return $x; + } - function expand($arr,$base = '',$namespace = '') { - $ret = []; + public function expand($arr, $base = '', $namespace = '') + { + $ret = []; - // right now use a hardwired recursion depth of 5 + // right now use a hardwired recursion depth of 5 - for ($z = 0; $z < 5; $z ++) { - if (is_array($arr) && $arr) { - foreach ($arr as $a) { - if (is_array($a)) { - $ret[] = $a; - } - else { - $x = $this->get_compound_property($a,$base,$namespace); - if ($x) { - $ret = array_merge($ret,$x); - } - } - } - } - } + for ($z = 0; $z < 5; $z++) { + if (is_array($arr) && $arr) { + foreach ($arr as $a) { + if (is_array($a)) { + $ret[] = $a; + } else { + $x = $this->get_compound_property($a, $base, $namespace); + if ($x) { + $ret = array_merge($ret, $x); + } + } + } + } + } - /// @fixme de-duplicate + /// @fixme de-duplicate - return $ret; - } + return $ret; + } - /** - * @brief + /** + * @brief + * + * @param array $base + * @param string $namespace if not set return empty string + * @return string|NULL + */ + + public function get_namespace($base, $namespace) + { + + if (!$namespace) { + return EMPTY_STR; + } + + $key = null; + + foreach ([$this->data, $base] as $b) { + if (!$b) { + continue; + } + + if (array_key_exists('@context', $b)) { + if (is_array($b['@context'])) { + foreach ($b['@context'] as $ns) { + if (is_array($ns)) { + foreach ($ns as $k => $v) { + if ($namespace === $v) { + $key = $k; + } + } + } else { + if ($namespace === $ns) { + $key = ''; + } + } + } + } else { + if ($namespace === $b['@context']) { + $key = ''; + } + } + } + } + + return $key; + } + + /** + * @brief + * + * @param string $property + * @param array $base (optional) + * @param string $namespace (optional) default empty + * @return NULL|mixed + */ + + public function get_property_obj($property, $base = '', $namespace = '') + { + $prefix = $this->get_namespace($base, $namespace); + if ($prefix === null) { + return null; + } + + $base = (($base) ? $base : $this->data); + $propname = (($prefix) ? $prefix . ':' : '') . $property; + + if (!is_array($base)) { + btlogger('not an array: ' . print_r($base, true)); + return null; + } + + return ((array_key_exists($propname, $base)) ? $base[$propname] : null); + } + + + /** + * @brief Fetches a property from an URL. + * + * @param string $url + * @param array $channel (signing channel, default system channel) + * @return NULL|mixed + */ + + public function fetch_property($url, $channel = null, $hub = null) + { + return Activity::fetch($url, $channel, $hub); + } + + /** + * @brief given a type, determine if this object represents an actor * - * @param array $base - * @param string $namespace if not set return empty string - * @return string|NULL - */ - - function get_namespace($base, $namespace) { - - if (! $namespace) { - return EMPTY_STR; - } - - $key = null; - - foreach ( [ $this->data, $base ] as $b ) { - if (! $b) { - continue; - } - - if (array_key_exists('@context', $b)) { - if (is_array($b['@context'])) { - foreach ($b['@context'] as $ns) { - if (is_array($ns)) { - foreach ($ns as $k => $v) { - if ($namespace === $v) { - $key = $k; - } - } - } - else { - if ($namespace === $ns) { - $key = ''; - } - } - } - } - else { - if ($namespace === $b['@context']) { - $key = ''; - } - } - } - } - - return $key; - } - - /** - * @brief + * If $type is an array, recurse through each element and return true if any + * of the elements are a known actor type * - * @param string $property - * @param array $base (optional) - * @param string $namespace (optional) default empty - * @return NULL|mixed - */ - - function get_property_obj($property, $base = '', $namespace = '') { - $prefix = $this->get_namespace($base, $namespace); - if ($prefix === null) { - return null; - } - - $base = (($base) ? $base : $this->data); - $propname = (($prefix) ? $prefix . ':' : '') . $property; - - if (! is_array($base)) { - btlogger('not an array: ' . print_r($base,true)); - return null; - } - - return ((array_key_exists($propname, $base)) ? $base[$propname] : null); - } - - - /** - * @brief Fetches a property from an URL. - * - * @param string $url - * @param array $channel (signing channel, default system channel) - * @return NULL|mixed - */ - - function fetch_property($url,$channel = null,$hub = null) { - return Activity::fetch($url,$channel,$hub); - } - - static function is_an_actor($s) { - if (! $s) { - return false; - } - return (in_array($s,[ 'Application','Group','Organization','Person','Service' ])); - } - - static function is_response_activity($s) { - if (! $s) { - return false; - } - return (in_array($s, [ 'Like', 'Dislike', 'Flag', 'Block', 'Announce', 'Accept', 'Reject', 'TentativeAccept', 'TentativeReject', 'emojiReaction', 'EmojiReaction', 'EmojiReact' ])); - } - - - - - - /** - * @brief - * - * @param string $property - * @param array $base - * @param string $namespace (optional) default empty - * @return NULL|mixed - */ - - function get_actor($property,$base='',$namespace = '') { - $x = $this->get_property_obj($property, $base, $namespace); - if (self::is_url($x)) { - $y = Activity::get_cached_actor($x); - if ($y) { - return $y; - } - } - - $actor = $this->get_compound_property($property,$base,$namespace,true); - if (is_array($actor) && self::is_an_actor($actor['type'])) { - if (array_key_exists('id',$actor) && (! array_key_exists('inbox',$actor))) { - $actor = $this->fetch_property($actor['id']); - } - return $actor; - } - return null; - } - - - /** - * @brief - * - * @param string $property - * @param array $base - * @param string $namespace (optional) default empty - * @param boolean $first (optional) default false, if true and result is a sequential array return only the first element - * @return NULL|mixed - */ - - function get_compound_property($property, $base = '', $namespace = '', $first = false) { - $x = $this->get_property_obj($property, $base, $namespace); - if (self::is_url($x)) { - $y = $this->fetch_property($x); - if (is_array($y)) { - $x = $y; - } - } - - // verify and unpack JSalmon signature if present - // This may be present in Zot6 packets - - if (is_array($x) && array_key_exists('signed',$x)) { - $ret = JSalmon::verify($x); - $tmp = JSalmon::unpack($x['data']); - if ($ret && $ret['success']) { - if ($ret['signer']) { - logger('Unpacked: ' . json_encode($tmp,JSON_UNESCAPED_SLASHES|JSON_PRETTY_PRINT),LOGGER_DATA,LOG_DEBUG); - $saved = json_encode($x,JSON_UNESCAPED_SLASHES); - $x = $tmp; - $x['signer'] = $ret['signer']; - $x['signed_data'] = $saved; - if ($ret['hubloc']) { - $x['hubloc'] = $ret['hubloc']; - } - } - } - } - if ($first && is_array($x) && array_key_exists(0,$x)) { - return $x[0]; - } - - return $x; - } - - /** - * @brief Check if string starts with http. - * - * @param string $url + * @param string|array $type * @return boolean */ - - static public function is_url($url) { - if (($url) && (! is_array($url)) && ((strpos($url, 'http') === 0) || (strpos($url,'x-zot') === 0) || (strpos($url,'bear') === 0))) { - return true; - } - return false; - } - - /** - * @brief Gets the type property. - * - * @param array $base - * @param string $namespace (optional) default empty - * @return NULL|mixed - */ - - function get_primary_type($base = '', $namespace = '') { - if (! $base) { - $base = $this->data; - } - $x = $this->get_property_obj('type', $base, $namespace); - if (is_array($x)) { - foreach ($x as $y) { - if (strpos($y, ':') === false) { - return $y; + public static function is_an_actor($type) + { + if (!$type) { + return false; + } + if (is_array($type)) { + foreach ($type as $x) { + if (self::is_an_actor($x)) { + return true; } } + return false; } + return (in_array($type, ['Application', 'Group', 'Organization', 'Person', 'Service'])); + } - return $x; - } - - function debug() { - $x = var_export($this, true); - return $x; - } + public static function is_response_activity($s) + { + if (!$s) { + return false; + } + return (in_array($s, ['Like', 'Dislike', 'Flag', 'Block', 'Announce', 'Accept', 'Reject', 'TentativeAccept', 'TentativeReject', 'emojiReaction', 'EmojiReaction', 'EmojiReact'])); + } - static function is_as_request() { + /** + * @brief + * + * @param string $property + * @param array $base + * @param string $namespace (optional) default empty + * @return NULL|mixed + */ - $x = getBestSupportedMimeType([ - 'application/ld+json;profile="https://www.w3.org/ns/activitystreams"', - 'application/activity+json', - 'application/ld+json;profile="http://www.w3.org/ns/activitystreams"', - 'application/ld+json', // required for Friendica ~2021-09, can possibly be removed after next release of that project - 'application/x-zot-activity+json' - ]); + public function get_actor($property, $base = '', $namespace = '') + { + $x = $this->get_property_obj($property, $base, $namespace); + if (self::is_url($x)) { + $y = Activity::get_cached_actor($x); + if ($y) { + return $y; + } + } - return(($x) ? true : false); - } -} \ No newline at end of file + $actor = $this->get_compound_property($property, $base, $namespace, true); + if (is_array($actor) && self::is_an_actor($actor['type'])) { + if (array_key_exists('id', $actor) && (!array_key_exists('inbox', $actor))) { + $actor = $this->fetch_property($actor['id']); + } + return $actor; + } + return null; + } + + + /** + * @brief + * + * @param string $property + * @param array $base + * @param string $namespace (optional) default empty + * @param bool $first (optional) default false, if true and result is a sequential array return only the first element + * @return NULL|mixed + */ + + public function get_compound_property($property, $base = '', $namespace = '', $first = false) + { + $x = $this->get_property_obj($property, $base, $namespace); + if (self::is_url($x)) { + $y = $this->fetch_property($x); + if (is_array($y)) { + $x = $y; + } + } + + // verify and unpack JSalmon signature if present + // This may be present in Zot6 packets + + if (is_array($x) && array_key_exists('signed', $x)) { + $ret = JSalmon::verify($x); + $tmp = JSalmon::unpack($x['data']); + if ($ret && $ret['success']) { + if ($ret['signer']) { + logger('Unpacked: ' . json_encode($tmp, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT), LOGGER_DATA, LOG_DEBUG); + $saved = json_encode($x, JSON_UNESCAPED_SLASHES); + $x = $tmp; + $x['signer'] = $ret['signer']; + $x['signed_data'] = $saved; + if ($ret['hubloc']) { + $x['hubloc'] = $ret['hubloc']; + } + } + } + } + if ($first && is_array($x) && array_key_exists(0, $x)) { + return $x[0]; + } + + return $x; + } + + /** + * @brief Check if string starts with http. + * + * @param string $url + * @return bool + */ + + public static function is_url($url) + { + if (($url) && (!is_array($url)) && ((strpos($url, 'http') === 0) || (strpos($url, 'x-zot') === 0) || (strpos($url, 'bear') === 0))) { + return true; + } + + return false; + } + + /** + * @brief Gets the type property. + * + * @param array $base + * @param string $namespace (optional) default empty + * @return NULL|mixed + */ + + public function get_primary_type($base = '', $namespace = '') + { + if (!$base) { + $base = $this->data; + } + $x = $this->get_property_obj('type', $base, $namespace); + if (is_array($x)) { + foreach ($x as $y) { + if (strpos($y, ':') === false) { + return $y; + } + } + } + + return $x; + } + + public function debug() + { + $x = var_export($this, true); + return $x; + } + + + public static function is_as_request() + { + + $x = getBestSupportedMimeType([ + 'application/ld+json;profile="https://www.w3.org/ns/activitystreams"', + 'application/activity+json', + 'application/ld+json;profile="http://www.w3.org/ns/activitystreams"', + 'application/ld+json', // required for Friendica ~2021-09, can possibly be removed after next release of that project + 'application/x-zot-activity+json' + ]); + + return (($x) ? true : false); + } +} diff --git a/Zotlabs/Lib/Api_router.php b/Zotlabs/Lib/Api_router.php index 147dc86de..4c238a49d 100644 --- a/Zotlabs/Lib/Api_router.php +++ b/Zotlabs/Lib/Api_router.php @@ -2,31 +2,33 @@ namespace Zotlabs\Lib; +class Api_router +{ -class Api_router { + private static $routes = []; - static private $routes = []; + public static function register($path, $fn, $auth_required) + { + self::$routes[$path] = ['func' => $fn, 'auth' => $auth_required]; + } - static function register($path,$fn,$auth_required) { - self::$routes[$path] = [ 'func' => $fn, 'auth' => $auth_required ]; - } + public static function find($path) + { + if (array_key_exists($path, self::$routes)) { + return self::$routes[$path]; + } - static function find($path) { - if (array_key_exists($path,self::$routes)) { - return self::$routes[$path]; - } + $with_params = dirname($path) . '/[id]'; - $with_params = dirname($path) . '/[id]'; + if (array_key_exists($with_params, self::$routes)) { + return self::$routes[$with_params]; + } - if (array_key_exists($with_params,self::$routes)) { - return self::$routes[$with_params]; - } + return null; + } - return null; - } - - static function dbg() { - return self::$routes; - } - -} \ No newline at end of file + public static function dbg() + { + return self::$routes; + } +} diff --git a/Zotlabs/Lib/Apps.php b/Zotlabs/Lib/Apps.php index b732c447f..b6b1215d4 100644 --- a/Zotlabs/Lib/Apps.php +++ b/Zotlabs/Lib/Apps.php @@ -9,1341 +9,1393 @@ use Zotlabs\Lib\Libsync; * Apps * */ - -class Apps { - - static public $available_apps = null; - static public $installed_apps = null; - - static public $base_apps = null; - - - static public function get_system_apps($translate = true) { - - $ret = []; - if (is_dir('apps')) { - $files = glob('apps/*.apd'); - } - else { - $files = glob('app/*.apd'); - } - if ($files) { - foreach ($files as $f) { - $x = self::parse_app_description($f,$translate); - if ($x) { - $ret[] = $x; - } - } - } - $files = glob('addon/*/*.apd'); - if ($files) { - foreach ($files as $f) { - $path = explode('/',$f); - $plugin = trim($path[1]); - if (addon_is_installed($plugin)) { - $x = self::parse_app_description($f,$translate); - if ($x) { - $x['plugin'] = $plugin; - $ret[] = $x; - } - } - } - } - - call_hooks('get_system_apps',$ret); - - return $ret; - - } - - static public function get_base_apps() { - - // to add additional default "base" apps to your site, put their English name, one per line, - // into 'cache/default_apps'. This will be merged with the default project base apps. - - if (file_exists('cache/default_apps')) { - $custom_apps = file('cache/default_apps', FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); - // do some cleanup in case the file was edited by hand and contains accidentally introduced whitespace - if (is_array($custom_apps) && $custom_apps) { - $custom_apps = array_map('trim', $custom_apps); - } - } - - $default_apps = [ - 'Channel Home', - 'Connections', - 'Directory', - 'Events', - 'Files', - 'Help', - 'Lists', - 'Photos', - 'Profile Photo', - 'Search', - 'Settings', - 'Stream', - 'Suggest Channels', - 'View Profile' - ]; - if (is_array($custom_apps)) { - $default_apps = array_values(array_unique(array_merge($default_apps,$custom_apps))); - } - - $x = get_config('system','base_apps',$default_apps); - call_hooks('get_base_apps',$x); - return $x; - } - - static public function import_system_apps() { - if (! local_channel()) { - return; - } - - self::$base_apps = self::get_base_apps(); - - $apps = self::get_system_apps(false); - - self::$available_apps = q("select * from app where app_channel = 0"); - - self::$installed_apps = q("select * from app where app_channel = %d", - intval(local_channel()) - ); - - if ($apps) { - foreach ($apps as $app) { - $id = self::check_install_system_app($app); - - // $id will be boolean true or false to install an app, or an integer id to update an existing app - if ($id !== false) { - $app['uid'] = 0; - $app['guid'] = hash('whirlpool',$app['name']); - $app['system'] = 1; - self::app_install(0,$app); - } - - $id = self::check_install_personal_app($app); - // $id will be boolean true or false to install an app, or an integer id to update an existing app - if ($id === false) { - continue; - } - if ($id !== true) { - // if we already installed this app, but it changed, preserve any categories we created - $r = q("select term from term where otype = %d and oid = %d", - intval(TERM_OBJ_APP), - intval($id) - ); - if ($r) { - $app['categories'] = array_elm_to_str($r,'term'); - } - } - $app['uid'] = local_channel(); - $app['guid'] = hash('whirlpool',$app['name']); - $app['system'] = 1; - self::app_install(local_channel(),$app); - - } - } - } - - /** - * Install the system app if no system apps have been installed, or if a new system app - * is discovered, or if the version of a system app changes. - */ - - static public function check_install_system_app($app) { - if ((! is_array(self::$available_apps)) || (! count(self::$available_apps))) { - return true; - } - $notfound = true; - foreach (self::$available_apps as $iapp) { - if ($iapp['app_id'] == hash('whirlpool',$app['name'])) { - $notfound = false; - if ((isset($app['version']) && $iapp['app_version'] !== $app['version']) - || ((isset($app['plugin']) && $app['plugin']) && (! (isset($iapp['app_plugin']) && $iapp['app_plugin'])))) { - return intval($iapp['app_id']); - } - - if (($iapp['app_url'] !== $app['url']) - || ($iapp['app_photo'] !== $app['photo'])) { - return intval($iapp['app_id']); - } - } - } - - return $notfound; - } - - - /** - * Install the system app if no system apps have been installed, or if a new system app - * is discovered, or if the version of a system app changes. - */ - - static public function check_install_personal_app($app) { - $installed = false; - foreach (self::$installed_apps as $iapp) { - if ($iapp['app_id'] == hash('whirlpool',$app['name'])) { - $installed = true; - if (($iapp['app_version'] != $app['version']) - || (isset($app['plugin']) && $app['plugin'] && (! (isset($iapp['app_plugin']) && $iapp['app_plugin'])))) { - return intval($iapp['app_id']); - } - } - } - if (! $installed && in_array($app['name'],self::$base_apps)) { - return true; - } - return false; - } - - - static public function app_name_compare($a,$b) { - return strcasecmp($a['name'],$b['name']); - } - - - static public function parse_app_description($f,$translate = true) { - - $ret = []; - - $baseurl = z_root(); - $channel = App::get_channel(); - $address = (($channel) ? $channel['channel_address'] : ''); - - //future expansion - - $observer = App::get_observer(); - - - $lines = @file($f); - if ($lines) { - foreach ($lines as $x) { - if (preg_match('/^([a-zA-Z].*?):(.*?)$/ism',$x,$matches)) { - $ret[$matches[1]] = trim($matches[2]); - } - } - } - - if (! $ret['photo']) { - $ret['photo'] = $baseurl . '/' . get_default_profile_photo(80); - } - - $ret['type'] = 'system'; - - foreach ($ret as $k => $v) { - if (strpos($v,'http') === 0) { - if (! (local_channel() && strpos($v,z_root()) === 0)) { - $ret[$k] = zid($v); - } - } - } - - if (array_key_exists('desc',$ret)) { - $ret['desc'] = str_replace(array('\'','"'),array(''','&dquot;'),$ret['desc']); - } - if (array_key_exists('target',$ret)) { - $ret['target'] = str_replace(array('\'','"'),array(''','&dquot;'),$ret['target']); - } - if (array_key_exists('version',$ret)) { - $ret['version'] = str_replace(array('\'','"'),array(''','&dquot;'),$ret['version']); - } - if (array_key_exists('categories',$ret)) { - $ret['categories'] = str_replace(array('\'','"'),array(''','&dquot;'),$ret['categories']); - } - if (array_key_exists('requires',$ret)) { - $requires = explode(',',$ret['requires']); - foreach ($requires as $require) { - $require = trim(strtolower($require)); - $config = false; - - if (substr($require, 0, 7) == 'config:') { - $config = true; - $require = ltrim($require, 'config:'); - $require = explode('=', $require); - } - - switch ($require) { - case 'nologin': - if (local_channel()) { - unset($ret); - } - break; - case 'admin': - if (! is_site_admin()) { - unset($ret); - } - break; - case 'local_channel': - if (! local_channel()) { - unset($ret); - } - break; - case 'public_profile': - if (! is_public_profile()) { - unset($ret); - } - break; - case 'public_stream': - if (! can_view_public_stream()) { - unset($ret); - } - break; - case 'custom_role': - if (get_pconfig(local_channel(),'system','permissions_role') !== 'custom') { - unset($ret); - } - break; - case 'observer': - if (! $observer) { - unset($ret); - } - break; - default: - if ($config) { - $unset = ((get_config('system', $require[0]) == $require[1]) ? false : true); - } - else { - $unset = ((local_channel() && feature_enabled(local_channel(),$require)) ? false : true); - } - if ($unset) { - unset($ret); - } - break; - } - } - } - if (isset($ret)) { - if ($translate) { - self::translate_system_apps($ret); - } - return $ret; - } - return false; - } - - - static public function translate_system_apps(&$arr) { - $apps = array( - 'Admin' => t('Site Admin'), - 'Apps' => t('Apps'), - 'Articles' => t('Articles'), - 'CalDAV' => t('CalDAV'), - 'CardDAV' => t('CardDAV'), - 'Cards' => t('Cards'), - 'Calendar' => t('Calendar'), - 'Categories' => t('Categories'), - 'Channel Home' => t('Channel Home'), - 'Channel Manager' => t('Channel Manager'), - 'Channel Sources' => t('Channel Sources'), - 'Chat' => t('Chat'), - 'Chatrooms' => t('Chatrooms'), - 'Clients' => t('Clients'), - 'Comment Control' => t('Comment Control'), - 'Connections' => t('Connections'), - 'Content Filter' => t('Content Filter'), - 'Content Import' => t('Content Import'), - 'Directory' => t('Directory'), - 'Drafts' => t('Drafts'), - 'Events' => t('Events'), - 'Expire Posts' => t('Expire Posts'), - 'Features' => t('Features'), - 'Files' => t('Files'), - 'Followlist' => t('Followlist'), - 'Friend Zoom' => t('Friend Zoom'), - 'Future Posting' => t('Future Posting'), - 'Gallery' => t('Gallery'), - 'Guest Pass' => t('Guest Pass'), - 'Help' => t('Help'), - 'Invite' => t('Invite'), - 'Language' => t('Language'), - 'Lists' => t('Lists'), - 'Login' => t('Login'), - 'Mail' => t('Mail'), - 'Markup' => t('Markup'), - 'Mood' => t('Mood'), - 'My Chatrooms' => t('My Chatrooms'), - 'No Comment' => t('No Comment'), - 'Notes' => t('Notes'), - 'Notifications' => t('Notifications'), - 'OAuth Apps Manager' => t('OAuth Apps Manager'), - 'OAuth2 Apps Manager' => t('OAuth2 Apps Manager'), - 'Order Apps' => t('Order Apps'), - 'PDL Editor' => t('PDL Editor'), - 'Permission Categories' => t('Permission Categories'), - 'Photos' => t('Photos'), - 'Photomap' => t('Photomap'), - 'Poke' => t('Poke'), - 'Post' => t('Post'), - 'Premium Channel' => t('Premium Channel'), - 'Probe' => t('Probe'), - 'Profile' => t('Profile'), - 'Profile Photo' => t('Profile Photo'), - 'Profiles' => t('Profiles'), - 'Public Stream' => t('Public Stream'), - 'Random Channel' => t('Random Channel'), - 'Remote Diagnostics' => t('Remote Diagnostics'), - 'Report Bug' => t('Report Bug'), - 'Search' => t('Search'), - 'Secrets' => t('Secrets'), - 'Settings' => t('Settings'), - 'Sites' => t('Sites'), - 'Stream' => t('Stream'), - 'Stream Order' => t('Stream Order'), - 'Suggest' => t('Suggest'), - 'Suggest Channels' => t('Suggest Channels'), - 'Tagadelic' => t('Tagadelic'), - 'Tasks' => t('Tasks'), - 'View Bookmarks' => t('View Bookmarks'), - 'View Profile' => t('View Profile'), - 'Virtual Lists' => t('Virtual Lists'), - 'Webpages' => t('Webpages'), - 'Wiki' => t('Wiki'), - 'ZotPost' => t('ZotPost'), - ); - - if (array_key_exists('name',$arr)) { - if (array_key_exists($arr['name'],$apps)) { - $arr['name'] = $apps[$arr['name']]; - } - } - else { - for ($x = 0; $x < count($arr); $x++) { - if (array_key_exists($arr[$x]['name'],$apps)) { - $arr[$x]['name'] = $apps[$arr[$x]['name']]; - } - else { - // Try to guess by app name if not in list - $arr[$x]['name'] = t(trim($arr[$x]['name'])); - } - } - } - } - - - // papp is a portable app - - static public function app_render($papp,$mode = 'view') { - - /** - * modes: - * view: normal mode for viewing an app via bbcode from a conversation or page - * provides install/update button if you're logged in locally - * install: like view but does not display app-bin options if they are present - * list: normal mode for viewing an app on the app page - * no buttons are shown - * edit: viewing the app page in editing mode provides a delete button - * nav: render apps for app-bin - */ - - $installed = false; - - if (! $papp) { - return; - } - - if (! $papp['photo']) { - $papp['photo'] = 'icon:gear'; - } - - self::translate_system_apps($papp); - - if (isset($papp['plugin']) && trim($papp['plugin']) && (! addon_is_installed(trim($papp['plugin'])))) { - return ''; - } - - $papp['papp'] = self::papp_encode($papp); - - // This will catch somebody clicking on a system "available" app that hasn't had the path macros replaced - // and they are allowed to see the app - - - if (strpos($papp['url'],'$baseurl') !== false || strpos($papp['url'],'$nick') !== false || strpos($papp['photo'],'$baseurl') !== false || strpos($papp['photo'],'$nick') !== false) { - $view_channel = local_channel(); - if (! $view_channel) { - $sys = get_sys_channel(); - $view_channel = $sys['channel_id']; - } - self::app_macros($view_channel,$papp); - } - - if (strpos($papp['url'], ',')) { - $urls = explode(',', $papp['url']); - $papp['url'] = trim($urls[0]); - $papp['settings_url'] = trim($urls[1]); - } - - if (! strstr($papp['url'],'://')) { - $papp['url'] = z_root() . ((strpos($papp['url'],'/') === 0) ? '' : '/') . $papp['url']; - } - - - foreach ($papp as $k => $v) { - if (strpos($v,'http') === 0 && $k != 'papp') { - if (! (local_channel() && strpos($v,z_root()) === 0)) { - $papp[$k] = zid($v); - } - } - if ($k === 'desc') { - $papp['desc'] = str_replace(array('\'','"'),array(''','&dquot;'),$papp['desc']); - } - - if ($k === 'requires') { - $requires = explode(',',$v); - - foreach ($requires as $require) { - $require = trim(strtolower($require)); - $config = false; - - if (substr($require, 0, 7) == 'config:') { - $config = true; - $require = ltrim($require, 'config:'); - $require = explode('=', $require); - } - - switch ($require) { - case 'nologin': - if (local_channel()) { - return ''; - } - break; - case 'admin': - if (! is_site_admin()) { - return ''; - } - break; - case 'local_channel': - if (! local_channel()) { - return ''; - } - break; - case 'public_profile': - if (! is_public_profile()) { - return ''; - } - break; - case 'public_stream': - if (! can_view_public_stream()) { - return ''; - } - break; - case 'custom_role': - if (get_pconfig(local_channel(),'system','permissions_role') != 'custom') { - return ''; - } - break; - case 'observer': - $observer = App::get_observer(); - if (! $observer) { - return ''; - } - break; - default: - if ($config) { - $unset = ((get_config('system', $require[0]) === $require[1]) ? false : true); - } - else { - $unset = ((local_channel() && feature_enabled(local_channel(),$require)) ? false : true); - } - if ($unset) { - return ''; - } - break; - } - } - } - } - - $hosturl = ''; - - if (local_channel()) { - if (self::app_installed(local_channel(),$papp) && (! (isset($papp['deleted']) && intval($papp['deleted'])))) { - $installed = true; - if ($mode === 'install') { - return ''; - } - } - - $hosturl = z_root() . '/'; - } - elseif (remote_channel()) { - $observer = App::get_observer(); +class Apps +{ + + public static $available_apps = null; + public static $installed_apps = null; + + public static $base_apps = null; + + + public static function get_system_apps($translate = true) + { + + $ret = []; + if (is_dir('apps')) { + $files = glob('apps/*.apd'); + } else { + $files = glob('app/*.apd'); + } + if ($files) { + foreach ($files as $f) { + $x = self::parse_app_description($f, $translate); + if ($x) { + $ret[] = $x; + } + } + } + $files = glob('addon/*/*.apd'); + if ($files) { + foreach ($files as $f) { + $path = explode('/', $f); + $plugin = trim($path[1]); + if (addon_is_installed($plugin)) { + $x = self::parse_app_description($f, $translate); + if ($x) { + $x['plugin'] = $plugin; + $ret[] = $x; + } + } + } + } + + call_hooks('get_system_apps', $ret); + + return $ret; + } + + public static function get_base_apps() + { + + // to add additional default "base" apps to your site, put their English name, one per line, + // into 'cache/default_apps'. This will be merged with the default project base apps. + + if (file_exists('cache/default_apps')) { + $custom_apps = file('cache/default_apps', FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); + // do some cleanup in case the file was edited by hand and contains accidentally introduced whitespace + if (is_array($custom_apps) && $custom_apps) { + $custom_apps = array_map('trim', $custom_apps); + } + } + + $default_apps = [ + 'Channel Home', + 'Connections', + 'Directory', + 'Events', + 'Files', + 'Help', + 'Lists', + 'Photos', + 'Profile Photo', + 'Search', + 'Settings', + 'Stream', + 'Suggest Channels', + 'View Profile' + ]; + if (is_array($custom_apps)) { + $default_apps = array_values(array_unique(array_merge($default_apps, $custom_apps))); + } + + $x = get_config('system', 'base_apps', $default_apps); + call_hooks('get_base_apps', $x); + return $x; + } + + public static function import_system_apps() + { + if (!local_channel()) { + return; + } + + self::$base_apps = self::get_base_apps(); + + $apps = self::get_system_apps(false); + + self::$available_apps = q("select * from app where app_channel = 0"); + + self::$installed_apps = q( + "select * from app where app_channel = %d", + intval(local_channel()) + ); + + if ($apps) { + foreach ($apps as $app) { + $id = self::check_install_system_app($app); + + // $id will be boolean true or false to install an app, or an integer id to update an existing app + if ($id !== false) { + $app['uid'] = 0; + $app['guid'] = hash('whirlpool', $app['name']); + $app['system'] = 1; + self::app_install(0, $app); + } + + $id = self::check_install_personal_app($app); + // $id will be boolean true or false to install an app, or an integer id to update an existing app + if ($id === false) { + continue; + } + if ($id !== true) { + // if we already installed this app, but it changed, preserve any categories we created + $r = q( + "select term from term where otype = %d and oid = %d", + intval(TERM_OBJ_APP), + intval($id) + ); + if ($r) { + $app['categories'] = array_elm_to_str($r, 'term'); + } + } + $app['uid'] = local_channel(); + $app['guid'] = hash('whirlpool', $app['name']); + $app['system'] = 1; + self::app_install(local_channel(), $app); + } + } + } + + /** + * Install the system app if no system apps have been installed, or if a new system app + * is discovered, or if the version of a system app changes. + */ + + public static function check_install_system_app($app) + { + if ((!is_array(self::$available_apps)) || (!count(self::$available_apps))) { + return true; + } + $notfound = true; + foreach (self::$available_apps as $iapp) { + if ($iapp['app_id'] == hash('whirlpool', $app['name'])) { + $notfound = false; + if ( + (isset($app['version']) && $iapp['app_version'] !== $app['version']) + || ((isset($app['plugin']) && $app['plugin']) && (!(isset($iapp['app_plugin']) && $iapp['app_plugin']))) + ) { + return intval($iapp['app_id']); + } + + if ( + ($iapp['app_url'] !== $app['url']) + || ($iapp['app_photo'] !== $app['photo']) + ) { + return intval($iapp['app_id']); + } + } + } + + return $notfound; + } + + + /** + * Install the system app if no system apps have been installed, or if a new system app + * is discovered, or if the version of a system app changes. + */ + + public static function check_install_personal_app($app) + { + $installed = false; + foreach (self::$installed_apps as $iapp) { + if ($iapp['app_id'] == hash('whirlpool', $app['name'])) { + $installed = true; + if ( + ($iapp['app_version'] != $app['version']) + || (isset($app['plugin']) && $app['plugin'] && (!(isset($iapp['app_plugin']) && $iapp['app_plugin']))) + ) { + return intval($iapp['app_id']); + } + } + } + if (!$installed && in_array($app['name'], self::$base_apps)) { + return true; + } + return false; + } + + + public static function app_name_compare($a, $b) + { + return strcasecmp($a['name'], $b['name']); + } + + + public static function parse_app_description($f, $translate = true) + { + + $ret = []; + + $baseurl = z_root(); + $channel = App::get_channel(); + $address = (($channel) ? $channel['channel_address'] : ''); + + //future expansion + + $observer = App::get_observer(); + + + $lines = @file($f); + if ($lines) { + foreach ($lines as $x) { + if (preg_match('/^([a-zA-Z].*?):(.*?)$/ism', $x, $matches)) { + $ret[$matches[1]] = trim($matches[2]); + } + } + } + + if (!$ret['photo']) { + $ret['photo'] = $baseurl . '/' . get_default_profile_photo(80); + } + + $ret['type'] = 'system'; + + foreach ($ret as $k => $v) { + if (strpos($v, 'http') === 0) { + if (!(local_channel() && strpos($v, z_root()) === 0)) { + $ret[$k] = zid($v); + } + } + } + + if (array_key_exists('desc', $ret)) { + $ret['desc'] = str_replace(array('\'', '"'), array(''', '&dquot;'), $ret['desc']); + } + if (array_key_exists('target', $ret)) { + $ret['target'] = str_replace(array('\'', '"'), array(''', '&dquot;'), $ret['target']); + } + if (array_key_exists('version', $ret)) { + $ret['version'] = str_replace(array('\'', '"'), array(''', '&dquot;'), $ret['version']); + } + if (array_key_exists('categories', $ret)) { + $ret['categories'] = str_replace(array('\'', '"'), array(''', '&dquot;'), $ret['categories']); + } + if (array_key_exists('requires', $ret)) { + $requires = explode(',', $ret['requires']); + foreach ($requires as $require) { + $require = trim(strtolower($require)); + $config = false; + + if (substr($require, 0, 7) == 'config:') { + $config = true; + $require = ltrim($require, 'config:'); + $require = explode('=', $require); + } + + switch ($require) { + case 'nologin': + if (local_channel()) { + unset($ret); + } + break; + case 'admin': + if (!is_site_admin()) { + unset($ret); + } + break; + case 'local_channel': + if (!local_channel()) { + unset($ret); + } + break; + case 'public_profile': + if (!is_public_profile()) { + unset($ret); + } + break; + case 'public_stream': + if (!can_view_public_stream()) { + unset($ret); + } + break; + case 'custom_role': + if (get_pconfig(local_channel(), 'system', 'permissions_role') !== 'custom') { + unset($ret); + } + break; + case 'observer': + if (!$observer) { + unset($ret); + } + break; + default: + if ($config) { + $unset = ((get_config('system', $require[0]) == $require[1]) ? false : true); + } else { + $unset = ((local_channel() && feature_enabled(local_channel(), $require)) ? false : true); + } + if ($unset) { + unset($ret); + } + break; + } + } + } + if (isset($ret)) { + if ($translate) { + self::translate_system_apps($ret); + } + return $ret; + } + return false; + } + + + public static function translate_system_apps(&$arr) + { + $apps = array( + 'Admin' => t('Site Admin'), + 'Apps' => t('Apps'), + 'Articles' => t('Articles'), + 'CalDAV' => t('CalDAV'), + 'CardDAV' => t('CardDAV'), + 'Cards' => t('Cards'), + 'Calendar' => t('Calendar'), + 'Categories' => t('Categories'), + 'Channel Home' => t('Channel Home'), + 'Channel Manager' => t('Channel Manager'), + 'Channel Sources' => t('Channel Sources'), + 'Chat' => t('Chat'), + 'Chatrooms' => t('Chatrooms'), + 'Clients' => t('Clients'), + 'Comment Control' => t('Comment Control'), + 'Connections' => t('Connections'), + 'Content Filter' => t('Content Filter'), + 'Content Import' => t('Content Import'), + 'Directory' => t('Directory'), + 'Drafts' => t('Drafts'), + 'Events' => t('Events'), + 'Expire Posts' => t('Expire Posts'), + 'Features' => t('Features'), + 'Files' => t('Files'), + 'Followlist' => t('Followlist'), + 'Friend Zoom' => t('Friend Zoom'), + 'Future Posting' => t('Future Posting'), + 'Gallery' => t('Gallery'), + 'Guest Pass' => t('Guest Pass'), + 'Help' => t('Help'), + 'Invite' => t('Invite'), + 'Language' => t('Language'), + 'Lists' => t('Lists'), + 'Login' => t('Login'), + 'Mail' => t('Mail'), + 'Markup' => t('Markup'), + 'Mood' => t('Mood'), + 'My Chatrooms' => t('My Chatrooms'), + 'No Comment' => t('No Comment'), + 'Notes' => t('Notes'), + 'Notifications' => t('Notifications'), + 'OAuth Apps Manager' => t('OAuth Apps Manager'), + 'OAuth2 Apps Manager' => t('OAuth2 Apps Manager'), + 'Order Apps' => t('Order Apps'), + 'PDL Editor' => t('PDL Editor'), + 'Permission Categories' => t('Permission Categories'), + 'Photos' => t('Photos'), + 'Photomap' => t('Photomap'), + 'Poke' => t('Poke'), + 'Post' => t('Post'), + 'Premium Channel' => t('Premium Channel'), + 'Probe' => t('Probe'), + 'Profile' => t('Profile'), + 'Profile Photo' => t('Profile Photo'), + 'Profiles' => t('Profiles'), + 'Public Stream' => t('Public Stream'), + 'Random Channel' => t('Random Channel'), + 'Remote Diagnostics' => t('Remote Diagnostics'), + 'Report Bug' => t('Report Bug'), + 'Search' => t('Search'), + 'Secrets' => t('Secrets'), + 'Settings' => t('Settings'), + 'Sites' => t('Sites'), + 'Stream' => t('Stream'), + 'Stream Order' => t('Stream Order'), + 'Suggest' => t('Suggest'), + 'Suggest Channels' => t('Suggest Channels'), + 'Tagadelic' => t('Tagadelic'), + 'Tasks' => t('Tasks'), + 'View Bookmarks' => t('View Bookmarks'), + 'View Profile' => t('View Profile'), + 'Virtual Lists' => t('Virtual Lists'), + 'Webpages' => t('Webpages'), + 'Wiki' => t('Wiki'), + 'ZotPost' => t('ZotPost'), + ); + + if (array_key_exists('name', $arr)) { + if (array_key_exists($arr['name'], $apps)) { + $arr['name'] = $apps[$arr['name']]; + } + } else { + for ($x = 0; $x < count($arr); $x++) { + if (array_key_exists($arr[$x]['name'], $apps)) { + $arr[$x]['name'] = $apps[$arr[$x]['name']]; + } else { + // Try to guess by app name if not in list + $arr[$x]['name'] = t(trim($arr[$x]['name'])); + } + } + } + } + + + // papp is a portable app + + public static function app_render($papp, $mode = 'view') + { + + /** + * modes: + * view: normal mode for viewing an app via bbcode from a conversation or page + * provides install/update button if you're logged in locally + * install: like view but does not display app-bin options if they are present + * list: normal mode for viewing an app on the app page + * no buttons are shown + * edit: viewing the app page in editing mode provides a delete button + * nav: render apps for app-bin + */ + + $installed = false; + + if (!$papp) { + return; + } + + if (!$papp['photo']) { + $papp['photo'] = 'icon:gear'; + } + + self::translate_system_apps($papp); + + if (isset($papp['plugin']) && trim($papp['plugin']) && (!addon_is_installed(trim($papp['plugin'])))) { + return ''; + } + + $papp['papp'] = self::papp_encode($papp); + + // This will catch somebody clicking on a system "available" app that hasn't had the path macros replaced + // and they are allowed to see the app + + + if (strpos($papp['url'], '$baseurl') !== false || strpos($papp['url'], '$nick') !== false || strpos($papp['photo'], '$baseurl') !== false || strpos($papp['photo'], '$nick') !== false) { + $view_channel = local_channel(); + if (!$view_channel) { + $sys = get_sys_channel(); + $view_channel = $sys['channel_id']; + } + self::app_macros($view_channel, $papp); + } + + if (strpos($papp['url'], ',')) { + $urls = explode(',', $papp['url']); + $papp['url'] = trim($urls[0]); + $papp['settings_url'] = trim($urls[1]); + } + + if (!strstr($papp['url'], '://')) { + $papp['url'] = z_root() . ((strpos($papp['url'], '/') === 0) ? '' : '/') . $papp['url']; + } + + + foreach ($papp as $k => $v) { + if (strpos($v, 'http') === 0 && $k != 'papp') { + if (!(local_channel() && strpos($v, z_root()) === 0)) { + $papp[$k] = zid($v); + } + } + if ($k === 'desc') { + $papp['desc'] = str_replace(array('\'', '"'), array(''', '&dquot;'), $papp['desc']); + } + + if ($k === 'requires') { + $requires = explode(',', $v); + + foreach ($requires as $require) { + $require = trim(strtolower($require)); + $config = false; + + if (substr($require, 0, 7) == 'config:') { + $config = true; + $require = ltrim($require, 'config:'); + $require = explode('=', $require); + } + + switch ($require) { + case 'nologin': + if (local_channel()) { + return ''; + } + break; + case 'admin': + if (!is_site_admin()) { + return ''; + } + break; + case 'local_channel': + if (!local_channel()) { + return ''; + } + break; + case 'public_profile': + if (!is_public_profile()) { + return ''; + } + break; + case 'public_stream': + if (!can_view_public_stream()) { + return ''; + } + break; + case 'custom_role': + if (get_pconfig(local_channel(), 'system', 'permissions_role') != 'custom') { + return ''; + } + break; + case 'observer': + $observer = App::get_observer(); + if (!$observer) { + return ''; + } + break; + default: + if ($config) { + $unset = ((get_config('system', $require[0]) === $require[1]) ? false : true); + } else { + $unset = ((local_channel() && feature_enabled(local_channel(), $require)) ? false : true); + } + if ($unset) { + return ''; + } + break; + } + } + } + } + + $hosturl = ''; + + if (local_channel()) { + if (self::app_installed(local_channel(), $papp) && (!(isset($papp['deleted']) && intval($papp['deleted'])))) { + $installed = true; + if ($mode === 'install') { + return ''; + } + } + + $hosturl = z_root() . '/'; + } elseif (remote_channel()) { + $observer = App::get_observer(); if ($observer && in_array($observer['xchan_network'],['nomad','zot6'])) { - // some folks might have xchan_url redirected offsite, use the connurl - $x = parse_url($observer['xchan_connurl']); - if ($x) { - $hosturl = $x['scheme'] . '://' . $x['host'] . '/'; - } - } - } + // some folks might have xchan_url redirected offsite, use the connurl + $x = parse_url($observer['xchan_connurl']); + if ($x) { + $hosturl = $x['scheme'] . '://' . $x['host'] . '/'; + } + } + } - $install_action = (($installed) ? t('Installed') : t('Install')); - $icon = ((strpos($papp['photo'],'icon:') === 0) ? substr($papp['photo'],5) : ''); + $install_action = (($installed) ? t('Installed') : t('Install')); + $icon = ((strpos($papp['photo'], 'icon:') === 0) ? substr($papp['photo'], 5) : ''); - if ($mode === 'navbar') { - return replace_macros(get_markup_template('app_nav.tpl'), [ - '$app' => $papp, - '$icon' => $icon, - ]); - } + if ($mode === 'navbar') { + return replace_macros(get_markup_template('app_nav.tpl'), [ + '$app' => $papp, + '$icon' => $icon, + ]); + } - if ($mode === 'install') { - $papp['embed'] = true; - } + if ($mode === 'install') { + $papp['embed'] = true; + } - $featured = $pinned = false; - if (isset($papp['categories'])) { - $featured = ((strpos($papp['categories'],'nav_featured_app') !== false) ? true : false); - $pinned = ((strpos($papp['categories'],'nav_pinned_app') !== false) ? true : false); - } + $featured = $pinned = false; + if (isset($papp['categories'])) { + $featured = ((strpos($papp['categories'], 'nav_featured_app') !== false) ? true : false); + $pinned = ((strpos($papp['categories'], 'nav_pinned_app') !== false) ? true : false); + } - return replace_macros(get_markup_template('app.tpl'), [ - '$app' => $papp, - '$icon' => $icon, - '$hosturl' => $hosturl, - '$purchase' => ((isset($papp['page']) && $papp['page'] && (! $installed)) ? t('Purchase') : ''), - '$installed' => $installed, - '$action_label' => (($hosturl && in_array($mode, ['view','install'])) ? $install_action : ''), - '$edit' => ((local_channel() && $installed && $mode === 'edit') ? t('Edit') : ''), - '$delete' => ((local_channel() && $installed && $mode === 'edit') ? t('Delete') : ''), - '$undelete' => ((local_channel() && $installed && $mode === 'edit') ? t('Undelete') : ''), - '$settings_url' => ((local_channel() && $installed && $mode === 'list' && isset($papp['settings_url'])) ? $papp['settings_url'] : ''), - '$deleted' => ((isset($papp['deleted'])) ? intval($papp['deleted']) : false), - '$feature' => (((isset($papp['embed']) && $papp['embed']) || $mode === 'edit') ? false : true), - '$pin' => (((isset($papp['embed']) && $papp['embed']) || $mode === 'edit') ? false : true), - '$featured' => $featured, - '$pinned' => $pinned, - '$navapps' => (($mode === 'nav') ? true : false), - '$order' => (($mode === 'nav-order' || $mode === 'nav-order-pinned') ? true : false), - '$mode' => $mode, - '$add' => t('Add to app-tray'), - '$remove' => t('Remove from app-tray'), - '$add_nav' => t('Pin to navbar'), - '$remove_nav' => t('Unpin from navbar') - ]); - } + return replace_macros(get_markup_template('app.tpl'), [ + '$app' => $papp, + '$icon' => $icon, + '$hosturl' => $hosturl, + '$purchase' => ((isset($papp['page']) && $papp['page'] && (!$installed)) ? t('Purchase') : ''), + '$installed' => $installed, + '$action_label' => (($hosturl && in_array($mode, ['view', 'install'])) ? $install_action : ''), + '$edit' => ((local_channel() && $installed && $mode === 'edit') ? t('Edit') : ''), + '$delete' => ((local_channel() && $installed && $mode === 'edit') ? t('Delete') : ''), + '$undelete' => ((local_channel() && $installed && $mode === 'edit') ? t('Undelete') : ''), + '$settings_url' => ((local_channel() && $installed && $mode === 'list' && isset($papp['settings_url'])) ? $papp['settings_url'] : ''), + '$deleted' => ((isset($papp['deleted'])) ? intval($papp['deleted']) : false), + '$feature' => (((isset($papp['embed']) && $papp['embed']) || $mode === 'edit') ? false : true), + '$pin' => (((isset($papp['embed']) && $papp['embed']) || $mode === 'edit') ? false : true), + '$featured' => $featured, + '$pinned' => $pinned, + '$navapps' => (($mode === 'nav') ? true : false), + '$order' => (($mode === 'nav-order' || $mode === 'nav-order-pinned') ? true : false), + '$mode' => $mode, + '$add' => t('Add to app-tray'), + '$remove' => t('Remove from app-tray'), + '$add_nav' => t('Pin to navbar'), + '$remove_nav' => t('Unpin from navbar') + ]); + } - static public function app_install($uid,$app) { + public static function app_install($uid, $app) + { - if (! is_array($app)) { - $r = q("select * from app where app_name = '%s' and app_channel = 0", - dbesc($app) - ); - if (! $r) { - return false; - } + if (!is_array($app)) { + $r = q( + "select * from app where app_name = '%s' and app_channel = 0", + dbesc($app) + ); + if (!$r) { + return false; + } - $app = self::app_encode($r[0]); - } + $app = self::app_encode($r[0]); + } - $app['uid'] = $uid; + $app['uid'] = $uid; - if (self::app_installed($uid,$app,true)) { - $x = self::app_update($app); - } - else { - $x = self::app_store($app); - } - - if ($x['success']) { - $r = q("select * from app where app_id = '%s' and app_channel = %d limit 1", - dbesc($x['app_id']), - intval($uid) - ); - if ($r) { - if (($app['uid']) && (! $r[0]['app_system'])) { - if ($app['categories'] && (! $app['term'])) { - $r[0]['term'] = q("select * from term where otype = %d and oid = %d", - intval(TERM_OBJ_APP), - intval($r[0]['id']) - ); - if (intval($r[0]['app_system'])) { - Libsync::build_sync_packet($uid,array('sysapp' => $r[0])); - } - else { - Libsync::build_sync_packet($uid,array('app' => $r[0])); - } - } - } - } - return $x['app_id']; - } - return false; - } - - - static public function can_delete($uid,$app) { - if (! $uid) { - return false; - } - - $base_apps = self::get_base_apps(); - if ($base_apps) { - foreach ($base_apps as $b) { - if ($app['guid'] === hash('whirlpool',$b)) { - return false; - } - } - } - return true; - } - - - static public function app_destroy($uid,$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 (! intval($x[0]['app_deleted'])) { - $x[0]['app_deleted'] = 1; - if (self::can_delete($uid,$app)) { - $r = q("delete from app where app_id = '%s' and app_channel = %d", - dbesc($app['guid']), - intval($uid) - ); - q("delete from term where otype = %d and oid = %d", - intval(TERM_OBJ_APP), - intval($x[0]['id']) - ); - call_hooks('app_destroy',$x[0]); - } - else { - $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_system'])) { - Libsync::build_sync_packet($uid,array('sysapp' => $x)); - } - else { - Libsync::build_sync_packet($uid,array('app' => $x)); - } - } - else { - 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) - ); - } - } - } - } - - static public function app_feature($uid,$app,$term) { - $r = q("select id from app where app_id = '%s' and app_channel = %d limit 1", - dbesc($app['guid']), - intval($uid) - ); - - $x = q("select * from term where otype = %d and oid = %d and term = '%s' limit 1", - intval(TERM_OBJ_APP), - intval($r[0]['id']), - dbesc($term) - ); - - if ($x) { - q("delete from term where otype = %d and oid = %d and term = '%s'", - intval(TERM_OBJ_APP), - intval($x[0]['oid']), - dbesc($term) - ); - } - else { - store_item_tag($uid, $r[0]['id'], TERM_OBJ_APP, TERM_CATEGORY, $term, escape_tags(z_root() . '/apps/?f=&cat=' . $term)); - } - } - - static public function app_installed($uid,$app,$bypass_filter = false) { - - $r = q("select id from app where app_id = '%s' and app_channel = %d limit 1", - dbesc((array_key_exists('guid',$app)) ? $app['guid'] : ''), - intval($uid) - ); - if (! $bypass_filter) { - $filter_arr = [ - 'uid'=>$uid, - 'app'=>$app, - 'installed'=>$r - ]; - call_hooks('app_installed_filter',$filter_arr); - $r = $filter_arr['installed']; - } - - return (($r) ? true : false); - - } - - static public function addon_app_installed($uid,$app,$bypass_filter = false) { - - $r = q("select id from app where app_plugin = '%s' and app_channel = %d limit 1", - dbesc($app), - intval($uid) - ); - if (! $bypass_filter) { - $filter_arr = [ - 'uid'=>$uid, - 'app'=>$app, - 'installed'=>$r - ]; - call_hooks('addon_app_installed_filter',$filter_arr); - $r = $filter_arr['installed']; - } - - return (($r) ? true : false); - - } - - static public function system_app_installed($uid,$app,$bypass_filter = false) { - - $r = q("select id from app where app_id = '%s' and app_channel = %d and app_deleted = 0 limit 1", - dbesc(hash('whirlpool',$app)), - intval($uid) - ); - if (! $bypass_filter) { - $filter_arr = [ - 'uid'=>$uid, - 'app'=>$app, - 'installed'=>$r - ]; - call_hooks('system_app_installed_filter',$filter_arr); - $r = $filter_arr['installed']; - } - - return (($r) ? true : false); - } - - static public function app_list($uid, $deleted = false, $cats = []) { - if ($deleted) { - $sql_extra = ""; - } - else { - $sql_extra = " and app_deleted = 0 "; - } - if ($cats) { - - $cat_sql_extra = " and ( "; - - foreach ($cats as $cat) { - if (strpos($cat_sql_extra, 'term')) - $cat_sql_extra .= "or "; - - $cat_sql_extra .= "term = '" . dbesc($cat) . "' "; - } - - $cat_sql_extra .= ") "; - - $r = q("select oid from term where otype = %d $cat_sql_extra", - intval(TERM_OBJ_APP) - ); - if (! $r) { - return $r; - } - $sql_extra .= " and app.id in ( " . array_elm_to_str($r,'oid') . ') '; - } - - $r = q("select * from app where app_channel = %d $sql_extra order by app_name asc", - intval($uid) - ); - - if ($r) { - $hookinfo = [ 'uid' => $uid, 'deleted' => $deleted, 'cats' => $cats, 'apps' => $r ]; - call_hooks('app_list',$hookinfo); - $r = $hookinfo['apps']; - for ($x = 0; $x < count($r); $x ++) { - if (! $r[$x]['app_system']) { - $r[$x]['type'] = 'personal'; - } - $r[$x]['term'] = q("select * from term where otype = %d and oid = %d", - intval(TERM_OBJ_APP), - intval($r[$x]['id']) + if (self::app_installed($uid, $app, true)) { + // preserve the existing deleted status across app updates + if (isset($app['guid'])) { + $check = q("select * from app where app_id = '%s' and app_channel = %d", + dbesc($app['guid']), + intval($uid) ); - } - } - - return ($r); - } - - - static public function app_search_available($str) { - - // not yet finished - // somehow need to account for translations - - $r = q("select * from app where app_channel = 0 $sql_extra order by app_name asc", - intval($uid) - ); - - return ($r); - } - - - - static public function app_order($uid,$apps,$menu) { - - if (! $apps) - return $apps; - - $conf = (($menu === 'nav_featured_app') ? 'app_order' : 'app_pin_order'); - - $x = (($uid) ? get_pconfig($uid,'system',$conf) : get_config('system',$conf)); - if (($x) && (! is_array($x))) { - $y = explode(',',$x); - $y = array_map('trim',$y); - $x = $y; - } - - if (! (is_array($x) && ($x))) { - return $apps; - } - - $ret = []; - foreach ($x as $xx) { - $y = self::find_app_in_array($xx,$apps); - if ($y) { - $ret[] = $y; - } - } - foreach ($apps as $ap) { - if (! self::find_app_in_array($ap['name'],$ret)) { - $ret[] = $ap; - } - } - return $ret; - - } - - static function find_app_in_array($name,$arr) { - if (! $arr) { - return false; - } - foreach ($arr as $x) { - if ($x['name'] === $name) { - return $x; - } - } - return false; - } - - static function moveup($uid,$guid,$menu) { - $syslist = []; - - $conf = (($menu === 'nav_featured_app') ? 'app_order' : 'app_pin_order'); - - $list = self::app_list($uid, false, [ $menu ]); - if ($list) { - foreach ($list as $li) { - $papp = self::app_encode($li); - $syslist[] = $papp; - } - } - self::translate_system_apps($syslist); - - usort($syslist,'self::app_name_compare'); - - $syslist = self::app_order($uid,$syslist,$menu); - - if (! $syslist) { - return; - } - - $newlist = []; - - foreach ($syslist as $k => $li) { - if ($li['guid'] === $guid) { - $position = $k; - break; - } - } - if (! $position) { - return; - } - $dest_position = $position - 1; - $saved = $syslist[$dest_position]; - $syslist[$dest_position] = $syslist[$position]; - $syslist[$position] = $saved; - - $narr = []; - foreach ($syslist as $x) { - $narr[] = $x['name']; - } - - set_pconfig($uid,'system',$conf,implode(',',$narr)); - - } - - static function movedown($uid,$guid,$menu) { - $syslist = []; - - $conf = (($menu === 'nav_featured_app') ? 'app_order' : 'app_pin_order'); - - $list = self::app_list($uid, false, [ $menu ]); - if ($list) { - foreach ($list as $li) { - $papp = self::app_encode($li); - $syslist[] = $papp; - } - } - self::translate_system_apps($syslist); - - usort($syslist,'self::app_name_compare'); - - $syslist = self::app_order($uid,$syslist,$menu); - - if (! $syslist) { - return; - } - - $newlist = []; - - foreach ($syslist as $k => $li) { - if ($li['guid'] === $guid) { - $position = $k; - break; - } - } - if ($position >= count($syslist) - 1) { - return; - } - $dest_position = $position + 1; - $saved = $syslist[$dest_position]; - $syslist[$dest_position] = $syslist[$position]; - $syslist[$position] = $saved; - - $narr = []; - foreach ($syslist as $x) { - $narr[] = $x['name']; - } - - set_pconfig($uid,'system',$conf,implode(',',$narr)); - - } - - static public function app_decode($s) { - $x = base64_decode(str_replace(array('
',"\r","\n",' '),array('','','',''),$s)); - return json_decode($x,true); - } - - - static public function app_macros($uid,&$arr) { - - if (! intval($uid)) { - return; - } - - $baseurl = z_root(); - $channel = channelx_by_n($uid); - $address = (($channel) ? $channel['channel_address'] : ''); - - // future expansion - - $observer = App::get_observer(); - - $arr['url'] = str_replace(array('$baseurl','$nick'),array($baseurl,$address),$arr['url']); - $arr['photo'] = str_replace(array('$baseurl','$nick'),array($baseurl,$address),$arr['photo']); - - } - - - static public function app_store($arr) { - - // logger('app_store: ' . print_r($arr,true)); - - $darray = []; - $ret = [ 'success' => false ]; - - $sys = get_sys_channel(); - - self::app_macros($arr['uid'],$arr); - - $darray['app_url'] = ((x($arr,'url')) ? $arr['url'] : ''); - $darray['app_channel'] = ((x($arr,'uid')) ? $arr['uid'] : 0); - - if (! $darray['app_url']) { - return $ret; - } - - if ((! $arr['uid']) && (! $arr['author'])) { - $arr['author'] = $sys['channel_hash']; - } - - if ($arr['photo'] && (strpos($arr['photo'],'icon:') === false) && (strpos($arr['photo'],z_root()) === false)) { - $x = import_remote_xchan_photo(str_replace('$baseurl',z_root(),$arr['photo']),get_observer_hash(),true); - if ((! $x) || ($x[4])) { - // $x[4] = true indicates storage failure of our cached/resized copy. If this failed, just keep the original url. - $arr['photo'] = $x[1]; - } - } - - - $darray['app_id'] = ((x($arr,'guid')) ? $arr['guid'] : random_string(). '.' . \App::get_hostname()); - $darray['app_sig'] = ((x($arr,'sig')) ? $arr['sig'] : ''); - $darray['app_author'] = ((x($arr,'author')) ? $arr['author'] : get_observer_hash()); - $darray['app_name'] = ((x($arr,'name')) ? escape_tags($arr['name']) : t('Unknown')); - $darray['app_desc'] = ((x($arr,'desc')) ? escape_tags($arr['desc']) : ''); - $darray['app_photo'] = ((x($arr,'photo')) ? $arr['photo'] : z_root() . '/' . get_default_profile_photo(80)); - $darray['app_version'] = ((x($arr,'version')) ? escape_tags($arr['version']) : ''); - $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(trim($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); - $darray['app_options'] = ((x($arr,'options')) ? intval($arr['options']) : 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_plugin, app_deleted, app_options ) values ( '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, '%s', '%s', '%s', '%s', '%s', '%s', %d, '%s', %d, %d )", - dbesc($darray['app_id']), - dbesc($darray['app_sig']), - dbesc($darray['app_author']), - dbesc($darray['app_name']), - dbesc($darray['app_desc']), - dbesc($darray['app_url']), - dbesc($darray['app_photo']), - dbesc($darray['app_version']), - intval($darray['app_channel']), - dbesc($darray['app_addr']), - dbesc($darray['app_price']), - dbesc($darray['app_page']), - dbesc($darray['app_requires']), - dbesc($created), - dbesc($created), - intval($darray['app_system']), - dbesc($darray['app_plugin']), - intval($darray['app_deleted']), - intval($darray['app_options']) - ); - - if ($r) { - $ret['success'] = true; - $ret['app_id'] = $darray['app_id']; - } - - if ($arr['categories']) { - $x = q("select id from app where app_id = '%s' and app_channel = %d limit 1", - dbesc($darray['app_id']), - intval($darray['app_channel']) - ); - $y = explode(',',$arr['categories']); - if ($y) { - foreach ($y as $t) { - $t = trim($t); - if ($t) { - store_item_tag($darray['app_channel'],$x[0]['id'],TERM_OBJ_APP,TERM_CATEGORY,escape_tags($t),escape_tags(z_root() . '/apps/?f=&cat=' . escape_tags($t))); - } + if ($check) { + $app['deleted'] = intval($check[0]['app_deleted']); } } + $x = self::app_update($app); + } else { + $x = self::app_store($app); + } + + if ($x['success']) { + $r = q( + "select * from app where app_id = '%s' and app_channel = %d limit 1", + dbesc($x['app_id']), + intval($uid) + ); + if ($r) { + if (($app['uid']) && (!$r[0]['app_system'])) { + if ($app['categories'] && (!$app['term'])) { + $r[0]['term'] = q( + "select * from term where otype = %d and oid = %d", + intval(TERM_OBJ_APP), + intval($r[0]['id']) + ); + if (intval($r[0]['app_system'])) { + Libsync::build_sync_packet($uid, array('sysapp' => $r[0])); + } else { + Libsync::build_sync_packet($uid, array('app' => $r[0])); + } + } + } + } + return $x['app_id']; + } + return false; + } + + + public static function can_delete($uid, $app) + { + if (!$uid) { + return false; + } + + $base_apps = self::get_base_apps(); + if ($base_apps) { + foreach ($base_apps as $b) { + if ($app['guid'] === hash('whirlpool', $b)) { + return false; + } + } + } + return true; + } + + + public static function app_destroy($uid, $app) + { + + if (! $uid) { + return; } - return $ret; - } + // permit the system channel to delete system apps (app_channel = 0). + // We will use targe_uid where needed to access the correct app record + + $target_uid = ((is_sys_channel($uid)) ? 0 : $uid); + + if ($uid && $app['guid']) { + $x = q( + "select * from app where app_id = '%s' and app_channel = %d limit 1", + dbesc($app['guid']), + intval($target_uid) + ); + if ($x) { + if (!intval($x[0]['app_deleted'])) { + $x[0]['app_deleted'] = 1; + if (self::can_delete($target_uid, $app)) { + $r = q( + "delete from app where app_id = '%s' and app_channel = %d", + dbesc($app['guid']), + intval($uid) + ); + q( + "delete from term where otype = %d and oid = %d", + intval(TERM_OBJ_APP), + intval($x[0]['id']) + ); + call_hooks('app_destroy', $x[0]); + } else { + $r = q( + "update app set app_deleted = 1 where app_id = '%s' and app_channel = %d", + dbesc($app['guid']), + intval($target_uid) + ); + } + if (intval($x[0]['app_system'])) { + Libsync::build_sync_packet($uid, array('sysapp' => $x)); + } else { + Libsync::build_sync_packet($uid, array('app' => $x)); + } + } else { + self::app_undestroy($uid, $app); + } + } + } + } + + public static 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) + ); + } + } + } + } + + public static function app_feature($uid, $app, $term) + { + $r = q( + "select id from app where app_id = '%s' and app_channel = %d limit 1", + dbesc($app['guid']), + intval($uid) + ); + + $x = q( + "select * from term where otype = %d and oid = %d and term = '%s' limit 1", + intval(TERM_OBJ_APP), + intval($r[0]['id']), + dbesc($term) + ); + + if ($x) { + q( + "delete from term where otype = %d and oid = %d and term = '%s'", + intval(TERM_OBJ_APP), + intval($x[0]['oid']), + dbesc($term) + ); + } else { + store_item_tag($uid, $r[0]['id'], TERM_OBJ_APP, TERM_CATEGORY, $term, escape_tags(z_root() . '/apps/?f=&cat=' . $term)); + } + } + + public static function app_installed($uid, $app, $bypass_filter = false) + { + + $r = q( + "select id from app where app_id = '%s' and app_channel = %d limit 1", + dbesc((array_key_exists('guid', $app)) ? $app['guid'] : ''), + intval($uid) + ); + if (!$bypass_filter) { + $filter_arr = [ + 'uid' => $uid, + 'app' => $app, + 'installed' => $r + ]; + call_hooks('app_installed_filter', $filter_arr); + $r = $filter_arr['installed']; + } + + return (($r) ? true : false); + } + + public static function addon_app_installed($uid, $app, $bypass_filter = false) + { + + $r = q( + "select id from app where app_plugin = '%s' and app_channel = %d limit 1", + dbesc($app), + intval($uid) + ); + if (!$bypass_filter) { + $filter_arr = [ + 'uid' => $uid, + 'app' => $app, + 'installed' => $r + ]; + call_hooks('addon_app_installed_filter', $filter_arr); + $r = $filter_arr['installed']; + } + + return (($r) ? true : false); + } + + public static function system_app_installed($uid, $app, $bypass_filter = false) + { + + $r = q( + "select id from app where app_id = '%s' and app_channel = %d and app_deleted = 0 limit 1", + dbesc(hash('whirlpool', $app)), + intval($uid) + ); + if (!$bypass_filter) { + $filter_arr = [ + 'uid' => $uid, + 'app' => $app, + 'installed' => $r + ]; + call_hooks('system_app_installed_filter', $filter_arr); + $r = $filter_arr['installed']; + } + + return (($r) ? true : false); + } + + public static function app_list($uid, $deleted = false, $cats = []) + { + if ($deleted) { + $sql_extra = ""; + } else { + $sql_extra = " and app_deleted = 0 "; + } + if ($cats) { + $cat_sql_extra = " and ( "; + + foreach ($cats as $cat) { + if (strpos($cat_sql_extra, 'term')) { + $cat_sql_extra .= "or "; + } + + $cat_sql_extra .= "term = '" . dbesc($cat) . "' "; + } + + $cat_sql_extra .= ") "; + + $r = q( + "select oid from term where otype = %d $cat_sql_extra", + intval(TERM_OBJ_APP) + ); + if (!$r) { + return $r; + } + $sql_extra .= " and app.id in ( " . array_elm_to_str($r, 'oid') . ') '; + } + + $r = q( + "select * from app where app_channel = %d $sql_extra order by app_name asc", + intval($uid) + ); + + if ($r) { + $hookinfo = ['uid' => $uid, 'deleted' => $deleted, 'cats' => $cats, 'apps' => $r]; + call_hooks('app_list', $hookinfo); + $r = $hookinfo['apps']; + for ($x = 0; $x < count($r); $x++) { + if (!$r[$x]['app_system']) { + $r[$x]['type'] = 'personal'; + } + $r[$x]['term'] = q( + "select * from term where otype = %d and oid = %d", + intval(TERM_OBJ_APP), + intval($r[$x]['id']) + ); + } + } + + return ($r); + } - static public function app_update($arr) { + public static function app_search_available($str) + { - // logger('app_update: ' . print_r($arr,true)); - $darray = []; - $ret = [ 'success' => false ]; + // not yet finished + // somehow need to account for translations - self::app_macros($arr['uid'],$arr); + $r = q( + "select * from app where app_channel = 0 $sql_extra order by app_name asc", + intval($uid) + ); + + return ($r); + } - $darray['app_url'] = ((x($arr,'url')) ? $arr['url'] : ''); - $darray['app_channel'] = ((x($arr,'uid')) ? $arr['uid'] : 0); - $darray['app_id'] = ((x($arr,'guid')) ? $arr['guid'] : 0); + public static function app_order($uid, $apps, $menu) + { - if ((! $darray['app_url']) || (! $darray['app_id'])) { - return $ret; - } + if (!$apps) { + return $apps; + } - if ($arr['photo'] && (strpos($arr['photo'],'icon:') === false) && (strpos($arr['photo'],z_root()) === false)) { - $x = import_remote_xchan_photo(str_replace('$baseurl',z_root(),$arr['photo']),get_observer_hash(),true); - if ((! $x) || ($x[4])) { - // $x[4] = true indicates storage failure of our cached/resized copy. If this failed, just keep the original url. - $arr['photo'] = $x[1]; - } - } + $conf = (($menu === 'nav_featured_app') ? 'app_order' : 'app_pin_order'); - $darray['app_sig'] = ((x($arr,'sig')) ? $arr['sig'] : ''); - $darray['app_author'] = ((x($arr,'author')) ? $arr['author'] : get_observer_hash()); - $darray['app_name'] = ((x($arr,'name')) ? escape_tags($arr['name']) : t('Unknown')); - $darray['app_desc'] = ((x($arr,'desc')) ? escape_tags($arr['desc']) : ''); - $darray['app_photo'] = ((x($arr,'photo')) ? $arr['photo'] : z_root() . '/' . get_default_profile_photo(80)); - $darray['app_version'] = ((x($arr,'version')) ? escape_tags($arr['version']) : ''); - $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(trim($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); - $darray['app_options'] = ((x($arr,'options')) ? intval($arr['options']) : 0); + $x = (($uid) ? get_pconfig($uid, 'system', $conf) : get_config('system', $conf)); + if (($x) && (!is_array($x))) { + $y = explode(',', $x); + $y = array_map('trim', $y); + $x = $y; + } - $edited = datetime_convert(); + if (!(is_array($x) && ($x))) { + return $apps; + } - $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, app_options = %d where app_id = '%s' and app_channel = %d", - dbesc($darray['app_sig']), - dbesc($darray['app_author']), - dbesc($darray['app_name']), - dbesc($darray['app_desc']), - dbesc($darray['app_url']), - dbesc($darray['app_photo']), - dbesc($darray['app_version']), - dbesc($darray['app_addr']), - dbesc($darray['app_price']), - dbesc($darray['app_page']), - dbesc($darray['app_requires']), - dbesc($edited), - intval($darray['app_system']), - dbesc($darray['app_plugin']), - intval($darray['app_deleted']), - intval($darray['app_options']), - dbesc($darray['app_id']), - intval($darray['app_channel']) - ); - if ($r) { - $ret['success'] = true; - $ret['app_id'] = $darray['app_id']; - } + $ret = []; + foreach ($x as $xx) { + $y = self::find_app_in_array($xx, $apps); + if ($y) { + $ret[] = $y; + } + } + foreach ($apps as $ap) { + if (!self::find_app_in_array($ap['name'], $ret)) { + $ret[] = $ap; + } + } + return $ret; + } - $x = q("select id from app where app_id = '%s' and app_channel = %d limit 1", - dbesc($darray['app_id']), - intval($darray['app_channel']) - ); + public static function find_app_in_array($name, $arr) + { + if (!$arr) { + return false; + } + foreach ($arr as $x) { + if ($x['name'] === $name) { + return $x; + } + } + return false; + } - // if updating an embed app and we don't have a 0 channel_id don't mess with any existing categories + public static function moveup($uid, $guid, $menu) + { + $syslist = []; - if (array_key_exists('embed',$arr) && intval($arr['embed']) && (intval($darray['app_channel']))) - return $ret; + $conf = (($menu === 'nav_featured_app') ? 'app_order' : 'app_pin_order'); - if ($x) { - q("delete from term where otype = %d and oid = %d", - intval(TERM_OBJ_APP), - intval($x[0]['id']) - ); - if (isset($arr['categories']) && $arr['categories']) { - $y = explode(',',$arr['categories']); - if ($y) { - foreach ($y as $t) { - $t = trim($t); - if ($t) { - store_item_tag($darray['app_channel'],$x[0]['id'],TERM_OBJ_APP,TERM_CATEGORY,escape_tags($t),escape_tags(z_root() . '/apps/?f=&cat=' . escape_tags($t))); - } - } - } - } - } + $list = self::app_list($uid, false, [$menu]); + if ($list) { + foreach ($list as $li) { + $papp = self::app_encode($li); + $syslist[] = $papp; + } + } + self::translate_system_apps($syslist); - return $ret; + usort($syslist, 'self::app_name_compare'); - } + $syslist = self::app_order($uid, $syslist, $menu); + + if (!$syslist) { + return; + } + + $newlist = []; + + foreach ($syslist as $k => $li) { + if ($li['guid'] === $guid) { + $position = $k; + break; + } + } + if (!$position) { + return; + } + $dest_position = $position - 1; + $saved = $syslist[$dest_position]; + $syslist[$dest_position] = $syslist[$position]; + $syslist[$position] = $saved; + + $narr = []; + foreach ($syslist as $x) { + $narr[] = $x['name']; + } + + set_pconfig($uid, 'system', $conf, implode(',', $narr)); + } + + public static function movedown($uid, $guid, $menu) + { + $syslist = []; + + $conf = (($menu === 'nav_featured_app') ? 'app_order' : 'app_pin_order'); + + $list = self::app_list($uid, false, [$menu]); + if ($list) { + foreach ($list as $li) { + $papp = self::app_encode($li); + $syslist[] = $papp; + } + } + self::translate_system_apps($syslist); + + usort($syslist, 'self::app_name_compare'); + + $syslist = self::app_order($uid, $syslist, $menu); + + if (!$syslist) { + return; + } + + $newlist = []; + + foreach ($syslist as $k => $li) { + if ($li['guid'] === $guid) { + $position = $k; + break; + } + } + if ($position >= count($syslist) - 1) { + return; + } + $dest_position = $position + 1; + $saved = $syslist[$dest_position]; + $syslist[$dest_position] = $syslist[$position]; + $syslist[$position] = $saved; + + $narr = []; + foreach ($syslist as $x) { + $narr[] = $x['name']; + } + + set_pconfig($uid, 'system', $conf, implode(',', $narr)); + } + + public static function app_decode($s) + { + $x = base64_decode(str_replace(array('
', "\r", "\n", ' '), array('', '', '', ''), $s)); + return json_decode($x, true); + } - static public function app_encode($app,$embed = false) { + public static function app_macros($uid, &$arr) + { - $ret = []; + if (!intval($uid)) { + return; + } - $ret['type'] = 'personal'; - - if (isset($app['app_id']) && $app['app_id']) { - $ret['guid'] = $app['app_id']; - } - if (isset($app['app_sig']) && $app['app_sig']) { - $ret['sig'] = $app['app_sig']; - } - if (isset($app['app_author']) && $app['app_author']) { - $ret['author'] = $app['app_author']; - } - if (isset($app['app_name']) && $app['app_name']) { - $ret['name'] = $app['app_name']; - } - if (isset($app['app_desc']) && $app['app_desc']) { - $ret['desc'] = $app['app_desc']; - } - if (isset($app['app_url']) && $app['app_url']) { - $ret['url'] = $app['app_url']; - } - if (isset($app['app_photo']) && $app['app_photo']) { - $ret['photo'] = $app['app_photo']; - } - if (isset($app['app_icon']) && $app['app_icon']) { - $ret['icon'] = $app['app_icon']; - } - if (isset($app['app_version']) && $app['app_version']) { - $ret['version'] = $app['app_version']; - } - if (isset($app['app_addr']) && $app['app_addr']) { - $ret['addr'] = $app['app_addr']; - } - if (isset($app['app_price']) && $app['app_price']) { - $ret['price'] = $app['app_price']; - } - if (isset($app['app_page']) && $app['app_page']) { - $ret['page'] = $app['app_page']; - } - if (isset($app['app_requires']) && $app['app_requires']) { - $ret['requires'] = $app['app_requires']; - } - if (isset($app['app_system']) && $app['app_system']) { - $ret['system'] = $app['app_system']; - } - if (isset($app['app_options']) && $app['app_options']) { - $ret['options'] = $app['app_options']; - } - if (isset($app['app_plugin']) && $app['app_plugin']) { - $ret['plugin'] = trim($app['app_plugin']); - } - if (isset($app['app_deleted']) && $app['app_deleted']) { - $ret['deleted'] = $app['app_deleted']; - } - if (isset($app['term']) && $app['term']) { - $ret['categories'] = array_elm_to_str($app['term'],'term'); - } + $baseurl = z_root(); + $channel = channelx_by_n($uid); + $address = (($channel) ? $channel['channel_address'] : ''); + + // future expansion + + $observer = App::get_observer(); + + $arr['url'] = str_replace(array('$baseurl', '$nick'), array($baseurl, $address), $arr['url']); + $arr['photo'] = str_replace(array('$baseurl', '$nick'), array($baseurl, $address), $arr['photo']); + } - if (! $embed) { - return $ret; - } - $ret['embed'] = true; + public static function app_store($arr) + { - if (array_key_exists('categories',$ret)) { - unset($ret['categories']); - } - - $j = json_encode($ret); - return '[app]' . chunk_split(base64_encode($j),72,"\n") . '[/app]'; + // logger('app_store: ' . print_r($arr,true)); - } + $darray = []; + $ret = ['success' => false]; + + $sys = get_sys_channel(); + + self::app_macros($arr['uid'], $arr); + + $darray['app_url'] = ((x($arr, 'url')) ? $arr['url'] : ''); + $darray['app_channel'] = ((x($arr, 'uid')) ? $arr['uid'] : 0); + + if (!$darray['app_url']) { + return $ret; + } + + if ((!$arr['uid']) && (!$arr['author'])) { + $arr['author'] = $sys['channel_hash']; + } + + if ($arr['photo'] && (strpos($arr['photo'], 'icon:') === false) && (strpos($arr['photo'], z_root()) === false)) { + $x = import_remote_xchan_photo(str_replace('$baseurl', z_root(), $arr['photo']), get_observer_hash(), true); + if ((!$x) || ($x[4])) { + // $x[4] = true indicates storage failure of our cached/resized copy. If this failed, just keep the original url. + $arr['photo'] = $x[1]; + } + } - static public function papp_encode($papp) { - return chunk_split(base64_encode(json_encode($papp)),72,"\n"); - } + $darray['app_id'] = ((x($arr, 'guid')) ? $arr['guid'] : random_string() . '.' . App::get_hostname()); + $darray['app_sig'] = ((x($arr, 'sig')) ? $arr['sig'] : ''); + $darray['app_author'] = ((x($arr, 'author')) ? $arr['author'] : get_observer_hash()); + $darray['app_name'] = ((x($arr, 'name')) ? escape_tags($arr['name']) : t('Unknown')); + $darray['app_desc'] = ((x($arr, 'desc')) ? escape_tags($arr['desc']) : ''); + $darray['app_photo'] = ((x($arr, 'photo')) ? $arr['photo'] : z_root() . '/' . get_default_profile_photo(80)); + $darray['app_version'] = ((x($arr, 'version')) ? escape_tags($arr['version']) : ''); + $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(trim($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); + $darray['app_options'] = ((x($arr, 'options')) ? intval($arr['options']) : 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_plugin, app_deleted, app_options ) values ( '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, '%s', '%s', '%s', '%s', '%s', '%s', %d, '%s', %d, %d )", + dbesc($darray['app_id']), + dbesc($darray['app_sig']), + dbesc($darray['app_author']), + dbesc($darray['app_name']), + dbesc($darray['app_desc']), + dbesc($darray['app_url']), + dbesc($darray['app_photo']), + dbesc($darray['app_version']), + intval($darray['app_channel']), + dbesc($darray['app_addr']), + dbesc($darray['app_price']), + dbesc($darray['app_page']), + dbesc($darray['app_requires']), + dbesc($created), + dbesc($created), + intval($darray['app_system']), + dbesc($darray['app_plugin']), + intval($darray['app_deleted']), + intval($darray['app_options']) + ); + + if ($r) { + $ret['success'] = true; + $ret['app_id'] = $darray['app_id']; + } + + if ($arr['categories']) { + $x = q( + "select id from app where app_id = '%s' and app_channel = %d limit 1", + dbesc($darray['app_id']), + intval($darray['app_channel']) + ); + $y = explode(',', $arr['categories']); + if ($y) { + foreach ($y as $t) { + $t = trim($t); + if ($t) { + store_item_tag($darray['app_channel'], $x[0]['id'], TERM_OBJ_APP, TERM_CATEGORY, escape_tags($t), escape_tags(z_root() . '/apps/?f=&cat=' . escape_tags($t))); + } + } + } + } + + return $ret; + } + + + public static function app_update($arr) + { + + // logger('app_update: ' . print_r($arr,true)); + $darray = []; + $ret = ['success' => false]; + + self::app_macros($arr['uid'], $arr); + + + $darray['app_url'] = ((x($arr, 'url')) ? $arr['url'] : ''); + $darray['app_channel'] = ((x($arr, 'uid')) ? $arr['uid'] : 0); + $darray['app_id'] = ((x($arr, 'guid')) ? $arr['guid'] : 0); + + if ((!$darray['app_url']) || (!$darray['app_id'])) { + return $ret; + } + + if ($arr['photo'] && (strpos($arr['photo'], 'icon:') === false) && (strpos($arr['photo'], z_root()) === false)) { + $x = import_remote_xchan_photo(str_replace('$baseurl', z_root(), $arr['photo']), get_observer_hash(), true); + if ((!$x) || ($x[4])) { + // $x[4] = true indicates storage failure of our cached/resized copy. If this failed, just keep the original url. + $arr['photo'] = $x[1]; + } + } + + $darray['app_sig'] = ((x($arr, 'sig')) ? $arr['sig'] : ''); + $darray['app_author'] = ((x($arr, 'author')) ? $arr['author'] : get_observer_hash()); + $darray['app_name'] = ((x($arr, 'name')) ? escape_tags($arr['name']) : t('Unknown')); + $darray['app_desc'] = ((x($arr, 'desc')) ? escape_tags($arr['desc']) : ''); + $darray['app_photo'] = ((x($arr, 'photo')) ? $arr['photo'] : z_root() . '/' . get_default_profile_photo(80)); + $darray['app_version'] = ((x($arr, 'version')) ? escape_tags($arr['version']) : ''); + $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(trim($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); + $darray['app_options'] = ((x($arr, 'options')) ? intval($arr['options']) : 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_plugin = '%s', app_deleted = %d, app_options = %d where app_id = '%s' and app_channel = %d", + dbesc($darray['app_sig']), + dbesc($darray['app_author']), + dbesc($darray['app_name']), + dbesc($darray['app_desc']), + dbesc($darray['app_url']), + dbesc($darray['app_photo']), + dbesc($darray['app_version']), + dbesc($darray['app_addr']), + dbesc($darray['app_price']), + dbesc($darray['app_page']), + dbesc($darray['app_requires']), + dbesc($edited), + intval($darray['app_system']), + dbesc($darray['app_plugin']), + intval($darray['app_deleted']), + intval($darray['app_options']), + dbesc($darray['app_id']), + intval($darray['app_channel']) + ); + if ($r) { + $ret['success'] = true; + $ret['app_id'] = $darray['app_id']; + } + + $x = q( + "select id from app where app_id = '%s' and app_channel = %d limit 1", + dbesc($darray['app_id']), + intval($darray['app_channel']) + ); + + // if updating an embed app and we don't have a 0 channel_id don't mess with any existing categories + + if (array_key_exists('embed', $arr) && intval($arr['embed']) && (intval($darray['app_channel']))) { + return $ret; + } + + if ($x) { + q( + "delete from term where otype = %d and oid = %d", + intval(TERM_OBJ_APP), + intval($x[0]['id']) + ); + if (isset($arr['categories']) && $arr['categories']) { + $y = explode(',', $arr['categories']); + if ($y) { + foreach ($y as $t) { + $t = trim($t); + if ($t) { + store_item_tag($darray['app_channel'], $x[0]['id'], TERM_OBJ_APP, TERM_CATEGORY, escape_tags($t), escape_tags(z_root() . '/apps/?f=&cat=' . escape_tags($t))); + } + } + } + } + } + + return $ret; + } + + + public static function app_encode($app, $embed = false) + { + + $ret = []; + + $ret['type'] = 'personal'; + + if (isset($app['app_id']) && $app['app_id']) { + $ret['guid'] = $app['app_id']; + } + if (isset($app['app_sig']) && $app['app_sig']) { + $ret['sig'] = $app['app_sig']; + } + if (isset($app['app_author']) && $app['app_author']) { + $ret['author'] = $app['app_author']; + } + if (isset($app['app_name']) && $app['app_name']) { + $ret['name'] = $app['app_name']; + } + if (isset($app['app_desc']) && $app['app_desc']) { + $ret['desc'] = $app['app_desc']; + } + if (isset($app['app_url']) && $app['app_url']) { + $ret['url'] = $app['app_url']; + } + if (isset($app['app_photo']) && $app['app_photo']) { + $ret['photo'] = $app['app_photo']; + } + if (isset($app['app_icon']) && $app['app_icon']) { + $ret['icon'] = $app['app_icon']; + } + if (isset($app['app_version']) && $app['app_version']) { + $ret['version'] = $app['app_version']; + } + if (isset($app['app_addr']) && $app['app_addr']) { + $ret['addr'] = $app['app_addr']; + } + if (isset($app['app_price']) && $app['app_price']) { + $ret['price'] = $app['app_price']; + } + if (isset($app['app_page']) && $app['app_page']) { + $ret['page'] = $app['app_page']; + } + if (isset($app['app_requires']) && $app['app_requires']) { + $ret['requires'] = $app['app_requires']; + } + if (isset($app['app_system']) && $app['app_system']) { + $ret['system'] = $app['app_system']; + } + if (isset($app['app_options']) && $app['app_options']) { + $ret['options'] = $app['app_options']; + } + if (isset($app['app_plugin']) && $app['app_plugin']) { + $ret['plugin'] = trim($app['app_plugin']); + } + if (isset($app['app_deleted']) && $app['app_deleted']) { + $ret['deleted'] = $app['app_deleted']; + } + if (isset($app['term']) && $app['term']) { + $ret['categories'] = array_elm_to_str($app['term'], 'term'); + } + + + if (!$embed) { + return $ret; + } + $ret['embed'] = true; + + if (array_key_exists('categories', $ret)) { + unset($ret['categories']); + } + + $j = json_encode($ret); + return '[app]' . chunk_split(base64_encode($j), 72, "\n") . '[/app]'; + } + + + public static function papp_encode($papp) + { + return chunk_split(base64_encode(json_encode($papp)), 72, "\n"); + } } - - diff --git a/Zotlabs/Lib/Cache.php b/Zotlabs/Lib/Cache.php index cea075659..f3c227261 100644 --- a/Zotlabs/Lib/Cache.php +++ b/Zotlabs/Lib/Cache.php @@ -1,51 +1,63 @@ - false); + $ret = array('success' => false); - $name = trim($arr['name']); - if(! $name) { - $ret['message'] = t('Missing room name'); - return $ret; - } + $name = trim($arr['name']); + if (!$name) { + $ret['message'] = t('Missing room name'); + return $ret; + } - $r = q("select cr_id from chatroom where cr_uid = %d and cr_name = '%s' limit 1", - intval($channel['channel_id']), - dbesc($name) - ); - if($r) { - $ret['message'] = t('Duplicate room name'); - return $ret; - } + $r = q( + "select cr_id from chatroom where cr_uid = %d and cr_name = '%s' limit 1", + intval($channel['channel_id']), + dbesc($name) + ); + if ($r) { + $ret['message'] = t('Duplicate room name'); + return $ret; + } - $r = q("select count(cr_id) as total from chatroom where cr_aid = %d", - intval($channel['channel_account_id']) - ); - if($r) - $limit = service_class_fetch($channel['channel_id'], 'chatrooms'); + $r = q( + "select count(cr_id) as total from chatroom where cr_aid = %d", + intval($channel['channel_account_id']) + ); + if ($r) { + $limit = service_class_fetch($channel['channel_id'], 'chatrooms'); + } - if(($r) && ($limit !== false) && ($r[0]['total'] >= $limit)) { - $ret['message'] = upgrade_message(); - return $ret; - } + if (($r) && ($limit !== false) && ($r[0]['total'] >= $limit)) { + $ret['message'] = upgrade_message(); + return $ret; + } - if(! array_key_exists('expire', $arr)) - $arr['expire'] = 120; // minutes, e.g. 2 hours + if (!array_key_exists('expire', $arr)) { + $arr['expire'] = 120; // minutes, e.g. 2 hours + } - $created = datetime_convert(); + $created = datetime_convert(); - $x = q("insert into chatroom ( cr_aid, cr_uid, cr_name, cr_created, cr_edited, cr_expire, allow_cid, allow_gid, deny_cid, deny_gid ) + $x = q( + "insert into chatroom ( cr_aid, cr_uid, cr_name, cr_created, cr_edited, cr_expire, allow_cid, allow_gid, deny_cid, deny_gid ) values ( %d, %d , '%s', '%s', '%s', %d, '%s', '%s', '%s', '%s' ) ", - intval($channel['channel_account_id']), - intval($channel['channel_id']), - dbesc($name), - dbesc($created), - dbesc($created), - intval($arr['expire']), - dbesc($arr['allow_cid']), - dbesc($arr['allow_gid']), - dbesc($arr['deny_cid']), - dbesc($arr['deny_gid']) - ); + intval($channel['channel_account_id']), + intval($channel['channel_id']), + dbesc($name), + dbesc($created), + dbesc($created), + intval($arr['expire']), + dbesc($arr['allow_cid']), + dbesc($arr['allow_gid']), + dbesc($arr['deny_cid']), + dbesc($arr['deny_gid']) + ); - if($x) - $ret['success'] = true; + if ($x) { + $ret['success'] = true; + } - return $ret; - } + return $ret; + } - static public function destroy($channel,$arr) { + public static function destroy($channel, $arr) + { - $ret = array('success' => false); + $ret = array('success' => false); - if(intval($arr['cr_id'])) - $sql_extra = " and cr_id = " . intval($arr['cr_id']) . " "; - elseif(trim($arr['cr_name'])) - $sql_extra = " and cr_name = '" . protect_sprintf(dbesc(trim($arr['cr_name']))) . "' "; - else { - $ret['message'] = t('Invalid room specifier.'); - return $ret; - } + if (intval($arr['cr_id'])) { + $sql_extra = " and cr_id = " . intval($arr['cr_id']) . " "; + } elseif (trim($arr['cr_name'])) { + $sql_extra = " and cr_name = '" . protect_sprintf(dbesc(trim($arr['cr_name']))) . "' "; + } else { + $ret['message'] = t('Invalid room specifier.'); + return $ret; + } - $r = q("select * from chatroom where cr_uid = %d $sql_extra limit 1", - intval($channel['channel_id']) - ); - if(! $r) { - $ret['message'] = t('Invalid room specifier.'); - return $ret; - } + $r = q( + "select * from chatroom where cr_uid = %d $sql_extra limit 1", + intval($channel['channel_id']) + ); + if (!$r) { + $ret['message'] = t('Invalid room specifier.'); + return $ret; + } - Libsync::build_sync_packet($channel['channel_id'],array('chatroom' => $r)); + Libsync::build_sync_packet($channel['channel_id'], array('chatroom' => $r)); - q("delete from chatroom where cr_id = %d", - intval($r[0]['cr_id']) - ); - if($r[0]['cr_id']) { - q("delete from chatpresence where cp_room = %d", - intval($r[0]['cr_id']) - ); - q("delete from chat where chat_room = %d", - intval($r[0]['cr_id']) - ); - } + q( + "delete from chatroom where cr_id = %d", + intval($r[0]['cr_id']) + ); + if ($r[0]['cr_id']) { + q( + "delete from chatpresence where cp_room = %d", + intval($r[0]['cr_id']) + ); + q( + "delete from chat where chat_room = %d", + intval($r[0]['cr_id']) + ); + } - $ret['success'] = true; - return $ret; - } + $ret['success'] = true; + return $ret; + } - static public function enter($observer_xchan, $room_id, $status, $client) { + public static function enter($observer_xchan, $room_id, $status, $client) + { - if(! $room_id || ! $observer_xchan) - return; + if (!$room_id || !$observer_xchan) { + return; + } - $r = q("select * from chatroom where cr_id = %d limit 1", - intval($room_id) - ); - if(! $r) { - notice( t('Room not found.') . EOL); - return false; - } - require_once('include/security.php'); - $sql_extra = permissions_sql($r[0]['cr_uid']); + $r = q( + "select * from chatroom where cr_id = %d limit 1", + intval($room_id) + ); + if (!$r) { + notice(t('Room not found.') . EOL); + return false; + } + require_once('include/security.php'); + $sql_extra = permissions_sql($r[0]['cr_uid']); - $x = q("select * from chatroom where cr_id = %d and cr_uid = %d $sql_extra limit 1", - intval($room_id), - intval($r[0]['cr_uid']) - ); - if(! $x) { - notice( t('Permission denied.') . EOL); - return false; - } + $x = q( + "select * from chatroom where cr_id = %d and cr_uid = %d $sql_extra limit 1", + intval($room_id), + intval($r[0]['cr_uid']) + ); + if (!$x) { + notice(t('Permission denied.') . EOL); + return false; + } - $limit = service_class_fetch($r[0]['cr_uid'], 'chatters_inroom'); - if($limit !== false) { - $y = q("select count(*) as total from chatpresence where cp_room = %d", - intval($room_id) - ); - if($y && $y[0]['total'] > $limit) { - notice( t('Room is full') . EOL); - return false; - } - } + $limit = service_class_fetch($r[0]['cr_uid'], 'chatters_inroom'); + if ($limit !== false) { + $y = q( + "select count(*) as total from chatpresence where cp_room = %d", + intval($room_id) + ); + if ($y && $y[0]['total'] > $limit) { + notice(t('Room is full') . EOL); + return false; + } + } - if(intval($x[0]['cr_expire'])) { - $r = q("delete from chat where created < %s - INTERVAL %s and chat_room = %d", - db_utcnow(), - db_quoteinterval( intval($x[0]['cr_expire']) . ' MINUTE' ), - intval($x[0]['cr_id']) - ); - } + if (intval($x[0]['cr_expire'])) { + $r = q( + "delete from chat where created < %s - INTERVAL %s and chat_room = %d", + db_utcnow(), + db_quoteinterval(intval($x[0]['cr_expire']) . ' MINUTE'), + intval($x[0]['cr_id']) + ); + } - $r = q("select * from chatpresence where cp_xchan = '%s' and cp_room = %d limit 1", - dbesc($observer_xchan), - intval($room_id) - ); - if($r) { - q("update chatpresence set cp_last = '%s' where cp_id = %d and cp_client = '%s'", - dbesc(datetime_convert()), - intval($r[0]['cp_id']), - dbesc($client) - ); - return true; - } + $r = q( + "select * from chatpresence where cp_xchan = '%s' and cp_room = %d limit 1", + dbesc($observer_xchan), + intval($room_id) + ); + if ($r) { + q( + "update chatpresence set cp_last = '%s' where cp_id = %d and cp_client = '%s'", + dbesc(datetime_convert()), + intval($r[0]['cp_id']), + dbesc($client) + ); + return true; + } - $r = q("insert into chatpresence ( cp_room, cp_xchan, cp_last, cp_status, cp_client ) + $r = q( + "insert into chatpresence ( cp_room, cp_xchan, cp_last, cp_status, cp_client ) values ( %d, '%s', '%s', '%s', '%s' )", - intval($room_id), - dbesc($observer_xchan), - dbesc(datetime_convert()), - dbesc($status), - dbesc($client) - ); + intval($room_id), + dbesc($observer_xchan), + dbesc(datetime_convert()), + dbesc($status), + dbesc($client) + ); - return $r; - } + return $r; + } - function leave($observer_xchan, $room_id, $client) { - if(! $room_id || ! $observer_xchan) - return; + public function leave($observer_xchan, $room_id, $client) + { + if (!$room_id || !$observer_xchan) { + return; + } - $r = q("select * from chatpresence where cp_xchan = '%s' and cp_room = %d and cp_client = '%s' limit 1", - dbesc($observer_xchan), - intval($room_id), - dbesc($client) - ); - if($r) { - q("delete from chatpresence where cp_id = %d", - intval($r[0]['cp_id']) - ); - } + $r = q( + "select * from chatpresence where cp_xchan = '%s' and cp_room = %d and cp_client = '%s' limit 1", + dbesc($observer_xchan), + intval($room_id), + dbesc($client) + ); + if ($r) { + q( + "delete from chatpresence where cp_id = %d", + intval($r[0]['cp_id']) + ); + } - return true; - } + return true; + } - static public function roomlist($uid) { - require_once('include/security.php'); - $sql_extra = permissions_sql($uid); + public static function roomlist($uid) + { + require_once('include/security.php'); + $sql_extra = permissions_sql($uid); - $r = q("select allow_cid, allow_gid, deny_cid, deny_gid, cr_name, cr_expire, cr_id, count(cp_id) as cr_inroom from chatroom left join chatpresence on cr_id = cp_room where cr_uid = %d $sql_extra group by cr_name, cr_id order by cr_name", - intval($uid) - ); + $r = q( + "select allow_cid, allow_gid, deny_cid, deny_gid, cr_name, cr_expire, cr_id, count(cp_id) as cr_inroom from chatroom left join chatpresence on cr_id = cp_room where cr_uid = %d $sql_extra group by cr_name, cr_id order by cr_name", + intval($uid) + ); - return $r; - } + return $r; + } - static public function list_count($uid) { - require_once('include/security.php'); - $sql_extra = permissions_sql($uid); + public static function list_count($uid) + { + require_once('include/security.php'); + $sql_extra = permissions_sql($uid); - $r = q("select count(*) as total from chatroom where cr_uid = %d $sql_extra", - intval($uid) - ); + $r = q( + "select count(*) as total from chatroom where cr_uid = %d $sql_extra", + intval($uid) + ); - return $r[0]['total']; - } + return $r[0]['total']; + } - /** - * @brief Create a chat message via API. - * - * It is the caller's responsibility to enter the room. - * - * @param int $uid - * @param int $room_id - * @param string $xchan - * @param string $text - * @return array - */ - static public function message($uid, $room_id, $xchan, $text) { + /** + * @brief Create a chat message via API. + * + * It is the caller's responsibility to enter the room. + * + * @param int $uid + * @param int $room_id + * @param string $xchan + * @param string $text + * @return array + */ + public static function message($uid, $room_id, $xchan, $text) + { - $ret = array('success' => false); + $ret = array('success' => false); - if(! $text) - return; + if (!$text) { + return; + } - $sql_extra = permissions_sql($uid); + $sql_extra = permissions_sql($uid); - $r = q("select * from chatroom where cr_uid = %d and cr_id = %d $sql_extra", - intval($uid), - intval($room_id) - ); - if(! $r) - return $ret; + $r = q( + "select * from chatroom where cr_uid = %d and cr_id = %d $sql_extra", + intval($uid), + intval($room_id) + ); + if (!$r) { + return $ret; + } - $arr = [ - 'chat_room' => $room_id, - 'chat_xchan' => $xchan, - 'chat_text' => $text - ]; - /** - * @hooks chat_message - * Called to create a chat message. - * * \e int \b chat_room - * * \e string \b chat_xchan - * * \e string \b chat_text - */ - call_hooks('chat_message', $arr); + $arr = [ + 'chat_room' => $room_id, + 'chat_xchan' => $xchan, + 'chat_text' => $text + ]; + /** + * @hooks chat_message + * Called to create a chat message. + * * \e int \b chat_room + * * \e string \b chat_xchan + * * \e string \b chat_text + */ + call_hooks('chat_message', $arr); - $x = q("insert into chat ( chat_room, chat_xchan, created, chat_text ) + $x = q( + "insert into chat ( chat_room, chat_xchan, created, chat_text ) values( %d, '%s', '%s', '%s' )", - intval($room_id), - dbesc($xchan), - dbesc(datetime_convert()), - dbesc(str_rot47(base64url_encode($arr['chat_text']))) - ); + intval($room_id), + dbesc($xchan), + dbesc(datetime_convert()), + dbesc(str_rot47(base64url_encode($arr['chat_text']))) + ); - $ret['success'] = true; - return $ret; - } + $ret['success'] = true; + return $ret; + } } diff --git a/Zotlabs/Lib/Config.php b/Zotlabs/Lib/Config.php index 380be5b8b..6aba9b319 100644 --- a/Zotlabs/Lib/Config.php +++ b/Zotlabs/Lib/Config.php @@ -2,161 +2,174 @@ namespace Zotlabs\Lib; +use App; -class Config { +class Config +{ - /** - * @brief Loads the hub's configuration from database to a cached storage. - * - * Retrieve a category ($family) of config variables from database to a cached - * storage in the global App::$config[$family]. - * - * @param string $family - * The category of the configuration value - */ - static public function Load($family) { - if(! array_key_exists($family, \App::$config)) - \App::$config[$family] = []; + /** + * @brief Loads the hub's configuration from database to a cached storage. + * + * Retrieve a category ($family) of config variables from database to a cached + * storage in the global App::$config[$family]. + * + * @param string $family + * The category of the configuration value + */ + public static function Load($family) + { + if (! array_key_exists($family, App::$config)) { + App::$config[$family] = []; + } - if(! array_key_exists('config_loaded', \App::$config[$family])) { - $r = q("SELECT * FROM config WHERE cat = '%s'", dbesc($family)); - if($r !== false) { - if($r) { - foreach($r as $rr) { - $k = $rr['k']; - \App::$config[$family][$k] = $rr['v']; - } - } - \App::$config[$family]['config_loaded'] = true; - } - } - } + if (! array_key_exists('config_loaded', App::$config[$family])) { + $r = q("SELECT * FROM config WHERE cat = '%s'", dbesc($family)); + if ($r !== false) { + if ($r) { + foreach ($r as $rr) { + $k = $rr['k']; + App::$config[$family][$k] = $rr['v']; + } + } + App::$config[$family]['config_loaded'] = true; + } + } + } - /** - * @brief Sets a configuration value for the hub. - * - * Stores a config value ($value) in the category ($family) under the key ($key). - * - * @param string $family - * The category of the configuration value - * @param string $key - * The configuration key to set - * @param mixed $value - * The value to store in the configuration - * @return mixed - * Return the set value, or false if the database update failed - */ - static public function Set($family, $key, $value) { - // manage array value - $dbvalue = ((is_array($value)) ? serialise($value) : $value); - $dbvalue = ((is_bool($dbvalue)) ? intval($dbvalue) : $dbvalue); + /** + * @brief Sets a configuration value for the hub. + * + * Stores a config value ($value) in the category ($family) under the key ($key). + * + * @param string $family + * The category of the configuration value + * @param string $key + * The configuration key to set + * @param mixed $value + * The value to store in the configuration + * @return mixed + * Return the set value, or false if the database update failed + */ + public static function Set($family, $key, $value) + { + // manage array value + $dbvalue = ((is_array($value)) ? serialise($value) : $value); + $dbvalue = ((is_bool($dbvalue)) ? intval($dbvalue) : $dbvalue); - if(self::Get($family, $key) === false || (! self::get_from_storage($family, $key))) { - $ret = q("INSERT INTO config ( cat, k, v ) VALUES ( '%s', '%s', '%s' ) ", - dbesc($family), - dbesc($key), - dbesc($dbvalue) - ); - if($ret) { - \App::$config[$family][$key] = $value; - $ret = $value; - } - return $ret; - } + if (self::Get($family, $key) === false || (! self::get_from_storage($family, $key))) { + $ret = q( + "INSERT INTO config ( cat, k, v ) VALUES ( '%s', '%s', '%s' ) ", + dbesc($family), + dbesc($key), + dbesc($dbvalue) + ); + if ($ret) { + App::$config[$family][$key] = $value; + $ret = $value; + } + return $ret; + } - $ret = q("UPDATE config SET v = '%s' WHERE cat = '%s' AND k = '%s'", - dbesc($dbvalue), - dbesc($family), - dbesc($key) - ); + $ret = q( + "UPDATE config SET v = '%s' WHERE cat = '%s' AND k = '%s'", + dbesc($dbvalue), + dbesc($family), + dbesc($key) + ); - if($ret) { - \App::$config[$family][$key] = $value; - $ret = $value; - } + if ($ret) { + App::$config[$family][$key] = $value; + $ret = $value; + } - return $ret; - } + return $ret; + } - /** - * @brief Get a particular config variable given the category name ($family) - * and a key. - * - * Get a particular config variable from the given category ($family) and the - * $key from a cached storage in App::$config[$family]. If a key is found in the - * DB but does not exist in local config cache, pull it into the cache so we - * do not have to hit the DB again for this item. - * - * Returns false if not set. - * - * @param string $family - * The category of the configuration value - * @param string $key - * The configuration key to query - * @param string $default (optional) default false - * @return mixed Return value or false on error or if not set - */ - 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); + /** + * @brief Get a particular config variable given the category name ($family) + * and a key. + * + * Get a particular config variable from the given category ($family) and the + * $key from a cached storage in App::$config[$family]. If a key is found in the + * DB but does not exist in local config cache, pull it into the cache so we + * do not have to hit the DB again for this item. + * + * Returns false if not set. + * + * @param string $family + * The category of the configuration value + * @param string $key + * The configuration key to query + * @param string $default (optional) default false + * @return mixed Return value or false on error or if not set + */ + public static 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 $default; - } - return unserialise(\App::$config[$family][$key]); - } + if (array_key_exists('config_loaded', App::$config[$family])) { + if (! array_key_exists($key, App::$config[$family])) { + return $default; + } + return unserialise(App::$config[$family][$key]); + } - return $default; - } + return $default; + } - /** - * @brief Deletes the given key from the hub's configuration database. - * - * Removes the configured value from the stored cache in App::$config[$family] - * and removes it from the database. - * - * @param string $family - * The category of the configuration value - * @param string $key - * The configuration key to delete - * @return mixed - */ - static public function Delete($family, $key) { + /** + * @brief Deletes the given key from the hub's configuration database. + * + * Removes the configured value from the stored cache in App::$config[$family] + * and removes it from the database. + * + * @param string $family + * The category of the configuration value + * @param string $key + * The configuration key to delete + * @return mixed + */ + public static function Delete($family, $key) + { - $ret = false; + $ret = false; - if(array_key_exists($family, \App::$config) && array_key_exists($key, \App::$config[$family])) - unset(\App::$config[$family][$key]); + if (array_key_exists($family, App::$config) && array_key_exists($key, App::$config[$family])) { + unset(App::$config[$family][$key]); + } - $ret = q("DELETE FROM config WHERE cat = '%s' AND k = '%s'", - dbesc($family), - dbesc($key) - ); + $ret = q( + "DELETE FROM config WHERE cat = '%s' AND k = '%s'", + dbesc($family), + dbesc($key) + ); - return $ret; - } + return $ret; + } - /** - * @brief Returns a record directly from the database configuration storage. - * - * This function queries directly the database and bypasses the cached storage - * from get_config($family, $key). - * - * @param string $family - * The category of the configuration value - * @param string $key - * The configuration key to query - * @return mixed - */ - static private function get_from_storage($family,$key) { - $ret = q("SELECT * FROM config WHERE cat = '%s' AND k = '%s' LIMIT 1", - dbesc($family), - dbesc($key) - ); - - return $ret; - } + /** + * @brief Returns a record directly from the database configuration storage. + * + * This function queries directly the database and bypasses the cached storage + * from get_config($family, $key). + * + * @param string $family + * The category of the configuration value + * @param string $key + * The configuration key to query + * @return mixed + */ + private static function get_from_storage($family, $key) + { + $ret = q( + "SELECT * FROM config WHERE cat = '%s' AND k = '%s' LIMIT 1", + dbesc($family), + dbesc($key) + ); + return $ret; + } } diff --git a/Zotlabs/Lib/Connect.php b/Zotlabs/Lib/Connect.php index 863ec3e05..21bb47eba 100644 --- a/Zotlabs/Lib/Connect.php +++ b/Zotlabs/Lib/Connect.php @@ -1,4 +1,6 @@ - false, 'message' => '']; + + $my_perms = false; + $protocol = ''; + + $ap_allowed = get_config('system', 'activitypub', ACTIVITYPUB_ENABLED) && get_pconfig($uid, 'system', 'activitypub', ACTIVITYPUB_ENABLED); + + if (substr($url, 0, 1) === '[') { + $x = strpos($url, ']'); + if ($x) { + $protocol = substr($url, 1, $x - 1); + $url = substr($url, $x + 1); + } + } + + if (!check_siteallowed($url)) { + $result['message'] = t('Channel is blocked on this site.'); + return $result; + } + + if (!$url) { + $result['message'] = t('Channel location missing.'); + return $result; + } + + // check service class limits + + $r = q( + "select count(*) as total from abook where abook_channel = %d and abook_self = 0 ", + intval($uid) + ); + if ($r) { + $total_channels = $r[0]['total']; + } + + if (!service_class_allows($uid, 'total_channels', $total_channels)) { + $result['message'] = upgrade_message(); + return $result; + } + + $xchan_hash = ''; + $sql_options = (($protocol) ? " and xchan_network = '" . dbesc($protocol) . "' " : ''); + + $r = q( + "select * from xchan where ( xchan_hash = '%s' or xchan_url = '%s' or xchan_addr = '%s') $sql_options ", + dbesc($url), + dbesc($url), + dbesc($url) + ); + + if ($r) { + // reset results to the best record or the first if we don't have the best + // note: this returns a single record and not an array of records + + $r = Libzot::zot_record_preferred($r, 'xchan_network'); + + // ensure there's a valid hubloc for this xchan before proceeding - you cannot connect without it + + if (in_array($r['xchan_network'], ['nomad', 'zot6', 'activitypub'])) { + $h = q( + "select * from hubloc where hubloc_hash = '%s'", + dbesc($r['xchan_hash']) + ); + if (!$h) { + $r = null; + } + } + + // we may have nulled out this record so check again + + if ($r) { + // Check the site table to see if we should have a zot6 hubloc, + // If so, clear the xchan and start fresh + + if ($r['xchan_network'] === 'activitypub') { + $m = parse_url($r['xchan_hash']); + unset($m['path']); + $h = unparse_url($m); + $s = q( + "select * from site where site_url = '%s'", + dbesc($h) + ); + if (intval($s['site_type']) === SITE_TYPE_ZOT) { + logger('got zot - ignore activitypub entry'); + $r = null; + } + } + } + } -class Connect { + $singleton = false; - /** - * Takes a $channel and a $url/handle and adds a new connection - * - * Returns array - * $return['success'] boolean true if successful - * $return['abook'] Address book entry joined with xchan if successful - * $return['message'] error text if success is false. - * - * This function does NOT send sync packets to clones. The caller is responsible for doing this - */ + if (!$r) { + // not in cache - try discovery - static function connect($channel, $url, $sub_channel = false) { + $wf = discover_by_webbie($url, $protocol, false); - $uid = $channel['channel_id']; + if (!$wf) { + $result['message'] = t('Remote channel or protocol unavailable.'); + return $result; + } + } - if (strpos($url,'@') === false && strpos($url,'/') === false) { - $url = $url . '@' . App::get_hostname(); - } + if ($wf) { + // something was discovered - find the record which was just created. - $result = [ 'success' => false, 'message' => '' ]; + $r = q( + "select * from xchan where ( xchan_hash = '%s' or xchan_url = '%s' or xchan_addr = '%s' ) $sql_options", + dbesc(($wf) ? $wf : $url), + dbesc($url), + dbesc($url) + ); - $my_perms = false; - $protocol = ''; + // convert to a single record (once again preferring a zot solution in the case of multiples) - $ap_allowed = get_config('system','activitypub', ACTIVITYPUB_ENABLED) && get_pconfig($uid,'system','activitypub', ACTIVITYPUB_ENABLED); + if ($r) { + $r = Libzot::zot_record_preferred($r, 'xchan_network'); + } + } - if (substr($url,0,1) === '[') { - $x = strpos($url,']'); - if ($x) { - $protocol = substr($url,1,$x-1); - $url = substr($url,$x+1); - } - } + // if discovery was a success or the channel was already cached we should have an xchan record in $r - if (! check_siteallowed($url)) { - $result['message'] = t('Channel is blocked on this site.'); - return $result; - } + if ($r) { + $xchan = $r; + $xchan_hash = $r['xchan_hash']; + $their_perms = EMPTY_STR; + } - if (! $url) { - $result['message'] = t('Channel location missing.'); - return $result; - } + // failure case - // check service class limits + if (!$xchan_hash) { + $result['message'] = t('Channel discovery failed.'); + logger('follow: ' . $result['message']); + return $result; + } - $r = q("select count(*) as total from abook where abook_channel = %d and abook_self = 0 ", - intval($uid) - ); - if ($r) { - $total_channels = $r[0]['total']; - } - - if (! service_class_allows($uid,'total_channels',$total_channels)) { - $result['message'] = upgrade_message(); - return $result; - } - - $xchan_hash = ''; - $sql_options = (($protocol) ? " and xchan_network = '" . dbesc($protocol) . "' " : ''); - - $r = q("select * from xchan where ( xchan_hash = '%s' or xchan_url = '%s' or xchan_addr = '%s') $sql_options ", - dbesc($url), - dbesc($url), - dbesc($url) - ); - - if ($r) { - - // reset results to the best record or the first if we don't have the best - // note: this returns a single record and not an array of records - - $r = Libzot::zot_record_preferred($r,'xchan_network'); - - // ensure there's a valid hubloc for this xchan before proceeding - you cannot connect without it - - if (in_array($r['xchan_network'], [ 'nomad','zot6','activitypub' ])) { - $h = q("select * from hubloc where hubloc_hash = '%s'", - dbesc($r['xchan_hash']) - ); - if (! $h) { - $r = null; - } - } - - // we may have nulled out this record so check again - - if ($r) { - - // Check the site table to see if we should have a zot6 hubloc, - // If so, clear the xchan and start fresh - - if ($r['xchan_network'] === 'activitypub') { - $m = parse_url($r['xchan_hash']); - unset($m['path']); - $h = unparse_url($m); - $s = q("select * from site where site_url = '%s'", - dbesc($h) - ); - if (intval($s['site_type']) === SITE_TYPE_ZOT) { - logger('got zot - ignore activitypub entry'); - $r = null; - } - } - } - } + if (!check_channelallowed($xchan_hash)) { + $result['message'] = t('Channel is blocked on this site.'); + logger('follow: ' . $result['message']); + return $result; + } - $singleton = false; + if ($r['xchan_network'] === 'activitypub') { + if (!$ap_allowed) { + $result['message'] = t('Protocol not supported'); + return $result; + } + $singleton = true; + } - if (! $r) { + // Now start processing the new connection - // not in cache - try discovery + $aid = $channel['channel_account_id']; + $hash = $channel['channel_hash']; + $default_group = $channel['channel_default_group']; - $wf = discover_by_webbie($url,$protocol,false); + if ($hash === $xchan_hash) { + $result['message'] = t('Cannot connect to yourself.'); + return $result; + } - if (! $wf) { - $result['message'] = t('Remote channel or protocol unavailable.'); - return $result; - } - } + $p = Permissions::connect_perms($uid); - if ($wf) { + // parent channels have unencumbered write permission - // something was discovered - find the record which was just created. + if ($sub_channel) { + $p['perms']['post_wall'] = 1; + $p['perms']['post_comments'] = 1; + $p['perms']['write_storage'] = 1; + $p['perms']['post_like'] = 1; + $p['perms']['delegate'] = 0; + $p['perms']['moderated'] = 0; + } - $r = q("select * from xchan where ( xchan_hash = '%s' or xchan_url = '%s' or xchan_addr = '%s' ) $sql_options", - dbesc(($wf) ? $wf : $url), - dbesc($url), - dbesc($url) - ); + $my_perms = Permissions::serialise($p['perms']); - // convert to a single record (once again preferring a zot solution in the case of multiples) + $profile_assign = get_pconfig($uid, 'system', 'profile_assign', ''); - if ($r) { - $r = Libzot::zot_record_preferred($r,'xchan_network'); - } - } + // See if we are already connected by virtue of having an abook record - // if discovery was a success or the channel was already cached we should have an xchan record in $r - - if ($r) { - $xchan = $r; - $xchan_hash = $r['xchan_hash']; - $their_perms = EMPTY_STR; - } - - // failure case - - if (! $xchan_hash) { - $result['message'] = t('Channel discovery failed.'); - logger('follow: ' . $result['message']); - return $result; - } - - if (! check_channelallowed($xchan_hash)) { - $result['message'] = t('Channel is blocked on this site.'); - logger('follow: ' . $result['message']); - return $result; - - } - - - if ($r['xchan_network'] === 'activitypub') { - if (! $ap_allowed) { - $result['message'] = t('Protocol not supported'); - return $result; - } - $singleton = true; - } - - // Now start processing the new connection - - $aid = $channel['channel_account_id']; - $hash = $channel['channel_hash']; - $default_group = $channel['channel_default_group']; - - if ($hash === $xchan_hash) { - $result['message'] = t('Cannot connect to yourself.'); - return $result; - } - - $p = Permissions::connect_perms($uid); - - // parent channels have unencumbered write permission - - if ($sub_channel) { - $p['perms']['post_wall'] = 1; - $p['perms']['post_comments'] = 1; - $p['perms']['write_storage'] = 1; - $p['perms']['post_like'] = 1; - $p['perms']['delegate'] = 0; - $p['perms']['moderated'] = 0; - } - - $my_perms = Permissions::serialise($p['perms']); - - $profile_assign = get_pconfig($uid,'system','profile_assign',''); - - // See if we are already connected by virtue of having an abook record - - $r = q("select abook_id, abook_xchan, abook_pending, abook_instance from abook + $r = q( + "select abook_id, abook_xchan, abook_pending, abook_instance from abook where abook_xchan = '%s' and abook_channel = %d limit 1", - dbesc($xchan_hash), - intval($uid) - ); + dbesc($xchan_hash), + intval($uid) + ); - if ($r) { + if ($r) { + $abook_instance = $r[0]['abook_instance']; - $abook_instance = $r[0]['abook_instance']; + // If they are on a non-nomadic network, add them to this location - // If they are on a non-nomadic network, add them to this location + if (($singleton) && strpos($abook_instance, z_root()) === false) { + if ($abook_instance) { + $abook_instance .= ','; + } + $abook_instance .= z_root(); - if (($singleton) && strpos($abook_instance,z_root()) === false) { - if ($abook_instance) { - $abook_instance .= ','; - } - $abook_instance .= z_root(); + $x = q( + "update abook set abook_instance = '%s', abook_not_here = 0 where abook_id = %d", + dbesc($abook_instance), + intval($r[0]['abook_id']) + ); + } - $x = q("update abook set abook_instance = '%s', abook_not_here = 0 where abook_id = %d", - dbesc($abook_instance), - intval($r[0]['abook_id']) - ); - } + // if they have a pending connection, we just followed them so approve the connection request - // if they have a pending connection, we just followed them so approve the connection request + if (intval($r[0]['abook_pending'])) { + $x = q( + "update abook set abook_pending = 0 where abook_id = %d", + intval($r[0]['abook_id']) + ); + } + } else { + // create a new abook record - if (intval($r[0]['abook_pending'])) { - $x = q("update abook set abook_pending = 0 where abook_id = %d", - intval($r[0]['abook_id']) - ); - } - } - else { + $closeness = get_pconfig($uid, 'system', 'new_abook_closeness', 80); - // create a new abook record + $r = abook_store_lowlevel( + [ + 'abook_account' => intval($aid), + 'abook_channel' => intval($uid), + 'abook_closeness' => intval($closeness), + 'abook_xchan' => $xchan_hash, + 'abook_profile' => $profile_assign, + 'abook_feed' => intval(($xchan['xchan_network'] === 'rss') ? 1 : 0), + 'abook_created' => datetime_convert(), + 'abook_updated' => datetime_convert(), + 'abook_instance' => (($singleton) ? z_root() : '') + ] + ); + } - $closeness = get_pconfig($uid,'system','new_abook_closeness',80); + if (!$r) { + logger('abook creation failed'); + $result['message'] = t('error saving data'); + return $result; + } - $r = abook_store_lowlevel( - [ - 'abook_account' => intval($aid), - 'abook_channel' => intval($uid), - 'abook_closeness' => intval($closeness), - 'abook_xchan' => $xchan_hash, - 'abook_profile' => $profile_assign, - 'abook_feed' => intval(($xchan['xchan_network'] === 'rss') ? 1 : 0), - 'abook_created' => datetime_convert(), - 'abook_updated' => datetime_convert(), - 'abook_instance' => (($singleton) ? z_root() : '') - ] - ); - } + // Set suitable permissions to the connection - if (! $r) { - logger('abook creation failed'); - $result['message'] = t('error saving data'); - return $result; - } + if ($my_perms) { + set_abconfig($uid, $xchan_hash, 'system', 'my_perms', $my_perms); + } - // Set suitable permissions to the connection + // fetch the entire record - if ($my_perms) { - set_abconfig($uid,$xchan_hash,'system','my_perms',$my_perms); - } - - // fetch the entire record - - $r = q("select abook.*, xchan.* from abook left join xchan on abook_xchan = xchan_hash + $r = q( + "select abook.*, xchan.* from abook left join xchan on abook_xchan = xchan_hash where abook_xchan = '%s' and abook_channel = %d limit 1", - dbesc($xchan_hash), - intval($uid) - ); + dbesc($xchan_hash), + intval($uid) + ); - if ($r) { - $result['abook'] = array_shift($r); - Run::Summon([ 'Notifier', 'permissions_create', $result['abook']['abook_id'] ]); - } + if ($r) { + $result['abook'] = array_shift($r); + Run::Summon(['Notifier', 'permissions_create', $result['abook']['abook_id']]); + } - $arr = [ 'channel_id' => $uid, 'channel' => $channel, 'abook' => $result['abook'] ]; + $arr = ['channel_id' => $uid, 'channel' => $channel, 'abook' => $result['abook']]; - call_hooks('follow', $arr); + call_hooks('follow', $arr); - /** If there is a default group for this channel, add this connection to it */ + /** If there is a default group for this channel, add this connection to it */ - if ($default_group) { - $g = AccessList::rec_byhash($uid,$default_group); - if ($g) { - AccessList::member_add($uid,'',$xchan_hash,$g['id']); - } - } + if ($default_group) { + $g = AccessList::rec_byhash($uid, $default_group); + if ($g) { + AccessList::member_add($uid, '', $xchan_hash, $g['id']); + } + } - $result['success'] = true; - return $result; - } -} \ No newline at end of file + $result['success'] = true; + return $result; + } +} diff --git a/Zotlabs/Lib/Crypto.php b/Zotlabs/Lib/Crypto.php index bf4de8a91..b08c967e4 100644 --- a/Zotlabs/Lib/Crypto.php +++ b/Zotlabs/Lib/Crypto.php @@ -1,210 +1,213 @@ 'sha1', - 'private_key_bits' => $bits, - 'encrypt_key' => false - ]; + $openssl_options = [ + 'digest_alg' => 'sha1', + 'private_key_bits' => $bits, + 'encrypt_key' => false + ]; - $conf = get_config('system','openssl_conf_file'); - - if ($conf) { - $openssl_options['config'] = $conf; - } + $conf = get_config('system', 'openssl_conf_file'); - $result = openssl_pkey_new($openssl_options); + if ($conf) { + $openssl_options['config'] = $conf; + } - if (empty($result)) { - return false; - } + $result = openssl_pkey_new($openssl_options); - // Get private key + if (empty($result)) { + return false; + } - $response = [ 'prvkey' => '', 'pubkey' => '' ]; + // Get private key - openssl_pkey_export($result, $response['prvkey']); + $response = [ 'prvkey' => '', 'pubkey' => '' ]; - // Get public key - $pkey = openssl_pkey_get_details($result); - $response['pubkey'] = $pkey["key"]; + openssl_pkey_export($result, $response['prvkey']); - return $response; + // Get public key + $pkey = openssl_pkey_get_details($result); + $response['pubkey'] = $pkey["key"]; - } + return $response; + } - static public function sign($data,$key,$alg = 'sha256') { + public static function sign($data, $key, $alg = 'sha256') + { - if (! $key) { - return false; - } + if (! $key) { + return false; + } - $sig = ''; - openssl_sign($data,$sig,$key,$alg); - return $sig; - } + $sig = ''; + openssl_sign($data, $sig, $key, $alg); + return $sig; + } - static public function verify($data,$sig,$key,$alg = 'sha256') { + public static function verify($data, $sig, $key, $alg = 'sha256') + { - if (! $key) { - return false; - } + if (! $key) { + return false; + } - try { - $verify = openssl_verify($data,$sig,$key,$alg); - } - catch (Exception $e) { - $verify = (-1); - } + try { + $verify = openssl_verify($data, $sig, $key, $alg); + } catch (Exception $e) { + $verify = (-1); + } - if ($verify === (-1)) { - while ($msg = openssl_error_string()) { - logger('openssl_verify: ' . $msg,LOGGER_NORMAL,LOG_ERR); - } - btlogger('openssl_verify: key: ' . $key, LOGGER_DEBUG, LOG_ERR); - } + if ($verify === (-1)) { + while ($msg = openssl_error_string()) { + logger('openssl_verify: ' . $msg, LOGGER_NORMAL, LOG_ERR); + } + btlogger('openssl_verify: key: ' . $key, LOGGER_DEBUG, LOG_ERR); + } - return (($verify > 0) ? true : false); - } + return (($verify > 0) ? true : false); + } - static public function encapsulate($data,$pubkey,$alg) { + public static function encapsulate($data, $pubkey, $alg) + { - if (! ($alg && $pubkey)) { - return $data; - } + if (! ($alg && $pubkey)) { + return $data; + } - $alg_base = $alg; - $padding = OPENSSL_PKCS1_PADDING; + $alg_base = $alg; + $padding = OPENSSL_PKCS1_PADDING; - $exts = explode('.',$alg); - if (count($exts) > 1) { - switch ($exts[1]) { - case 'oaep': - $padding = OPENSSL_PKCS1_OAEP_PADDING; - break; - default: - break; - } - $alg_base = $exts[0]; - } + $exts = explode('.', $alg); + if (count($exts) > 1) { + switch ($exts[1]) { + case 'oaep': + $padding = OPENSSL_PKCS1_OAEP_PADDING; + break; + default: + break; + } + $alg_base = $exts[0]; + } - $method = null; + $method = null; - foreach (self::$openssl_algorithms as $ossl) { - if ($ossl[0] === $alg_base) { - $method = $ossl; - break; - } - } + foreach (self::$openssl_algorithms as $ossl) { + if ($ossl[0] === $alg_base) { + $method = $ossl; + break; + } + } - if ($method) { - $result = [ 'encrypted' => true ]; + if ($method) { + $result = [ 'encrypted' => true ]; - $key = openssl_random_pseudo_bytes(256); - $iv = openssl_random_pseudo_bytes(256); + $key = openssl_random_pseudo_bytes(256); + $iv = openssl_random_pseudo_bytes(256); - $key1 = substr($key, 0, $method[2]); - $iv1 = substr($iv, 0, $method[3]); + $key1 = substr($key, 0, $method[2]); + $iv1 = substr($iv, 0, $method[3]); - $result['data'] = base64url_encode(openssl_encrypt($data,$method[1],$key1,OPENSSL_RAW_DATA,$iv1),true); + $result['data'] = base64url_encode(openssl_encrypt($data, $method[1], $key1, OPENSSL_RAW_DATA, $iv1), true); - openssl_public_encrypt($key, $k, $pubkey, $padding); - openssl_public_encrypt($iv, $i, $pubkey, $padding); + openssl_public_encrypt($key, $k, $pubkey, $padding); + openssl_public_encrypt($iv, $i, $pubkey, $padding); - $result['alg'] = $alg; - $result['key'] = base64url_encode($k,true); - $result['iv'] = base64url_encode($i,true); - return $result; + $result['alg'] = $alg; + $result['key'] = base64url_encode($k, true); + $result['iv'] = base64url_encode($i, true); + return $result; + } else { + $x = [ 'data' => $data, 'pubkey' => $pubkey, 'alg' => $alg, 'result' => $data ]; + call_hooks('crypto_encapsulate', $x); + return $x['result']; + } + } - } - else { - $x = [ 'data' => $data, 'pubkey' => $pubkey, 'alg' => $alg, 'result' => $data ]; - call_hooks('crypto_encapsulate', $x); - return $x['result']; - } - } + public static function unencapsulate($data, $prvkey) + { - static public function unencapsulate($data,$prvkey) { + if (! (is_array($data) && array_key_exists('encrypted', $data) && array_key_exists('alg', $data) && $data['alg'])) { + logger('not encrypted'); - if (! (is_array($data) && array_key_exists('encrypted',$data) && array_key_exists('alg',$data) && $data['alg'])) { - logger('not encrypted'); + return $data; + } - return $data; - } + $alg_base = $data['alg']; + $padding = OPENSSL_PKCS1_PADDING; - $alg_base = $data['alg']; - $padding = OPENSSL_PKCS1_PADDING; + $exts = explode('.', $data['alg']); + if (count($exts) > 1) { + switch ($exts[1]) { + case 'oaep': + $padding = OPENSSL_PKCS1_OAEP_PADDING; + break; + default: + break; + } + $alg_base = $exts[0]; + } - $exts = explode('.',$data['alg']); - if (count($exts) > 1) { - switch ($exts[1]) { - case 'oaep': - $padding = OPENSSL_PKCS1_OAEP_PADDING; - break; - default: - break; - } - $alg_base = $exts[0]; - } + $method = null; - $method = null; + foreach (self::$openssl_algorithms as $ossl) { + if ($ossl[0] === $alg_base) { + $method = $ossl; + break; + } + } - foreach (self::$openssl_algorithms as $ossl) { - if ($ossl[0] === $alg_base) { - $method = $ossl; - break; - } - } - - if ($method) { - openssl_private_decrypt(base64url_decode($data['key']),$k,$prvkey,$padding); - openssl_private_decrypt(base64url_decode($data['iv']), $i,$prvkey,$padding); - return openssl_decrypt(base64url_decode($data['data']),$method[1],substr($k,0,$method[2]),OPENSSL_RAW_DATA,substr($i,0,$method[3])); - } - else { - $x = [ 'data' => $data, 'prvkey' => $prvkey, 'alg' => $data['alg'], 'result' => $data ]; - call_hooks('crypto_unencapsulate',$x); - return $x['result']; - } - } + if ($method) { + openssl_private_decrypt(base64url_decode($data['key']), $k, $prvkey, $padding); + openssl_private_decrypt(base64url_decode($data['iv']), $i, $prvkey, $padding); + return openssl_decrypt(base64url_decode($data['data']), $method[1], substr($k, 0, $method[2]), OPENSSL_RAW_DATA, substr($i, 0, $method[3])); + } else { + $x = [ 'data' => $data, 'prvkey' => $prvkey, 'alg' => $data['alg'], 'result' => $data ]; + call_hooks('crypto_unencapsulate', $x); + return $x['result']; + } + } } diff --git a/Zotlabs/Lib/DB_Upgrade.php b/Zotlabs/Lib/DB_Upgrade.php index dd9f26aa1..56d8fc88b 100644 --- a/Zotlabs/Lib/DB_Upgrade.php +++ b/Zotlabs/Lib/DB_Upgrade.php @@ -2,113 +2,116 @@ namespace Zotlabs\Lib; +use App; -class DB_Upgrade { +class DB_Upgrade +{ - public $config_name = ''; - public $func_prefix = ''; + public $config_name = ''; + public $func_prefix = ''; - function __construct($db_revision) { + public function __construct($db_revision) + { - $this->config_name = 'db_version'; - $this->func_prefix = '_'; + $this->config_name = 'db_version'; + $this->func_prefix = '_'; - $build = get_config('system', 'db_version', 0); - if(! intval($build)) - $build = set_config('system', 'db_version', $db_revision); + $build = get_config('system', 'db_version', 0); + if (!intval($build)) { + $build = set_config('system', 'db_version', $db_revision); + } - if($build == $db_revision) { - // Nothing to be done. - return; - } - else { - $stored = intval($build); - if(! $stored) { - logger('Critical: check_config unable to determine database schema version'); - return; - } - - $current = intval($db_revision); + if ($build == $db_revision) { + // Nothing to be done. + return; + } else { + $stored = intval($build); + if (!$stored) { + logger('Critical: check_config unable to determine database schema version'); + return; + } - if($stored < $current) { + $current = intval($db_revision); - // The last update we performed was $stored. - // Start at $stored + 1 and continue until we have completed $current + if ($stored < $current) { + // The last update we performed was $stored. + // Start at $stored + 1 and continue until we have completed $current - for($x = $stored + 1; $x <= $current; $x ++) { - $s = '_' . $x; - $cls = '\\Zotlabs\Update\\' . $s ; - if(! class_exists($cls)) { - return; - } + for ($x = $stored + 1; $x <= $current; $x++) { + $s = '_' . $x; + $cls = '\\Zotlabs\Update\\' . $s; + if (!class_exists($cls)) { + return; + } - // There could be a lot of processes running or about to run. - // We want exactly one process to run the update command. - // So store the fact that we're taking responsibility - // after first checking to see if somebody else already has. + // There could be a lot of processes running or about to run. + // We want exactly one process to run the update command. + // So store the fact that we're taking responsibility + // after first checking to see if somebody else already has. - // If the update fails or times-out completely you may need to - // delete the config entry to try again. + // If the update fails or times-out completely you may need to + // delete the config entry to try again. - Config::Load('database'); + Config::Load('database'); - if(get_config('database', $s)) - break; - set_config('database',$s, '1'); - - - $c = new $cls(); - - $retval = $c->run(); - - if($retval != UPDATE_SUCCESS) { + if (get_config('database', $s)) { + break; + } + set_config('database', $s, '1'); - $source = t('Source code of failed update: ') . "\n\n" . @file_get_contents('Zotlabs/Update/' . $s . '.php'); - + $c = new $cls(); - // Prevent sending hundreds of thousands of emails by creating - // a lockfile. + $retval = $c->run(); - $lockfile = 'cache/mailsent'; + if ($retval != UPDATE_SUCCESS) { + $source = t('Source code of failed update: ') . "\n\n" . @file_get_contents('Zotlabs/Update/' . $s . '.php'); - if ((file_exists($lockfile)) && (filemtime($lockfile) > (time() - 86400))) - return; - @unlink($lockfile); - //send the administrator an e-mail - file_put_contents($lockfile, $x); - - $r = q("select account_language from account where account_email = '%s' limit 1", - dbesc(\App::$config['system']['admin_email']) - ); - push_lang(($r) ? $r[0]['account_language'] : 'en'); - z_mail( - [ - 'toEmail' => \App::$config['system']['admin_email'], - 'messageSubject' => sprintf( t('Update Error at %s'), z_root()), - 'textVersion' => replace_macros(get_intltext_template('update_fail_eml.tpl'), - [ - '$sitename' => \App::$config['system']['sitename'], - '$siteurl' => z_root(), - '$update' => $x, - '$error' => sprintf( t('Update %s failed. See error logs.'), $x), - '$baseurl' => z_root(), - '$source' => $source - ] - ) - ] - ); - //try the logger - logger('CRITICAL: Update Failed: ' . $x); - pop_lang(); - } - else { - set_config('database',$s, 'success'); - } - } - } - set_config('system', 'db_version', $db_revision); - } - } -} \ No newline at end of file + // Prevent sending hundreds of thousands of emails by creating + // a lockfile. + + $lockfile = 'cache/mailsent'; + + if ((file_exists($lockfile)) && (filemtime($lockfile) > (time() - 86400))) { + return; + } + @unlink($lockfile); + //send the administrator an e-mail + file_put_contents($lockfile, $x); + + $r = q( + "select account_language from account where account_email = '%s' limit 1", + dbesc(App::$config['system']['admin_email']) + ); + push_lang(($r) ? $r[0]['account_language'] : 'en'); + z_mail( + [ + 'toEmail' => App::$config['system']['admin_email'], + 'messageSubject' => sprintf(t('Update Error at %s'), z_root()), + 'textVersion' => replace_macros( + get_intltext_template('update_fail_eml.tpl'), + [ + '$sitename' => App::$config['system']['sitename'], + '$siteurl' => z_root(), + '$update' => $x, + '$error' => sprintf(t('Update %s failed. See error logs.'), $x), + '$baseurl' => z_root(), + '$source' => $source + ] + ) + ] + ); + + //try the logger + logger('CRITICAL: Update Failed: ' . $x); + pop_lang(); + } else { + set_config('database', $s, 'success'); + } + } + } + set_config('system', 'db_version', $db_revision); + } + } +} diff --git a/Zotlabs/Lib/DReport.php b/Zotlabs/Lib/DReport.php index 7a2cb062f..d0330e098 100644 --- a/Zotlabs/Lib/DReport.php +++ b/Zotlabs/Lib/DReport.php @@ -1,135 +1,154 @@ location = $location; - $this->sender = $sender; - $this->recipient = $recipient; - $this->name = EMPTY_STR; - $this->message_id = $message_id; - $this->status = $status; - $this->date = datetime_convert(); - } + public function __construct($location, $sender, $recipient, $message_id, $status = 'deliver') + { + $this->location = $location; + $this->sender = $sender; + $this->recipient = $recipient; + $this->name = EMPTY_STR; + $this->message_id = $message_id; + $this->status = $status; + $this->date = datetime_convert(); + } - function update($status) { - $this->status = $status; - $this->date = datetime_convert(); - } + public function update($status) + { + $this->status = $status; + $this->date = datetime_convert(); + } - function set_name($name) { - $this->name = $name; - } + public function set_name($name) + { + $this->name = $name; + } - function addto_update($status) { - $this->status = $this->status . ' ' . $status; - } + public function addto_update($status) + { + $this->status = $this->status . ' ' . $status; + } - function set($arr) { - $this->location = $arr['location']; - $this->sender = $arr['sender']; - $this->recipient = $arr['recipient']; - $this->name = $arr['name']; - $this->message_id = $arr['message_id']; - $this->status = $arr['status']; - $this->date = $arr['date']; - } + public function set($arr) + { + $this->location = $arr['location']; + $this->sender = $arr['sender']; + $this->recipient = $arr['recipient']; + $this->name = $arr['name']; + $this->message_id = $arr['message_id']; + $this->status = $arr['status']; + $this->date = $arr['date']; + } - function get() { - return array( - 'location' => $this->location, - 'sender' => $this->sender, - 'recipient' => $this->recipient, - 'name' => $this->name, - 'message_id' => $this->message_id, - 'status' => $this->status, - 'date' => $this->date - ); - } + public function get() + { + return array( + 'location' => $this->location, + 'sender' => $this->sender, + 'recipient' => $this->recipient, + 'name' => $this->name, + 'message_id' => $this->message_id, + 'status' => $this->status, + 'date' => $this->date + ); + } - /** - * @brief decide whether to store a returned delivery report - * - * @param array $dr - * @return boolean - */ + /** + * @brief decide whether to store a returned delivery report + * + * @param array $dr + * @return bool + */ - static function is_storable($dr) { + public static function is_storable($dr) + { - if(get_config('system', 'disable_dreport')) - return false; + if (get_config('system', 'disable_dreport')) { + return false; + } - /** - * @hooks dreport_is_storable - * Called before storing a dreport record to determine whether to store it. - * * \e array - */ + /** + * @hooks dreport_is_storable + * Called before storing a dreport record to determine whether to store it. + * * \e array + */ - call_hooks('dreport_is_storable', $dr); + call_hooks('dreport_is_storable', $dr); - // let plugins accept or reject - if neither, continue on - if(array_key_exists('accept',$dr) && intval($dr['accept'])) - return true; - if(array_key_exists('reject',$dr) && intval($dr['reject'])) - return false; + // let plugins accept or reject - if neither, continue on + if (array_key_exists('accept', $dr) && intval($dr['accept'])) { + return true; + } + if (array_key_exists('reject', $dr) && intval($dr['reject'])) { + return false; + } - if(! ($dr['sender'])) - return false; + if (!($dr['sender'])) { + return false; + } - // Is the sender one of our channels? + // Is the sender one of our channels? - $c = q("select channel_id from channel where channel_hash = '%s' limit 1", - dbesc($dr['sender']) - ); - if(! $c) - return false; + $c = q( + "select channel_id from channel where channel_hash = '%s' limit 1", + dbesc($dr['sender']) + ); + if (!$c) { + return false; + } - // is the recipient one of our connections, or do we want to store every report? + // is the recipient one of our connections, or do we want to store every report? - $rxchan = $dr['recipient']; - $pcf = get_pconfig($c[0]['channel_id'],'system','dreport_store_all'); - if($pcf) - return true; + $rxchan = $dr['recipient']; + $pcf = get_pconfig($c[0]['channel_id'], 'system', 'dreport_store_all'); + if ($pcf) { + return true; + } - // We always add ourself as a recipient to private and relayed posts - // So if a remote site says they can't find us, that's no big surprise - // and just creates a lot of extra report noise + // We always add ourself as a recipient to private and relayed posts + // So if a remote site says they can't find us, that's no big surprise + // and just creates a lot of extra report noise - if(($dr['location'] !== z_root()) && ($dr['sender'] === $rxchan) && ($dr['status'] === 'recipient not found')) - return false; + if (($dr['location'] !== z_root()) && ($dr['sender'] === $rxchan) && ($dr['status'] === 'recipient not found')) { + return false; + } - // If you have a private post with a recipient list, every single site is going to report - // back a failed delivery for anybody on that list that isn't local to them. We're only - // concerned about this if we have a local hubloc record which says we expected them to - // have a channel on that site. + // If you have a private post with a recipient list, every single site is going to report + // back a failed delivery for anybody on that list that isn't local to them. We're only + // concerned about this if we have a local hubloc record which says we expected them to + // have a channel on that site. - $r = q("select hubloc_id from hubloc where hubloc_hash = '%s' and hubloc_url = '%s'", - dbesc($rxchan), - dbesc($dr['location']) - ); - if((! $r) && ($dr['status'] === 'recipient not found')) - return false; - - $r = q("select abook_id from abook where abook_xchan = '%s' and abook_channel = %d limit 1", - dbesc($rxchan), - intval($c[0]['channel_id']) - ); - if($r) - return true; - - return false; - } + $r = q( + "select hubloc_id from hubloc where hubloc_hash = '%s' and hubloc_url = '%s'", + dbesc($rxchan), + dbesc($dr['location']) + ); + if ((!$r) && ($dr['status'] === 'recipient not found')) { + return false; + } + $r = q( + "select abook_id from abook where abook_xchan = '%s' and abook_channel = %d limit 1", + dbesc($rxchan), + intval($c[0]['channel_id']) + ); + if ($r) { + return true; + } + return false; + } } diff --git a/Zotlabs/Lib/Enotify.php b/Zotlabs/Lib/Enotify.php index 5c32b5e86..ba7915d0d 100644 --- a/Zotlabs/Lib/Enotify.php +++ b/Zotlabs/Lib/Enotify.php @@ -11,938 +11,971 @@ use Zotlabs\Lib\System; */ -class Enotify { +class Enotify +{ - /** - * @brief - * - * @param array $params an associative array with: - * * \e string \b from_xchan sender xchan hash - * * \e string \b to_xchan recipient xchan hash - * * \e array \b item an assoziative array - * * \e int \b type one of the NOTIFY_* constants from boot.php - * * \e string \b link - * * \e string \b parent_mid - * * \e string \b otype - * * \e string \b verb - * * \e string \b activity - */ + /** + * @brief + * + * @param array $params an associative array with: + * * \e string \b from_xchan sender xchan hash + * * \e string \b to_xchan recipient xchan hash + * * \e array \b item an assoziative array + * * \e int \b type one of the NOTIFY_* constants from boot.php + * * \e string \b link + * * \e string \b parent_mid + * * \e string \b otype + * * \e string \b verb + * * \e string \b activity + */ - static public function submit($params) { + public static function submit($params) + { - logger('notification: entry', LOGGER_DEBUG); + logger('notification: entry', LOGGER_DEBUG); - // throw a small amount of entropy into the system to breakup duplicates arriving at the same precise instant. - usleep(mt_rand(0, 10000)); + // throw a small amount of entropy into the system to breakup duplicates arriving at the same precise instant. + usleep(mt_rand(0, 10000)); - if ($params['from_xchan']) { - $x = q("select * from xchan where xchan_hash = '%s' limit 1", - dbesc($params['from_xchan']) - ); - } - if ($params['to_xchan']) { - $y = q("select channel.*, account.* from channel left join account on channel_account_id = account_id + if ($params['from_xchan']) { + $x = q( + "select * from xchan where xchan_hash = '%s' limit 1", + dbesc($params['from_xchan']) + ); + } + if ($params['to_xchan']) { + $y = q( + "select channel.*, account.* from channel left join account on channel_account_id = account_id where channel_hash = '%s' and channel_removed = 0 limit 1", - dbesc($params['to_xchan']) - ); - } - if ($x && $y) { - $sender = $x[0]; - $recip = $y[0]; - } else { - logger('notification: no sender or recipient.'); - logger('sender: ' . $params['from_xchan']); - logger('recip: ' . $params['to_xchan']); - return; - } - - - - // from here on everything is in the recipients language - - push_lang($recip['account_language']); // should probably have a channel language - - $banner = t('$Projectname Notification'); - $product = t('$projectname'); // PLATFORM_NAME; - $siteurl = z_root(); - $thanks = t('Thank You,'); - $sitename = get_config('system','sitename'); - $site_admin = sprintf( t('%s Administrator'), $sitename); - $opt_out1 = sprintf( t('This email was sent by %1$s at %2$s.'), t('$Projectname'), App::get_hostname()); - $opt_out2 = sprintf( t('To stop receiving these messages, please adjust your Notification Settings at %s'), z_root() . '/settings'); - $hopt_out2 = sprintf( t('To stop receiving these messages, please adjust your %s.'), '' . t('Notification Settings') . ''); - $sender_name = $product; - $hostname = App::get_hostname(); - if(strpos($hostname,':')) - $hostname = substr($hostname,0,strpos($hostname,':')); - - // Do not translate 'noreply' as it must be a legal 7-bit email address - - $reply_email = get_config('system','reply_address'); - if(! $reply_email) - $reply_email = 'noreply' . '@' . $hostname; - - $sender_email = get_config('system','from_email'); - if(! $sender_email) - $sender_email = 'Administrator' . '@' . $hostname; - - $sender_name = get_config('system','from_email_name'); - if(! $sender_name) - $sender_name = System::get_site_name(); - - - $additional_mail_header = ""; - - if(array_key_exists('item', $params)) { - require_once('include/conversation.php'); - // if it's a normal item... - if (array_key_exists('verb', $params['item'])) { - // localize_item() alters the original item so make a copy first - $i = $params['item']; - logger('calling localize'); - localize_item($i); - $title = $i['title']; - $body = $i['body']; - $private = (($i['item_private']) || intval($i['item_obscured'])); - } - else { - $title = $params['item']['title']; - $body = $params['item']['body']; - } - 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; - } - } - else { - $title = $body = ''; - } - - - $always_show_in_notices = get_pconfig($recip['channel_id'],'system','always_show_in_notices'); - $vnotify = get_pconfig($recip['channel_id'],'system','vnotify'); - - $salutation = $recip['channel_name']; - - // e.g. "your post", "David's photo", etc. - $possess_desc = t('%s '); - - if ($params['type'] == NOTIFY_MAIL) { - logger('notification: mail'); - $subject = sprintf( t('[$Projectname:Notify] New mail received at %s'),$sitename); - - if ($params['item']['mid'] === $params['item']['parent_mid']) { - $preamble = sprintf( t('%1$s sent you a new private message at %2$s.'), $sender['xchan_name'],$sitename); - $epreamble = sprintf( t('%1$s sent you %2$s.'),'[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]', '[zrl=$itemlink]' . t('a private message') . '[/zrl]'); - } - else { - $preamble = sprintf( t('%1$s replied to a private message at %2$s.'), $sender['xchan_name'],$sitename); - $epreamble = sprintf( t('%1$s replied to %2$s.'),'[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]', '[zrl=$itemlink]' . t('a private message') . '[/zrl]'); - } - $sitelink = t('Please visit %s to view and/or reply to your private messages.'); - - $tsitelink = sprintf( $sitelink, $siteurl . '/display/' . gen_link_id($params['item']['mid']) ); - $hsitelink = sprintf( $sitelink, '' . $sitename . ''); - $itemlink = z_root() . '/display/' . gen_link_id($params['item']['mid']); - } - - if (in_array(intval($params['type']), [ NOTIFY_COMMENT, NOTIFY_RESHARE ] )) { - // logger("notification: params = " . print_r($params, true), LOGGER_DEBUG); - - $moderated = (($params['item']['item_blocked'] == ITEM_MODERATED) ? true : false); - - $itemlink = $params['link']; - - $action = t('commented on'); - - if(array_key_exists('item',$params) && in_array($params['item']['verb'], [ACTIVITY_LIKE, ACTIVITY_DISLIKE])) { - - if(! $always_show_in_notices || !($vnotify & VNOTIFY_LIKE)) { - logger('notification: not a visible activity. Ignoring.'); - pop_lang(); - return; - } - - if(activity_match($params['verb'], ACTIVITY_LIKE)) - $action = t('liked'); - - if(activity_match($params['verb'], ACTIVITY_DISLIKE)) - $action = t('disliked'); - - } - - $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: comment 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]; - - //$possess_desc = str_replace('',$possess_desc); - - // "a post" - $dest_str = sprintf(t('%1$s %2$s [zrl=%3$s]a %4$s[/zrl]'), - '[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]', - $action, - $itemlink, - $item_post_type); - - // "George Bull's post" - if($p) - $dest_str = sprintf(t('%1$s %2$s [zrl=%3$s]%4$s\'s %5$s[/zrl]'), - '[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]', - $action, - $itemlink, - $p[0]['author']['xchan_name'], - $item_post_type); - - // "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 [zrl=%3$s]your %4$s[/zrl]'), - '[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]', - $action, - $itemlink, - $item_post_type); - - // 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. - - if($moderated) { - $subject = sprintf( t('[$Projectname:Notify] Moderated Comment to conversation #%1$d by %2$s'), $parent_id, $sender['xchan_name']); - $itemlink = z_root() . '/moderate/' . gen_link_id($params['item']['mid']); - } - else - $subject = sprintf( t('[$Projectname:Notify] Comment to conversation #%1$d by %2$s'), $parent_id, $sender['xchan_name']); - $preamble = sprintf( t('%1$s commented on an item/conversation you have been following.'), $sender['xchan_name']); - $epreamble = $dest_str; - - if ($moderated) { - $epreamble .= ' ' . t('(Moderated)'); - } - - $sitelink = t('Please visit %s to view and/or reply to the conversation.'); - $tsitelink = sprintf( $sitelink, $siteurl ); - $hsitelink = sprintf( $sitelink, '' . $sitename . ''); - if($moderated) { - $tsitelink .= "\n\n" . sprintf( t('Please visit %s to approve or reject this comment.'), z_root() . '/moderate' ); - $hsitelink .= "

" . sprintf( t('Please visit %s to approve or reject this comment.'), '' . z_root() . '/moderate' ); - } - - } - - if ($params['type'] == NOTIFY_LIKE) { -// logger("notification: params = " . print_r($params, true), LOGGER_DEBUG); - - $itemlink = $params['link']; - - if (array_key_exists('item',$params) && (! activity_match($params['item']['verb'],ACTIVITY_LIKE))) { - if(! $always_show_in_notices || !($vnotify & VNOTIFY_LIKE)) { - logger('notification: not a visible 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 liked [zrl=%2$s]your %3$s[/zrl]'), - '[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 liked an item/conversation you created.'), $sender['xchan_name']); - $epreamble = $dest_str; - - $sitelink = t('Please visit %s to view and/or reply to the conversation.'); - $tsitelink = sprintf( $sitelink, $siteurl ); - $hsitelink = sprintf( $sitelink, '' . $sitename . ''); - } - - - - if($params['type'] == NOTIFY_WALL) { - $subject = sprintf( t('[$Projectname:Notify] %s posted to your profile wall') , $sender['xchan_name']); - - $moderated = (($params['item']['item_blocked'] == ITEM_MODERATED) ? true : false); - - $itemlink = (($moderated) ? z_root() . '/moderate/' . gen_link_id($params['item']['mid']) : $params['link']); - - $preamble = sprintf( t('%1$s posted to your profile wall at %2$s') , $sender['xchan_name'], $sitename); - - $epreamble = sprintf( t('%1$s posted to [zrl=%2$s]your wall[/zrl]') , - '[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]', - $itemlink); - - - if($moderated) { - $subject .= t(' - ') . t('Moderated'); - $epreamble .= t(' - ') . t('Moderated'); - } - - $sitelink = t('Please visit %s to view and/or reply to the conversation.'); - $tsitelink = sprintf( $sitelink, $siteurl ); - $hsitelink = sprintf( $sitelink, '' . $sitename . ''); - - if($moderated) { - $tsitelink .= "\n\n" . sprintf( t('Please visit %s to approve or reject this post.'), z_root() . '/moderate' ); - $hsitelink .= "

" . sprintf( t('Please visit %s to approve or reject this post.'), '' . z_root() . '/moderate' ); - } - - } - - if ($params['type'] == NOTIFY_TAGSELF) { - - $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('enotify: tag: already notified about this post'); - pop_lang(); - return; - } - - $subject = sprintf( t('[$Projectname:Notify] %s tagged you') , $sender['xchan_name']); - $preamble = sprintf( t('%1$s tagged you at %2$s') , $sender['xchan_name'], $sitename); - $epreamble = sprintf( t('%1$s [zrl=%2$s]tagged you[/zrl].') , - '[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]', - $params['link']); - - $sitelink = t('Please visit %s to view and/or reply to the conversation.'); - $tsitelink = sprintf( $sitelink, $siteurl ); - $hsitelink = sprintf( $sitelink, '' . $sitename . ''); - $itemlink = $params['link']; - } - - if ($params['type'] == NOTIFY_POKE) { - $subject = sprintf( t('[$Projectname:Notify] %1$s poked you') , $sender['xchan_name']); - $preamble = sprintf( t('%1$s poked you at %2$s') , $sender['xchan_name'], $sitename); - $epreamble = sprintf( t('%1$s [zrl=%2$s]poked you[/zrl].') , - '[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]', - $params['link']); - - $subject = str_replace('poked', t($params['activity']), $subject); - $preamble = str_replace('poked', t($params['activity']), $preamble); - $epreamble = str_replace('poked', t($params['activity']), $epreamble); - - $sitelink = t('Please visit %s to view and/or reply to the conversation.'); - $tsitelink = sprintf( $sitelink, $siteurl ); - $hsitelink = sprintf( $sitelink, '' . $sitename . ''); - $itemlink = $params['link']; - } - - if ($params['type'] == NOTIFY_TAGSHARE) { - $subject = sprintf( t('[$Projectname:Notify] %s tagged your post') , $sender['xchan_name']); - $preamble = sprintf( t('%1$s tagged your post at %2$s'),$sender['xchan_name'], $sitename); - $epreamble = sprintf( t('%1$s tagged [zrl=%2$s]your post[/zrl]') , - '[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]', - $itemlink); - - $sitelink = t('Please visit %s to view and/or reply to the conversation.'); - $tsitelink = sprintf( $sitelink, $siteurl ); - $hsitelink = sprintf( $sitelink, '' . $sitename . ''); - $itemlink = $params['link']; - } - - if ($params['type'] == NOTIFY_INTRO) { - $subject = sprintf( t('[$Projectname:Notify] Introduction received')); - $preamble = sprintf( t('You\'ve received an new connection request from \'%1$s\' at %2$s'), $sender['xchan_name'], $sitename); - $epreamble = sprintf( t('You\'ve received [zrl=%1$s]a new connection request[/zrl] from %2$s.'), - $siteurl . '/connections/ifpending', - '[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]'); - $body = sprintf( t('You may visit their profile at %s'),$sender['xchan_url']); - - $sitelink = t('Please visit %s to approve or reject the connection request.'); - $tsitelink = sprintf( $sitelink, $siteurl . '/connections/ifpending'); - $hsitelink = sprintf( $sitelink, '' . $sitename . ''); - $itemlink = $params['link']; - } - - if ($params['type'] == NOTIFY_SUGGEST) { - $subject = sprintf( t('[$Projectname:Notify] Friend suggestion received')); - $preamble = sprintf( t('You\'ve received a friend suggestion from \'%1$s\' at %2$s'), $sender['xchan_name'], $sitename); - $epreamble = sprintf( t('You\'ve received [zrl=%1$s]a friend suggestion[/zrl] for %2$s from %3$s.'), - $itemlink, - '[zrl=' . $params['item']['url'] . ']' . $params['item']['name'] . '[/zrl]', - '[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]'); - - $body = t('Name:') . ' ' . $params['item']['name'] . "\n"; - $body .= t('Photo:') . ' ' . $params['item']['photo'] . "\n"; - $body .= sprintf( t('You may visit their profile at %s'),$params['item']['url']); - - $sitelink = t('Please visit %s to approve or reject the suggestion.'); - $tsitelink = sprintf( $sitelink, $siteurl ); - $hsitelink = sprintf( $sitelink, '' . $sitename . ''); - $itemlink = $params['link']; - } - - if ($params['type'] == NOTIFY_CONFIRM) { - // ? - } - - if ($params['type'] == NOTIFY_SYSTEM) { - // ? - } - - $h = array( - 'params' => $params, - 'subject' => $subject, - 'preamble' => $preamble, - 'epreamble' => $epreamble, - 'body' => $body, - 'sitelink' => $sitelink, - 'sitename' => $sitename, - 'tsitelink' => $tsitelink, - 'hsitelink' => $hsitelink, - 'itemlink' => $itemlink, - 'sender' => $sender, - 'recipient' => $recip - ); - - call_hooks('enotify', $h); - - $subject = $h['subject']; - $preamble = $h['preamble']; - $epreamble = $h['epreamble']; - $body = $h['body']; - $sitelink = $h['sitelink']; - $tsitelink = $h['tsitelink']; - $hsitelink = $h['hsitelink']; - $itemlink = $h['itemlink']; - - - require_once('include/html2bbcode.php'); - - do { - $dups = false; - $hash = random_string(); - $r = q("SELECT id FROM notify WHERE hash = '%s' LIMIT 1", - dbesc($hash)); - if ($r) - $dups = true; - } while ($dups === true); - - - $datarray = []; - $datarray['hash'] = $hash; - $datarray['sender_hash'] = $sender['xchan_hash']; - $datarray['xname'] = $sender['xchan_name']; - $datarray['url'] = $sender['xchan_url']; - $datarray['photo'] = $sender['xchan_photo_s']; - $datarray['created'] = datetime_convert(); - $datarray['aid'] = $recip['channel_account_id']; - $datarray['uid'] = $recip['channel_id']; - $datarray['link'] = $itemlink; - $datarray['parent'] = $parent_mid; - $datarray['parent_item'] = $parent_item; - $datarray['ntype'] = $params['type']; - $datarray['verb'] = $params['verb']; - $datarray['otype'] = $params['otype']; - $datarray['abort'] = false; - - $datarray['item'] = $params['item']; - - if (LibBlock::fetch_by_entity($datarray['uid'],$datarray['sender_hash'])) { - pop_lang(); - return; - } - - if (is_array($datarray['parent_item'])) { - if (LibBlock::fetch_by_entity($datarray['uid'],$datarray['parent_item']['author_xchan']) || LibBlock::fetch_by_entity($datarray['uid'],$datarray['parent_item']['owner_xchan'])) { - pop_lang(); - return; - } - } - - call_hooks('enotify_store', $datarray); - - if ($datarray['abort']) { - pop_lang(); - return; - } - - - // create notification entry in DB - $seen = 0; - - // Mark some notifications as seen right away - // Note! The notification have to be created, because they are used to send emails - // So easiest solution to hide them from Notices is to mark them as seen right away. - // Another option would be to not add them to the DB, and change how emails are handled - // (probably would be better that way) - - if (!$always_show_in_notices) { - if (($params['type'] == NOTIFY_WALL) || ($params['type'] == NOTIFY_INTRO)) { - $seen = 1; - } - // set back to unseen for moderated wall posts - if($params['type'] == NOTIFY_WALL && $params['item']['item_blocked'] == ITEM_MODERATED) { - $seen = 0; - } - - } - - $e = q("select * from notify where otype = '%s' and xname = '%s' and verb = '%s' and link = '%s' and ntype = %d limit 1", - dbesc($datarray['otype']), - dbesc($datarray['xname']), - dbesc($datarray['verb']), - dbesc($datarray['link']), - intval($datarray['ntype']) - ); - if($e) { - logger('duplicated notification'); - pop_lang(); - return; - } - - $r = q("insert into notify (hash,xname,url,photo,created,msg,aid,uid,link,parent,seen,ntype,verb,otype) + dbesc($params['to_xchan']) + ); + } + if ($x && $y) { + $sender = $x[0]; + $recip = $y[0]; + } else { + logger('notification: no sender or recipient.'); + logger('sender: ' . $params['from_xchan']); + logger('recip: ' . $params['to_xchan']); + return; + } + + + + // from here on everything is in the recipients language + + push_lang($recip['account_language']); // should probably have a channel language + + $banner = t('$Projectname Notification'); + $product = t('$projectname'); // PLATFORM_NAME; + $siteurl = z_root(); + $thanks = t('Thank You,'); + $sitename = get_config('system', 'sitename'); + $site_admin = sprintf(t('%s Administrator'), $sitename); + $opt_out1 = sprintf(t('This email was sent by %1$s at %2$s.'), t('$Projectname'), App::get_hostname()); + $opt_out2 = sprintf(t('To stop receiving these messages, please adjust your Notification Settings at %s'), z_root() . '/settings'); + $hopt_out2 = sprintf(t('To stop receiving these messages, please adjust your %s.'), '' . t('Notification Settings') . ''); + $sender_name = $product; + $hostname = App::get_hostname(); + if (strpos($hostname, ':')) { + $hostname = substr($hostname, 0, strpos($hostname, ':')); + } + + // Do not translate 'noreply' as it must be a legal 7-bit email address + + $reply_email = get_config('system', 'reply_address'); + if (! $reply_email) { + $reply_email = 'noreply' . '@' . $hostname; + } + + $sender_email = get_config('system', 'from_email'); + if (! $sender_email) { + $sender_email = 'Administrator' . '@' . $hostname; + } + + $sender_name = get_config('system', 'from_email_name'); + if (! $sender_name) { + $sender_name = System::get_site_name(); + } + + + $additional_mail_header = ""; + + if (array_key_exists('item', $params)) { + require_once('include/conversation.php'); + // if it's a normal item... + if (array_key_exists('verb', $params['item'])) { + // localize_item() alters the original item so make a copy first + $i = $params['item']; + logger('calling localize'); + localize_item($i); + $title = $i['title']; + $body = $i['body']; + $private = (($i['item_private']) || intval($i['item_obscured'])); + } else { + $title = $params['item']['title']; + $body = $params['item']['body']; + } + 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; + } + } else { + $title = $body = ''; + } + + + $always_show_in_notices = get_pconfig($recip['channel_id'], 'system', 'always_show_in_notices'); + $vnotify = get_pconfig($recip['channel_id'], 'system', 'vnotify'); + + $salutation = $recip['channel_name']; + + // e.g. "your post", "David's photo", etc. + $possess_desc = t('%s '); + + if ($params['type'] == NOTIFY_MAIL) { + logger('notification: mail'); + $subject = sprintf(t('[$Projectname:Notify] New mail received at %s'), $sitename); + + if ($params['item']['mid'] === $params['item']['parent_mid']) { + $preamble = sprintf(t('%1$s sent you a new private message at %2$s.'), $sender['xchan_name'], $sitename); + $epreamble = sprintf(t('%1$s sent you %2$s.'), '[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]', '[zrl=$itemlink]' . t('a private message') . '[/zrl]'); + } else { + $preamble = sprintf(t('%1$s replied to a private message at %2$s.'), $sender['xchan_name'], $sitename); + $epreamble = sprintf(t('%1$s replied to %2$s.'), '[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]', '[zrl=$itemlink]' . t('a private message') . '[/zrl]'); + } + $sitelink = t('Please visit %s to view and/or reply to your private messages.'); + + $tsitelink = sprintf($sitelink, $siteurl . '/display/' . gen_link_id($params['item']['mid'])); + $hsitelink = sprintf($sitelink, '' . $sitename . ''); + $itemlink = z_root() . '/display/' . gen_link_id($params['item']['mid']); + } + + if (in_array(intval($params['type']), [ NOTIFY_COMMENT, NOTIFY_RESHARE ])) { + // logger("notification: params = " . print_r($params, true), LOGGER_DEBUG); + + $moderated = (($params['item']['item_blocked'] == ITEM_MODERATED) ? true : false); + + $itemlink = $params['link']; + + $action = t('commented on'); + + if (array_key_exists('item', $params) && in_array($params['item']['verb'], [ACTIVITY_LIKE, ACTIVITY_DISLIKE])) { + if (! $always_show_in_notices || !($vnotify & VNOTIFY_LIKE)) { + logger('notification: not a visible activity. Ignoring.'); + pop_lang(); + return; + } + + if (activity_match($params['verb'], ACTIVITY_LIKE)) { + $action = t('liked'); + } + + if (activity_match($params['verb'], ACTIVITY_DISLIKE)) { + $action = t('disliked'); + } + } + + $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: comment 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]; + + //$possess_desc = str_replace('',$possess_desc); + + // "a post" + $dest_str = sprintf( + t('%1$s %2$s [zrl=%3$s]a %4$s[/zrl]'), + '[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]', + $action, + $itemlink, + $item_post_type + ); + + // "George Bull's post" + if ($p) { + $dest_str = sprintf( + t('%1$s %2$s [zrl=%3$s]%4$s\'s %5$s[/zrl]'), + '[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]', + $action, + $itemlink, + $p[0]['author']['xchan_name'], + $item_post_type + ); + } + + // "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 [zrl=%3$s]your %4$s[/zrl]'), + '[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]', + $action, + $itemlink, + $item_post_type + ); + } + + // 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. + + if ($moderated) { + $subject = sprintf(t('[$Projectname:Notify] Moderated Comment to conversation #%1$d by %2$s'), $parent_id, $sender['xchan_name']); + $itemlink = z_root() . '/moderate/' . gen_link_id($params['item']['mid']); + } else { + $subject = sprintf(t('[$Projectname:Notify] Comment to conversation #%1$d by %2$s'), $parent_id, $sender['xchan_name']); + } + $preamble = sprintf(t('%1$s commented on an item/conversation you have been following.'), $sender['xchan_name']); + $epreamble = $dest_str; + + if ($moderated) { + $epreamble .= ' ' . t('(Moderated)'); + } + + $sitelink = t('Please visit %s to view and/or reply to the conversation.'); + $tsitelink = sprintf($sitelink, $siteurl); + $hsitelink = sprintf($sitelink, '' . $sitename . ''); + if ($moderated) { + $tsitelink .= "\n\n" . sprintf(t('Please visit %s to approve or reject this comment.'), z_root() . '/moderate'); + $hsitelink .= "

" . sprintf(t('Please visit %s to approve or reject this comment.'), '' . z_root() . '/moderate'); + } + } + + if ($params['type'] == NOTIFY_LIKE) { + // logger("notification: params = " . print_r($params, true), LOGGER_DEBUG); + + $itemlink = $params['link']; + + if (array_key_exists('item', $params) && (! activity_match($params['item']['verb'], ACTIVITY_LIKE))) { + if (! $always_show_in_notices || !($vnotify & VNOTIFY_LIKE)) { + logger('notification: not a visible 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 liked [zrl=%2$s]your %3$s[/zrl]'), + '[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 liked an item/conversation you created.'), $sender['xchan_name']); + $epreamble = $dest_str; + + $sitelink = t('Please visit %s to view and/or reply to the conversation.'); + $tsitelink = sprintf($sitelink, $siteurl); + $hsitelink = sprintf($sitelink, '' . $sitename . ''); + } + + + + if ($params['type'] == NOTIFY_WALL) { + $subject = sprintf(t('[$Projectname:Notify] %s posted to your profile wall'), $sender['xchan_name']); + + $moderated = (($params['item']['item_blocked'] == ITEM_MODERATED) ? true : false); + + $itemlink = (($moderated) ? z_root() . '/moderate/' . gen_link_id($params['item']['mid']) : $params['link']); + + $preamble = sprintf(t('%1$s posted to your profile wall at %2$s'), $sender['xchan_name'], $sitename); + + $epreamble = sprintf( + t('%1$s posted to [zrl=%2$s]your wall[/zrl]'), + '[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]', + $itemlink + ); + + + if ($moderated) { + $subject .= t(' - ') . t('Moderated'); + $epreamble .= t(' - ') . t('Moderated'); + } + + $sitelink = t('Please visit %s to view and/or reply to the conversation.'); + $tsitelink = sprintf($sitelink, $siteurl); + $hsitelink = sprintf($sitelink, '' . $sitename . ''); + + if ($moderated) { + $tsitelink .= "\n\n" . sprintf(t('Please visit %s to approve or reject this post.'), z_root() . '/moderate'); + $hsitelink .= "

" . sprintf(t('Please visit %s to approve or reject this post.'), '' . z_root() . '/moderate'); + } + } + + if ($params['type'] == NOTIFY_TAGSELF) { + $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('enotify: tag: already notified about this post'); + pop_lang(); + return; + } + + $subject = sprintf(t('[$Projectname:Notify] %s tagged you'), $sender['xchan_name']); + $preamble = sprintf(t('%1$s tagged you at %2$s'), $sender['xchan_name'], $sitename); + $epreamble = sprintf( + t('%1$s [zrl=%2$s]tagged you[/zrl].'), + '[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]', + $params['link'] + ); + + $sitelink = t('Please visit %s to view and/or reply to the conversation.'); + $tsitelink = sprintf($sitelink, $siteurl); + $hsitelink = sprintf($sitelink, '' . $sitename . ''); + $itemlink = $params['link']; + } + + if ($params['type'] == NOTIFY_POKE) { + $subject = sprintf(t('[$Projectname:Notify] %1$s poked you'), $sender['xchan_name']); + $preamble = sprintf(t('%1$s poked you at %2$s'), $sender['xchan_name'], $sitename); + $epreamble = sprintf( + t('%1$s [zrl=%2$s]poked you[/zrl].'), + '[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]', + $params['link'] + ); + + $subject = str_replace('poked', t($params['activity']), $subject); + $preamble = str_replace('poked', t($params['activity']), $preamble); + $epreamble = str_replace('poked', t($params['activity']), $epreamble); + + $sitelink = t('Please visit %s to view and/or reply to the conversation.'); + $tsitelink = sprintf($sitelink, $siteurl); + $hsitelink = sprintf($sitelink, '' . $sitename . ''); + $itemlink = $params['link']; + } + + if ($params['type'] == NOTIFY_TAGSHARE) { + $subject = sprintf(t('[$Projectname:Notify] %s tagged your post'), $sender['xchan_name']); + $preamble = sprintf(t('%1$s tagged your post at %2$s'), $sender['xchan_name'], $sitename); + $epreamble = sprintf( + t('%1$s tagged [zrl=%2$s]your post[/zrl]'), + '[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]', + $itemlink + ); + + $sitelink = t('Please visit %s to view and/or reply to the conversation.'); + $tsitelink = sprintf($sitelink, $siteurl); + $hsitelink = sprintf($sitelink, '' . $sitename . ''); + $itemlink = $params['link']; + } + + if ($params['type'] == NOTIFY_INTRO) { + $subject = sprintf(t('[$Projectname:Notify] Introduction received')); + $preamble = sprintf(t('You\'ve received an new connection request from \'%1$s\' at %2$s'), $sender['xchan_name'], $sitename); + $epreamble = sprintf( + t('You\'ve received [zrl=%1$s]a new connection request[/zrl] from %2$s.'), + $siteurl . '/connections/ifpending', + '[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]' + ); + $body = sprintf(t('You may visit their profile at %s'), $sender['xchan_url']); + + $sitelink = t('Please visit %s to approve or reject the connection request.'); + $tsitelink = sprintf($sitelink, $siteurl . '/connections/ifpending'); + $hsitelink = sprintf($sitelink, '' . $sitename . ''); + $itemlink = $params['link']; + } + + if ($params['type'] == NOTIFY_SUGGEST) { + $subject = sprintf(t('[$Projectname:Notify] Friend suggestion received')); + $preamble = sprintf(t('You\'ve received a friend suggestion from \'%1$s\' at %2$s'), $sender['xchan_name'], $sitename); + $epreamble = sprintf( + t('You\'ve received [zrl=%1$s]a friend suggestion[/zrl] for %2$s from %3$s.'), + $itemlink, + '[zrl=' . $params['item']['url'] . ']' . $params['item']['name'] . '[/zrl]', + '[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]' + ); + + $body = t('Name:') . ' ' . $params['item']['name'] . "\n"; + $body .= t('Photo:') . ' ' . $params['item']['photo'] . "\n"; + $body .= sprintf(t('You may visit their profile at %s'), $params['item']['url']); + + $sitelink = t('Please visit %s to approve or reject the suggestion.'); + $tsitelink = sprintf($sitelink, $siteurl); + $hsitelink = sprintf($sitelink, '' . $sitename . ''); + $itemlink = $params['link']; + } + + if ($params['type'] == NOTIFY_CONFIRM) { + // ? + } + + if ($params['type'] == NOTIFY_SYSTEM) { + // ? + } + + $h = array( + 'params' => $params, + 'subject' => $subject, + 'preamble' => $preamble, + 'epreamble' => $epreamble, + 'body' => $body, + 'sitelink' => $sitelink, + 'sitename' => $sitename, + 'tsitelink' => $tsitelink, + 'hsitelink' => $hsitelink, + 'itemlink' => $itemlink, + 'sender' => $sender, + 'recipient' => $recip + ); + + call_hooks('enotify', $h); + + $subject = $h['subject']; + $preamble = $h['preamble']; + $epreamble = $h['epreamble']; + $body = $h['body']; + $sitelink = $h['sitelink']; + $tsitelink = $h['tsitelink']; + $hsitelink = $h['hsitelink']; + $itemlink = $h['itemlink']; + + + require_once('include/html2bbcode.php'); + + do { + $dups = false; + $hash = random_string(); + $r = q( + "SELECT id FROM notify WHERE hash = '%s' LIMIT 1", + dbesc($hash) + ); + if ($r) { + $dups = true; + } + } while ($dups === true); + + + $datarray = []; + $datarray['hash'] = $hash; + $datarray['sender_hash'] = $sender['xchan_hash']; + $datarray['xname'] = $sender['xchan_name']; + $datarray['url'] = $sender['xchan_url']; + $datarray['photo'] = $sender['xchan_photo_s']; + $datarray['created'] = datetime_convert(); + $datarray['aid'] = $recip['channel_account_id']; + $datarray['uid'] = $recip['channel_id']; + $datarray['link'] = $itemlink; + $datarray['parent'] = $parent_mid; + $datarray['parent_item'] = $parent_item; + $datarray['ntype'] = $params['type']; + $datarray['verb'] = $params['verb']; + $datarray['otype'] = $params['otype']; + $datarray['abort'] = false; + + $datarray['item'] = $params['item']; + + if (LibBlock::fetch_by_entity($datarray['uid'], $datarray['sender_hash'])) { + pop_lang(); + return; + } + + if (is_array($datarray['parent_item'])) { + if (LibBlock::fetch_by_entity($datarray['uid'], $datarray['parent_item']['author_xchan']) || LibBlock::fetch_by_entity($datarray['uid'], $datarray['parent_item']['owner_xchan'])) { + pop_lang(); + return; + } + } + + call_hooks('enotify_store', $datarray); + + if ($datarray['abort']) { + pop_lang(); + return; + } + + + // create notification entry in DB + $seen = 0; + + // Mark some notifications as seen right away + // Note! The notification have to be created, because they are used to send emails + // So easiest solution to hide them from Notices is to mark them as seen right away. + // Another option would be to not add them to the DB, and change how emails are handled + // (probably would be better that way) + + if (!$always_show_in_notices) { + if (($params['type'] == NOTIFY_WALL) || ($params['type'] == NOTIFY_INTRO)) { + $seen = 1; + } + // set back to unseen for moderated wall posts + if ($params['type'] == NOTIFY_WALL && $params['item']['item_blocked'] == ITEM_MODERATED) { + $seen = 0; + } + } + + $e = q( + "select * from notify where otype = '%s' and xname = '%s' and verb = '%s' and link = '%s' and ntype = %d limit 1", + dbesc($datarray['otype']), + dbesc($datarray['xname']), + dbesc($datarray['verb']), + dbesc($datarray['link']), + intval($datarray['ntype']) + ); + if ($e) { + logger('duplicated notification'); + pop_lang(); + return; + } + + $r = q( + "insert into notify (hash,xname,url,photo,created,msg,aid,uid,link,parent,seen,ntype,verb,otype) values('%s','%s','%s','%s','%s','%s',%d,%d,'%s','%s',%d,%d,'%s','%s')", - dbesc($datarray['hash']), - dbesc($datarray['xname']), - dbesc($datarray['url']), - dbesc($datarray['photo']), - dbesc($datarray['created']), - dbesc(''), // will fill this in below after the record is created - intval($datarray['aid']), - intval($datarray['uid']), - dbesc($datarray['link']), - dbesc($datarray['parent']), - intval($seen), - intval($datarray['ntype']), - dbesc($datarray['verb']), - dbesc($datarray['otype']) - ); + dbesc($datarray['hash']), + dbesc($datarray['xname']), + dbesc($datarray['url']), + dbesc($datarray['photo']), + dbesc($datarray['created']), + dbesc(''), // will fill this in below after the record is created + intval($datarray['aid']), + intval($datarray['uid']), + dbesc($datarray['link']), + dbesc($datarray['parent']), + intval($seen), + intval($datarray['ntype']), + dbesc($datarray['verb']), + dbesc($datarray['otype']) + ); - $r = q("select id from notify where hash = '%s' and uid = %d limit 1", - dbesc($hash), - intval($recip['channel_id']) - ); - if ($r) { - $notify_id = $r[0]['id']; - } else { - logger('notification not found.'); - pop_lang(); - return; - } + $r = q( + "select id from notify where hash = '%s' and uid = %d limit 1", + dbesc($hash), + intval($recip['channel_id']) + ); + if ($r) { + $notify_id = $r[0]['id']; + } else { + logger('notification not found.'); + pop_lang(); + return; + } - $itemlink = z_root() . '/notify/view/' . $notify_id; - $msg = str_replace('$itemlink',$itemlink,$epreamble); + $itemlink = z_root() . '/notify/view/' . $notify_id; + $msg = str_replace('$itemlink', $itemlink, $epreamble); - // wretched hack, but we don't want to duplicate all the preamble variations and we also don't want to screw up a translation + // wretched hack, but we don't want to duplicate all the preamble variations and we also don't want to screw up a translation - if ((App::$language === 'en' || (! App::$language)) && strpos($msg,', ')) - $msg = substr($msg,strpos($msg,', ') + 1); + if ((App::$language === 'en' || (! App::$language)) && strpos($msg, ', ')) { + $msg = substr($msg, strpos($msg, ', ') + 1); + } - $r = q("update notify set msg = '%s' where id = %d and uid = %d", - dbesc($msg), - intval($notify_id), - intval($datarray['uid']) - ); + $r = q( + "update notify set msg = '%s' where id = %d and uid = %d", + dbesc($msg), + intval($notify_id), + intval($datarray['uid']) + ); - // send email notification if notification preferences permit + // send email notification if notification preferences permit - require_once('bbcode.php'); - if ((intval($recip['channel_notifyflags']) & intval($params['type'])) || $params['type'] == NOTIFY_SYSTEM) { + require_once('bbcode.php'); + if ((intval($recip['channel_notifyflags']) & intval($params['type'])) || $params['type'] == NOTIFY_SYSTEM) { + logger('notification: sending notification email'); - logger('notification: sending notification email'); + $hn = get_pconfig($recip['channel_id'], 'system', 'email_notify_host'); + if ($hn && (! stristr(App::get_hostname(), $hn))) { + // this isn't the email notification host + pop_lang(); + return; + } - $hn = get_pconfig($recip['channel_id'],'system','email_notify_host'); - if($hn && (! stristr(App::get_hostname(),$hn))) { - // this isn't the email notification host - pop_lang(); - return; - } + $textversion = strip_tags(html_entity_decode(bbcode(stripslashes(str_replace(array("\\r", "\\n"), array( "", "\n"), $body))), ENT_QUOTES, 'UTF-8')); - $textversion = strip_tags(html_entity_decode(bbcode(stripslashes(str_replace(array("\\r", "\\n"), array( "", "\n"), $body))),ENT_QUOTES,'UTF-8')); - - $htmlversion = bbcode(stripslashes(str_replace(array("\\r","\\n"), array("","
\n"),$body))); + $htmlversion = bbcode(stripslashes(str_replace(array("\\r","\\n"), array("","
\n"), $body))); - // use $_SESSION['zid_override'] to force zid() to use - // the recipient address instead of the current observer + // use $_SESSION['zid_override'] to force zid() to use + // the recipient address instead of the current observer - $_SESSION['zid_override'] = channel_reddress($recip); - $_SESSION['zrl_override'] = z_root() . '/channel/' . $recip['channel_address']; - - $textversion = zidify_links($textversion); - $htmlversion = zidify_links($htmlversion); + $_SESSION['zid_override'] = channel_reddress($recip); + $_SESSION['zrl_override'] = z_root() . '/channel/' . $recip['channel_address']; - // unset when done to revert to normal behaviour + $textversion = zidify_links($textversion); + $htmlversion = zidify_links($htmlversion); - unset($_SESSION['zid_override']); - unset($_SESSION['zrl_override']); + // unset when done to revert to normal behaviour - $datarray = []; - $datarray['banner'] = $banner; - $datarray['product'] = $product; - $datarray['preamble'] = $preamble; - $datarray['sitename'] = $sitename; - $datarray['siteurl'] = $siteurl; - $datarray['type'] = $params['type']; - $datarray['parent'] = $params['parent_mid']; - $datarray['source_name'] = $sender['xchan_name']; - $datarray['source_link'] = $sender['xchan_url']; - $datarray['source_photo'] = $sender['xchan_photo_s']; - $datarray['uid'] = $recip['channel_id']; - $datarray['username'] = $recip['channel_name']; - $datarray['hsitelink'] = $hsitelink; - $datarray['tsitelink'] = $tsitelink; - $datarray['hitemlink'] = '' . $itemlink . ''; - $datarray['titemlink'] = $itemlink; - $datarray['thanks'] = $thanks; - $datarray['site_admin'] = $site_admin; - $datarray['opt_out1'] = $opt_out1; - $datarray['opt_out2'] = $opt_out2; - $datarray['hopt_out2'] = $hopt_out2; - $datarray['title'] = stripslashes($title); - $datarray['htmlversion'] = $htmlversion; - $datarray['textversion'] = $textversion; - $datarray['subject'] = $subject; - $datarray['headers'] = $additional_mail_header; - $datarray['email_secure'] = false; + unset($_SESSION['zid_override']); + unset($_SESSION['zrl_override']); - call_hooks('enotify_mail', $datarray); + $datarray = []; + $datarray['banner'] = $banner; + $datarray['product'] = $product; + $datarray['preamble'] = $preamble; + $datarray['sitename'] = $sitename; + $datarray['siteurl'] = $siteurl; + $datarray['type'] = $params['type']; + $datarray['parent'] = $params['parent_mid']; + $datarray['source_name'] = $sender['xchan_name']; + $datarray['source_link'] = $sender['xchan_url']; + $datarray['source_photo'] = $sender['xchan_photo_s']; + $datarray['uid'] = $recip['channel_id']; + $datarray['username'] = $recip['channel_name']; + $datarray['hsitelink'] = $hsitelink; + $datarray['tsitelink'] = $tsitelink; + $datarray['hitemlink'] = '' . $itemlink . ''; + $datarray['titemlink'] = $itemlink; + $datarray['thanks'] = $thanks; + $datarray['site_admin'] = $site_admin; + $datarray['opt_out1'] = $opt_out1; + $datarray['opt_out2'] = $opt_out2; + $datarray['hopt_out2'] = $hopt_out2; + $datarray['title'] = stripslashes($title); + $datarray['htmlversion'] = $htmlversion; + $datarray['textversion'] = $textversion; + $datarray['subject'] = $subject; + $datarray['headers'] = $additional_mail_header; + $datarray['email_secure'] = false; - // Default to private - don't disclose message contents over insecure channels (such as email) - // Might be interesting to use GPG,PGP,S/MIME encryption instead - // but we'll save that for a clever plugin developer to implement + call_hooks('enotify_mail', $datarray); - $private_activity = false; + // Default to private - don't disclose message contents over insecure channels (such as email) + // Might be interesting to use GPG,PGP,S/MIME encryption instead + // but we'll save that for a clever plugin developer to implement - if (! $datarray['email_secure']) { - switch ($params['type']) { - case NOTIFY_WALL: - case NOTIFY_TAGSELF: - case NOTIFY_POKE: - case NOTIFY_RESHARE: - case NOTIFY_COMMENT: - if (! $private) - break; - $private_activity = true; - case NOTIFY_MAIL: - $datarray['textversion'] = $datarray['htmlversion'] = $datarray['title'] = ''; - $datarray['subject'] = preg_replace('/' . preg_quote(t('[$Projectname:Notify]'), '/') . '/','$0*',$datarray['subject']); - break; - default: - break; - } - } + $private_activity = false; - if ($private_activity - && intval(get_pconfig($datarray['uid'], 'system', 'ignore_private_notifications'))) { + if (! $datarray['email_secure']) { + switch ($params['type']) { + case NOTIFY_WALL: + case NOTIFY_TAGSELF: + case NOTIFY_POKE: + case NOTIFY_RESHARE: + case NOTIFY_COMMENT: + if (! $private) { + break; + } + $private_activity = true; + case NOTIFY_MAIL: + $datarray['textversion'] = $datarray['htmlversion'] = $datarray['title'] = ''; + $datarray['subject'] = preg_replace('/' . preg_quote(t('[$Projectname:Notify]'), '/') . '/', '$0*', $datarray['subject']); + break; + default: + break; + } + } - pop_lang(); - return; - } + if ( + $private_activity + && intval(get_pconfig($datarray['uid'], 'system', 'ignore_private_notifications')) + ) { + pop_lang(); + return; + } - // load the template for private message notifications - $tpl = get_markup_template('email_notify_html.tpl'); - $email_html_body = replace_macros($tpl,array( - '$banner' => $datarray['banner'], - '$notify_icon' => System::get_site_icon(), - '$product' => $datarray['product'], - '$preamble' => $salutation . '

' . $datarray['preamble'], - '$sitename' => $datarray['sitename'], - '$siteurl' => $datarray['siteurl'], - '$source_name' => $datarray['source_name'], - '$source_link' => $datarray['source_link'], - '$source_photo' => $datarray['source_photo'], - '$username' => $datarray['to_name'], - '$hsitelink' => $datarray['hsitelink'], - '$hitemlink' => $datarray['hitemlink'], - '$thanks' => $datarray['thanks'], - '$site_admin' => $datarray['site_admin'], - '$opt_out1' => $datarray['opt_out1'], - '$opt_out2' => $datarray['hopt_out2'], - '$title' => $datarray['title'], - '$htmlversion' => $datarray['htmlversion'], - )); + // load the template for private message notifications + $tpl = get_markup_template('email_notify_html.tpl'); + $email_html_body = replace_macros($tpl, array( + '$banner' => $datarray['banner'], + '$notify_icon' => System::get_site_icon(), + '$product' => $datarray['product'], + '$preamble' => $salutation . '

' . $datarray['preamble'], + '$sitename' => $datarray['sitename'], + '$siteurl' => $datarray['siteurl'], + '$source_name' => $datarray['source_name'], + '$source_link' => $datarray['source_link'], + '$source_photo' => $datarray['source_photo'], + '$username' => $datarray['to_name'], + '$hsitelink' => $datarray['hsitelink'], + '$hitemlink' => $datarray['hitemlink'], + '$thanks' => $datarray['thanks'], + '$site_admin' => $datarray['site_admin'], + '$opt_out1' => $datarray['opt_out1'], + '$opt_out2' => $datarray['hopt_out2'], + '$title' => $datarray['title'], + '$htmlversion' => $datarray['htmlversion'], + )); - // load the template for private message notifications - $tpl = get_markup_template('email_notify_text.tpl'); - $email_text_body = replace_macros($tpl, array( - '$banner' => $datarray['banner'], - '$product' => $datarray['product'], - '$preamble' => $salutation . "\n\n" . $datarray['preamble'], - '$sitename' => $datarray['sitename'], - '$siteurl' => $datarray['siteurl'], - '$source_name' => $datarray['source_name'], - '$source_link' => $datarray['source_link'], - '$source_photo' => $datarray['source_photo'], - '$username' => $datarray['to_name'], - '$tsitelink' => $datarray['tsitelink'], - '$titemlink' => $datarray['titemlink'], - '$thanks' => $datarray['thanks'], - '$site_admin' => $datarray['site_admin'], - '$opt_out1' => $datarray['opt_out1'], - '$opt_out2' => $datarray['opt_out2'], - '$title' => $datarray['title'], - '$textversion' => $datarray['textversion'], - )); + // load the template for private message notifications + $tpl = get_markup_template('email_notify_text.tpl'); + $email_text_body = replace_macros($tpl, array( + '$banner' => $datarray['banner'], + '$product' => $datarray['product'], + '$preamble' => $salutation . "\n\n" . $datarray['preamble'], + '$sitename' => $datarray['sitename'], + '$siteurl' => $datarray['siteurl'], + '$source_name' => $datarray['source_name'], + '$source_link' => $datarray['source_link'], + '$source_photo' => $datarray['source_photo'], + '$username' => $datarray['to_name'], + '$tsitelink' => $datarray['tsitelink'], + '$titemlink' => $datarray['titemlink'], + '$thanks' => $datarray['thanks'], + '$site_admin' => $datarray['site_admin'], + '$opt_out1' => $datarray['opt_out1'], + '$opt_out2' => $datarray['opt_out2'], + '$title' => $datarray['title'], + '$textversion' => $datarray['textversion'], + )); -// logger('text: ' . $email_text_body); + // logger('text: ' . $email_text_body); - // use the EmailNotification library to send the message + // use the EmailNotification library to send the message - $to_email = $recip['account_email']; + $to_email = $recip['account_email']; - $e = get_pconfig($recip['channel_id'],'system','notification_email', false); - if ($e) { - $to_email = $e; - } + $e = get_pconfig($recip['channel_id'], 'system', 'notification_email', false); + if ($e) { + $to_email = $e; + } - $addrs = explode(',', $to_email); + $addrs = explode(',', $to_email); - foreach($addrs as $addr) { + foreach ($addrs as $addr) { + self::send(array( + 'fromName' => $sender_name, + 'fromEmail' => $sender_email, + 'replyTo' => $reply_email, + 'toEmail' => $addr, + 'messageSubject' => $datarray['subject'], + 'htmlVersion' => $email_html_body, + 'textVersion' => $email_text_body, + 'additionalMailHeader' => $datarray['headers'], + )); + } + } - self::send(array( - 'fromName' => $sender_name, - 'fromEmail' => $sender_email, - 'replyTo' => $reply_email, - 'toEmail' => $addr, - 'messageSubject' => $datarray['subject'], - 'htmlVersion' => $email_html_body, - 'textVersion' => $email_text_body, - 'additionalMailHeader' => $datarray['headers'], - )); - } - } + pop_lang(); + } - pop_lang(); - -} - - - /** - * @brief Send a multipart/alternative message with Text and HTML versions. - * - * @param array $params an assoziative array with: - * * \e string \b fromName name of the sender - * * \e string \b fromEmail email of the sender - * * \e string \b replyTo replyTo address to direct responses - * * \e string \b toEmail destination email address - * * \e string \b messageSubject subject of the message - * * \e string \b htmlVersion html version of the message - * * \e string \b textVersion text only version of the message - * * \e string \b additionalMailHeader additions to the smtp mail header - */ - static public function send($params) { - - $params['sent'] = false; - $params['result'] = false; - - call_hooks('email_send', $params); - - if($params['sent']) { - logger("notification: enotify::send (addon) returns " . (($params['result']) ? 'success' : 'failure'), LOGGER_DEBUG); - return $params['result']; - } - - $fromName = email_header_encode(html_entity_decode($params['fromName'],ENT_QUOTES,'UTF-8'),'UTF-8'); - $messageSubject = email_header_encode(html_entity_decode($params['messageSubject'],ENT_QUOTES,'UTF-8'),'UTF-8'); - - // generate a mime boundary - $mimeBoundary = rand(0, 9) . "-" - .rand(100000000, 999999999) . "-" - .rand(100000000, 999999999) . "=:" - .rand(10000, 99999); - - // generate a multipart/alternative message header - $messageHeader = - $params['additionalMailHeader'] . - "From: $fromName <{$params['fromEmail']}>" . PHP_EOL . - "Reply-To: $fromName <{$params['replyTo']}>" . PHP_EOL . - "MIME-Version: 1.0" . PHP_EOL . - "Content-Type: multipart/alternative; boundary=\"{$mimeBoundary}\""; - - // assemble the final multipart message body with the text and html types included - $textBody = chunk_split(base64_encode($params['textVersion'])); - $htmlBody = chunk_split(base64_encode($params['htmlVersion'])); - - $multipartMessageBody = - "--" . $mimeBoundary . PHP_EOL . // plain text section - "Content-Type: text/plain; charset=UTF-8" . PHP_EOL . - "Content-Transfer-Encoding: base64" . PHP_EOL . PHP_EOL . - $textBody . PHP_EOL . - "--" . $mimeBoundary . PHP_EOL . // text/html section - "Content-Type: text/html; charset=UTF-8" . PHP_EOL . - "Content-Transfer-Encoding: base64" . PHP_EOL . PHP_EOL . - $htmlBody . PHP_EOL . - "--" . $mimeBoundary . "--" . PHP_EOL; // message ending - - // send the message - $res = mail( - $params['toEmail'], // send to address - $messageSubject, // subject - $multipartMessageBody, // message body - $messageHeader // message headers - ); - logger("notification: enotify::send returns " . (($res) ? 'success' : 'failure'), LOGGER_DEBUG); - return $res; - } - - static public function format($item) { - - $ret = ''; - - $expire = intval(get_config('system','default_expire_days')); - $expire_date = (($expire) ? datetime_convert('UTC','UTC','now - ' . $expire . ' days') : NULL_DATE); - - require_once('include/conversation.php'); - - // Call localize_item to get a one line status for activities. - // This should set $item['localize'] to indicate we have a brief summary. - // and perhaps $item['shortlocalize'] for an even briefer summary - - localize_item($item); - - if($item['shortlocalize']) { - $itemem_text = $item['shortlocalize']; - } - elseif($item['localize']) { - $itemem_text = $item['localize']; - } - else { - $itemem_text = (($item['item_thread_top']) - ? t('created a new post') - : sprintf( t('reacted to %s\'s conversation'), $item['owner']['xchan_name'])); - if($item['verb'] === 'Announce') { - $itemem_text = sprintf( t('shared %s\'s post'), $item['owner']['xchan_name']); - } - } - if ($item['item_private'] == 2) { - $itemem_text = t('sent a direct message'); - } - - $edit = false; - - if($item['edited'] > $item['created']) { - if($item['item_thread_top']) { - $itemem_text = sprintf( t('edited a post dated %s'), relative_date($item['created'])); - $edit = true; - } - else { - $itemem_text = sprintf( t('edited a comment dated %s'), relative_date($item['created'])); - $edit = true; - } - } - - if (LibBlock::fetch_by_entity(local_channel(),$item['author']['xchan_hash'])) { - return []; - } - - // convert this logic into a json array just like the system notifications - - $x = array( - 'notify_link' => $item['llink'], - 'name' => $item['author']['xchan_name'], - 'addr' => $item['author']['xchan_addr'], - 'url' => $item['author']['xchan_url'], - 'photo' => $item['author']['xchan_photo_s'], - 'when' => relative_date(($edit)? $item['edited'] : $item['created']), - 'class' => (intval($item['item_unseen']) ? 'notify-unseen' : 'notify-seen'), - 'b64mid' => ((in_array($item['verb'], [ACTIVITY_LIKE, ACTIVITY_DISLIKE])) ? gen_link_id($item['thr_parent']) : gen_link_id($item['mid'])), - 'notify_id' => 'undefined', - 'thread_top' => (($item['item_thread_top']) ? true : false), - 'message' => strip_tags(bbcode($itemem_text)), - // these are for the superblock addon - 'hash' => $item['author']['xchan_hash'], - 'uid' => local_channel(), - 'display' => true - ); - - $post_date = (($edit)? $item['edited'] : $item['created']); - if($post_date && $post_date < $expire_date) { - return []; - } - - call_hooks('enotify_format',$x); - if(! $x['display']) { - return []; - } - - return $x; - } + /** + * @brief Send a multipart/alternative message with Text and HTML versions. + * + * @param array $params an assoziative array with: + * * \e string \b fromName name of the sender + * * \e string \b fromEmail email of the sender + * * \e string \b replyTo replyTo address to direct responses + * * \e string \b toEmail destination email address + * * \e string \b messageSubject subject of the message + * * \e string \b htmlVersion html version of the message + * * \e string \b textVersion text only version of the message + * * \e string \b additionalMailHeader additions to the smtp mail header + */ + public static function send($params) + { + + $params['sent'] = false; + $params['result'] = false; + + call_hooks('email_send', $params); + + if ($params['sent']) { + logger("notification: enotify::send (addon) returns " . (($params['result']) ? 'success' : 'failure'), LOGGER_DEBUG); + return $params['result']; + } + + $fromName = email_header_encode(html_entity_decode($params['fromName'], ENT_QUOTES, 'UTF-8'), 'UTF-8'); + $messageSubject = email_header_encode(html_entity_decode($params['messageSubject'], ENT_QUOTES, 'UTF-8'), 'UTF-8'); + + // generate a mime boundary + $mimeBoundary = rand(0, 9) . "-" + . rand(100000000, 999999999) . "-" + . rand(100000000, 999999999) . "=:" + . rand(10000, 99999); + + // generate a multipart/alternative message header + $messageHeader = + $params['additionalMailHeader'] . + "From: $fromName <{$params['fromEmail']}>" . PHP_EOL . + "Reply-To: $fromName <{$params['replyTo']}>" . PHP_EOL . + "MIME-Version: 1.0" . PHP_EOL . + "Content-Type: multipart/alternative; boundary=\"{$mimeBoundary}\""; + + // assemble the final multipart message body with the text and html types included + $textBody = chunk_split(base64_encode($params['textVersion'])); + $htmlBody = chunk_split(base64_encode($params['htmlVersion'])); + + $multipartMessageBody = + "--" . $mimeBoundary . PHP_EOL . // plain text section + "Content-Type: text/plain; charset=UTF-8" . PHP_EOL . + "Content-Transfer-Encoding: base64" . PHP_EOL . PHP_EOL . + $textBody . PHP_EOL . + "--" . $mimeBoundary . PHP_EOL . // text/html section + "Content-Type: text/html; charset=UTF-8" . PHP_EOL . + "Content-Transfer-Encoding: base64" . PHP_EOL . PHP_EOL . + $htmlBody . PHP_EOL . + "--" . $mimeBoundary . "--" . PHP_EOL; // message ending + + // send the message + $res = mail( + $params['toEmail'], // send to address + $messageSubject, // subject + $multipartMessageBody, // message body + $messageHeader // message headers + ); + logger("notification: enotify::send returns " . (($res) ? 'success' : 'failure'), LOGGER_DEBUG); + return $res; + } + + public static function format($item) + { + + $ret = ''; + + $expire = intval(get_config('system', 'default_expire_days')); + $expire_date = (($expire) ? datetime_convert('UTC', 'UTC', 'now - ' . $expire . ' days') : NULL_DATE); + + require_once('include/conversation.php'); + + // Call localize_item to get a one line status for activities. + // This should set $item['localize'] to indicate we have a brief summary. + // and perhaps $item['shortlocalize'] for an even briefer summary + + localize_item($item); + + if ($item['shortlocalize']) { + $itemem_text = $item['shortlocalize']; + } elseif ($item['localize']) { + $itemem_text = $item['localize']; + } else { + $itemem_text = (($item['item_thread_top']) + ? t('created a new post') + : sprintf(t('reacted to %s\'s conversation'), $item['owner']['xchan_name'])); + if ($item['verb'] === 'Announce') { + $itemem_text = sprintf(t('shared %s\'s post'), $item['owner']['xchan_name']); + } + } + if ($item['item_private'] == 2) { + $itemem_text = t('sent a direct message'); + } + + $edit = false; + + if ($item['edited'] > $item['created']) { + if ($item['item_thread_top']) { + $itemem_text = sprintf(t('edited a post dated %s'), relative_date($item['created'])); + $edit = true; + } else { + $itemem_text = sprintf(t('edited a comment dated %s'), relative_date($item['created'])); + $edit = true; + } + } + + if (LibBlock::fetch_by_entity(local_channel(), $item['author']['xchan_hash'])) { + return []; + } + + // convert this logic into a json array just like the system notifications + + $x = array( + 'notify_link' => $item['llink'], + 'name' => $item['author']['xchan_name'], + 'addr' => $item['author']['xchan_addr'], + 'url' => $item['author']['xchan_url'], + 'photo' => $item['author']['xchan_photo_s'], + 'when' => relative_date(($edit) ? $item['edited'] : $item['created']), + 'class' => (intval($item['item_unseen']) ? 'notify-unseen' : 'notify-seen'), + 'b64mid' => ((in_array($item['verb'], [ACTIVITY_LIKE, ACTIVITY_DISLIKE])) ? gen_link_id($item['thr_parent']) : gen_link_id($item['mid'])), + 'notify_id' => 'undefined', + 'thread_top' => (($item['item_thread_top']) ? true : false), + 'message' => strip_tags(bbcode($itemem_text)), + // these are for the superblock addon + 'hash' => $item['author']['xchan_hash'], + 'uid' => local_channel(), + 'display' => true + ); + + $post_date = (($edit) ? $item['edited'] : $item['created']); + if ($post_date && $post_date < $expire_date) { + return []; + } + + call_hooks('enotify_format', $x); + if (! $x['display']) { + return []; + } + + return $x; + } } diff --git a/Zotlabs/Lib/ExtendedZip.php b/Zotlabs/Lib/ExtendedZip.php index a40110c55..3b1bea668 100644 --- a/Zotlabs/Lib/ExtendedZip.php +++ b/Zotlabs/Lib/ExtendedZip.php @@ -8,50 +8,56 @@ namespace Zotlabs\Lib; +use ZipArchive; + /** * Description of ExtendedZip * * @author andrew */ -class ExtendedZip extends \ZipArchive { - - // Member function to add a whole file system subtree to the archive - public function addTree($dirname, $localname = '') { - if ($localname) - $this->addEmptyDir($localname); - $this->_addTree($dirname, $localname); - } +class ExtendedZip extends ZipArchive +{ - // Internal function, to recurse - protected function _addTree($dirname, $localname) { - $dir = opendir($dirname); - while ($filename = readdir($dir)) { - // Discard . and .. - if ($filename == '.' || $filename == '..') - continue; + // Member function to add a whole file system subtree to the archive + public function addTree($dirname, $localname = '') + { + if ($localname) { + $this->addEmptyDir($localname); + } + $this->_addTree($dirname, $localname); + } - // Proceed according to type - $path = $dirname . '/' . $filename; - $localpath = $localname ? ($localname . '/' . $filename) : $filename; - if (is_dir($path)) { - // Directory: add & recurse - $this->addEmptyDir($localpath); - $this->_addTree($path, $localpath); - } - else if (is_file($path)) { - // File: just add - $this->addFile($path, $localpath); - } - } - closedir($dir); - } + // Internal function, to recurse + protected function _addTree($dirname, $localname) + { + $dir = opendir($dirname); + while ($filename = readdir($dir)) { + // Discard . and .. + if ($filename == '.' || $filename == '..') { + continue; + } - // Helper function - public static function zipTree($dirname, $zipFilename, $flags = 0, $localname = '') { - $zip = new self(); - $zip->open($zipFilename, $flags); - $zip->addTree($dirname, $localname); - $zip->close(); - } - + // Proceed according to type + $path = $dirname . '/' . $filename; + $localpath = $localname ? ($localname . '/' . $filename) : $filename; + if (is_dir($path)) { + // Directory: add & recurse + $this->addEmptyDir($localpath); + $this->_addTree($path, $localpath); + } elseif (is_file($path)) { + // File: just add + $this->addFile($path, $localpath); + } + } + closedir($dir); + } + + // Helper function + public static function zipTree($dirname, $zipFilename, $flags = 0, $localname = '') + { + $zip = new self(); + $zip->open($zipFilename, $flags); + $zip->addTree($dirname, $localname); + $zip->close(); + } } diff --git a/Zotlabs/Lib/Hashpath.php b/Zotlabs/Lib/Hashpath.php index e27838ced..1f8745b64 100644 --- a/Zotlabs/Lib/Hashpath.php +++ b/Zotlabs/Lib/Hashpath.php @@ -1,6 +1,6 @@ $family, 'k' => $key, 'v' => $value, 'sharing' => $sharing); - if ((! array_key_exists('iconfig',$item)) || (! is_array($item['iconfig']))) { - $item['iconfig'] = []; - } + if (is_null($idx)) { + $item['iconfig'][] = $entry; + } else { + $item['iconfig'][$idx] = $entry; + } + return $value; + } - if (array_key_exists('item_id',$item)) { - $iid = $item['item_id']; - } - else { - $iid = ((isset($item['id'])) ? $item['id'] : 0); - } - - if (array_key_exists('iconfig',$item) && is_array($item['iconfig'])) { - foreach ($item['iconfig'] as $c) { - if ($c['cat'] == $family && $c['k'] == $key) { - return $c['v']; - } - } - } - } - elseif (intval($item)) { - $iid = $item; - } + if (intval($item)) { + $iid = intval($item); + } - if (! $iid) { - return $default; - } + if (! $iid) { + return false; + } - $r = q("select * from iconfig where iid = %d and cat = '%s' and k = '%s' limit 1", - intval($iid), - dbesc($family), - dbesc($key) - ); - if ($r) { - $r[0]['v'] = unserialise($r[0]['v']); - if ($is_item) { - $item['iconfig'][] = $r[0]; - } - return $r[0]['v']; - } - return $default; - } + if (self::Get($item, $family, $key) === false) { + $r = q( + "insert into iconfig( iid, cat, k, v, sharing ) values ( %d, '%s', '%s', '%s', %d ) ", + intval($iid), + dbesc($family), + dbesc($key), + dbesc($dbvalue), + intval($sharing) + ); + } else { + $r = q( + "update iconfig set v = '%s', sharing = %d where iid = %d and cat = '%s' and k = '%s' ", + dbesc($dbvalue), + intval($sharing), + intval($iid), + dbesc($family), + dbesc($key) + ); + } - /** - * IConfig::Set(&$item, $family, $key, $value, $sharing = false); - * - * $item - item array or item id. If passed an array the iconfig meta information is - * added to the item structure (which will need to be saved with item_store eventually). - * If passed an id, the DB is updated, but may not be federated and/or cloned. - * $family - namespace of meta variable - * $key - key of meta variable - * $value - value of meta variable - * $sharing - boolean (default false); if true the meta information is propagated with the item - * to other sites/channels, mostly useful when $item is an array and has not yet been stored/delivered. - * If the meta information is added after delivery and you wish it to be shared, it may be necessary to - * alter the item edited timestamp and invoke the delivery process on the updated item. The edited - * timestamp needs to be altered in order to trigger an item_store_update() at the receiving end. - */ - + if (! $r) { + return false; + } - static public function Set(&$item, $family, $key, $value, $sharing = false) { - - $dbvalue = ((is_array($value)) ? serialise($value) : $value); - $dbvalue = ((is_bool($dbvalue)) ? intval($dbvalue) : $dbvalue); - - $is_item = false; - $idx = null; - - if(is_array($item)) { - $is_item = true; - if((! array_key_exists('iconfig',$item)) || (! is_array($item['iconfig']))) - $item['iconfig'] = []; - elseif($item['iconfig']) { - for($x = 0; $x < count($item['iconfig']); $x ++) { - if($item['iconfig'][$x]['cat'] == $family && $item['iconfig'][$x]['k'] == $key) { - $idx = $x; - } - } - } - $entry = array('cat' => $family, 'k' => $key, 'v' => $value, 'sharing' => $sharing); - - if(is_null($idx)) - $item['iconfig'][] = $entry; - else - $item['iconfig'][$idx] = $entry; - return $value; - } - - if(intval($item)) - $iid = intval($item); - - if(! $iid) - return false; - - if(self::Get($item, $family, $key) === false) { - $r = q("insert into iconfig( iid, cat, k, v, sharing ) values ( %d, '%s', '%s', '%s', %d ) ", - intval($iid), - dbesc($family), - dbesc($key), - dbesc($dbvalue), - intval($sharing) - ); - } - else { - $r = q("update iconfig set v = '%s', sharing = %d where iid = %d and cat = '%s' and k = '%s' ", - dbesc($dbvalue), - intval($sharing), - intval($iid), - dbesc($family), - dbesc($key) - ); - } - - if(! $r) - return false; - - return $value; - } + return $value; + } - static public function Delete(&$item, $family, $key) { + public static function Delete(&$item, $family, $key) + { - $is_item = false; - $idx = null; + $is_item = false; + $idx = null; - if(is_array($item)) { - $is_item = true; - if(is_array($item['iconfig'])) { - for($x = 0; $x < count($item['iconfig']); $x ++) { - if($item['iconfig'][$x]['cat'] == $family && $item['iconfig'][$x]['k'] == $key) { - unset($item['iconfig'][$x]); - } - } - // re-order the array index - $item['iconfig'] = array_values($item['iconfig']); - } - return true; - } + if (is_array($item)) { + $is_item = true; + if (is_array($item['iconfig'])) { + for ($x = 0; $x < count($item['iconfig']); $x++) { + if ($item['iconfig'][$x]['cat'] == $family && $item['iconfig'][$x]['k'] == $key) { + unset($item['iconfig'][$x]); + } + } + // re-order the array index + $item['iconfig'] = array_values($item['iconfig']); + } + return true; + } - if(intval($item)) - $iid = intval($item); + if (intval($item)) { + $iid = intval($item); + } - if(! $iid) - return false; + if (! $iid) { + return false; + } - return q("delete from iconfig where iid = %d and cat = '%s' and k = '%s' ", - intval($iid), - dbesc($family), - dbesc($key) - ); - - } - -} \ No newline at end of file + return q( + "delete from iconfig where iid = %d and cat = '%s' and k = '%s' ", + intval($iid), + dbesc($family), + dbesc($key) + ); + } +} diff --git a/Zotlabs/Lib/Img_cache.php b/Zotlabs/Lib/Img_cache.php index 5391538ad..1682d07b2 100644 --- a/Zotlabs/Lib/Img_cache.php +++ b/Zotlabs/Lib/Img_cache.php @@ -1,87 +1,83 @@ = self::$cache_life) { - Run::Summon( [ 'Cache_image', $url, $path ] ); - return false; - } - else { - return ((filesize($path)) ? true : false); - } - } - - // Cache_image invokes url_to_cache() as a background task - - Run::Summon( [ 'Cache_image', $url, $path ] ); - return false; - } + public static function check($url, $prefix = '.') + { - static function url_to_cache($url,$file) { + if (strpos($url, z_root()) !== false) { + return false; + } - $fp = fopen($file,'wb'); + $path = self::get_filename($url, $prefix); + if (file_exists($path)) { + $t = filemtime($path); + if ($t && time() - $t >= self::$cache_life) { + Run::Summon(['Cache_image', $url, $path]); + return false; + } else { + return ((filesize($path)) ? true : false); + } + } - if (! $fp) { - logger('failed to open storage file: ' . $file,LOGGER_NORMAL,LOG_ERR); - return false; - } + // Cache_image invokes url_to_cache() as a background task - // don't check certs, and since we're running in the background, - // allow a two-minute timeout rather than the default one minute. - // This is a compromise. We want to cache all the slow sites we can, - // but don't want to rack up too many processes doing so. - - $redirects = 0; - $x = z_fetch_url($url,true,$redirects,[ 'filep' => $fp, 'novalidate' => true, 'timeout' => 120 ]); + Run::Summon(['Cache_image', $url, $path]); + return false; + } - fclose($fp); - - if ($x['success'] && file_exists($file)) { - $i = @getimagesize($file); - if ($i && $i[2]) { // looking for non-zero imagetype - Run::Summon( [ 'CacheThumb' , basename($file) ] ); - return true; - } - } + public static function url_to_cache($url, $file) + { - // We could not cache the image for some reason. Leave an empty file here - // to provide a record of the attempt. We'll use this as a flag to avoid - // doing it again repeatedly. + $fp = fopen($file, 'wb'); - file_put_contents($file, EMPTY_STR); - logger('cache failed from ' . $url); - return false; - } + if (!$fp) { + logger('failed to open storage file: ' . $file, LOGGER_NORMAL, LOG_ERR); + return false; + } + // don't check certs, and since we're running in the background, + // allow a two-minute timeout rather than the default one minute. + // This is a compromise. We want to cache all the slow sites we can, + // but don't want to rack up too many processes doing so. + + $redirects = 0; + $x = z_fetch_url($url, true, $redirects, ['filep' => $fp, 'novalidate' => true, 'timeout' => 120]); + + fclose($fp); + + if ($x['success'] && file_exists($file)) { + $i = @getimagesize($file); + if ($i && $i[2]) { // looking for non-zero imagetype + Run::Summon(['CacheThumb', basename($file)]); + return true; + } + } + + // We could not cache the image for some reason. Leave an empty file here + // to provide a record of the attempt. We'll use this as a flag to avoid + // doing it again repeatedly. + + file_put_contents($file, EMPTY_STR); + logger('cache failed from ' . $url); + return false; + } } - - - - - - - diff --git a/Zotlabs/Lib/Img_filesize.php b/Zotlabs/Lib/Img_filesize.php index 196697733..6ca8a6cd1 100644 --- a/Zotlabs/Lib/Img_filesize.php +++ b/Zotlabs/Lib/Img_filesize.php @@ -2,51 +2,56 @@ namespace Zotlabs\Lib; -class Img_filesize { +class Img_filesize +{ - private $url; + private $url; - function __construct($url) { - $this->url = $url; - } + public function __construct($url) + { + $this->url = $url; + } - function getSize() { - $size = null; + public function getSize() + { + $size = null; - if(stripos($this->url,z_root() . '/photo') !== false) { - $size = self::getLocalFileSize($this->url); - } - if(! $size) { - $size = getRemoteFileSize($this->url); - } + if (stripos($this->url, z_root() . '/photo') !== false) { + $size = self::getLocalFileSize($this->url); + } + if (!$size) { + $size = getRemoteFileSize($this->url); + } - return $size; - } + return $size; + } - static function getLocalFileSize($url) { - - $fname = basename($url); - $resolution = 0; - - if(strpos($fname,'.') !== false) - $fname = substr($fname,0,strpos($fname,'.')); - - if(substr($fname,-2,1) == '-') { - $resolution = intval(substr($fname,-1,1)); - $fname = substr($fname,0,-2); - } - - $r = q("SELECT filesize FROM photo WHERE resource_id = '%s' AND imgscale = %d LIMIT 1", - dbesc($fname), - intval($resolution) - ); - if($r) { - return $r[0]['filesize']; - } - return null; - } + public static function getLocalFileSize($url) + { + $fname = basename($url); + $resolution = 0; + + if (strpos($fname, '.') !== false) { + $fname = substr($fname, 0, strpos($fname, '.')); + } + + if (substr($fname, -2, 1) == '-') { + $resolution = intval(substr($fname, -1, 1)); + $fname = substr($fname, 0, -2); + } + + $r = q( + "SELECT filesize FROM photo WHERE resource_id = '%s' AND imgscale = %d LIMIT 1", + dbesc($fname), + intval($resolution) + ); + if ($r) { + return $r[0]['filesize']; + } + return null; + } } /** @@ -78,7 +83,7 @@ function getRemoteFileSize($url) curl_setopt($ch, CURLOPT_VERBOSE, 0); // set to 1 to debug curl_setopt($ch, CURLOPT_STDERR, fopen('php://output', 'r')); - curl_setopt($ch, CURLOPT_HEADERFUNCTION, function($curl, $line) use (&$in_headers, &$size) { + curl_setopt($ch, CURLOPT_HEADERFUNCTION, function ($curl, $line) use (&$in_headers, &$size) { $length = strlen($line); if (trim($line) == '') { @@ -93,7 +98,7 @@ function getRemoteFileSize($url) list($rng, $s) = explode('/', $content, 2); $size = (int)$s; return 0; // aborts transfer - } else if ($header == 'content-length' && 206 != curl_getinfo($curl, CURLINFO_HTTP_CODE)) { + } elseif ($header == 'content-length' && 206 != curl_getinfo($curl, CURLINFO_HTTP_CODE)) { // found content-length header and this is not a 206 Partial Content response (range response) $size = (int)$content; return 0; @@ -103,7 +108,7 @@ function getRemoteFileSize($url) } }); - curl_setopt($ch, CURLOPT_WRITEFUNCTION, function($curl, $data) use ($in_headers) { + curl_setopt($ch, CURLOPT_WRITEFUNCTION, function ($curl, $data) use ($in_headers) { if (!$in_headers) { // shouldn't be here unless we couldn't determine file size // abort transfer @@ -116,7 +121,7 @@ function getRemoteFileSize($url) curl_exec($ch); curl_getinfo($ch); - curl_close($ch); + curl_close($ch); return $size; -} \ No newline at end of file +} diff --git a/Zotlabs/Lib/JSalmon.php b/Zotlabs/Lib/JSalmon.php index f31415711..acd7ddf10 100644 --- a/Zotlabs/Lib/JSalmon.php +++ b/Zotlabs/Lib/JSalmon.php @@ -4,69 +4,70 @@ namespace Zotlabs\Lib; use Zotlabs\Web\HTTPSig; -class JSalmon { +class JSalmon +{ - static function sign($data,$key_id,$key,$data_type = 'application/x-nomad+json') { + public static function sign($data, $key_id, $key, $data_type = 'application/x-nomad+json') + { - $data = base64url_encode(json_encode($data,true),true); // strip padding - $encoding = 'base64url'; - $algorithm = 'RSA-SHA256'; + $data = base64url_encode(json_encode($data, true), true); // strip padding + $encoding = 'base64url'; + $algorithm = 'RSA-SHA256'; - $data = preg_replace('/\s+/','',$data); + $data = preg_replace('/\s+/', '', $data); - // precomputed base64url encoding of data_type, encoding, algorithm concatenated with periods + // precomputed base64url encoding of data_type, encoding, algorithm concatenated with periods - $precomputed = '.' . base64url_encode($data_type,true) . '.YmFzZTY0dXJs.UlNBLVNIQTI1Ng'; + $precomputed = '.' . base64url_encode($data_type, true) . '.YmFzZTY0dXJs.UlNBLVNIQTI1Ng'; - $signature = base64url_encode(Crypto::sign($data . $precomputed, $key), true); + $signature = base64url_encode(Crypto::sign($data . $precomputed, $key), true); - return ([ - 'signed' => true, - 'data' => $data, - 'data_type' => $data_type, - 'encoding' => $encoding, - 'alg' => $algorithm, - 'sigs' => [ - 'value' => $signature, - 'key_id' => base64url_encode($key_id, true) - ] - ]); + return ([ + 'signed' => true, + 'data' => $data, + 'data_type' => $data_type, + 'encoding' => $encoding, + 'alg' => $algorithm, + 'sigs' => [ + 'value' => $signature, + 'key_id' => base64url_encode($key_id, true) + ] + ]); + } - } + public static function verify($x) + { - static function verify($x) { + logger('verify'); + $ret = ['results' => []]; - logger('verify'); - $ret = [ 'results' => [] ]; + if (!is_array($x)) { + return false; + } + if (!(array_key_exists('signed', $x) && $x['signed'])) { + return false; + } - if(! is_array($x)) { - return false; - } - if(! ( array_key_exists('signed',$x) && $x['signed'])) { - return false; - } + $signed_data = preg_replace('/\s+/', '', $x['data']) . '.' + . base64url_encode($x['data_type'], true) . '.' + . base64url_encode($x['encoding'], true) . '.' + . base64url_encode($x['alg'], true); - $signed_data = preg_replace('/\s+/','',$x['data']) . '.' - . base64url_encode($x['data_type'],true) . '.' - . base64url_encode($x['encoding'],true) . '.' - . base64url_encode($x['alg'],true); + $key = HTTPSig::get_key(EMPTY_STR, 'zot6', base64url_decode($x['sigs']['key_id'])); + logger('key: ' . print_r($key, true)); + if ($key['portable_id'] && $key['public_key']) { + if (Crypto::verify($signed_data, base64url_decode($x['sigs']['value']), $key['public_key'])) { + logger('verified'); + $ret = ['success' => true, 'signer' => $key['portable_id'], 'hubloc' => $key['hubloc']]; + } + } - $key = HTTPSig::get_key(EMPTY_STR,'zot6',base64url_decode($x['sigs']['key_id'])); - logger('key: ' . print_r($key,true)); - if($key['portable_id'] && $key['public_key']) { - if(Crypto::verify($signed_data,base64url_decode($x['sigs']['value']),$key['public_key'])) { - logger('verified'); - $ret = [ 'success' => true, 'signer' => $key['portable_id'], 'hubloc' => $key['hubloc'] ]; - } - } + return $ret; + } - return $ret; + public static function unpack($data) + { + return json_decode(base64url_decode($data), true); + } +} - } - - static function unpack($data) { - return json_decode(base64url_decode($data),true); - } - - -} \ No newline at end of file diff --git a/Zotlabs/Lib/Keyutils.php b/Zotlabs/Lib/Keyutils.php index 616ecfcf6..67c6f85fc 100644 --- a/Zotlabs/Lib/Keyutils.php +++ b/Zotlabs/Lib/Keyutils.php @@ -9,91 +9,94 @@ use phpseclib\Math\BigInteger; * Keyutils * Convert RSA keys between various formats */ -class Keyutils { +class Keyutils +{ - /** - * @param string $m modulo - * @param string $e exponent - * @return string - */ - public static function meToPem($m, $e) { + /** + * @param string $m modulo + * @param string $e exponent + * @return string + */ + public static function meToPem($m, $e) + { - $rsa = new RSA(); - $rsa->loadKey([ - 'e' => new BigInteger($e, 256), - 'n' => new BigInteger($m, 256) - ]); - return $rsa->getPublicKey(); + $rsa = new RSA(); + $rsa->loadKey([ + 'e' => new BigInteger($e, 256), + 'n' => new BigInteger($m, 256) + ]); + return $rsa->getPublicKey(); + } - } + /** + * @param string key + * @return string + */ + public static function rsaToPem($key) + { - /** - * @param string key - * @return string - */ - public static function rsaToPem($key) { + $rsa = new RSA(); + $rsa->setPublicKey($key); - $rsa = new RSA(); - $rsa->setPublicKey($key); + return $rsa->getPublicKey(RSA::PUBLIC_FORMAT_PKCS8); + } - return $rsa->getPublicKey(RSA::PUBLIC_FORMAT_PKCS8); + /** + * @param string key + * @return string + */ + public static function pemToRsa($key) + { - } + $rsa = new RSA(); + $rsa->setPublicKey($key); - /** - * @param string key - * @return string - */ - public static function pemToRsa($key) { + return $rsa->getPublicKey(RSA::PUBLIC_FORMAT_PKCS1); + } - $rsa = new RSA(); - $rsa->setPublicKey($key); + /** + * @param string $key key + * @param string $m reference modulo + * @param string $e reference exponent + */ + public static function pemToMe($key, &$m, &$e) + { - return $rsa->getPublicKey(RSA::PUBLIC_FORMAT_PKCS1); + $rsa = new RSA(); + $rsa->loadKey($key); + $rsa->setPublicKey(); - } + $m = $rsa->modulus->toBytes(); + $e = $rsa->exponent->toBytes(); + } - /** - * @param string $key key - * @param string $m reference modulo - * @param string $e reference exponent - */ - public static function pemToMe($key, &$m, &$e) { + /** + * @param string $pubkey + * @return string + */ + public static function salmonKey($pubkey) + { + self::pemToMe($pubkey, $m, $e); + return 'RSA' . '.' . base64url_encode($m, true) . '.' . base64url_encode($e, true); + } - $rsa = new RSA(); - $rsa->loadKey($key); - $rsa->setPublicKey(); + /** + * @param string $key + * @return string + */ + public static function convertSalmonKey($key) + { + if (strstr($key, ',')) { + $rawkey = substr($key, strpos($key, ',') + 1); + } else { + $rawkey = substr($key, 5); + } - $m = $rsa->modulus->toBytes(); - $e = $rsa->exponent->toBytes(); + $key_info = explode('.', $rawkey); - } + $m = base64url_decode($key_info[1]); + $e = base64url_decode($key_info[2]); - /** - * @param string $pubkey - * @return string - */ - public static function salmonKey($pubkey) { - self::pemToMe($pubkey, $m, $e); - return 'RSA' . '.' . base64url_encode($m, true) . '.' . base64url_encode($e, true); - } - - /** - * @param string $key - * @return string - */ - public static function convertSalmonKey($key) { - if (strstr($key, ',')) - $rawkey = substr($key, strpos($key, ',') + 1); - else - $rawkey = substr($key, 5); - - $key_info = explode('.', $rawkey); - - $m = base64url_decode($key_info[1]); - $e = base64url_decode($key_info[2]); - - return self::meToPem($m, $e); - } - -} \ No newline at end of file + return self::meToPem($m, $e); + } +} diff --git a/Zotlabs/Lib/LDSignatures.php b/Zotlabs/Lib/LDSignatures.php index c03cb7865..ae88db9cd 100644 --- a/Zotlabs/Lib/LDSignatures.php +++ b/Zotlabs/Lib/LDSignatures.php @@ -2,131 +2,137 @@ namespace Zotlabs\Lib; +use Exception; use Zotlabs\Lib\Activity; require_once('library/jsonld/jsonld.php'); -class LDSignatures { +class LDSignatures +{ - static function verify($data,$pubkey) { + public static function verify($data, $pubkey) + { - $ohash = self::hash(self::signable_options($data['signature'])); - $dhash = self::hash(self::signable_data($data)); + $ohash = self::hash(self::signable_options($data['signature'])); + $dhash = self::hash(self::signable_data($data)); - $x = Crypto::verify($ohash . $dhash,base64_decode($data['signature']['signatureValue']), $pubkey); - logger('LD-verify: ' . intval($x)); + $x = Crypto::verify($ohash . $dhash, base64_decode($data['signature']['signatureValue']), $pubkey); + logger('LD-verify: ' . intval($x)); - return $x; - } + return $x; + } - static function dopplesign(&$data,$channel) { - // remove for the time being - performance issues - // $data['magicEnv'] = self::salmon_sign($data,$channel); - return self::sign($data,$channel); - } + public static function dopplesign(&$data, $channel) + { + // remove for the time being - performance issues + // $data['magicEnv'] = self::salmon_sign($data,$channel); + return self::sign($data, $channel); + } - static function sign($data,$channel) { + public static function sign($data, $channel) + { - $options = [ - 'type' => 'RsaSignature2017', - 'nonce' => random_string(64), - 'creator' => channel_url($channel), - 'created' => datetime_convert('UTC','UTC', 'now', 'Y-m-d\TH:i:s\Z') - ]; + $options = [ + 'type' => 'RsaSignature2017', + 'nonce' => random_string(64), + 'creator' => channel_url($channel), + 'created' => datetime_convert('UTC', 'UTC', 'now', 'Y-m-d\TH:i:s\Z') + ]; - $ohash = self::hash(self::signable_options($options)); - $dhash = self::hash(self::signable_data($data)); - $options['signatureValue'] = base64_encode(Crypto::sign($ohash . $dhash,$channel['channel_prvkey'])); + $ohash = self::hash(self::signable_options($options)); + $dhash = self::hash(self::signable_data($data)); + $options['signatureValue'] = base64_encode(Crypto::sign($ohash . $dhash, $channel['channel_prvkey'])); - return $options; - } + return $options; + } - static function signable_data($data) { + public static function signable_data($data) + { - $newdata = []; - if($data) { - foreach($data as $k => $v) { - if(! in_array($k,[ 'signature' ])) { - $newdata[$k] = $v; - } - } - } - return json_encode($newdata,JSON_UNESCAPED_SLASHES); - } + $newdata = []; + if ($data) { + foreach ($data as $k => $v) { + if (!in_array($k, ['signature'])) { + $newdata[$k] = $v; + } + } + } + return json_encode($newdata, JSON_UNESCAPED_SLASHES); + } - static function signable_options($options) { + public static function signable_options($options) + { - $newopts = [ '@context' => 'https://w3id.org/identity/v1' ]; - if($options) { - foreach($options as $k => $v) { - if(! in_array($k,[ 'type','id','signatureValue' ])) { - $newopts[$k] = $v; - } - } - } - return json_encode($newopts,JSON_UNESCAPED_SLASHES); - } + $newopts = ['@context' => 'https://w3id.org/identity/v1']; + if ($options) { + foreach ($options as $k => $v) { + if (!in_array($k, ['type', 'id', 'signatureValue'])) { + $newopts[$k] = $v; + } + } + } + return json_encode($newopts, JSON_UNESCAPED_SLASHES); + } - static function hash($obj) { + public static function hash($obj) + { - return hash('sha256',self::normalise($obj)); - } + return hash('sha256', self::normalise($obj)); + } - static function normalise($data) { - if(is_string($data)) { - $data = json_decode($data); - } + public static function normalise($data) + { + if (is_string($data)) { + $data = json_decode($data); + } - if(! is_object($data)) - return ''; + if (!is_object($data)) { + return ''; + } - jsonld_set_document_loader('jsonld_document_loader'); - - try { - $d = jsonld_normalize($data,[ 'algorithm' => 'URDNA2015', 'format' => 'application/nquads' ]); - } - catch (\Exception $e) { - // Don't log the exception - this can exhaust memory - // logger('normalise error:' . print_r($e,true)); - logger('normalise error: ' . print_r($data,true)); - } + jsonld_set_document_loader('jsonld_document_loader'); - return $d; - } + try { + $d = jsonld_normalize($data, ['algorithm' => 'URDNA2015', 'format' => 'application/nquads']); + } catch (Exception $e) { + // Don't log the exception - this can exhaust memory + // logger('normalise error:' . print_r($e,true)); + logger('normalise error: ' . print_r($data, true)); + } - static function salmon_sign($data,$channel) { + return $d; + } - $arr = $data; - $data = json_encode($data,JSON_UNESCAPED_SLASHES); - $data = base64url_encode($data, false); // do not strip padding - $data_type = 'application/activity+json'; - $encoding = 'base64url'; - $algorithm = 'RSA-SHA256'; - $keyhash = base64url_encode(channel_url($channel)); + public static function salmon_sign($data, $channel) + { - $data = str_replace(array(" ","\t","\r","\n"),array("","","",""),$data); + $arr = $data; + $data = json_encode($data, JSON_UNESCAPED_SLASHES); + $data = base64url_encode($data, false); // do not strip padding + $data_type = 'application/activity+json'; + $encoding = 'base64url'; + $algorithm = 'RSA-SHA256'; + $keyhash = base64url_encode(channel_url($channel)); - // precomputed base64url encoding of data_type, encoding, algorithm concatenated with periods + $data = str_replace(array(" ", "\t", "\r", "\n"), array("", "", "", ""), $data); - $precomputed = '.' . base64url_encode($data_type,false) . '.YmFzZTY0dXJs.UlNBLVNIQTI1Ng=='; + // precomputed base64url encoding of data_type, encoding, algorithm concatenated with periods - $signature = base64url_encode(Crypto::sign($data . $precomputed,$channel['channel_prvkey'])); + $precomputed = '.' . base64url_encode($data_type, false) . '.YmFzZTY0dXJs.UlNBLVNIQTI1Ng=='; - return ([ - 'id' => $arr['id'], - 'meData' => $data, - 'meDataType' => $data_type, - 'meEncoding' => $encoding, - 'meAlgorithm' => $algorithm, - 'meCreator' => channel_url($channel), - 'meSignatureValue' => $signature - ]); + $signature = base64url_encode(Crypto::sign($data . $precomputed, $channel['channel_prvkey'])); - } - - - -} \ No newline at end of file + return ([ + 'id' => $arr['id'], + 'meData' => $data, + 'meDataType' => $data_type, + 'meEncoding' => $encoding, + 'meAlgorithm' => $algorithm, + 'meCreator' => channel_url($channel), + 'meSignatureValue' => $signature + ]); + } +} diff --git a/Zotlabs/Lib/LibBlock.php b/Zotlabs/Lib/LibBlock.php index 21509eb96..0e79ea627 100644 --- a/Zotlabs/Lib/LibBlock.php +++ b/Zotlabs/Lib/LibBlock.php @@ -2,109 +2,117 @@ namespace Zotlabs\Lib; +class LibBlock +{ -class LibBlock { + public static $cache = []; + public static $empty = []; - static $cache = []; - static $empty = []; + // This limits the number of DB queries for fetch_by_entity to once per page load. - // This limits the number of DB queries for fetch_by_entity to once per page load. - - static function fetch_from_cache($channel_id,$entity) { - if (! isset(self::$cache[$channel_id])) { - if (! isset(self::$empty[$channel_id])) { - self::$cache[$channel_id] = self::fetch($channel_id); - if (! self::$cache[$channel_id]) { - self::$empty[$channel_id] = true; - } - } - } - if (isset(self::$cache[$channel_id]) && self::$cache[$channel_id] && is_array(self::$cache[$channel_id])) { - foreach (self::$cache[$channel_id] as $entry) { - if (is_array($entry) && strcasecmp($entry['block_entity'],$entity) === 0) { - return $entry; - } - } - } - return false; - } + public static function fetch_from_cache($channel_id, $entity) + { + if (!isset(self::$cache[$channel_id])) { + if (!isset(self::$empty[$channel_id])) { + self::$cache[$channel_id] = self::fetch($channel_id); + if (!self::$cache[$channel_id]) { + self::$empty[$channel_id] = true; + } + } + } + if (isset(self::$cache[$channel_id]) && self::$cache[$channel_id] && is_array(self::$cache[$channel_id])) { + foreach (self::$cache[$channel_id] as $entry) { + if (is_array($entry) && strcasecmp($entry['block_entity'], $entity) === 0) { + return $entry; + } + } + } + return false; + } - static function store($arr) { + public static function store($arr) + { - $arr['block_entity'] = trim($arr['block_entity']); + $arr['block_entity'] = trim($arr['block_entity']); - if (! $arr['block_entity']) { - return false; - } - - $arr['block_channel_id'] = ((array_key_exists('block_channel_id',$arr)) ? intval($arr['block_channel_id']) : 0); - $arr['block_type'] = ((array_key_exists('block_type',$arr)) ? intval($arr['block_type']) : BLOCKTYPE_CHANNEL ); - $arr['block_comment'] = ((array_key_exists('block_comment',$arr)) ? escape_tags(trim($arr['block_comment'])) : EMPTY_STR); + if (!$arr['block_entity']) { + return false; + } - if (! intval($arr['block_id'])) { - $r = q("select * from block where block_channel_id = %d and block_entity = '%s' and block_type = %d limit 1", - intval($arr['block_channel_id']), - dbesc($arr['block_entity']), - intval($arr['block_type']) - ); - if ($r) { - $arr['block_id'] = $r[0]['block_id']; - } - } + $arr['block_channel_id'] = ((array_key_exists('block_channel_id', $arr)) ? intval($arr['block_channel_id']) : 0); + $arr['block_type'] = ((array_key_exists('block_type', $arr)) ? intval($arr['block_type']) : BLOCKTYPE_CHANNEL); + $arr['block_comment'] = ((array_key_exists('block_comment', $arr)) ? escape_tags(trim($arr['block_comment'])) : EMPTY_STR); - if (intval($arr['block_id'])) { - return q("UPDATE block set block_channel_id = %d, block_entity = '%s', block_type = %d, block_comment = '%s' where block_id = %d", - intval($arr['block_channel_id']), - dbesc($arr['block_entity']), - intval($arr['block_type']), - dbesc($arr['block_comment']), - intval($arr['block_id']) - ); - } - else { - return create_table_from_array('block',$arr); - } - } + if (!intval($arr['block_id'])) { + $r = q( + "select * from block where block_channel_id = %d and block_entity = '%s' and block_type = %d limit 1", + intval($arr['block_channel_id']), + dbesc($arr['block_entity']), + intval($arr['block_type']) + ); + if ($r) { + $arr['block_id'] = $r[0]['block_id']; + } + } - static function remove($channel_id,$entity) { - return q("delete from block where block_channel_id = %d and block_entity = '%s'", - intval($channel_id), - dbesc($entity) - ); - } + if (intval($arr['block_id'])) { + return q( + "UPDATE block set block_channel_id = %d, block_entity = '%s', block_type = %d, block_comment = '%s' where block_id = %d", + intval($arr['block_channel_id']), + dbesc($arr['block_entity']), + intval($arr['block_type']), + dbesc($arr['block_comment']), + intval($arr['block_id']) + ); + } else { + return create_table_from_array('block', $arr); + } + } - static function fetch_by_id($channel_id,$id) { - if (! intval($channel_id)) { - return false; - } - $r = q("select * from block where block_channel_id = %d and block_id = %d ", - intval($channel_id) - ); - return (($r) ? array_shift($r) : $r); - } + public static function remove($channel_id, $entity) + { + return q( + "delete from block where block_channel_id = %d and block_entity = '%s'", + intval($channel_id), + dbesc($entity) + ); + } + + public static function fetch_by_id($channel_id, $id) + { + if (!intval($channel_id)) { + return false; + } + $r = q( + "select * from block where block_channel_id = %d and block_id = %d ", + intval($channel_id) + ); + return (($r) ? array_shift($r) : $r); + } - static function fetch_by_entity($channel_id,$entity) { - if (! intval($channel_id)) { - return false; - } + public static function fetch_by_entity($channel_id, $entity) + { + if (!intval($channel_id)) { + return false; + } - return self::fetch_from_cache($channel_id,$entity); - - } + return self::fetch_from_cache($channel_id, $entity); + } - static function fetch($channel_id,$type = false) { - if (! intval($channel_id)) { - return []; - } + public static function fetch($channel_id, $type = false) + { + if (!intval($channel_id)) { + return []; + } - $sql_extra = (($type === false) ? EMPTY_STR : " and block_type = " . intval($type)); - - $r = q("select * from block where block_channel_id = %d $sql_extra", - intval($channel_id) - ); - return $r; - } + $sql_extra = (($type === false) ? EMPTY_STR : " and block_type = " . intval($type)); -} \ No newline at end of file + $r = q( + "select * from block where block_channel_id = %d $sql_extra", + intval($channel_id) + ); + return $r; + } +} diff --git a/Zotlabs/Lib/Libprofile.php b/Zotlabs/Lib/Libprofile.php index 622345901..2ce56d17d 100644 --- a/Zotlabs/Lib/Libprofile.php +++ b/Zotlabs/Lib/Libprofile.php @@ -4,608 +4,667 @@ namespace Zotlabs\Lib; use App; -class Libprofile { +class Libprofile +{ + /** + * @brief Loads a profile into the App structure. + * + * The function requires the nickname of a valid channel. + * + * Permissions of the current observer are checked. If a restricted profile is available + * to the current observer, that will be loaded instead of the channel default profile. + * + * The channel owner can set $profile to a valid profile_guid to preview that profile. + * + * The channel default theme is also selected for use, unless over-riden elsewhere. + * + * @param string $nickname + * @param string $profile_guid + */ - /** - * @brief Loads a profile into the App structure. - * - * The function requires the nickname of a valid channel. - * - * Permissions of the current observer are checked. If a restricted profile is available - * to the current observer, that will be loaded instead of the channel default profile. - * - * The channel owner can set $profile to a valid profile_guid to preview that profile. - * - * The channel default theme is also selected for use, unless over-riden elsewhere. - * - * @param string $nickname - * @param string $profile_guid - */ + public static function load($nickname, $profile = '') + { - static function load($nickname, $profile = '') { + // logger('Libprofile::load: ' . $nickname . (($profile) ? ' profile: ' . $profile : '')); - // logger('Libprofile::load: ' . $nickname . (($profile) ? ' profile: ' . $profile : '')); + $channel = channelx_by_nick($nickname); - $channel = channelx_by_nick($nickname); - - if (! $channel) { - logger('profile error: ' . App::$query_string, LOGGER_DEBUG); - notice( t('Requested channel is not available.') . EOL ); - App::$error = 404; - return; - } + if (!$channel) { + logger('profile error: ' . App::$query_string, LOGGER_DEBUG); + notice(t('Requested channel is not available.') . EOL); + App::$error = 404; + return; + } - // get the current observer - $observer = App::get_observer(); + // get the current observer + $observer = App::get_observer(); - $can_view_profile = true; + $can_view_profile = true; - // Can the observer see our profile? - require_once('include/permissions.php'); - if (! perm_is_allowed($channel['channel_id'],(($observer) ? $observer['xchan_hash'] : ''),'view_profile')) { - $can_view_profile = false; - } + // Can the observer see our profile? + require_once('include/permissions.php'); + if (!perm_is_allowed($channel['channel_id'], (($observer) ? $observer['xchan_hash'] : ''), 'view_profile')) { + $can_view_profile = false; + } - if (! $profile) { - $r = q("SELECT abook_profile FROM abook WHERE abook_xchan = '%s' and abook_channel = '%d' limit 1", - dbesc(($observer) ? $observer['xchan_hash'] : ''), - intval($channel['channel_id']) - ); - if ($r) - $profile = $r[0]['abook_profile']; - } + if (!$profile) { + $r = q( + "SELECT abook_profile FROM abook WHERE abook_xchan = '%s' and abook_channel = '%d' limit 1", + dbesc(($observer) ? $observer['xchan_hash'] : ''), + intval($channel['channel_id']) + ); + if ($r) { + $profile = $r[0]['abook_profile']; + } + } - $p = null; + $p = null; - if ($profile) { - $p = q("SELECT profile.uid AS profile_uid, profile.*, channel.* FROM profile + if ($profile) { + $p = q( + "SELECT profile.uid AS profile_uid, profile.*, channel.* FROM profile LEFT JOIN channel ON profile.uid = channel.channel_id WHERE channel.channel_address = '%s' AND profile.profile_guid = '%s' LIMIT 1", - dbesc($nickname), - dbesc($profile) - ); - if (! $p) { - $p = q("SELECT profile.uid AS profile_uid, profile.*, channel.* FROM profile + dbesc($nickname), + dbesc($profile) + ); + if (!$p) { + $p = q( + "SELECT profile.uid AS profile_uid, profile.*, channel.* FROM profile LEFT JOIN channel ON profile.uid = channel.channel_id WHERE channel.channel_address = '%s' AND profile.id = %d LIMIT 1", - dbesc($nickname), - intval($profile) - ); - } - } + dbesc($nickname), + intval($profile) + ); + } + } - if (! $p) { - $p = q("SELECT profile.uid AS profile_uid, profile.*, channel.* FROM profile + if (!$p) { + $p = q( + "SELECT profile.uid AS profile_uid, profile.*, channel.* FROM profile LEFT JOIN channel ON profile.uid = channel.channel_id WHERE channel.channel_address = '%s' and channel_removed = 0 AND profile.is_default = 1 LIMIT 1", - dbesc($nickname) - ); - } - - if (! $p) { - logger('profile error: ' . App::$query_string, LOGGER_DEBUG); - notice( t('Requested profile is not available.') . EOL ); - App::$error = 404; - return; - } - - $q = q("select * from profext where hash = '%s' and channel_id = %d", - dbesc($p[0]['profile_guid']), - intval($p[0]['profile_uid']) - ); - if ($q) { - - $extra_fields = []; - - $profile_fields_basic = get_profile_fields_basic(); - $profile_fields_advanced = get_profile_fields_advanced(); - - $advanced = ((feature_enabled(local_channel(),'advanced_profiles')) ? true : false); - if ($advanced) - $fields = $profile_fields_advanced; - else - $fields = $profile_fields_basic; - - foreach ($q as $qq) { - foreach ($fields as $k => $f) { - if ($k == $qq['k']) { - $p[0][$k] = $qq['v']; - $extra_fields[] = $k; - break; - } - } - } - } - - $p[0]['extra_fields'] = ((isset($extra_fields)) ? $extra_fields : []); - - $z = q("select xchan_photo_date, xchan_addr from xchan where xchan_hash = '%s' limit 1", - dbesc($p[0]['channel_hash']) - ); - if ($z) { - $p[0]['picdate'] = $z[0]['xchan_photo_date']; - $p[0]['reddress'] = str_replace('@','@',unpunify($z[0]['xchan_addr'])); - } - - // fetch user tags if this isn't the default profile - - if (! $p[0]['is_default']) { - $x = q("select keywords from profile where uid = %d and is_default = 1 limit 1", - intval($p[0]['profile_uid']) - ); - if ($x && $can_view_profile) - $p[0]['keywords'] = $x[0]['keywords']; - } - - if ($p[0]['keywords']) { - $keywords = str_replace(array('#',',',' ',',,'),array('',' ',',',','),$p[0]['keywords']); - if (strlen($keywords) && $can_view_profile) { - if (! isset(App::$page['htmlhead'])) { - App::$page['htmlhead'] = EMPTY_STR; - } - App::$page['htmlhead'] .= '' . "\r\n" ; - } - } - - App::$profile = $p[0]; - App::$profile_uid = $p[0]['profile_uid']; - App::$page['title'] = App::$profile['channel_name'] . " - " . unpunify(channel_reddress(App::$profile)); - - App::$profile['permission_to_view'] = $can_view_profile; - - if ($can_view_profile) { - $online = get_online_status($nickname); - App::$profile['online_status'] = $online['result']; - } - - if (local_channel()) { - App::$profile['channel_mobile_theme'] = get_pconfig(local_channel(),'system', 'mobile_theme'); - $_SESSION['mobile_theme'] = App::$profile['channel_mobile_theme']; - } - - /* - * load/reload current theme info - */ - - // $_SESSION['theme'] = $p[0]['channel_theme']; - - } - - static function edit_menu($uid) { - - $ret = []; - - $is_owner = (($uid == local_channel()) ? true : false); - - // show edit profile to profile owner - if ($is_owner) { - $ret['menu'] = array( - 'chg_photo' => t('Change profile photo'), - 'entries' => [], - ); - - $multi_profiles = feature_enabled(local_channel(), 'multi_profiles'); - if ($multi_profiles) { - $ret['multi'] = 1; - $ret['edit'] = [ z_root(). '/profiles', t('Edit Profiles'), '', t('Edit') ]; - $ret['menu']['cr_new'] = t('Create New Profile'); - } - else { - $ret['edit'] = [ z_root() . '/profiles/' . $uid, t('Edit Profile'), '', t('Edit') ]; - } - - $r = q("SELECT * FROM profile WHERE uid = %d", - local_channel() - ); - - if($r) { - foreach($r as $rr) { - if(!($multi_profiles || $rr['is_default'])) - continue; - - $ret['menu']['entries'][] = [ - 'photo' => $rr['thumb'], - 'id' => $rr['id'], - 'alt' => t('Profile Image'), - 'profile_name' => $rr['profile_name'], - 'isdefault' => $rr['is_default'], - 'visible_to_everybody' => t('Visible to everybody'), - 'edit_visibility' => t('Edit visibility'), - ]; - } - } - } - - return $ret; - } - - /** - * @brief Formats a profile for display in the sidebar. - * - * It is very difficult to templatise the HTML completely - * because of all the conditional logic. - * - * @param array $profile - * @param int $block - * @param boolean $show_connect (optional) default true - * @param mixed $zcard (optional) default false - * - * @return HTML string suitable for sidebar inclusion - * Exceptions: Returns empty string if passed $profile is wrong type or not populated - */ - - static function widget($profile, $block = 0, $show_connect = true, $zcard = false) { - - $observer = App::get_observer(); - - $o = ''; - $location = false; - $pdesc = true; - $reddress = true; - - if(! perm_is_allowed($profile['uid'],((is_array($observer)) ? $observer['xchan_hash'] : ''),'view_profile')) { - $block = true; - } - - if((! is_array($profile)) && (! count($profile))) - return $o; - - head_set_icon($profile['thumb']); - - if(is_sys_channel($profile['uid'])) - $show_connect = false; - - $profile['picdate'] = urlencode($profile['picdate']); - - /** - * @hooks profile_sidebar_enter - * Called before generating the 'channel sidebar' or mini-profile. - */ - call_hooks('profile_sidebar_enter', $profile); - - if($show_connect) { - - // This will return an empty string if we're already connected. - - $connect_url = rconnect_url($profile['uid'],get_observer_hash()); - $connect = (($connect_url) ? t('Connect') : ''); - if($connect_url) - $connect_url = sprintf($connect_url,urlencode(channel_reddress($profile))); - - // premium channel - over-ride - - if($profile['channel_pageflags'] & PAGE_PREMIUM) - $connect_url = z_root() . '/connect/' . $profile['channel_address']; - } - - if((x($profile,'address') == 1) - || (x($profile,'locality') == 1) - || (x($profile,'region') == 1) - || (x($profile,'postal_code') == 1) - || (x($profile,'country_name') == 1)) - $location = t('Location:'); - - $profile['homepage'] = linkify($profile['homepage'],true); - - $gender = ((x($profile,'gender') == 1) ? t('Gender:') : False); - $marital = ((x($profile,'marital') == 1) ? t('Status:') : False); - $homepage = ((x($profile,'homepage') == 1) ? t('Homepage:') : False); - $pronouns = ((x($profile,'pronouns') == 1) ? t('Pronouns:') : False); - - // zap/osada do not have a realtime chat system at this time so don't show online state - // $profile['online'] = (($profile['online_status'] === 'online') ? t('Online Now') : False); - // logger('online: ' . $profile['online']); - - $profile['online'] = false; - - if(($profile['hidewall'] && (! local_channel()) && (! remote_channel())) || $block ) { - $location = $reddress = $pdesc = $gender = $marital = $homepage = False; - } - - if($profile['gender']) { - $profile['gender_icon'] = self::gender_icon($profile['gender']); - } - - if($profile['pronouns']) { - $profile['pronouns_icon'] = self::pronouns_icon($profile['pronouns']); - } - - $firstname = ((strpos($profile['channel_name'],' ')) - ? trim(substr($profile['channel_name'],0,strpos($profile['channel_name'],' '))) : $profile['channel_name']); - $lastname = (($firstname === $profile['channel_name']) ? '' : trim(substr($profile['channel_name'],strlen($firstname)))); - - - $contact_block = contact_block(); - - $channel_menu = false; - $menu = get_pconfig($profile['uid'],'system','channel_menu'); - if($menu && ! $block) { - require_once('include/menu.php'); - $m = menu_fetch($menu,$profile['uid'],$observer['xchan_hash']); - if($m) - $channel_menu = menu_render($m); - } - $menublock = get_pconfig($profile['uid'],'system','channel_menublock'); - if ($menublock && (! $block)) { - $comanche = new Comanche(); - $channel_menu .= $comanche->block($menublock); - } - - if($zcard) - $tpl = get_markup_template('profile_vcard_short.tpl'); - else - $tpl = get_markup_template('profile_vcard.tpl'); - - $o .= replace_macros($tpl, array( - '$zcard' => $zcard, - '$profile' => $profile, - '$connect' => $connect, - '$connect_url' => $connect_url, - '$location' => $location, - '$gender' => $gender, - '$pronouns' => $pronouns, - '$pdesc' => $pdesc, - '$marital' => $marital, - '$homepage' => $homepage, - '$chanmenu' => $channel_menu, - '$reddress' => $reddress, - '$active' => t('Active'), - '$activewhen' => relative_date($profile['channel_lastpost']), - '$rating' => '', - '$contact_block' => $contact_block, - '$change_photo' => t('Change your profile photo'), - '$copyto' => t('Copy to clipboard'), - '$copied' => t('Address copied to clipboard'), - '$editmenu' => self::edit_menu($profile['uid']) - )); - - $arr = [ - 'profile' => $profile, - 'entry' => $o - ]; - - /** - * @hooks profile_sidebar - * Called when generating the 'channel sidebar' or mini-profile. - * * \e array \b profile - * * \e string \b entry - The parsed HTML template - */ - call_hooks('profile_sidebar', $arr); - - return $arr['entry']; - } - - static function gender_icon($gender) { - - // logger('gender: ' . $gender); - - // This can easily get throw off if the observer language is different - // than the channel owner language. - - if(strpos(strtolower($gender),strtolower(t('Female'))) !== false) - return 'venus'; - if(strpos(strtolower($gender),strtolower(t('Male'))) !== false) - return 'mars'; - if(strpos(strtolower($gender),strtolower(t('Trans'))) !== false) - return 'transgender'; - if(strpos(strtolower($gender),strtolower(t('Inter'))) !== false) - return 'transgender'; - if(strpos(strtolower($gender),strtolower(t('Neuter'))) !== false) - return 'neuter'; - if(strpos(strtolower($gender),strtolower(t('Non-specific'))) !== false) - return 'genderless'; - - return ''; - } - - static function pronouns_icon($pronouns) { - - - // This can easily get throw off if the observer language is different - // than the channel owner language. - - if(strpos(strtolower($pronouns),strtolower(t('She'))) !== false) - return 'venus'; - if(strpos(strtolower($pronouns),strtolower(t('Him'))) !== false) - return 'mars'; - if(strpos(strtolower($pronouns),strtolower(t('Them'))) !== false) - return 'users'; - - return ''; - } - - - static function advanced() { - - if(! perm_is_allowed(App::$profile['profile_uid'],get_observer_hash(),'view_profile')) - return ''; - - if(App::$profile['fullname']) { - - $profile_fields_basic = get_profile_fields_basic(); - $profile_fields_advanced = get_profile_fields_advanced(); - - $advanced = ((feature_enabled(App::$profile['profile_uid'],'advanced_profiles')) ? true : false); - if($advanced) - $fields = $profile_fields_advanced; - else - $fields = $profile_fields_basic; - - $clean_fields = []; - if($fields) { - foreach($fields as $k => $v) { - $clean_fields[] = trim($k); - } - } + dbesc($nickname) + ); + } + + if (!$p) { + logger('profile error: ' . App::$query_string, LOGGER_DEBUG); + notice(t('Requested profile is not available.') . EOL); + App::$error = 404; + return; + } + + $q = q( + "select * from profext where hash = '%s' and channel_id = %d", + dbesc($p[0]['profile_guid']), + intval($p[0]['profile_uid']) + ); + if ($q) { + $extra_fields = []; + + $profile_fields_basic = get_profile_fields_basic(); + $profile_fields_advanced = get_profile_fields_advanced(); + + $advanced = ((feature_enabled(local_channel(), 'advanced_profiles')) ? true : false); + if ($advanced) { + $fields = $profile_fields_advanced; + } else { + $fields = $profile_fields_basic; + } + + foreach ($q as $qq) { + foreach ($fields as $k => $f) { + if ($k == $qq['k']) { + $p[0][$k] = $qq['v']; + $extra_fields[] = $k; + break; + } + } + } + } + + $p[0]['extra_fields'] = ((isset($extra_fields)) ? $extra_fields : []); + + $z = q( + "select xchan_photo_date, xchan_addr from xchan where xchan_hash = '%s' limit 1", + dbesc($p[0]['channel_hash']) + ); + if ($z) { + $p[0]['picdate'] = $z[0]['xchan_photo_date']; + $p[0]['reddress'] = str_replace('@', '@', unpunify($z[0]['xchan_addr'])); + } + + // fetch user tags if this isn't the default profile + + if (!$p[0]['is_default']) { + $x = q( + "select keywords from profile where uid = %d and is_default = 1 limit 1", + intval($p[0]['profile_uid']) + ); + if ($x && $can_view_profile) { + $p[0]['keywords'] = $x[0]['keywords']; + } + } + + if ($p[0]['keywords']) { + $keywords = str_replace(array('#', ',', ' ', ',,'), array('', ' ', ',', ','), $p[0]['keywords']); + if (strlen($keywords) && $can_view_profile) { + if (!isset(App::$page['htmlhead'])) { + App::$page['htmlhead'] = EMPTY_STR; + } + App::$page['htmlhead'] .= '' . "\r\n"; + } + } + + App::$profile = $p[0]; + App::$profile_uid = $p[0]['profile_uid']; + App::$page['title'] = App::$profile['channel_name'] . " - " . unpunify(channel_reddress(App::$profile)); + + App::$profile['permission_to_view'] = $can_view_profile; + + if ($can_view_profile) { + $online = get_online_status($nickname); + App::$profile['online_status'] = $online['result']; + } + + if (local_channel()) { + App::$profile['channel_mobile_theme'] = get_pconfig(local_channel(), 'system', 'mobile_theme'); + $_SESSION['mobile_theme'] = App::$profile['channel_mobile_theme']; + } + + /* + * load/reload current theme info + */ + + // $_SESSION['theme'] = $p[0]['channel_theme']; + } + + public static function edit_menu($uid) + { + + $ret = []; + + $is_owner = (($uid == local_channel()) ? true : false); + + // show edit profile to profile owner + if ($is_owner) { + $ret['menu'] = array( + 'chg_photo' => t('Change profile photo'), + 'entries' => [], + ); + + $multi_profiles = feature_enabled(local_channel(), 'multi_profiles'); + if ($multi_profiles) { + $ret['multi'] = 1; + $ret['edit'] = [z_root() . '/profiles', t('Edit Profiles'), '', t('Edit')]; + $ret['menu']['cr_new'] = t('Create New Profile'); + } else { + $ret['edit'] = [z_root() . '/profiles/' . $uid, t('Edit Profile'), '', t('Edit')]; + } + + $r = q( + "SELECT * FROM profile WHERE uid = %d", + local_channel() + ); + + if ($r) { + foreach ($r as $rr) { + if (!($multi_profiles || $rr['is_default'])) { + continue; + } + + $ret['menu']['entries'][] = [ + 'photo' => $rr['thumb'], + 'id' => $rr['id'], + 'alt' => t('Profile Image'), + 'profile_name' => $rr['profile_name'], + 'isdefault' => $rr['is_default'], + 'visible_to_everybody' => t('Visible to everybody'), + 'edit_visibility' => t('Edit visibility'), + ]; + } + } + } + + return $ret; + } + + /** + * @brief Formats a profile for display in the sidebar. + * + * It is very difficult to templatise the HTML completely + * because of all the conditional logic. + * + * @param array $profile + * @param int $block + * @param bool $show_connect (optional) default true + * @param mixed $zcard (optional) default false + * + * @return HTML string suitable for sidebar inclusion + * Exceptions: Returns empty string if passed $profile is wrong type or not populated + */ + + public static function widget($profile, $block = 0, $show_connect = true, $zcard = false) + { + + $observer = App::get_observer(); + + $o = ''; + $location = false; + $pdesc = true; + $reddress = true; + + if (!perm_is_allowed($profile['uid'], ((is_array($observer)) ? $observer['xchan_hash'] : ''), 'view_profile')) { + $block = true; + } + + if ((!is_array($profile)) && (!count($profile))) { + return $o; + } + + head_set_icon($profile['thumb']); + + if (is_sys_channel($profile['uid'])) { + $show_connect = false; + } + + $profile['picdate'] = urlencode($profile['picdate']); + + /** + * @hooks profile_sidebar_enter + * Called before generating the 'channel sidebar' or mini-profile. + */ + call_hooks('profile_sidebar_enter', $profile); + + if ($show_connect) { + // This will return an empty string if we're already connected. + + $connect_url = rconnect_url($profile['uid'], get_observer_hash()); + $connect = (($connect_url) ? t('Connect') : ''); + if ($connect_url) { + $connect_url = sprintf($connect_url, urlencode(channel_reddress($profile))); + } + + // premium channel - over-ride + + if ($profile['channel_pageflags'] & PAGE_PREMIUM) { + $connect_url = z_root() . '/connect/' . $profile['channel_address']; + } + } + + if ( + (x($profile, 'address') == 1) + || (x($profile, 'locality') == 1) + || (x($profile, 'region') == 1) + || (x($profile, 'postal_code') == 1) + || (x($profile, 'country_name') == 1) + ) { + $location = t('Location:'); + } + + $profile['homepage'] = linkify($profile['homepage'], true); + + $gender = ((x($profile, 'gender') == 1) ? t('Gender:') : false); + $marital = ((x($profile, 'marital') == 1) ? t('Status:') : false); + $homepage = ((x($profile, 'homepage') == 1) ? t('Homepage:') : false); + $pronouns = ((x($profile, 'pronouns') == 1) ? t('Pronouns:') : false); + + // zap/osada do not have a realtime chat system at this time so don't show online state + // $profile['online'] = (($profile['online_status'] === 'online') ? t('Online Now') : False); + // logger('online: ' . $profile['online']); + + $profile['online'] = false; + + if (($profile['hidewall'] && (!local_channel()) && (!remote_channel())) || $block) { + $location = $reddress = $pdesc = $gender = $marital = $homepage = false; + } + + if ($profile['gender']) { + $profile['gender_icon'] = self::gender_icon($profile['gender']); + } + + if ($profile['pronouns']) { + $profile['pronouns_icon'] = self::pronouns_icon($profile['pronouns']); + } + + $firstname = ((strpos($profile['channel_name'], ' ')) + ? trim(substr($profile['channel_name'], 0, strpos($profile['channel_name'], ' '))) : $profile['channel_name']); + $lastname = (($firstname === $profile['channel_name']) ? '' : trim(substr($profile['channel_name'], strlen($firstname)))); + + + $contact_block = contact_block(); + + $channel_menu = false; + $menu = get_pconfig($profile['uid'], 'system', 'channel_menu'); + if ($menu && !$block) { + require_once('include/menu.php'); + $m = menu_fetch($menu, $profile['uid'], $observer['xchan_hash']); + if ($m) { + $channel_menu = menu_render($m); + } + } + $menublock = get_pconfig($profile['uid'], 'system', 'channel_menublock'); + if ($menublock && (!$block)) { + $comanche = new Comanche(); + $channel_menu .= $comanche->block($menublock); + } + + if ($zcard) { + $tpl = get_markup_template('profile_vcard_short.tpl'); + } else { + $tpl = get_markup_template('profile_vcard.tpl'); + } + + $o .= replace_macros($tpl, array( + '$zcard' => $zcard, + '$profile' => $profile, + '$connect' => $connect, + '$connect_url' => $connect_url, + '$location' => $location, + '$gender' => $gender, + '$pronouns' => $pronouns, + '$pdesc' => $pdesc, + '$marital' => $marital, + '$homepage' => $homepage, + '$chanmenu' => $channel_menu, + '$reddress' => $reddress, + '$active' => t('Active'), + '$activewhen' => relative_date($profile['channel_lastpost']), + '$rating' => '', + '$contact_block' => $contact_block, + '$change_photo' => t('Change your profile photo'), + '$copyto' => t('Copy to clipboard'), + '$copied' => t('Address copied to clipboard'), + '$editmenu' => self::edit_menu($profile['uid']) + )); + + $arr = [ + 'profile' => $profile, + 'entry' => $o + ]; + + /** + * @hooks profile_sidebar + * Called when generating the 'channel sidebar' or mini-profile. + * * \e array \b profile + * * \e string \b entry - The parsed HTML template + */ + call_hooks('profile_sidebar', $arr); + + return $arr['entry']; + } + + public static function gender_icon($gender) + { + + // logger('gender: ' . $gender); + + // This can easily get throw off if the observer language is different + // than the channel owner language. + + if (strpos(strtolower($gender), strtolower(t('Female'))) !== false) { + return 'venus'; + } + if (strpos(strtolower($gender), strtolower(t('Male'))) !== false) { + return 'mars'; + } + if (strpos(strtolower($gender), strtolower(t('Trans'))) !== false) { + return 'transgender'; + } + if (strpos(strtolower($gender), strtolower(t('Inter'))) !== false) { + return 'transgender'; + } + if (strpos(strtolower($gender), strtolower(t('Neuter'))) !== false) { + return 'neuter'; + } + if (strpos(strtolower($gender), strtolower(t('Non-specific'))) !== false) { + return 'genderless'; + } + + return ''; + } + + public static function pronouns_icon($pronouns) + { + + + // This can easily get throw off if the observer language is different + // than the channel owner language. + + if (strpos(strtolower($pronouns), strtolower(t('She'))) !== false) { + return 'venus'; + } + if (strpos(strtolower($pronouns), strtolower(t('Him'))) !== false) { + return 'mars'; + } + if (strpos(strtolower($pronouns), strtolower(t('Them'))) !== false) { + return 'users'; + } + + return ''; + } + + + public static function advanced() + { + + if (!perm_is_allowed(App::$profile['profile_uid'], get_observer_hash(), 'view_profile')) { + return ''; + } + + if (App::$profile['fullname']) { + $profile_fields_basic = get_profile_fields_basic(); + $profile_fields_advanced = get_profile_fields_advanced(); + + $advanced = ((feature_enabled(App::$profile['profile_uid'], 'advanced_profiles')) ? true : false); + if ($advanced) { + $fields = $profile_fields_advanced; + } else { + $fields = $profile_fields_basic; + } + + $clean_fields = []; + if ($fields) { + foreach ($fields as $k => $v) { + $clean_fields[] = trim($k); + } + } + + + $tpl = get_markup_template('profile_advanced.tpl'); + + $profile = []; + + $profile['fullname'] = array(t('Full Name:'), App::$profile['fullname']); + if (App::$profile['gender']) { + $profile['gender'] = array(t('Gender:'), App::$profile['gender']); + } - $tpl = get_markup_template('profile_advanced.tpl'); - $profile = []; - - $profile['fullname'] = array( t('Full Name:'), App::$profile['fullname'] ) ; - - if(App::$profile['gender']) $profile['gender'] = array( t('Gender:'), App::$profile['gender'] ); - - - $ob_hash = get_observer_hash(); + $ob_hash = get_observer_hash(); // this may not work at all any more, but definitely won't work correctly if the liked profile belongs to a group // comment out until we are able to look at it much closer -// if($ob_hash && perm_is_allowed(App::$profile['profile_uid'],$ob_hash,'post_like')) { -// $profile['canlike'] = true; -// $profile['likethis'] = t('Like this channel'); -// $profile['profile_guid'] = App::$profile['profile_guid']; -// } +// if($ob_hash && perm_is_allowed(App::$profile['profile_uid'],$ob_hash,'post_like')) { +// $profile['canlike'] = true; +// $profile['likethis'] = t('Like this channel'); +// $profile['profile_guid'] = App::$profile['profile_guid']; +// } -// $likers = q("select liker, xchan.* from likes left join xchan on liker = xchan_hash where channel_id = %d and target_type = '%s' and verb = '%s'", -// intval(App::$profile['profile_uid']), -// dbesc(ACTIVITY_OBJ_PROFILE), -// dbesc(ACTIVITY_LIKE) -// ); -// $profile['likers'] = []; -// $profile['like_count'] = count($likers); -// $profile['like_button_label'] = tt('Like','Likes',$profile['like_count'],'noun'); +// $likers = q("select liker, xchan.* from likes left join xchan on liker = xchan_hash where channel_id = %d and target_type = '%s' and verb = '%s'", +// intval(App::$profile['profile_uid']), +// dbesc(ACTIVITY_OBJ_PROFILE), +// dbesc(ACTIVITY_LIKE) +// ); +// $profile['likers'] = []; +// $profile['like_count'] = count($likers); +// $profile['like_button_label'] = tt('Like','Likes',$profile['like_count'],'noun'); -// if($likers) { -// foreach($likers as $l) -// $profile['likers'][] = array('name' => $l['xchan_name'],'photo' => zid($l['xchan_photo_s']), 'url' => zid($l['xchan_url'])); -// } +// if($likers) { +// foreach($likers as $l) +// $profile['likers'][] = array('name' => $l['xchan_name'],'photo' => zid($l['xchan_photo_s']), 'url' => zid($l['xchan_url'])); +// } - if((App::$profile['dob']) && (App::$profile['dob'] != '0000-00-00')) { + if ((App::$profile['dob']) && (App::$profile['dob'] != '0000-00-00')) { + $val = ''; - $val = ''; + if ((substr(App::$profile['dob'], 5, 2) === '00') || (substr(App::$profile['dob'], 8, 2) === '00')) { + $val = substr(App::$profile['dob'], 0, 4); + } - if((substr(App::$profile['dob'],5,2) === '00') || (substr(App::$profile['dob'],8,2) === '00')) - $val = substr(App::$profile['dob'],0,4); + $year_bd_format = t('j F, Y'); + $short_bd_format = t('j F'); - $year_bd_format = t('j F, Y'); - $short_bd_format = t('j F'); + if (!$val) { + $val = ((intval(App::$profile['dob'])) + ? day_translate(datetime_convert('UTC', 'UTC', App::$profile['dob'] . ' 00:00 +00:00', $year_bd_format)) + : day_translate(datetime_convert('UTC', 'UTC', '2001-' . substr(App::$profile['dob'], 5) . ' 00:00 +00:00', $short_bd_format))); + } + $profile['birthday'] = array(t('Birthday:'), $val); + } - if(! $val) { - $val = ((intval(App::$profile['dob'])) - ? day_translate(datetime_convert('UTC','UTC',App::$profile['dob'] . ' 00:00 +00:00',$year_bd_format)) - : day_translate(datetime_convert('UTC','UTC','2001-' . substr(App::$profile['dob'],5) . ' 00:00 +00:00',$short_bd_format))); - } - $profile['birthday'] = array( t('Birthday:'), $val); - } + if ($age = age(App::$profile['dob'], App::$profile['timezone'], '')) { + $profile['age'] = array(t('Age:'), $age); + } - if($age = age(App::$profile['dob'],App::$profile['timezone'],'')) - $profile['age'] = array( t('Age:'), $age ); + if (App::$profile['marital']) { + $profile['marital'] = array(t('Status:'), App::$profile['marital']); + } - if(App::$profile['marital']) - $profile['marital'] = array( t('Status:'), App::$profile['marital']); + if (App::$profile['partner']) { + $profile['marital']['partner'] = zidify_links(bbcode(App::$profile['partner'])); + } - if(App::$profile['partner']) - $profile['marital']['partner'] = zidify_links(bbcode(App::$profile['partner'])); + if (strlen(App::$profile['howlong']) && App::$profile['howlong'] > NULL_DATE) { + $profile['howlong'] = relative_date(App::$profile['howlong'], t('for %1$d %2$s')); + } - if(strlen(App::$profile['howlong']) && App::$profile['howlong'] > NULL_DATE) { - $profile['howlong'] = relative_date(App::$profile['howlong'], t('for %1$d %2$s')); - } - - if(App::$profile['keywords']) { - $keywords = str_replace(',',' ', App::$profile['keywords']); - $keywords = str_replace(' ',' ', $keywords); - $karr = explode(' ', $keywords); - if($karr) { - for($cnt = 0; $cnt < count($karr); $cnt ++) { - $karr[$cnt] = '' . $karr[$cnt] . ''; - } - } - $profile['keywords'] = array( t('Tags:'), implode(' ', $karr)); - } + if (App::$profile['keywords']) { + $keywords = str_replace(',', ' ', App::$profile['keywords']); + $keywords = str_replace(' ', ' ', $keywords); + $karr = explode(' ', $keywords); + if ($karr) { + for ($cnt = 0; $cnt < count($karr); $cnt++) { + $karr[$cnt] = '' . $karr[$cnt] . ''; + } + } + $profile['keywords'] = array(t('Tags:'), implode(' ', $karr)); + } - if(App::$profile['sexual']) $profile['sexual'] = array( t('Sexual Preference:'), App::$profile['sexual'] ); + if (App::$profile['sexual']) { + $profile['sexual'] = array(t('Sexual Preference:'), App::$profile['sexual']); + } - if(App::$profile['pronouns']) $profile['pronouns'] = array( t('Pronouns:'), App::$profile['pronouns'] ); + if (App::$profile['pronouns']) { + $profile['pronouns'] = array(t('Pronouns:'), App::$profile['pronouns']); + } - if(App::$profile['homepage']) $profile['homepage'] = array( t('Homepage:'), linkify(App::$profile['homepage']) ); + if (App::$profile['homepage']) { + $profile['homepage'] = array(t('Homepage:'), linkify(App::$profile['homepage'])); + } - if(App::$profile['hometown']) $profile['hometown'] = array( t('Hometown:'), linkify(App::$profile['hometown']) ); + if (App::$profile['hometown']) { + $profile['hometown'] = array(t('Hometown:'), linkify(App::$profile['hometown'])); + } - if(App::$profile['politic']) $profile['politic'] = array( t('Political Views:'), App::$profile['politic']); + if (App::$profile['politic']) { + $profile['politic'] = array(t('Political Views:'), App::$profile['politic']); + } - if(App::$profile['religion']) $profile['religion'] = array( t('Religion:'), App::$profile['religion']); + if (App::$profile['religion']) { + $profile['religion'] = array(t('Religion:'), App::$profile['religion']); + } - if($txt = prepare_text(App::$profile['about'])) $profile['about'] = array( t('About:'), $txt ); + if ($txt = prepare_text(App::$profile['about'])) { + $profile['about'] = array(t('About:'), $txt); + } - if($txt = prepare_text(App::$profile['interest'])) $profile['interest'] = array( t('Hobbies/Interests:'), $txt); + if ($txt = prepare_text(App::$profile['interest'])) { + $profile['interest'] = array(t('Hobbies/Interests:'), $txt); + } - if($txt = prepare_text(App::$profile['likes'])) $profile['likes'] = array( t('Likes:'), $txt); + if ($txt = prepare_text(App::$profile['likes'])) { + $profile['likes'] = array(t('Likes:'), $txt); + } - if($txt = prepare_text(App::$profile['dislikes'])) $profile['dislikes'] = array( t('Dislikes:'), $txt); + if ($txt = prepare_text(App::$profile['dislikes'])) { + $profile['dislikes'] = array(t('Dislikes:'), $txt); + } - if($txt = prepare_text(App::$profile['contact'])) $profile['contact'] = array( t('Contact information and Social Networks:'), $txt); + if ($txt = prepare_text(App::$profile['contact'])) { + $profile['contact'] = array(t('Contact information and Social Networks:'), $txt); + } - if($txt = prepare_text(App::$profile['channels'])) $profile['channels'] = array( t('My other channels:'), $txt); + if ($txt = prepare_text(App::$profile['channels'])) { + $profile['channels'] = array(t('My other channels:'), $txt); + } - if($txt = prepare_text(App::$profile['music'])) $profile['music'] = array( t('Musical interests:'), $txt); + if ($txt = prepare_text(App::$profile['music'])) { + $profile['music'] = array(t('Musical interests:'), $txt); + } - if($txt = prepare_text(App::$profile['book'])) $profile['book'] = array( t('Books, literature:'), $txt); + if ($txt = prepare_text(App::$profile['book'])) { + $profile['book'] = array(t('Books, literature:'), $txt); + } - if($txt = prepare_text(App::$profile['tv'])) $profile['tv'] = array( t('Television:'), $txt); + if ($txt = prepare_text(App::$profile['tv'])) { + $profile['tv'] = array(t('Television:'), $txt); + } - if($txt = prepare_text(App::$profile['film'])) $profile['film'] = array( t('Film/dance/culture/entertainment:'), $txt); + if ($txt = prepare_text(App::$profile['film'])) { + $profile['film'] = array(t('Film/dance/culture/entertainment:'), $txt); + } - if($txt = prepare_text(App::$profile['romance'])) $profile['romance'] = array( t('Love/Romance:'), $txt); + if ($txt = prepare_text(App::$profile['romance'])) { + $profile['romance'] = array(t('Love/Romance:'), $txt); + } - if($txt = prepare_text(App::$profile['employment'])) $profile['employment'] = array( t('Work/employment:'), $txt); + if ($txt = prepare_text(App::$profile['employment'])) { + $profile['employment'] = array(t('Work/employment:'), $txt); + } - if($txt = prepare_text(App::$profile['education'])) $profile['education'] = array( t('School/education:'), $txt ); + if ($txt = prepare_text(App::$profile['education'])) { + $profile['education'] = array(t('School/education:'), $txt); + } - if(App::$profile['extra_fields']) { - foreach(App::$profile['extra_fields'] as $f) { - $x = q("select * from profdef where field_name = '%s' limit 1", - dbesc($f) - ); - if($x && $txt = prepare_text(App::$profile[$f])) - $profile[$f] = array( $x[0]['field_desc'] . ':',$txt); - } - $profile['extra_fields'] = App::$profile['extra_fields']; - } + if (App::$profile['extra_fields']) { + foreach (App::$profile['extra_fields'] as $f) { + $x = q( + "select * from profdef where field_name = '%s' limit 1", + dbesc($f) + ); + if ($x && $txt = prepare_text(App::$profile[$f])) { + $profile[$f] = array($x[0]['field_desc'] . ':', $txt); + } + } + $profile['extra_fields'] = App::$profile['extra_fields']; + } - $things = get_things(App::$profile['profile_guid'],App::$profile['profile_uid']); + $things = get_things(App::$profile['profile_guid'], App::$profile['profile_uid']); - // logger('mod_profile: things: ' . print_r($things,true), LOGGER_DATA); + // logger('mod_profile: things: ' . print_r($things,true), LOGGER_DATA); - // $exportlink = ((App::$profile['profile_vcard']) ? zid(z_root() . '/profile/' . App::$profile['channel_address'] . '/vcard') : ''); + // $exportlink = ((App::$profile['profile_vcard']) ? zid(z_root() . '/profile/' . App::$profile['channel_address'] . '/vcard') : ''); - return replace_macros($tpl, array( - '$title' => t('Profile'), - '$canlike' => (($profile['canlike'])? true : false), - '$likethis' => t('Like this thing'), - '$export' => t('Export'), - '$exportlink' => '', // $exportlink, - '$profile' => $profile, - '$fields' => $clean_fields, - '$editmenu' => self::edit_menu(App::$profile['profile_uid']), - '$things' => $things - )); - } + return replace_macros($tpl, array( + '$title' => t('Profile'), + '$canlike' => (($profile['canlike']) ? true : false), + '$likethis' => t('Like this thing'), + '$export' => t('Export'), + '$exportlink' => '', // $exportlink, + '$profile' => $profile, + '$fields' => $clean_fields, + '$editmenu' => self::edit_menu(App::$profile['profile_uid']), + '$things' => $things + )); + } - return ''; - } - - - - - - - - - - - - - - - - - - - -} \ No newline at end of file + return ''; + } +} diff --git a/Zotlabs/Lib/Libsync.php b/Zotlabs/Lib/Libsync.php index 91ffed633..2ec40fe1f 100644 --- a/Zotlabs/Lib/Libsync.php +++ b/Zotlabs/Lib/Libsync.php @@ -9,1255 +9,1292 @@ use Zotlabs\Lib\Connect; use Zotlabs\Lib\DReport; use Zotlabs\Daemon\Run; -class Libsync { +class Libsync +{ - /** - * @brief Builds and sends a sync packet. - * - * Send a zot packet to all hubs where this channel is duplicated, refreshing - * such things as personal settings, channel permissions, address book updates, etc. - * - * By default, sync the channel and any pconfig changes which were made in the current process - * AccessLists (aka privacy groups) will also be included if $groups_changed is true. - * To include other data sources, provide them as $packet. - * - * @param int $uid (optional) default 0 - * @param array $packet (optional) default null - * @param boolean $groups_changed (optional) default false - */ + /** + * @brief Builds and sends a sync packet. + * + * Send a zot packet to all hubs where this channel is duplicated, refreshing + * such things as personal settings, channel permissions, address book updates, etc. + * + * By default, sync the channel and any pconfig changes which were made in the current process + * AccessLists (aka privacy groups) will also be included if $groups_changed is true. + * To include other data sources, provide them as $packet. + * + * @param int $uid (optional) default 0 + * @param array $packet (optional) default null + * @param bool $groups_changed (optional) default false + */ - static function build_sync_packet($uid = 0, $packet = null, $groups_changed = false) { + public static function build_sync_packet($uid = 0, $packet = null, $groups_changed = false) + { - //logger('build_sync_packet'); + //logger('build_sync_packet'); - $keychange = (($packet && array_key_exists('keychange',$packet)) ? true : false); - if ($keychange) { - logger('keychange sync'); - } + $keychange = (($packet && array_key_exists('keychange', $packet)) ? true : false); + if ($keychange) { + logger('keychange sync'); + } - if (! $uid) { - $uid = local_channel(); - } - - if (! $uid) { - return; - } + if (!$uid) { + $uid = local_channel(); + } - $channel = channelx_by_n($uid); - if (! $channel) { - return; - } + if (!$uid) { + return; + } - // don't provide these in the export + $channel = channelx_by_n($uid); + if (!$channel) { + return; + } - unset($channel['channel_active']); - unset($channel['channel_password']); - unset($channel['channel_salt']); + // don't provide these in the export + + unset($channel['channel_active']); + unset($channel['channel_password']); + unset($channel['channel_salt']); $h = q("select hubloc.*, site.site_crypto from hubloc left join site on site_url = hubloc_url where hubloc_hash = '%s' and hubloc_network in ('nomad','zot6') and hubloc_deleted = 0", dbesc(($keychange) ? $packet['keychange']['old_hash'] : $channel['channel_hash']) ); - if (! $h) { - return; - } + if (!$h) { + return; + } - $synchubs = []; + $synchubs = []; - foreach ($h as $x) { - if ($x['hubloc_host'] == App::get_hostname()) { - continue; - } + foreach ($h as $x) { + if ($x['hubloc_host'] == App::get_hostname()) { + continue; + } - $y = q("select site_dead from site where site_url = '%s' limit 1", - dbesc($x['hubloc_url']) - ); + $y = q( + "select site_dead from site where site_url = '%s' limit 1", + dbesc($x['hubloc_url']) + ); - if ((! $y) || ($y[0]['site_dead'] == 0)) { - $synchubs[] = $x; - } - } + if ((!$y) || ($y[0]['site_dead'] == 0)) { + $synchubs[] = $x; + } + } - if (! $synchubs) { - return; - } + if (!$synchubs) { + return; + } - $env_recips = [ $channel['channel_hash'] ]; + $env_recips = [$channel['channel_hash']]; - if ($packet) { - logger('packet: ' . print_r($packet, true),LOGGER_DATA, LOG_DEBUG); - } - - $info = (($packet) ? $packet : [] ); - $info['type'] = 'sync'; - $info['encoding'] = 'red'; // note: not zot, this packet is very platform specific - $info['relocate'] = ['channel_address' => $channel['channel_address'], 'url' => z_root() ]; + if ($packet) { + logger('packet: ' . print_r($packet, true), LOGGER_DATA, LOG_DEBUG); + } - if (array_key_exists($uid, App::$config) && array_key_exists('transient', App::$config[$uid])) { - $settings = App::$config[$uid]['transient']; - if ($settings) { - $info['config'] = $settings; - } - } + $info = (($packet) ? $packet : []); + $info['type'] = 'sync'; + $info['encoding'] = 'red'; // note: not zot, this packet is very platform specific + $info['relocate'] = ['channel_address' => $channel['channel_address'], 'url' => z_root()]; - if ($channel) { - $info['channel'] = []; - foreach ($channel as $k => $v) { + if (array_key_exists($uid, App::$config) && array_key_exists('transient', App::$config[$uid])) { + $settings = App::$config[$uid]['transient']; + if ($settings) { + $info['config'] = $settings; + } + } - // filter out any joined tables like xchan + if ($channel) { + $info['channel'] = []; + foreach ($channel as $k => $v) { + // filter out any joined tables like xchan - if (strpos($k,'channel_') !== 0) { - continue; - } + if (strpos($k, 'channel_') !== 0) { + continue; + } - // don't pass these elements, they should not be synchronised + // don't pass these elements, they should not be synchronised - $disallowed = [ 'channel_id','channel_account_id','channel_primary','channel_address', - 'channel_deleted','channel_removed','channel_system' ]; + $disallowed = ['channel_id', 'channel_account_id', 'channel_primary', 'channel_address', + 'channel_deleted', 'channel_removed', 'channel_system']; - if (! $keychange) { - $disallowed[] = 'channel_prvkey'; - } + if (!$keychange) { + $disallowed[] = 'channel_prvkey'; + } - if (in_array($k,$disallowed)) { - continue; - } + if (in_array($k, $disallowed)) { + continue; + } - $info['channel'][$k] = $v; - } - } + $info['channel'][$k] = $v; + } + } - if ($groups_changed) { - $r = q("select hash as collection, visible, deleted, rule, gname as name from pgrp where uid = %d ", - intval($uid) - ); - if ($r) { - $info['collections'] = $r; - } + if ($groups_changed) { + $r = q( + "select hash as collection, visible, deleted, rule, gname as name from pgrp where uid = %d ", + intval($uid) + ); + if ($r) { + $info['collections'] = $r; + } - $r = q("select pgrp.hash as collection, pgrp_member.xchan as member from pgrp left join pgrp_member on pgrp.id = pgrp_member.gid + $r = q( + "select pgrp.hash as collection, pgrp_member.xchan as member from pgrp left join pgrp_member on pgrp.id = pgrp_member.gid where pgrp_member.uid = %d ", - intval($uid) - ); - if ($r) { - $info['collection_members'] = $r; - } - } + intval($uid) + ); + if ($r) { + $info['collection_members'] = $r; + } + } - $interval = get_config('system','delivery_interval', 2); + $interval = get_config('system', 'delivery_interval', 2); - logger('Packet: ' . print_r($info,true), LOGGER_DATA, LOG_DEBUG); + logger('Packet: ' . print_r($info, true), LOGGER_DATA, LOG_DEBUG); - $total = count($synchubs); + $total = count($synchubs); - foreach ($synchubs as $hub) { - $hash = random_string(); - $n = Libzot::build_packet($channel,'sync',$env_recips,json_encode($info),'red',$hub['hubloc_sitekey'],$hub['site_crypto']); - Queue::insert([ - 'hash' => $hash, - 'account_id' => $channel['channel_account_id'], - 'channel_id' => $channel['channel_id'], - 'posturl' => $hub['hubloc_callback'], - 'notify' => $n, - 'msg' => EMPTY_STR - ]); + foreach ($synchubs as $hub) { + $hash = random_string(); + $n = Libzot::build_packet($channel, 'sync', $env_recips, json_encode($info), 'red', $hub['hubloc_sitekey'], $hub['site_crypto']); + Queue::insert([ + 'hash' => $hash, + 'account_id' => $channel['channel_account_id'], + 'channel_id' => $channel['channel_id'], + 'posturl' => $hub['hubloc_callback'], + 'notify' => $n, + 'msg' => EMPTY_STR + ]); - $x = q("select count(outq_hash) as total from outq where outq_delivered = 0"); - if (intval($x[0]['total']) > intval(get_config('system','force_queue_threshold',3000))) { - logger('immediate delivery deferred.', LOGGER_DEBUG, LOG_INFO); - Queue::update($hash); - continue; - } + $x = q("select count(outq_hash) as total from outq where outq_delivered = 0"); + if (intval($x[0]['total']) > intval(get_config('system', 'force_queue_threshold', 3000))) { + logger('immediate delivery deferred.', LOGGER_DEBUG, LOG_INFO); + Queue::update($hash); + continue; + } - Run::Summon([ 'Deliver', $hash ]); - $total = $total - 1; + Run::Summon(['Deliver', $hash]); + $total = $total - 1; - if ($interval && $total) { - @time_sleep_until(microtime(true) + (float) $interval); - } - } - } + if ($interval && $total) { + @time_sleep_until(microtime(true) + (float)$interval); + } + } + } - static function build_link_packet($uid = 0, $packet = null) { + public static function build_link_packet($uid = 0, $packet = null) + { - // logger('build_link_packet'); + // logger('build_link_packet'); - if (! $uid) { - $uid = local_channel(); - } - - if (! $uid) { - return; - } + if (!$uid) { + $uid = local_channel(); + } - $channel = channelx_by_n($uid); - if (! $channel) { - return; - } + if (!$uid) { + return; + } - $l = q("select link from linkid where ident = '%s' and sigtype = 2", - dbesc($channel['channel_hash']) - ); + $channel = channelx_by_n($uid); + if (!$channel) { + return; + } - if (! $l) { - return; - } + $l = q( + "select link from linkid where ident = '%s' and sigtype = 2", + dbesc($channel['channel_hash']) + ); + + if (!$l) { + return; + } + + $hashes = ids_to_querystr($l, 'link', true); - $hashes = ids_to_querystr($l,'link',true); - $h = q("select hubloc.*, site.site_crypto from hubloc left join site on site_url = hubloc_url where hubloc_hash in (" . protect_sprintf($hashes) . ") and hubloc_network in ('nomad','zot6') and hubloc_deleted = 0"); - if (! $h) { - return; - } - - $interval = get_config('system','delivery_interval',2); + if (!$h) { + return; + } + + $interval = get_config('system', 'delivery_interval', 2); + + + foreach ($h as $x) { + if ($x['hubloc_host'] == App::get_hostname()) { + continue; + } + + $y = q( + "select site_dead from site where site_url = '%s' limit 1", + dbesc($x['hubloc_url']) + ); + if (($y) && (intval($y[0]['site_dead']) == 1)) { + $continue; + } + + $env_recips = [$x['hubloc_hash']]; - foreach ($h as $x) { - if ($x['hubloc_host'] == App::get_hostname()) { - continue; - } + if ($packet) { + logger('packet: ' . print_r($packet, true), LOGGER_DATA, LOG_DEBUG); + } + + $info = (($packet) ? $packet : []); + $info['type'] = 'sync'; + $info['encoding'] = 'red'; // note: not zot, this packet is very platform specific - $y = q("select site_dead from site where site_url = '%s' limit 1", - dbesc($x['hubloc_url']) - ); - - if (($y) && (intval($y[0]['site_dead']) == 1)) { - $continue; - } - - $env_recips = [ $x['hubloc_hash'] ]; - - if ($packet) { - logger('packet: ' . print_r($packet, true),LOGGER_DATA, LOG_DEBUG); - } - - $info = (($packet) ? $packet : []); - $info['type'] = 'sync'; - $info['encoding'] = 'red'; // note: not zot, this packet is very platform specific - - logger('Packet: ' . print_r($info,true), LOGGER_DATA, LOG_DEBUG); - - - $hash = random_string(); - $n = Libzot::build_packet($channel,'sync',$env_recips,json_encode($info),'red',$x['hubloc_sitekey'],$x['site_crypto']); - Queue::insert([ - 'hash' => $hash, - 'account_id' => $channel['channel_account_id'], - 'channel_id' => $channel['channel_id'], - 'posturl' => $x['hubloc_callback'], - 'notify' => $n, - 'msg' => EMPTY_STR - ]); - - $y = q("select count(outq_hash) as total from outq where outq_delivered = 0"); - if (intval($y[0]['total']) > intval(get_config('system','force_queue_threshold',3000))) { - logger('immediate delivery deferred.', LOGGER_DEBUG, LOG_INFO); - Queue::update($hash); - continue; - } - - Run::Summon([ 'Deliver', $hash ]); - - if ($interval && count($h) > 1) { - @time_sleep_until(microtime(true) + (float) $interval); - } - } - } - - - /** - * @brief - * - * @param array $sender - * @param array $arr - * @param array $deliveries - * @return array - */ - - static function process_channel_sync_delivery($sender, $arr, $deliveries) { - - require_once('include/import.php'); - - $result = []; - - $keychange = ((array_key_exists('keychange',$arr)) ? true : false); - - foreach ($deliveries as $d) { - $linked_channel = false; - - $r = q("select * from channel where channel_hash = '%s' limit 1", - dbesc($sender) - ); - - $DR = new DReport(z_root(),$sender,$d,'sync'); - - if (! $r) { - $l = q("select ident from linkid where link = '%s' and sigtype = 2 limit 1", - dbesc($sender) - ); - if ($l) { - $linked_channel = true; - $r = q("select * from channel where channel_hash = '%s' limit 1", - dbesc($l[0]['ident']) - ); - } - } - - if (! $r) { - $DR->update('recipient not found'); - $result[] = $DR->get(); - continue; - } - - $channel = $r[0]; - - $DR->set_name($channel['channel_name'] . ' <' . channel_reddress($channel) . '>'); - - $max_friends = service_class_fetch($channel['channel_id'],'total_channels'); - $max_feeds = account_service_class_fetch($channel['channel_account_id'],'total_feeds'); - - if ($channel['channel_hash'] != $sender && (! $linked_channel)) { - logger('Possible forgery. Sender ' . $sender . ' is not ' . $channel['channel_hash']); - $DR->update('channel mismatch'); - $result[] = $DR->get(); - continue; - } - - if ($keychange) { - self::keychange($channel,$arr); - continue; - } - - // if the clone is active, so are we - - if (substr($channel['channel_active'],0,10) !== substr(datetime_convert(),0,10)) { - q("UPDATE channel set channel_active = '%s' where channel_id = %d", - dbesc(datetime_convert()), - intval($channel['channel_id']) - ); - } - - if (array_key_exists('config',$arr) && is_array($arr['config']) && count($arr['config'])) { - foreach ($arr['config'] as $cat => $k) { - foreach ($arr['config'][$cat] as $k => $v) { - set_pconfig($channel['channel_id'],$cat,$k,$v); - } - } - } - - if (array_key_exists('atoken',$arr) && $arr['atoken']) { - sync_atoken($channel,$arr['atoken']); - } - - if (array_key_exists('xign',$arr) && $arr['xign']) { - sync_xign($channel,$arr['xign']); - } - - if (array_key_exists('block',$arr) && $arr['block']) { - sync_block($channel,$arr['block']); - } - - if (array_key_exists('obj',$arr) && $arr['obj']) { - sync_objs($channel,$arr['obj']); - } - - if (array_key_exists('likes',$arr) && $arr['likes']) { - import_likes($channel,$arr['likes']); - } - - if (array_key_exists('app',$arr) && $arr['app']) { - sync_apps($channel,$arr['app']); - } - - if (array_key_exists('sysapp',$arr) && $arr['sysapp']) { - sync_sysapps($channel,$arr['sysapp']); - } - - if (array_key_exists('chatroom',$arr) && $arr['chatroom']) { - sync_chatrooms($channel,$arr['chatroom']); - } - - if (array_key_exists('conv',$arr) && $arr['conv']) { - import_conv($channel,$arr['conv']); - } - - if (array_key_exists('mail',$arr) && $arr['mail']) { - sync_mail($channel,$arr['mail']); - } - - if (array_key_exists('event',$arr) && $arr['event']) { - sync_events($channel,$arr['event']); - } - - if (array_key_exists('event_item',$arr) && $arr['event_item']) { - sync_items($channel,$arr['event_item'],((array_key_exists('relocate',$arr)) ? $arr['relocate'] : null)); - } - - if (array_key_exists('item',$arr) && $arr['item']) { - sync_items($channel,$arr['item'],((array_key_exists('relocate',$arr)) ? $arr['relocate'] : null)); - } - - if (array_key_exists('menu',$arr) && $arr['menu']) { - sync_menus($channel,$arr['menu']); - } - - if (array_key_exists('file',$arr) && $arr['file']) { - sync_files($channel,$arr['file']); - } - - if (array_key_exists('wiki',$arr) && $arr['wiki']) { - sync_items($channel,$arr['wiki'],((array_key_exists('relocate',$arr)) ? $arr['relocate'] : null)); - } - - if(array_key_exists('channel',$arr) && is_array($arr['channel']) && count($arr['channel'])) { - - $remote_channel = $arr['channel']; - $remote_channel['channel_id'] = $channel['channel_id']; - - if (array_key_exists('channel_pageflags',$arr['channel']) && intval($arr['channel']['channel_pageflags'])) { - - // Several pageflags are site-specific and cannot be sync'd. - // Only allow those bits which are shareable from the remote and then - // logically OR with the local flags - - $arr['channel']['channel_pageflags'] = $arr['channel']['channel_pageflags'] & (PAGE_HIDDEN|PAGE_AUTOCONNECT|PAGE_APPLICATION|PAGE_PREMIUM|PAGE_ADULT); - $arr['channel']['channel_pageflags'] = $arr['channel']['channel_pageflags'] | $channel['channel_pageflags']; - - } - - $columns = db_columns('channel'); - - $disallowed = [ - 'channel_id', 'channel_account_id', 'channel_primary', 'channel_prvkey', - 'channel_address', 'channel_notifyflags', 'channel_removed', 'channel_deleted', - 'channel_system', 'channel_r_stream', 'channel_r_profile', 'channel_r_abook', - 'channel_r_storage', 'channel_r_pages', 'channel_w_stream', 'channel_w_wall', - 'channel_w_comment', 'channel_w_mail', 'channel_w_like', 'channel_w_tagwall', - 'channel_w_chat', 'channel_w_storage', 'channel_w_pages', 'channel_a_republish', - 'channel_a_delegate', 'channel_moved' - ]; - - foreach($arr['channel'] as $k => $v) { - if (in_array($k,$disallowed)) { - continue; - } - if (! in_array($k,$columns)) { - continue; - } - $r = dbq("UPDATE channel set " . dbesc($k) . " = '" . dbesc($v) - . "' where channel_id = " . intval($channel['channel_id']) ); - } - } - - if (array_key_exists('abook',$arr) && is_array($arr['abook']) && count($arr['abook'])) { - - $total_friends = 0; - $total_feeds = 0; - - $r = q("select abook_id, abook_feed from abook where abook_channel = %d", - intval($channel['channel_id']) - ); - if ($r) { - // don't count yourself - $total_friends = ((count($r) > 0) ? count($r) - 1 : 0); - foreach ($r as $rr) { - if (intval($rr['abook_feed'])) { - $total_feeds ++; - } - } - } - - - $disallowed = [ 'abook_id', 'abook_account', 'abook_channel', 'abook_rating', 'abook_rating_text', 'abook_not_here' ]; - - $fields = db_columns('abook'); - - foreach ($arr['abook'] as $abook) { - - // this is here for debugging so we can find the issue source - - if (! is_array($abook)) { - btlogger('abook is not an array'); - continue; - } - - $abconfig = null; - - if (array_key_exists('abconfig',$abook) && is_array($abook['abconfig']) && count($abook['abconfig'])) { - $abconfig = $abook['abconfig']; - } - - $clean = []; - - if ($abook['abook_xchan'] && $abook['entry_deleted']) { - logger('Removing abook entry for ' . $abook['abook_xchan']); - - $r = q("select abook_id, abook_feed from abook where abook_xchan = '%s' and abook_channel = %d and abook_self = 0 limit 1", - dbesc($abook['abook_xchan']), - intval($channel['channel_id']) - ); - if ($r) { - contact_remove($channel['channel_id'],$r[0]['abook_id']); - if ($total_friends) { - $total_friends --; - } - if (intval($r[0]['abook_feed'])) { - $total_feeds --; - } - } - continue; - } - - // Perform discovery if the referenced xchan hasn't ever been seen on this hub. - // This relies on the undocumented behaviour that red sites send xchan info with the abook - // and import_author_xchan will look them up on all federated networks - - $found = false; - if ($abook['abook_xchan'] && $abook['xchan_addr'] && (! in_array($abook['xchan_network'], [ 'token', 'unknown' ]))) { - $h = Libzot::get_hublocs($abook['abook_xchan']); - if ($h) { - $found = true; - } - else { - $xhash = import_author_xchan(encode_item_xchan($abook)); - if ($xhash) { - $found = true; - } - else { - logger('Import of ' . $abook['xchan_addr'] . ' failed.'); - } - } - } - - if ((! $found) && (! in_array($abook['xchan_network'], [ 'nomad', 'zot6', 'activitypub' ]))) { - // just import the record. - $xc = []; - foreach ($abook as $k => $v) { - if (strpos($k,'xchan_') === 0) { - $xc[$k] = $v; - } - } - $r = q("select * from xchan where xchan_hash = '%s'", - dbesc($xc['xchan_hash']) - ); - if (! $r) { - xchan_store_lowlevel($xc); - } - } - - foreach ($abook as $k => $v) { - if (in_array($k,$disallowed) || (strpos($k,'abook_') !== 0)) { - continue; - } - if (! in_array($k,$fields)) { - continue; - } - $clean[$k] = $v; - } - - if (! array_key_exists('abook_xchan',$clean)) { - continue; - } - - $reconnect = false; - if (array_key_exists('abook_instance',$clean) && $clean['abook_instance'] && strpos($clean['abook_instance'],z_root()) === false) { - // guest pass or access token - don't try to probe since it is one-way - // we are relying on the undocumented behaviour that the abook record also contains the xchan - if ($abook['xchan_network'] === 'token') { - $clean['abook_instance'] .= ','; - $clean['abook_instance'] .= z_root(); - $clean['abook_not_here'] = 0; - } - else { - $clean['abook_not_here'] = 1; - if (! ($abook['abook_pending'] || $abook['abook_blocked'])) { - $reconnect = true; - } - } - } - - $r = q("select * from abook where abook_xchan = '%s' and abook_channel = %d limit 1", - dbesc($clean['abook_xchan']), - intval($channel['channel_id']) - ); - - // make sure we have an abook entry for this xchan on this system - - if (! $r) { - if ($max_friends !== false && $total_friends > $max_friends) { - logger('total_channels service class limit exceeded'); - continue; - } - if ($max_feeds !== false && intval($clean['abook_feed']) && $total_feeds > $max_feeds) { - logger('total_feeds service class limit exceeded'); - continue; - } - abook_store_lowlevel( - [ - 'abook_xchan' => $clean['abook_xchan'], - 'abook_account' => $channel['channel_account_id'], - 'abook_channel' => $channel['channel_id'] - ] - ); - $total_friends ++; - if (intval($clean['abook_feed'])) { - $total_feeds ++; - } - } - - if (count($clean)) { - foreach ($clean as $k => $v) { - if ($k == 'abook_dob') { - $v = dbescdate($v); - } - - $r = dbq("UPDATE abook set " . dbesc($k) . " = '" . dbesc($v) - . "' where abook_xchan = '" . dbesc($clean['abook_xchan']) . "' and abook_channel = " . intval($channel['channel_id'])); - - } - } - - // This will set abconfig vars if the sender is using old-style fixed permissions - // using the raw abook record as passed to us. New-style permissions will fall through - // and be set using abconfig - - // translate_abook_perms_inbound($channel,$abook); - - if ($abconfig) { - /// @fixme does not handle sync of del_abconfig - foreach ($abconfig as $abc) { - set_abconfig($channel['channel_id'],$abc['xchan'],$abc['cat'],$abc['k'],$abc['v']); - } - } - if ($reconnect) { - Connect::connect($channel,$abook['abook_xchan']); - } - } - } - - // sync collections (privacy groups) oh joy... - - if (array_key_exists('collections',$arr) && is_array($arr['collections']) && count($arr['collections'])) { - $x = q("select * from pgrp where uid = %d ", - intval($channel['channel_id']) - ); - foreach ($arr['collections'] as $cl) { - $found = false; - if ($x) { - foreach ($x as $y) { - if ($cl['collection'] == $y['hash']) { - $found = true; - break; - } - } - if ($found) { - if (($y['gname'] != $cl['name']) - || ($y['visible'] != $cl['visible']) - || ($y['deleted'] != $cl['deleted'])) { - q("update pgrp set gname = '%s', visible = %d, deleted = %d where hash = '%s' and uid = %d", - dbesc($cl['name']), - intval($cl['visible']), - intval($cl['deleted']), - dbesc($cl['collection']), - intval($channel['channel_id']) - ); - } - if (intval($cl['deleted']) && (! intval($y['deleted']))) { - q("delete from pgrp_member where gid = %d", - intval($y['id']) - ); - } - } - } - if (! $found) { - $r = q("INSERT INTO pgrp ( hash, uid, visible, deleted, gname, rule ) + logger('Packet: ' . print_r($info, true), LOGGER_DATA, LOG_DEBUG); + + + $hash = random_string(); + $n = Libzot::build_packet($channel, 'sync', $env_recips, json_encode($info), 'red', $x['hubloc_sitekey'], $x['site_crypto']); + Queue::insert([ + 'hash' => $hash, + 'account_id' => $channel['channel_account_id'], + 'channel_id' => $channel['channel_id'], + 'posturl' => $x['hubloc_callback'], + 'notify' => $n, + 'msg' => EMPTY_STR + ]); + + $y = q("select count(outq_hash) as total from outq where outq_delivered = 0"); + if (intval($y[0]['total']) > intval(get_config('system', 'force_queue_threshold', 3000))) { + logger('immediate delivery deferred.', LOGGER_DEBUG, LOG_INFO); + Queue::update($hash); + continue; + } + + Run::Summon(['Deliver', $hash]); + + if ($interval && count($h) > 1) { + @time_sleep_until(microtime(true) + (float)$interval); + } + } + } + + + /** + * @brief + * + * @param array $sender + * @param array $arr + * @param array $deliveries + * @return array + */ + + public static function process_channel_sync_delivery($sender, $arr, $deliveries) + { + + require_once('include/import.php'); + + $result = []; + + $keychange = ((array_key_exists('keychange', $arr)) ? true : false); + + foreach ($deliveries as $d) { + $linked_channel = false; + + $r = q( + "select * from channel where channel_hash = '%s' limit 1", + dbesc($sender) + ); + + $DR = new DReport(z_root(), $sender, $d, 'sync'); + + if (!$r) { + $l = q( + "select ident from linkid where link = '%s' and sigtype = 2 limit 1", + dbesc($sender) + ); + if ($l) { + $linked_channel = true; + $r = q( + "select * from channel where channel_hash = '%s' limit 1", + dbesc($l[0]['ident']) + ); + } + } + + if (!$r) { + $DR->update('recipient not found'); + $result[] = $DR->get(); + continue; + } + + $channel = $r[0]; + + $DR->set_name($channel['channel_name'] . ' <' . channel_reddress($channel) . '>'); + + $max_friends = service_class_fetch($channel['channel_id'], 'total_channels'); + $max_feeds = account_service_class_fetch($channel['channel_account_id'], 'total_feeds'); + + if ($channel['channel_hash'] != $sender && (!$linked_channel)) { + logger('Possible forgery. Sender ' . $sender . ' is not ' . $channel['channel_hash']); + $DR->update('channel mismatch'); + $result[] = $DR->get(); + continue; + } + + if ($keychange) { + self::keychange($channel, $arr); + continue; + } + + // if the clone is active, so are we + + if (substr($channel['channel_active'], 0, 10) !== substr(datetime_convert(), 0, 10)) { + q( + "UPDATE channel set channel_active = '%s' where channel_id = %d", + dbesc(datetime_convert()), + intval($channel['channel_id']) + ); + } + + if (array_key_exists('config', $arr) && is_array($arr['config']) && count($arr['config'])) { + foreach ($arr['config'] as $cat => $k) { + foreach ($arr['config'][$cat] as $k => $v) { + set_pconfig($channel['channel_id'], $cat, $k, $v); + } + } + } + + if (array_key_exists('atoken', $arr) && $arr['atoken']) { + sync_atoken($channel, $arr['atoken']); + } + + if (array_key_exists('xign', $arr) && $arr['xign']) { + sync_xign($channel, $arr['xign']); + } + + if (array_key_exists('block', $arr) && $arr['block']) { + sync_block($channel, $arr['block']); + } + + if (array_key_exists('obj', $arr) && $arr['obj']) { + sync_objs($channel, $arr['obj']); + } + + if (array_key_exists('likes', $arr) && $arr['likes']) { + import_likes($channel, $arr['likes']); + } + + if (array_key_exists('app', $arr) && $arr['app']) { + sync_apps($channel, $arr['app']); + } + + if (array_key_exists('sysapp', $arr) && $arr['sysapp']) { + sync_sysapps($channel, $arr['sysapp']); + } + + if (array_key_exists('chatroom', $arr) && $arr['chatroom']) { + sync_chatrooms($channel, $arr['chatroom']); + } + + if (array_key_exists('conv', $arr) && $arr['conv']) { + import_conv($channel, $arr['conv']); + } + + if (array_key_exists('mail', $arr) && $arr['mail']) { + sync_mail($channel, $arr['mail']); + } + + if (array_key_exists('event', $arr) && $arr['event']) { + sync_events($channel, $arr['event']); + } + + if (array_key_exists('event_item', $arr) && $arr['event_item']) { + sync_items($channel, $arr['event_item'], ((array_key_exists('relocate', $arr)) ? $arr['relocate'] : null)); + } + + if (array_key_exists('item', $arr) && $arr['item']) { + sync_items($channel, $arr['item'], ((array_key_exists('relocate', $arr)) ? $arr['relocate'] : null)); + } + + if (array_key_exists('menu', $arr) && $arr['menu']) { + sync_menus($channel, $arr['menu']); + } + + if (array_key_exists('file', $arr) && $arr['file']) { + sync_files($channel, $arr['file']); + } + + if (array_key_exists('wiki', $arr) && $arr['wiki']) { + sync_items($channel, $arr['wiki'], ((array_key_exists('relocate', $arr)) ? $arr['relocate'] : null)); + } + + if (array_key_exists('channel', $arr) && is_array($arr['channel']) && count($arr['channel'])) { + $remote_channel = $arr['channel']; + $remote_channel['channel_id'] = $channel['channel_id']; + + if (array_key_exists('channel_pageflags', $arr['channel']) && intval($arr['channel']['channel_pageflags'])) { + // Several pageflags are site-specific and cannot be sync'd. + // Only allow those bits which are shareable from the remote and then + // logically OR with the local flags + + $arr['channel']['channel_pageflags'] = $arr['channel']['channel_pageflags'] & (PAGE_HIDDEN | PAGE_AUTOCONNECT | PAGE_APPLICATION | PAGE_PREMIUM | PAGE_ADULT); + $arr['channel']['channel_pageflags'] = $arr['channel']['channel_pageflags'] | $channel['channel_pageflags']; + } + + $columns = db_columns('channel'); + + $disallowed = [ + 'channel_id', 'channel_account_id', 'channel_primary', 'channel_prvkey', + 'channel_address', 'channel_notifyflags', 'channel_removed', 'channel_deleted', + 'channel_system', 'channel_r_stream', 'channel_r_profile', 'channel_r_abook', + 'channel_r_storage', 'channel_r_pages', 'channel_w_stream', 'channel_w_wall', + 'channel_w_comment', 'channel_w_mail', 'channel_w_like', 'channel_w_tagwall', + 'channel_w_chat', 'channel_w_storage', 'channel_w_pages', 'channel_a_republish', + 'channel_a_delegate', 'channel_moved' + ]; + + foreach ($arr['channel'] as $k => $v) { + if (in_array($k, $disallowed)) { + continue; + } + if (!in_array($k, $columns)) { + continue; + } + $r = dbq("UPDATE channel set " . dbesc($k) . " = '" . dbesc($v) + . "' where channel_id = " . intval($channel['channel_id'])); + } + } + + if (array_key_exists('abook', $arr) && is_array($arr['abook']) && count($arr['abook'])) { + $total_friends = 0; + $total_feeds = 0; + + $r = q( + "select abook_id, abook_feed from abook where abook_channel = %d", + intval($channel['channel_id']) + ); + if ($r) { + // don't count yourself + $total_friends = ((count($r) > 0) ? count($r) - 1 : 0); + foreach ($r as $rr) { + if (intval($rr['abook_feed'])) { + $total_feeds++; + } + } + } + + + $disallowed = ['abook_id', 'abook_account', 'abook_channel', 'abook_rating', 'abook_rating_text', 'abook_not_here']; + + $fields = db_columns('abook'); + + foreach ($arr['abook'] as $abook) { + // this is here for debugging so we can find the issue source + + if (!is_array($abook)) { + btlogger('abook is not an array'); + continue; + } + + $abconfig = null; + + if (array_key_exists('abconfig', $abook) && is_array($abook['abconfig']) && count($abook['abconfig'])) { + $abconfig = $abook['abconfig']; + } + + $clean = []; + + if ($abook['abook_xchan'] && $abook['entry_deleted']) { + logger('Removing abook entry for ' . $abook['abook_xchan']); + + $r = q( + "select abook_id, abook_feed from abook where abook_xchan = '%s' and abook_channel = %d and abook_self = 0 limit 1", + dbesc($abook['abook_xchan']), + intval($channel['channel_id']) + ); + if ($r) { + contact_remove($channel['channel_id'], $r[0]['abook_id']); + if ($total_friends) { + $total_friends--; + } + if (intval($r[0]['abook_feed'])) { + $total_feeds--; + } + } + continue; + } + + // Perform discovery if the referenced xchan hasn't ever been seen on this hub. + // This relies on the undocumented behaviour that red sites send xchan info with the abook + // and import_author_xchan will look them up on all federated networks + + $found = false; + if ($abook['abook_xchan'] && $abook['xchan_addr'] && (!in_array($abook['xchan_network'], ['token', 'unknown']))) { + $h = Libzot::get_hublocs($abook['abook_xchan']); + if ($h) { + $found = true; + } else { + $xhash = import_author_xchan(encode_item_xchan($abook)); + if ($xhash) { + $found = true; + } else { + logger('Import of ' . $abook['xchan_addr'] . ' failed.'); + } + } + } + + if ((!$found) && (!in_array($abook['xchan_network'], ['nomad', 'zot6', 'activitypub']))) { + // just import the record. + $xc = []; + foreach ($abook as $k => $v) { + if (strpos($k, 'xchan_') === 0) { + $xc[$k] = $v; + } + } + $r = q( + "select * from xchan where xchan_hash = '%s'", + dbesc($xc['xchan_hash']) + ); + if (!$r) { + xchan_store_lowlevel($xc); + } + } + + foreach ($abook as $k => $v) { + if (in_array($k, $disallowed) || (strpos($k, 'abook_') !== 0)) { + continue; + } + if (!in_array($k, $fields)) { + continue; + } + $clean[$k] = $v; + } + + if (!array_key_exists('abook_xchan', $clean)) { + continue; + } + + $reconnect = false; + if (array_key_exists('abook_instance', $clean) && $clean['abook_instance'] && strpos($clean['abook_instance'], z_root()) === false) { + // guest pass or access token - don't try to probe since it is one-way + // we are relying on the undocumented behaviour that the abook record also contains the xchan + if ($abook['xchan_network'] === 'token') { + $clean['abook_instance'] .= ','; + $clean['abook_instance'] .= z_root(); + $clean['abook_not_here'] = 0; + } else { + $clean['abook_not_here'] = 1; + if (!($abook['abook_pending'] || $abook['abook_blocked'])) { + $reconnect = true; + } + } + } + + $r = q( + "select * from abook where abook_xchan = '%s' and abook_channel = %d limit 1", + dbesc($clean['abook_xchan']), + intval($channel['channel_id']) + ); + + // make sure we have an abook entry for this xchan on this system + + if (!$r) { + if ($max_friends !== false && $total_friends > $max_friends) { + logger('total_channels service class limit exceeded'); + continue; + } + if ($max_feeds !== false && intval($clean['abook_feed']) && $total_feeds > $max_feeds) { + logger('total_feeds service class limit exceeded'); + continue; + } + abook_store_lowlevel( + [ + 'abook_xchan' => $clean['abook_xchan'], + 'abook_account' => $channel['channel_account_id'], + 'abook_channel' => $channel['channel_id'] + ] + ); + $total_friends++; + if (intval($clean['abook_feed'])) { + $total_feeds++; + } + } + + if (count($clean)) { + foreach ($clean as $k => $v) { + if ($k == 'abook_dob') { + $v = dbescdate($v); + } + + $r = dbq("UPDATE abook set " . dbesc($k) . " = '" . dbesc($v) + . "' where abook_xchan = '" . dbesc($clean['abook_xchan']) . "' and abook_channel = " . intval($channel['channel_id'])); + } + } + + // This will set abconfig vars if the sender is using old-style fixed permissions + // using the raw abook record as passed to us. New-style permissions will fall through + // and be set using abconfig + + // translate_abook_perms_inbound($channel,$abook); + + if ($abconfig) { + /// @fixme does not handle sync of del_abconfig + foreach ($abconfig as $abc) { + set_abconfig($channel['channel_id'], $abc['xchan'], $abc['cat'], $abc['k'], $abc['v']); + } + } + if ($reconnect) { + Connect::connect($channel, $abook['abook_xchan']); + } + } + } + + // sync collections (privacy groups) oh joy... + + if (array_key_exists('collections', $arr) && is_array($arr['collections']) && count($arr['collections'])) { + $x = q( + "select * from pgrp where uid = %d ", + intval($channel['channel_id']) + ); + foreach ($arr['collections'] as $cl) { + $found = false; + if ($x) { + foreach ($x as $y) { + if ($cl['collection'] == $y['hash']) { + $found = true; + break; + } + } + if ($found) { + if ( + ($y['gname'] != $cl['name']) + || ($y['visible'] != $cl['visible']) + || ($y['deleted'] != $cl['deleted']) + ) { + q( + "update pgrp set gname = '%s', visible = %d, deleted = %d where hash = '%s' and uid = %d", + dbesc($cl['name']), + intval($cl['visible']), + intval($cl['deleted']), + dbesc($cl['collection']), + intval($channel['channel_id']) + ); + } + if (intval($cl['deleted']) && (!intval($y['deleted']))) { + q( + "delete from pgrp_member where gid = %d", + intval($y['id']) + ); + } + } + } + if (!$found) { + $r = q( + "INSERT INTO pgrp ( hash, uid, visible, deleted, gname, rule ) VALUES( '%s', %d, %d, %d, '%s', '%s' ) ", - dbesc($cl['collection']), - intval($channel['channel_id']), - intval($cl['visible']), - intval($cl['deleted']), - dbesc($cl['name']), - dbesc($cl['rule']) - ); - } + dbesc($cl['collection']), + intval($channel['channel_id']), + intval($cl['visible']), + intval($cl['deleted']), + dbesc($cl['name']), + dbesc($cl['rule']) + ); + } - // now look for any collections locally which weren't in the list we just received. - // They need to be removed by marking deleted and removing the members. - // This shouldn't happen except for clones created before this function was written. + // now look for any collections locally which weren't in the list we just received. + // They need to be removed by marking deleted and removing the members. + // This shouldn't happen except for clones created before this function was written. - if ($x) { - $found_local = false; - foreach ($x as $y) { - foreach ($arr['collections'] as $cl) { - if ($cl['collection'] == $y['hash']) { - $found_local = true; - break; - } - } - if (! $found_local) { - q("delete from pgrp_member where gid = %d", - intval($y['id']) - ); - q("update pgrp set deleted = 1 where id = %d and uid = %d", - intval($y['id']), - intval($channel['channel_id']) - ); - } - } - } - } + if ($x) { + $found_local = false; + foreach ($x as $y) { + foreach ($arr['collections'] as $cl) { + if ($cl['collection'] == $y['hash']) { + $found_local = true; + break; + } + } + if (!$found_local) { + q( + "delete from pgrp_member where gid = %d", + intval($y['id']) + ); + q( + "update pgrp set deleted = 1 where id = %d and uid = %d", + intval($y['id']), + intval($channel['channel_id']) + ); + } + } + } + } - // reload the group list with any updates - $x = q("select * from pgrp where uid = %d", - intval($channel['channel_id']) - ); + // reload the group list with any updates + $x = q( + "select * from pgrp where uid = %d", + intval($channel['channel_id']) + ); - // now sync the members + // now sync the members - if (array_key_exists('collection_members', $arr) - && is_array($arr['collection_members']) - && count($arr['collection_members'])) { + if ( + array_key_exists('collection_members', $arr) + && is_array($arr['collection_members']) + && count($arr['collection_members']) + ) { + // first sort into groups keyed by the group hash + $members = []; + foreach ($arr['collection_members'] as $cm) { + if (!array_key_exists($cm['collection'], $members)) { + $members[$cm['collection']] = []; + } - // first sort into groups keyed by the group hash - $members = []; - foreach ($arr['collection_members'] as $cm) { - if (! array_key_exists($cm['collection'],$members)) { - $members[$cm['collection']] = []; - } + $members[$cm['collection']][] = $cm['member']; + } - $members[$cm['collection']][] = $cm['member']; - } + // our group list is already synchronised + if ($x) { + foreach ($x as $y) { + // for each group, loop on members list we just received + if (isset($y['hash']) && isset($members[$y['hash']])) { + foreach ($members[$y['hash']] as $member) { + $found = false; + $z = q( + "select xchan from pgrp_member where gid = %d and uid = %d and xchan = '%s' limit 1", + intval($y['id']), + intval($channel['channel_id']), + dbesc($member) + ); + if ($z) { + $found = true; + } - // our group list is already synchronised - if ($x) { - foreach ($x as $y) { - - // for each group, loop on members list we just received - if (isset($y['hash']) && isset($members[$y['hash']])) { - foreach ($members[$y['hash']] as $member) { - $found = false; - $z = q("select xchan from pgrp_member where gid = %d and uid = %d and xchan = '%s' limit 1", - intval($y['id']), - intval($channel['channel_id']), - dbesc($member) - ); - if ($z) { - $found = true; - } - - // if somebody is in the group that wasn't before - add them - - if (! $found) { - q("INSERT INTO pgrp_member (uid, gid, xchan) + // if somebody is in the group that wasn't before - add them + + if (!$found) { + q( + "INSERT INTO pgrp_member (uid, gid, xchan) VALUES( %d, %d, '%s' ) ", - intval($channel['channel_id']), - intval($y['id']), - dbesc($member) - ); - } - } - } - - // now retrieve a list of members we have on this site - $m = q("select xchan from pgrp_member where gid = %d and uid = %d", - intval($y['id']), - intval($channel['channel_id']) - ); - if ($m) { - foreach ($m as $mm) { - // if the local existing member isn't in the list we just received - remove them - if (! in_array($mm['xchan'],$members[$y['hash']])) { - q("delete from pgrp_member where xchan = '%s' and gid = %d and uid = %d", - dbesc($mm['xchan']), - intval($y['id']), - intval($channel['channel_id']) - ); - } - } - } - } - } - } - } + intval($channel['channel_id']), + intval($y['id']), + dbesc($member) + ); + } + } + } - if (array_key_exists('profile',$arr) && is_array($arr['profile']) && count($arr['profile'])) { + // now retrieve a list of members we have on this site + $m = q( + "select xchan from pgrp_member where gid = %d and uid = %d", + intval($y['id']), + intval($channel['channel_id']) + ); + if ($m) { + foreach ($m as $mm) { + // if the local existing member isn't in the list we just received - remove them + if (!in_array($mm['xchan'], $members[$y['hash']])) { + q( + "delete from pgrp_member where xchan = '%s' and gid = %d and uid = %d", + dbesc($mm['xchan']), + intval($y['id']), + intval($channel['channel_id']) + ); + } + } + } + } + } + } + } - $disallowed = array('id','aid','uid','guid'); + if (array_key_exists('profile', $arr) && is_array($arr['profile']) && count($arr['profile'])) { + $disallowed = array('id', 'aid', 'uid', 'guid'); - foreach ($arr['profile'] as $profile) { - - $x = q("select * from profile where profile_guid = '%s' and uid = %d limit 1", - dbesc($profile['profile_guid']), - intval($channel['channel_id']) - ); - if (! $x) { - profile_store_lowlevel( - [ - 'aid' => $channel['channel_account_id'], - 'uid' => $channel['channel_id'], - 'profile_guid' => $profile['profile_guid'], - ] - ); - - $x = q("select * from profile where profile_guid = '%s' and uid = %d limit 1", - dbesc($profile['profile_guid']), - intval($channel['channel_id']) - ); - if (! $x) { - continue; - } - } - $clean = []; - foreach ($profile as $k => $v) { - if (in_array($k,$disallowed)) { - continue; - } + foreach ($arr['profile'] as $profile) { + $x = q( + "select * from profile where profile_guid = '%s' and uid = %d limit 1", + dbesc($profile['profile_guid']), + intval($channel['channel_id']) + ); + if (!$x) { + profile_store_lowlevel( + [ + 'aid' => $channel['channel_account_id'], + 'uid' => $channel['channel_id'], + 'profile_guid' => $profile['profile_guid'], + ] + ); - if ($profile['is_default'] && in_array($k,['photo','thumb'])) { - continue; - } + $x = q( + "select * from profile where profile_guid = '%s' and uid = %d limit 1", + dbesc($profile['profile_guid']), + intval($channel['channel_id']) + ); + if (!$x) { + continue; + } + } + $clean = []; + foreach ($profile as $k => $v) { + if (in_array($k, $disallowed)) { + continue; + } - if ($k === 'name') { - $clean['fullname'] = $v; - } - elseif ($k === 'with') { - $clean['partner'] = $v; - } - elseif ($k === 'work') { - $clean['employment'] = $v; - } - elseif (array_key_exists($k,$x[0])) { - $clean[$k] = $v; - } + if ($profile['is_default'] && in_array($k, ['photo', 'thumb'])) { + continue; + } - /** - * @TODO - * We also need to import local photos if a custom photo is selected - */ + if ($k === 'name') { + $clean['fullname'] = $v; + } elseif ($k === 'with') { + $clean['partner'] = $v; + } elseif ($k === 'work') { + $clean['employment'] = $v; + } elseif (array_key_exists($k, $x[0])) { + $clean[$k] = $v; + } - if ((strpos($profile['thumb'],'/photo/profile/l/') !== false) || intval($profile['is_default'])) { - $profile['photo'] = z_root() . '/photo/profile/l/' . $channel['channel_id']; - $profile['thumb'] = z_root() . '/photo/profile/m/' . $channel['channel_id']; - } - else { - $profile['photo'] = z_root() . '/photo/' . basename($profile['photo']); - $profile['thumb'] = z_root() . '/photo/' . basename($profile['thumb']); - } - } + /** + * @TODO + * We also need to import local photos if a custom photo is selected + */ - if (count($clean)) { - foreach ($clean as $k => $v) { - $r = dbq("UPDATE profile set " . TQUOT . dbesc($k) . TQUOT . " = '" . dbesc($v) - . "' where profile_guid = '" . dbesc($profile['profile_guid']) - . "' and uid = " . intval($channel['channel_id'])); - } - } - } - } + if ((strpos($profile['thumb'], '/photo/profile/l/') !== false) || intval($profile['is_default'])) { + $profile['photo'] = z_root() . '/photo/profile/l/' . $channel['channel_id']; + $profile['thumb'] = z_root() . '/photo/profile/m/' . $channel['channel_id']; + } else { + $profile['photo'] = z_root() . '/photo/' . basename($profile['photo']); + $profile['thumb'] = z_root() . '/photo/' . basename($profile['thumb']); + } + } - $addon = [ 'channel' => $channel, 'data' => $arr ]; - /** - * @hooks process_channel_sync_delivery - * Called when accepting delivery of a 'sync packet' containing structure and table updates from a channel clone. - * * \e array \b channel - * * \e array \b data - */ - call_hooks('process_channel_sync_delivery', $addon); + if (count($clean)) { + foreach ($clean as $k => $v) { + $r = dbq("UPDATE profile set " . TQUOT . dbesc($k) . TQUOT . " = '" . dbesc($v) + . "' where profile_guid = '" . dbesc($profile['profile_guid']) + . "' and uid = " . intval($channel['channel_id'])); + } + } + } + } - $DR = new DReport(z_root(),$d,$d,'sync','channel sync delivered'); + $addon = ['channel' => $channel, 'data' => $arr]; + /** + * @hooks process_channel_sync_delivery + * Called when accepting delivery of a 'sync packet' containing structure and table updates from a channel clone. + * * \e array \b channel + * * \e array \b data + */ + call_hooks('process_channel_sync_delivery', $addon); - $DR->set_name($channel['channel_name'] . ' <' . channel_reddress($channel) . '>'); + $DR = new DReport(z_root(), $d, $d, 'sync', 'channel sync delivered'); - $result[] = $DR->get(); - } + $DR->set_name($channel['channel_name'] . ' <' . channel_reddress($channel) . '>'); - return $result; - } + $result[] = $DR->get(); + } - /** - * @brief Synchronises locations. - * - * @param array $sender - * @param array $arr - * @param boolean $absolute (optional) default false - * @return array - */ + return $result; + } - static function sync_locations($sender, $arr, $absolute = false) { + /** + * @brief Synchronises locations. + * + * @param array $sender + * @param array $arr + * @param bool $absolute (optional) default false + * @return array + */ - $ret = []; - $what = EMPTY_STR; - $changed = false; + public static function sync_locations($sender, $arr, $absolute = false) + { - // If a sender reports that the channel has been deleted, delete its hubloc + $ret = []; + $what = EMPTY_STR; + $changed = false; - if (isset($arr['deleted_locally']) && intval($arr['deleted_locally'])) { - q("UPDATE hubloc SET hubloc_deleted = 1, hubloc_updated = '%s' WHERE hubloc_hash = '%s' AND hubloc_url = '%s'", - dbesc(datetime_convert()), - dbesc($sender['hash']), - dbesc($sender['site']['url']) - ); - } + // If a sender reports that the channel has been deleted, delete its hubloc - if($arr['locations']) { + if (isset($arr['deleted_locally']) && intval($arr['deleted_locally'])) { + q( + "UPDATE hubloc SET hubloc_deleted = 1, hubloc_updated = '%s' WHERE hubloc_hash = '%s' AND hubloc_url = '%s'", + dbesc(datetime_convert()), + dbesc($sender['hash']), + dbesc($sender['site']['url']) + ); + } - $x = q("select * from xchan where xchan_hash = '%s'", - dbesc($sender['hash']) - ); - if ($x) { - $xchan = array_shift($x); - } + if ($arr['locations']) { + $x = q( + "select * from xchan where xchan_hash = '%s'", + dbesc($sender['hash']) + ); + if ($x) { + $xchan = array_shift($x); + } - if ($absolute) { - Libzot::check_location_move($sender['hash'],$arr['locations']); - } - - $xisting = q("select * from hubloc where hubloc_hash = '%s'", - dbesc($sender['hash']) - ); + if ($absolute) { + Libzot::check_location_move($sender['hash'], $arr['locations']); + } - if (! $xisting) { - $xisting = []; - } - - // See if a primary is specified + $xisting = q( + "select * from hubloc where hubloc_hash = '%s'", + dbesc($sender['hash']) + ); - $has_primary = false; - foreach($arr['locations'] as $location) { - if($location['primary']) { - $has_primary = true; - break; - } - } + if (!$xisting) { + $xisting = []; + } - // Ensure that they have one primary hub + // See if a primary is specified - if(! $has_primary) - $arr['locations'][0]['primary'] = true; + $has_primary = false; + foreach ($arr['locations'] as $location) { + if ($location['primary']) { + $has_primary = true; + break; + } + } - foreach($arr['locations'] as $location) { + // Ensure that they have one primary hub - $network = isset($location['driver']) ? $location['driver'] : 'zot6'; - // only set nomad if the location info is coming from the same site as the original zotinfo packet - if (isset($sender['site']) && isset($sender['site']['url']) && $sender['site']['url'] === $location['url']) { - if (isset($sender['site']['protocol_version']) && intval($sender['site']['protocol_version']) > 10) { - $network = 'nomad'; - } - } - if(! Libzot::verify($location['url'],$location['url_sig'],$sender['public_key'])) { - logger('Unable to verify site signature for ' . $location['url']); - $ret['message'] .= sprintf( t('Unable to verify site signature for %s'), $location['url']) . EOL; - continue; - } + if (!$has_primary) { + $arr['locations'][0]['primary'] = true; + } - for($x = 0; $x < count($xisting); $x ++) { - if(($xisting[$x]['hubloc_url'] === $location['url']) - && ($xisting[$x]['hubloc_sitekey'] === $location['sitekey'])) { - $xisting[$x]['updated'] = true; - } - } + foreach ($arr['locations'] as $location) { - if(! $location['sitekey']) { - logger('Empty hubloc sitekey. ' . print_r($location,true)); - continue; - } + $network = isset($location['driver']) ? $location['driver'] : 'zot6'; + // only set nomad if the location info is coming from the same site as the original zotinfo packet + if (isset($sender['site']) && isset($sender['site']['url']) && $sender['site']['url'] === $location['url']) { + if (isset($sender['site']['protocol_version']) && intval($sender['site']['protocol_version']) > 10) { + $network = 'nomad'; + } + } + + if (!Libzot::verify($location['url'], $location['url_sig'], $sender['public_key'])) { + logger('Unable to verify site signature for ' . $location['url']); + $ret['message'] .= sprintf(t('Unable to verify site signature for %s'), $location['url']) . EOL; + continue; + } - // match as many fields as possible in case anything at all changed. + for ($x = 0; $x < count($xisting); $x++) { + if ( + ($xisting[$x]['hubloc_url'] === $location['url']) + && ($xisting[$x]['hubloc_sitekey'] === $location['sitekey']) + ) { + $xisting[$x]['updated'] = true; + } + } - $r = q("select * from hubloc where hubloc_hash = '%s' and hubloc_guid = '%s' and hubloc_guid_sig = '%s' and hubloc_id_url = '%s' and hubloc_url = '%s' and hubloc_url_sig = '%s' and hubloc_host = '%s' and hubloc_addr = '%s' and hubloc_callback = '%s' and hubloc_sitekey = '%s' ", - dbesc($sender['hash']), - dbesc($sender['id']), - dbesc($sender['id_sig']), - dbesc($location['id_url']), - dbesc($location['url']), - dbesc($location['url_sig']), - dbesc($location['host']), - dbesc($location['address']), - dbesc($location['callback']), - dbesc($location['sitekey']) - ); - if ($r) { - logger('Hub exists: ' . $location['url'], LOGGER_DEBUG); + if (!$location['sitekey']) { + logger('Empty hubloc sitekey. ' . print_r($location, true)); + continue; + } - // generate a new hubloc_site_id if it's wrong due to historical bugs 2021-11-30 - - if ($r[0]['hubloc_site_id'] !== $location['site_id']) { - q("update hubloc set hubloc_site_id = '%s' where hubloc_id = %d", - dbesc(Libzot::make_xchan_hash($location['url'],$location['sitekey'])), - intval($r[0]['hubloc_id']) - ); - } - - // update connection timestamp if this is the site we're talking to - // This only happens when called from import_xchan + // match as many fields as possible in case anything at all changed. - $current_site = false; + $r = q( + "select * from hubloc where hubloc_hash = '%s' and hubloc_guid = '%s' and hubloc_guid_sig = '%s' and hubloc_id_url = '%s' and hubloc_url = '%s' and hubloc_url_sig = '%s' and hubloc_host = '%s' and hubloc_addr = '%s' and hubloc_callback = '%s' and hubloc_sitekey = '%s' ", + dbesc($sender['hash']), + dbesc($sender['id']), + dbesc($sender['id_sig']), + dbesc($location['id_url']), + dbesc($location['url']), + dbesc($location['url_sig']), + dbesc($location['host']), + dbesc($location['address']), + dbesc($location['callback']), + dbesc($location['sitekey']) + ); + if ($r) { + logger('Hub exists: ' . $location['url'], LOGGER_DEBUG); - $t = datetime_convert('UTC','UTC','now - 15 minutes'); + // generate a new hubloc_site_id if it's wrong due to historical bugs 2021-11-30 - // upgrade network driver if required - - if (isset($location['driver']) && $location['driver'] === 'nomad' && $location['driver'] !== $r[0]['hubloc_network']) { - q("update hubloc set hubloc_network = '%s' where hubloc_id = %d", - dbesc($location['driver']), - intval($r[0]['hubloc_id']) - ); - } - - if(array_key_exists('site',$arr) && $location['url'] == $arr['site']['url']) { - q("update hubloc set hubloc_connected = '%s', hubloc_updated = '%s' where hubloc_id = %d and hubloc_updated < '%s'", - dbesc(datetime_convert()), - dbesc(datetime_convert()), - intval($r[0]['hubloc_id']), - dbesc($t) - ); - $current_site = true; - } + if ($r[0]['hubloc_site_id'] !== $location['site_id']) { + q( + "update hubloc set hubloc_site_id = '%s' where hubloc_id = %d", + dbesc(Libzot::make_xchan_hash($location['url'], $location['sitekey'])), + intval($r[0]['hubloc_id']) + ); + } - if($current_site && (intval($r[0]['hubloc_error']) || intval($r[0]['hubloc_deleted']))) { - q("update hubloc set hubloc_error = 0, hubloc_deleted = 0 where hubloc_id = %d", - intval($r[0]['hubloc_id']) - ); - if(intval($r[0]['hubloc_orphancheck'])) { - q("update hubloc set hubloc_orphancheck = 0 where hubloc_id = %d", - intval($r[0]['hubloc_id']) - ); - } - q("update xchan set xchan_orphan = 0 where xchan_orphan = 1 and xchan_hash = '%s'", - dbesc($sender['hash']) - ); - } + // update connection timestamp if this is the site we're talking to + // This only happens when called from import_xchan - // Remove pure duplicates - if(count($r) > 1) { - for($h = 1; $h < count($r); $h ++) { - q("delete from hubloc where hubloc_id = %d", - intval($r[$h]['hubloc_id']) - ); - $what .= 'duplicate_hubloc_removed '; - $changed = true; - } - } + $current_site = false; - if(intval($r[0]['hubloc_primary']) && (! $location['primary'])) { - $m = q("update hubloc set hubloc_primary = 0, hubloc_updated = '%s' where hubloc_id = %d", - dbesc(datetime_convert()), - intval($r[0]['hubloc_id']) - ); - $r[0]['hubloc_primary'] = intval($location['primary']); - hubloc_change_primary($r[0]); - $what .= 'primary_hub '; - $changed = true; - } - elseif ((! intval($r[0]['hubloc_primary'])) && ($location['primary'])) { - $m = q("update hubloc set hubloc_primary = 1, hubloc_updated = '%s' where hubloc_id = %d", - dbesc(datetime_convert()), - intval($r[0]['hubloc_id']) - ); - // make sure hubloc_change_primary() has current data - $r[0]['hubloc_primary'] = intval($location['primary']); - hubloc_change_primary($r[0]); - $what .= 'primary_hub '; - $changed = true; - } - elseif ($absolute) { - // Absolute sync - make sure the current primary is correctly reflected in the xchan - $pr = hubloc_change_primary($r[0]); - if($pr) { - $what .= 'xchan_primary '; - $changed = true; - } - } - elseif(intval($r[0]['hubloc_primary']) && $xchan && $xchan['xchan_url'] !== $r[0]['hubloc_id_url']) { - $pr = hubloc_change_primary($r[0]); - if($pr) { - $what .= 'xchan_primary '; - $changed = true; - } - } + $t = datetime_convert('UTC', 'UTC', 'now - 15 minutes'); - if(intval($r[0]['hubloc_deleted']) && (! intval($location['deleted']))) { - $n = q("update hubloc set hubloc_deleted = 0, hubloc_updated = '%s' where hubloc_id = %d", - dbesc(datetime_convert()), - intval($r[0]['hubloc_id']) - ); - $what .= 'undelete_hub '; - $changed = true; - } - elseif((! intval($r[0]['hubloc_deleted'])) && (intval($location['deleted']))) { - logger('deleting hubloc: ' . $r[0]['hubloc_addr']); - $n = q("update hubloc set hubloc_deleted = 1, hubloc_updated = '%s' where hubloc_id = %d", - dbesc(datetime_convert()), - intval($r[0]['hubloc_id']) - ); - $what .= 'delete_hub '; - $changed = true; - } - continue; - } + if (isset($location['driver']) && $location['driver'] === 'nomad' && $location['driver'] !== $r[0]['hubloc_network']) { + q("update hubloc set hubloc_network = '%s' where hubloc_id = %d", + dbesc($location['driver']), + intval($r[0]['hubloc_id']) + ); + } - // Existing hubs are dealt with. Now let's process any new ones. - // New hub claiming to be primary. Make it so by removing any existing primaries. + if (array_key_exists('site', $arr) && $location['url'] == $arr['site']['url']) { + q( + "update hubloc set hubloc_connected = '%s', hubloc_updated = '%s' where hubloc_id = %d and hubloc_updated < '%s'", + dbesc(datetime_convert()), + dbesc(datetime_convert()), + intval($r[0]['hubloc_id']), + dbesc($t) + ); + $current_site = true; + } - if(intval($location['primary'])) { - $r = q("update hubloc set hubloc_primary = 0, hubloc_updated = '%s' where hubloc_hash = '%s' and hubloc_primary = 1", - dbesc(datetime_convert()), - dbesc($sender['hash']) - ); - } + if ($current_site && (intval($r[0]['hubloc_error']) || intval($r[0]['hubloc_deleted']))) { + q( + "update hubloc set hubloc_error = 0, hubloc_deleted = 0 where hubloc_id = %d", + intval($r[0]['hubloc_id']) + ); + if (intval($r[0]['hubloc_orphancheck'])) { + q( + "update hubloc set hubloc_orphancheck = 0 where hubloc_id = %d", + intval($r[0]['hubloc_id']) + ); + } + q( + "update xchan set xchan_orphan = 0 where xchan_orphan = 1 and xchan_hash = '%s'", + dbesc($sender['hash']) + ); + } - logger('New hub: ' . $location['url']); + // Remove pure duplicates + if (count($r) > 1) { + for ($h = 1; $h < count($r); $h++) { + q( + "delete from hubloc where hubloc_id = %d", + intval($r[$h]['hubloc_id']) + ); + $what .= 'duplicate_hubloc_removed '; + $changed = true; + } + } - $r = hubloc_store_lowlevel( - [ - 'hubloc_guid' => $sender['id'], - 'hubloc_guid_sig' => $sender['id_sig'], - 'hubloc_id_url' => $location['id_url'], - 'hubloc_hash' => $sender['hash'], - 'hubloc_addr' => $location['address'], - 'hubloc_network' => $network, - 'hubloc_primary' => intval($location['primary']), - 'hubloc_url' => $location['url'], - 'hubloc_url_sig' => $location['url_sig'], - 'hubloc_site_id' => Libzot::make_xchan_hash($location['url'],$location['sitekey']), - 'hubloc_host' => $location['host'], - 'hubloc_callback' => $location['callback'], - 'hubloc_sitekey' => $location['sitekey'], - 'hubloc_updated' => datetime_convert(), - 'hubloc_connected' => datetime_convert() - ] - ); + if (intval($r[0]['hubloc_primary']) && (!$location['primary'])) { + $m = q( + "update hubloc set hubloc_primary = 0, hubloc_updated = '%s' where hubloc_id = %d", + dbesc(datetime_convert()), + intval($r[0]['hubloc_id']) + ); + $r[0]['hubloc_primary'] = intval($location['primary']); + hubloc_change_primary($r[0]); + $what .= 'primary_hub '; + $changed = true; + } elseif ((!intval($r[0]['hubloc_primary'])) && ($location['primary'])) { + $m = q( + "update hubloc set hubloc_primary = 1, hubloc_updated = '%s' where hubloc_id = %d", + dbesc(datetime_convert()), + intval($r[0]['hubloc_id']) + ); + // make sure hubloc_change_primary() has current data + $r[0]['hubloc_primary'] = intval($location['primary']); + hubloc_change_primary($r[0]); + $what .= 'primary_hub '; + $changed = true; + } elseif ($absolute) { + // Absolute sync - make sure the current primary is correctly reflected in the xchan + $pr = hubloc_change_primary($r[0]); + if ($pr) { + $what .= 'xchan_primary '; + $changed = true; + } + } elseif (intval($r[0]['hubloc_primary']) && $xchan && $xchan['xchan_url'] !== $r[0]['hubloc_id_url']) { + $pr = hubloc_change_primary($r[0]); + if ($pr) { + $what .= 'xchan_primary '; + $changed = true; + } + } - $what .= 'newhub '; - $changed = true; + if (intval($r[0]['hubloc_deleted']) && (!intval($location['deleted']))) { + $n = q( + "update hubloc set hubloc_deleted = 0, hubloc_updated = '%s' where hubloc_id = %d", + dbesc(datetime_convert()), + intval($r[0]['hubloc_id']) + ); + $what .= 'undelete_hub '; + $changed = true; + } elseif ((!intval($r[0]['hubloc_deleted'])) && (intval($location['deleted']))) { + logger('deleting hubloc: ' . $r[0]['hubloc_addr']); + $n = q( + "update hubloc set hubloc_deleted = 1, hubloc_updated = '%s' where hubloc_id = %d", + dbesc(datetime_convert()), + intval($r[0]['hubloc_id']) + ); + $what .= 'delete_hub '; + $changed = true; + } + continue; + } - if($location['primary']) { - $r = q("select * from hubloc where hubloc_addr = '%s' and hubloc_sitekey = '%s' limit 1", - dbesc($location['address']), - dbesc($location['sitekey']) - ); - if($r) - hubloc_change_primary($r[0]); - } - } + // Existing hubs are dealt with. Now let's process any new ones. + // New hub claiming to be primary. Make it so by removing any existing primaries. - // get rid of any hubs we have for this channel which weren't reported. + if (intval($location['primary'])) { + $r = q( + "update hubloc set hubloc_primary = 0, hubloc_updated = '%s' where hubloc_hash = '%s' and hubloc_primary = 1", + dbesc(datetime_convert()), + dbesc($sender['hash']) + ); + } - if($absolute && $xisting) { - foreach($xisting as $x) { - if(! array_key_exists('updated',$x)) { - logger('Deleting unreferenced hub location ' . $x['hubloc_addr']); - $r = q("update hubloc set hubloc_deleted = 1, hubloc_updated = '%s' where hubloc_id = %d", - dbesc(datetime_convert()), - intval($x['hubloc_id']) - ); - $what .= 'removed_hub '; - $changed = true; - } - } - } - } - else { - logger('No locations to sync!'); - } + logger('New hub: ' . $location['url']); - $ret['change_message'] = $what; - $ret['changed'] = $changed; + $r = hubloc_store_lowlevel( + [ + 'hubloc_guid' => $sender['id'], + 'hubloc_guid_sig' => $sender['id_sig'], + 'hubloc_id_url' => $location['id_url'], + 'hubloc_hash' => $sender['hash'], + 'hubloc_addr' => $location['address'], + 'hubloc_network' => $network, + 'hubloc_primary' => intval($location['primary']), + 'hubloc_url' => $location['url'], + 'hubloc_url_sig' => $location['url_sig'], + 'hubloc_site_id' => Libzot::make_xchan_hash($location['url'], $location['sitekey']), + 'hubloc_host' => $location['host'], + 'hubloc_callback' => $location['callback'], + 'hubloc_sitekey' => $location['sitekey'], + 'hubloc_updated' => datetime_convert(), + 'hubloc_connected' => datetime_convert() + ] + ); - return $ret; - } + $what .= 'newhub '; + $changed = true; + + if ($location['primary']) { + $r = q( + "select * from hubloc where hubloc_addr = '%s' and hubloc_sitekey = '%s' limit 1", + dbesc($location['address']), + dbesc($location['sitekey']) + ); + if ($r) { + hubloc_change_primary($r[0]); + } + } + } + + // get rid of any hubs we have for this channel which weren't reported. + + if ($absolute && $xisting) { + foreach ($xisting as $x) { + if (!array_key_exists('updated', $x)) { + logger('Deleting unreferenced hub location ' . $x['hubloc_addr']); + $r = q( + "update hubloc set hubloc_deleted = 1, hubloc_updated = '%s' where hubloc_id = %d", + dbesc(datetime_convert()), + intval($x['hubloc_id']) + ); + $what .= 'removed_hub '; + $changed = true; + } + } + } + } else { + logger('No locations to sync!'); + } + + $ret['change_message'] = $what; + $ret['changed'] = $changed; + + return $ret; + } - static function keychange($channel,$arr) { + public static function keychange($channel, $arr) + { - // verify the keychange operation - if(! Libzot::verify($arr['channel']['channel_pubkey'],$arr['keychange']['new_sig'],$channel['channel_prvkey'])) { - logger('sync keychange: verification failed'); - return; - } + // verify the keychange operation + if (!Libzot::verify($arr['channel']['channel_pubkey'], $arr['keychange']['new_sig'], $channel['channel_prvkey'])) { + logger('sync keychange: verification failed'); + return; + } - $sig = Libzot::sign($channel['channel_guid'],$arr['channel']['channel_prvkey']); - $hash = Libzot::make_xchan_hash($channel['channel_guid'],$arr['channel']['channel_pubkey']); + $sig = Libzot::sign($channel['channel_guid'], $arr['channel']['channel_prvkey']); + $hash = Libzot::make_xchan_hash($channel['channel_guid'], $arr['channel']['channel_pubkey']); - $r = q("update channel set channel_prvkey = '%s', channel_pubkey = '%s', channel_guid_sig = '%s', + $r = q( + "update channel set channel_prvkey = '%s', channel_pubkey = '%s', channel_guid_sig = '%s', channel_hash = '%s' where channel_id = %d", - dbesc($arr['channel']['channel_prvkey']), - dbesc($arr['channel']['channel_pubkey']), - dbesc($sig), - dbesc($hash), - intval($channel['channel_id']) - ); - if(! $r) { - logger('keychange sync: channel update failed'); - return; - } + dbesc($arr['channel']['channel_prvkey']), + dbesc($arr['channel']['channel_pubkey']), + dbesc($sig), + dbesc($hash), + intval($channel['channel_id']) + ); + if (!$r) { + logger('keychange sync: channel update failed'); + return; + } - $r = q("select * from channel where channel_id = %d", - intval($channel['channel_id']) - ); + $r = q( + "select * from channel where channel_id = %d", + intval($channel['channel_id']) + ); - if(! $r) { - logger('keychange sync: channel retrieve failed'); - return; - } + if (!$r) { + logger('keychange sync: channel retrieve failed'); + return; + } - $channel = $r[0]; + $channel = $r[0]; - $h = q("select * from hubloc where hubloc_hash = '%s' and hubloc_url = '%s' ", - dbesc($arr['keychange']['old_hash']), - dbesc(z_root()) - ); + $h = q( + "select * from hubloc where hubloc_hash = '%s' and hubloc_url = '%s' ", + dbesc($arr['keychange']['old_hash']), + dbesc(z_root()) + ); - if($h) { - foreach($h as $hv) { - $hv['hubloc_guid_sig'] = $sig; - $hv['hubloc_hash'] = $hash; - $hv['hubloc_url_sig'] = Libzot::sign(z_root(),$channel['channel_prvkey']); - hubloc_store_lowlevel($hv); - } - } + if ($h) { + foreach ($h as $hv) { + $hv['hubloc_guid_sig'] = $sig; + $hv['hubloc_hash'] = $hash; + $hv['hubloc_url_sig'] = Libzot::sign(z_root(), $channel['channel_prvkey']); + hubloc_store_lowlevel($hv); + } + } - $x = q("select * from xchan where xchan_hash = '%s' ", - dbesc($arr['keychange']['old_hash']) - ); + $x = q( + "select * from xchan where xchan_hash = '%s' ", + dbesc($arr['keychange']['old_hash']) + ); - $check = q("select * from xchan where xchan_hash = '%s'", - dbesc($hash) - ); + $check = q( + "select * from xchan where xchan_hash = '%s'", + dbesc($hash) + ); - if(($x) && (! $check)) { - $oldxchan = $x[0]; - foreach($x as $xv) { - $xv['xchan_guid_sig'] = $sig; - $xv['xchan_hash'] = $hash; - $xv['xchan_pubkey'] = $channel['channel_pubkey']; - $xv['xchan_updated'] = datetime_convert(); - xchan_store_lowlevel($xv); - $newxchan = $xv; - } - } + if (($x) && (!$check)) { + $oldxchan = $x[0]; + foreach ($x as $xv) { + $xv['xchan_guid_sig'] = $sig; + $xv['xchan_hash'] = $hash; + $xv['xchan_pubkey'] = $channel['channel_pubkey']; + $xv['xchan_updated'] = datetime_convert(); + xchan_store_lowlevel($xv); + $newxchan = $xv; + } + } - $a = q("select * from abook where abook_xchan = '%s' and abook_self = 1", - dbesc($arr['keychange']['old_hash']) - ); + $a = q( + "select * from abook where abook_xchan = '%s' and abook_self = 1", + dbesc($arr['keychange']['old_hash']) + ); - if($a) { - q("update abook set abook_xchan = '%s' where abook_id = %d", - dbesc($hash), - intval($a[0]['abook_id']) - ); - } + if ($a) { + q( + "update abook set abook_xchan = '%s' where abook_id = %d", + dbesc($hash), + intval($a[0]['abook_id']) + ); + } - xchan_change_key($oldxchan,$newxchan,$arr['keychange']); - - } - -} \ No newline at end of file + xchan_change_key($oldxchan, $newxchan, $arr['keychange']); + } +} diff --git a/Zotlabs/Lib/Libzot.php b/Zotlabs/Lib/Libzot.php index 66721dc3d..02b3e293c 100644 --- a/Zotlabs/Lib/Libzot.php +++ b/Zotlabs/Lib/Libzot.php @@ -20,228 +20,239 @@ use Zotlabs\Daemon\Run; require_once('include/html2bbcode.php'); -class Libzot { +class Libzot +{ - /** - * @brief Generates a unique string for use as a zot guid. - * - * Generates a unique string for use as a zot guid using our DNS-based url, the - * channel nickname and some entropy. - * The entropy ensures uniqueness against re-installs where the same URL and - * nickname are chosen. - * - * @note zot doesn't require this to be unique. Internally we use a whirlpool - * hash of this guid and the signature of this guid signed with the channel - * private key. This can be verified and should make the probability of - * collision of the verified result negligible within the constraints of our - * immediate universe. - * - * @param string $channel_nick a unique nickname of controlling entity - * @returns string - */ + /** + * @brief Generates a unique string for use as a zot guid. + * + * Generates a unique string for use as a zot guid using our DNS-based url, the + * channel nickname and some entropy. + * The entropy ensures uniqueness against re-installs where the same URL and + * nickname are chosen. + * + * @note zot doesn't require this to be unique. Internally we use a whirlpool + * hash of this guid and the signature of this guid signed with the channel + * private key. This can be verified and should make the probability of + * collision of the verified result negligible within the constraints of our + * immediate universe. + * + * @param string $channel_nick a unique nickname of controlling entity + * @returns string + */ - static function new_uid($channel_nick) { - $rawstr = z_root() . '/' . $channel_nick . '.' . mt_rand(); - return(base64url_encode(hash('whirlpool', $rawstr, true), true)); - } + public static function new_uid($channel_nick) + { + $rawstr = z_root() . '/' . $channel_nick . '.' . mt_rand(); + return (base64url_encode(hash('whirlpool', $rawstr, true), true)); + } - /** - * @brief Generates a portable hash identifier for a channel. - * - * Generates a portable hash identifier for the channel identified by $guid and - * $pubkey. - * - * @note This ID is portable across the network but MUST be calculated locally - * by verifying the signature and can not be trusted as an identity. - * - * @param string $guid - * @param string $pubkey - */ + /** + * @brief Generates a portable hash identifier for a channel. + * + * Generates a portable hash identifier for the channel identified by $guid and + * $pubkey. + * + * @note This ID is portable across the network but MUST be calculated locally + * by verifying the signature and can not be trusted as an identity. + * + * @param string $guid + * @param string $pubkey + */ - static function make_xchan_hash($guid, $pubkey) { - return base64url_encode(hash('whirlpool', $guid . $pubkey, true)); - } + public static function make_xchan_hash($guid, $pubkey) + { + return base64url_encode(hash('whirlpool', $guid . $pubkey, true)); + } - /** - * @brief Given a zot hash, return all distinct hubs. - * - * This function is used in building the zot discovery packet and therefore - * should only be used by channels which are defined on this hub. - * - * @param string $hash - xchan_hash - * @returns array of hubloc (hub location structures) - * - */ + /** + * @brief Given a zot hash, return all distinct hubs. + * + * This function is used in building the zot discovery packet and therefore + * should only be used by channels which are defined on this hub. + * + * @param string $hash - xchan_hash + * @returns array of hubloc (hub location structures) + * + */ - static function get_hublocs($hash) { + public static function get_hublocs($hash) + { - /* Only search for active hublocs - e.g. those that haven't been marked deleted */ + /* Only search for active hublocs - e.g. those that haven't been marked deleted */ - $ret = q("select * from hubloc where hubloc_hash = '%s' and hubloc_deleted = 0 order by hubloc_url ", - dbesc($hash) - ); + $ret = q( + "select * from hubloc where hubloc_hash = '%s' and hubloc_deleted = 0 order by hubloc_url ", + dbesc($hash) + ); - return $ret; - } + return $ret; + } - /** - * @brief Builds a zot6 notification packet. - * - * Builds a zot6 notification packet that you can either store in the queue with - * a message array or call zot_zot to immediately zot it to the other side. - * - * @param array $channel - * sender channel structure - * @param string $type - * packet type: one of 'activity', 'response', 'sync', 'purge', 'refresh', 'force_refresh', 'rekey' - * @param array $recipients - * envelope recipients, array of portable_id's; empty for public posts - * @param string msg - * optional message - * @param string $remote_key - * optional public site key of target hub used to encrypt entire packet - * @param string $methods - * optional comma separated list of encryption methods @ref self::best_algorithm() - * @returns string json encoded zot packet - */ + /** + * @brief Builds a zot6 notification packet. + * + * Builds a zot6 notification packet that you can either store in the queue with + * a message array or call zot_zot to immediately zot it to the other side. + * + * @param array $channel + * sender channel structure + * @param string $type + * packet type: one of 'activity', 'response', 'sync', 'purge', 'refresh', 'force_refresh', 'rekey' + * @param array $recipients + * envelope recipients, array of portable_id's; empty for public posts + * @param string msg + * optional message + * @param string $remote_key + * optional public site key of target hub used to encrypt entire packet + * @param string $methods + * optional comma separated list of encryption methods @ref self::best_algorithm() + * @returns string json encoded zot packet + */ - static function build_packet($channel, $type = 'activity', $recipients = null, $msg = '', $encoding = 'activitystreams', $remote_key = null, $methods = '') { + public static function build_packet($channel, $type = 'activity', $recipients = null, $msg = '', $encoding = 'activitystreams', $remote_key = null, $methods = '') + { - $data = [ - 'type' => $type, - 'encoding' => $encoding, - 'sender' => $channel['channel_hash'], - 'site_id' => self::make_xchan_hash(z_root(), get_config('system','pubkey')), - 'version' => System::get_zot_revision(), - ]; + $data = [ + 'type' => $type, + 'encoding' => $encoding, + 'sender' => $channel['channel_hash'], + 'site_id' => self::make_xchan_hash(z_root(), get_config('system', 'pubkey')), + 'version' => System::get_zot_revision(), + ]; - if ($recipients) { - $data['recipients'] = $recipients; - } + if ($recipients) { + $data['recipients'] = $recipients; + } - if ($msg) { - $actor = channel_url($channel); - if ($encoding === 'activitystreams' && array_key_exists('actor',$msg) && is_string($msg['actor']) && $actor === $msg['actor']) { - $msg = JSalmon::sign($msg,$actor,$channel['channel_prvkey']); - } - $data['data'] = $msg; - } - else { - unset($data['encoding']); - } + if ($msg) { + $actor = channel_url($channel); + if ($encoding === 'activitystreams' && array_key_exists('actor', $msg) && is_string($msg['actor']) && $actor === $msg['actor']) { + $msg = JSalmon::sign($msg, $actor, $channel['channel_prvkey']); + } + $data['data'] = $msg; + } else { + unset($data['encoding']); + } - logger('packet: ' . print_r($data,true), LOGGER_DATA, LOG_DEBUG); + logger('packet: ' . print_r($data, true), LOGGER_DATA, LOG_DEBUG); - if ($remote_key) { - $algorithm = self::best_algorithm($methods); - if ($algorithm) { - $data = Crypto::encapsulate(json_encode($data),$remote_key, $algorithm); - } - } + if ($remote_key) { + $algorithm = self::best_algorithm($methods); + if ($algorithm) { + $data = Crypto::encapsulate(json_encode($data), $remote_key, $algorithm); + } + } - return json_encode($data); - } + return json_encode($data); + } - /** - * @brief Choose best encryption function from those available on both sites. - * - * @param string $methods - * comma separated list of encryption methods - * @return string first match from our site method preferences crypto_methods() array - * of a method which is common to both sites; or an empty string if no matches are found. - * - * Failure to find a common algorithm is not an issue as our communications - * take place primarily over https, so this is just redundant encryption in many cases. - * - * In any case, the receiver is free to reject unencrypted private content if they have - * reason to distrust https. - * - * We are not using array_intersect() here because the specification for that function - * does not guarantee the order of results. It probably returns entries in the correct - * order for our needs and would simplify this function dramatically, but we cannot be - * certain that it will always do so on all operating systems. - * - */ + /** + * @brief Choose best encryption function from those available on both sites. + * + * @param string $methods + * comma separated list of encryption methods + * @return string first match from our site method preferences crypto_methods() array + * of a method which is common to both sites; or an empty string if no matches are found. + * + * Failure to find a common algorithm is not an issue as our communications + * take place primarily over https, so this is just redundant encryption in many cases. + * + * In any case, the receiver is free to reject unencrypted private content if they have + * reason to distrust https. + * + * We are not using array_intersect() here because the specification for that function + * does not guarantee the order of results. It probably returns entries in the correct + * order for our needs and would simplify this function dramatically, but we cannot be + * certain that it will always do so on all operating systems. + * + */ - static function best_algorithm($methods) { + public static function best_algorithm($methods) + { - $x = [ - 'methods' => $methods, - 'result' => '' - ]; + $x = [ + 'methods' => $methods, + 'result' => '' + ]; - /** - * @hooks zot_best_algorithm - * Called when negotiating crypto algorithms with remote sites. - * * \e string \b methods - comma separated list of encryption methods - * * \e string \b result - the algorithm to return - */ - - call_hooks('zot_best_algorithm', $x); + /** + * @hooks zot_best_algorithm + * Called when negotiating crypto algorithms with remote sites. + * * \e string \b methods - comma separated list of encryption methods + * * \e string \b result - the algorithm to return + */ - if ($x['result']) { - return $x['result']; - } + call_hooks('zot_best_algorithm', $x); - if ($methods) { - // $x = their methods as an array - $x = explode(',', $methods); - if ($x) { - // $y = our methods as an array - $y = Crypto::methods(); - if ($y) { - foreach ($y as $yv) { - $yv = trim($yv); - if (in_array($yv, $x)) { - return($yv); - } - } - } - } - } + if ($x['result']) { + return $x['result']; + } - return EMPTY_STR; - } + if ($methods) { + // $x = their methods as an array + $x = explode(',', $methods); + if ($x) { + // $y = our methods as an array + $y = Crypto::methods(); + if ($y) { + foreach ($y as $yv) { + $yv = trim($yv); + if (in_array($yv, $x)) { + return ($yv); + } + } + } + } + } + + return EMPTY_STR; + } - /** - * @brief send a zot message - * - * @see z_post_url() - * - * @param string $url - * @param array $data - * @param array $channel (required if using zot6 delivery) - * @param array $crypto (required if encrypted httpsig, requires hubloc_sitekey and site_crypto elements) - * @return array see z_post_url() for returned data format - */ + /** + * @brief send a zot message + * + * @param string $url + * @param array $data + * @param array $channel (required if using zot6 delivery) + * @param array $crypto (required if encrypted httpsig, requires hubloc_sitekey and site_crypto elements) + * @return array see z_post_url() for returned data format + * @see z_post_url() + * + */ - static function zot($url, $data, $channel = null,$crypto = null) { + public static function zot($url, $data, $channel = null, $crypto = null) + { - if ($channel) { - $headers = [ - 'X-Zot-Token' => random_string(), - 'Digest' => HTTPSig::generate_digest_header($data), - 'Content-type' => 'application/x-zot+json', - '(request-target)' => 'post ' . get_request_string($url) - ]; + if ($channel) { + $headers = [ + 'X-Zot-Token' => random_string(), + 'Digest' => HTTPSig::generate_digest_header($data), + 'Content-type' => 'application/x-zot+json', + '(request-target)' => 'post ' . get_request_string($url) + ]; - $h = HTTPSig::create_sig($headers,$channel['channel_prvkey'],channel_url($channel),false,'sha512', - (($crypto) ? [ 'key' => $crypto['hubloc_sitekey'], 'algorithm' => self::best_algorithm($crypto['site_crypto']) ] : false)); - } - else { - $h = []; - } + $h = HTTPSig::create_sig( + $headers, + $channel['channel_prvkey'], + channel_url($channel), + false, + 'sha512', + (($crypto) ? ['key' => $crypto['hubloc_sitekey'], 'algorithm' => self::best_algorithm($crypto['site_crypto'])] : false) + ); + } else { + $h = []; + } - $redirects = 0; + $redirects = 0; - return z_post_url($url,$data,$redirects,((empty($h)) ? [] : [ 'headers' => $h ])); - } + return z_post_url($url, $data, $redirects, ((empty($h)) ? [] : ['headers' => $h])); + } - - static function nomad($url, $data, $channel = null,$crypto = null) { + public static function nomad($url, $data, $channel = null,$crypto = null) { if ($channel) { $headers = [ @@ -265,3323 +276,3366 @@ class Libzot { - /** - * @brief Refreshes after permission changed or friending, etc. - * - * - * refresh is typically invoked when somebody has changed permissions of a channel and they are notified - * to fetch new permissions via a finger/discovery operation. This may result in a new connection - * (abook entry) being added to a local channel and it may result in auto-permissions being granted. - * - * Friending in zot is accomplished by sending a refresh packet to a specific channel which indicates a - * permission change has been made by the sender which affects the target channel. The hub controlling - * the target channel does targeted discovery (in which a zot discovery request contains permissions for - * the sender which were set by the local channel). These are decoded here, and if necessary an abook - * structure (addressbook) is created to store the permissions assigned to this channel. - * - * Initially these abook structures are created with a 'pending' flag, so that no reverse permissions are - * implied until this is approved by the owner channel. A channel can also auto-populate permissions in - * return and send back a refresh packet of its own. This is used by forum and group communication channels - * so that friending and membership in the channel's "club" is automatic. - * - * If $force is set when calling this function, this operation will be attempted even if our records indicate - * the remote site is permanently down. - * - * @param array $them => xchan structure of sender - * @param array $channel => local channel structure of target recipient, required for "friending" operations - * @param array $force (optional) default false - * - * @return boolean - * * \b true if successful - * * otherwise \b false - */ - static function refresh($them, $channel = null, $force = false) { + /** + * @brief Refreshes after permission changed or friending, etc. + * + * + * refresh is typically invoked when somebody has changed permissions of a channel and they are notified + * to fetch new permissions via a finger/discovery operation. This may result in a new connection + * (abook entry) being added to a local channel and it may result in auto-permissions being granted. + * + * Friending in zot is accomplished by sending a refresh packet to a specific channel which indicates a + * permission change has been made by the sender which affects the target channel. The hub controlling + * the target channel does targeted discovery (in which a zot discovery request contains permissions for + * the sender which were set by the local channel). These are decoded here, and if necessary an abook + * structure (addressbook) is created to store the permissions assigned to this channel. + * + * Initially these abook structures are created with a 'pending' flag, so that no reverse permissions are + * implied until this is approved by the owner channel. A channel can also auto-populate permissions in + * return and send back a refresh packet of its own. This is used by forum and group communication channels + * so that friending and membership in the channel's "club" is automatic. + * + * If $force is set when calling this function, this operation will be attempted even if our records indicate + * the remote site is permanently down. + * + * @param array $them => xchan structure of sender + * @param array $channel => local channel structure of target recipient, required for "friending" operations + * @param array $force (optional) default false + * + * @return boolean + * * \b true if successful + * * otherwise \b false + */ - $hsig_valid = false; + public static function refresh($them, $channel = null, $force = false) + { - logger('them: ' . print_r($them,true), LOGGER_DATA, LOG_DEBUG); - if ($channel) { - logger('channel: ' . print_r($channel,true), LOGGER_DATA, LOG_DEBUG); - } + $hsig_valid = false; - $url = null; + logger('them: ' . print_r($them, true), LOGGER_DATA, LOG_DEBUG); + if ($channel) { + logger('channel: ' . print_r($channel, true), LOGGER_DATA, LOG_DEBUG); + } - if ($them['hubloc_id_url']) { - $url = $them['hubloc_id_url']; - } - else { - $r = null; - - // if they re-installed the server we could end up with the wrong record - pointing to a hash generated by the old install. - // We'll order by reverse id to try and pick off the most recently created ones first and hopefully end up with the correct hubloc. - // We are looking for the most recently created primary hub, and the most recently created if for some reason we do not have a primary. - // hubloc_id_url is set to the channel home, which corresponds to an ActivityStreams actor id. - - $r = q("select hubloc_id_url, hubloc_primary from hubloc where hubloc_hash = '%s' and hubloc_network in ('nomad','zot6') order by hubloc_id desc", - dbesc($them['xchan_hash']) - ); + $url = null; - if ($r) { - foreach ($r as $rr) { - if (intval($rr['hubloc_primary'])) { - $url = $rr['hubloc_id_url']; - break; - } - } - if (! $url) { - $url = $r[0]['hubloc_id_url']; - } - } - } - - if (! $url) { - logger('zot_refresh: no url'); - return false; - } + if ($them['hubloc_id_url']) { + $url = $them['hubloc_id_url']; + } else { + $r = null; - $m = parse_url($url); - $site_url = unparse_url([ 'scheme' => $m['scheme'], 'host' => $m['host'] ]); + // if they re-installed the server we could end up with the wrong record - pointing to a hash generated by the old install. + // We'll order by reverse id to try and pick off the most recently created ones first and hopefully end up with the correct hubloc. + // We are looking for the most recently created primary hub, and the most recently created if for some reason we do not have a primary. + // hubloc_id_url is set to the channel home, which corresponds to an ActivityStreams actor id. + + $r = q( + "select hubloc_id_url, hubloc_primary from hubloc where hubloc_hash = '%s' and hubloc_network in ('zot6','nomad') order by hubloc_id desc", + dbesc($them['xchan_hash']) + ); + + if ($r) { + foreach ($r as $rr) { + if (intval($rr['hubloc_primary'])) { + $url = $rr['hubloc_id_url']; + break; + } + } + if (!$url) { + $url = $r[0]['hubloc_id_url']; + } + } + } + + if (!$url) { + logger('zot_refresh: no url'); + return false; + } + + $m = parse_url($url); + $site_url = unparse_url(['scheme' => $m['scheme'], 'host' => $m['host']]); - $s = q("select site_dead from site where site_url = '%s' limit 1", - dbesc($site_url) - ); + $s = q( + "select site_dead from site where site_url = '%s' limit 1", + dbesc($site_url) + ); - if ($s && intval($s[0]['site_dead']) && (! $force)) { - logger('zot_refresh: site ' . $site_url . ' is marked dead and force flag is not set. Cancelling operation.'); - return false; - } + if ($s && intval($s[0]['site_dead']) && (!$force)) { + logger('zot_refresh: site ' . $site_url . ' is marked dead and force flag is not set. Cancelling operation.'); + return false; + } - $record = Zotfinger::exec($url,$channel); + $record = Zotfinger::exec($url, $channel); - // Check the HTTP signature + // Check the HTTP signature - $hsig = $record['signature']; - if ($hsig && $hsig['signer'] === $url && $hsig['header_valid'] === true && $hsig['content_valid'] === true) { - $hsig_valid = true; - } + $hsig = $record['signature']; + if ($hsig && $hsig['signer'] === $url && $hsig['header_valid'] === true && $hsig['content_valid'] === true) { + $hsig_valid = true; + } - if (! $hsig_valid) { - logger('http signature not valid: ' . (($record['data']) ? print_r($hsig,true) : 'fetch failed')); - return false; - } + if (!$hsig_valid) { + logger('http signature not valid: ' . (($record['data']) ? print_r($hsig, true) : 'fetch failed')); + return false; + } - // If we reach this point, the signature is valid and we can trust the channel discovery data, so try and store - // the generic information in the returned discovery packet. + // If we reach this point, the signature is valid and we can trust the channel discovery data, so try and store + // the generic information in the returned discovery packet. - logger('zot-info: ' . print_r($record,true), LOGGER_DATA, LOG_DEBUG); + logger('zot-info: ' . print_r($record, true), LOGGER_DATA, LOG_DEBUG); - $x = self::import_xchan($record['data'], (($force) ? UPDATE_FLAGS_FORCED : UPDATE_FLAGS_UPDATED)); + $x = self::import_xchan($record['data'], (($force) ? UPDATE_FLAGS_FORCED : UPDATE_FLAGS_UPDATED)); - if (! $x['success']) { - return false; - } + if (!$x['success']) { + return false; + } - // Here we handle discovery packets that return targeted permissions and require an abook (either an existing connection - // or a new one) - - if ($channel && $record['data']['permissions']) { - $old_read_stream_perm = their_perms_contains($channel['channel_id'],$x['hash'],'view_stream'); - set_abconfig($channel['channel_id'],$x['hash'],'system','their_perms',$record['data']['permissions']); + // Here we handle discovery packets that return targeted permissions and require an abook (either an existing connection + // or a new one) - if (array_key_exists('profile',$record['data']) && array_key_exists('next_birthday',$record['data']['profile'])) { - $next_birthday = datetime_convert('UTC','UTC',$record['data']['profile']['next_birthday']); - } - else { - $next_birthday = NULL_DATE; - } + if ($channel && $record['data']['permissions']) { + $old_read_stream_perm = their_perms_contains($channel['channel_id'], $x['hash'], 'view_stream'); + set_abconfig($channel['channel_id'], $x['hash'], 'system', 'their_perms', $record['data']['permissions']); - $profile_assign = get_pconfig($channel['channel_id'],'system','profile_assign',''); + if (array_key_exists('profile', $record['data']) && array_key_exists('next_birthday', $record['data']['profile'])) { + $next_birthday = datetime_convert('UTC', 'UTC', $record['data']['profile']['next_birthday']); + } else { + $next_birthday = NULL_DATE; + } - // Keep original perms to check if we need to notify them - $previous_perms = get_all_perms($channel['channel_id'],$x['hash']); + $profile_assign = get_pconfig($channel['channel_id'], 'system', 'profile_assign', ''); - $r = q("select * from abook where abook_xchan = '%s' and abook_channel = %d and abook_self = 0 limit 1", - dbesc($x['hash']), - intval($channel['channel_id']) - ); + // Keep original perms to check if we need to notify them + $previous_perms = get_all_perms($channel['channel_id'], $x['hash']); - if ($r) { + $r = q( + "select * from abook where abook_xchan = '%s' and abook_channel = %d and abook_self = 0 limit 1", + dbesc($x['hash']), + intval($channel['channel_id']) + ); - // connection exists + if ($r) { + // connection exists - // if the dob is the same as what we have stored (disregarding the year), keep the one - // we have as we may have updated the year after sending a notification; and resetting - // to the one we just received would cause us to create duplicated events. + // if the dob is the same as what we have stored (disregarding the year), keep the one + // we have as we may have updated the year after sending a notification; and resetting + // to the one we just received would cause us to create duplicated events. - if (substr($r[0]['abook_dob'],5) == substr($next_birthday,5)) - $next_birthday = $r[0]['abook_dob']; + if (substr($r[0]['abook_dob'], 5) == substr($next_birthday, 5)) { + $next_birthday = $r[0]['abook_dob']; + } - $y = q("update abook set abook_dob = '%s' + $y = q( + "update abook set abook_dob = '%s' where abook_xchan = '%s' and abook_channel = %d and abook_self = 0 ", - dbescdate($next_birthday), - dbesc($x['hash']), - intval($channel['channel_id']) - ); + dbescdate($next_birthday), + dbesc($x['hash']), + intval($channel['channel_id']) + ); - if (! $y) { - logger('abook update failed'); - } - else { - // if we were just granted read stream permission and didn't have it before, try to pull in some posts - if ((! $old_read_stream_perm) && (intval($permissions['view_stream']))) - Run::Summon([ 'Onepoll', $r[0]['abook_id'] ]); - } - } - else { + if (!$y) { + logger('abook update failed'); + } else { + // if we were just granted read stream permission and didn't have it before, try to pull in some posts + if ((!$old_read_stream_perm) && (intval($permissions['view_stream']))) { + Run::Summon(['Onepoll', $r[0]['abook_id']]); + } + } + } else { + // limit the ability to do connection spamming, this limit is per channel + $lim = intval(get_config('system', 'max_connections_per_day', 50)); + if ($lim) { + $n = q( + "select count(abook_id) as total from abook where abook_channel = %d and abook_created > '%s'", + intval($channel['channel_id']), + dbesc(datetime_convert('UTC', 'UTC', 'now - 24 hours')) + ); + if ($n && intval($n['total']) > $lim) { + logger('channel: ' . $channel['channel_id'] . ' too many new connections per day. This one from ' . $hsig['signer'], LOGGER_NORMAL, LOG_WARNING); + return false; + } + } - // limit the ability to do connection spamming, this limit is per channel - $lim = intval(get_config('system','max_connections_per_day',50)); - if ($lim) { - $n = q("select count(abook_id) as total from abook where abook_channel = %d and abook_created > '%s'", - intval($channel['channel_id']), - dbesc(datetime_convert('UTC','UTC','now - 24 hours')) - ); - if ($n && intval($n['total']) > $lim) { - logger('channel: ' . $channel['channel_id'] . ' too many new connections per day. This one from ' . $hsig['signer'], LOGGER_NORMAL, LOG_WARNING); - return false; - } - } + // check personal blocklists - // check personal blocklists - - $blocked = LibBlock::fetch($channel['channel_id'],BLOCKTYPE_SERVER); - if ($blocked) { - foreach($blocked as $b) { - if (strpos($url,$b['block_entity']) !== false) { - logger('siteblock - follower denied'); - return; - } - } - } - if (LibBlock::fetch_by_entity($channel['channel_id'],$x['hash'])) { - logger('actorblock - follower denied'); - return; - } + $blocked = LibBlock::fetch($channel['channel_id'], BLOCKTYPE_SERVER); + if ($blocked) { + foreach ($blocked as $b) { + if (strpos($url, $b['block_entity']) !== false) { + logger('siteblock - follower denied'); + return; + } + } + } + if (LibBlock::fetch_by_entity($channel['channel_id'], $x['hash'])) { + logger('actorblock - follower denied'); + return; + } - $p = Permissions::connect_perms($channel['channel_id']); - $my_perms = Permissions::serialise($p['perms']); + $p = Permissions::connect_perms($channel['channel_id']); + $my_perms = Permissions::serialise($p['perms']); - $automatic = $p['automatic']; + $automatic = $p['automatic']; - // new connection + // new connection - if ($my_perms) { - set_abconfig($channel['channel_id'],$x['hash'],'system','my_perms',$my_perms); - } + if ($my_perms) { + set_abconfig($channel['channel_id'], $x['hash'], 'system', 'my_perms', $my_perms); + } - $closeness = get_pconfig($channel['channel_id'],'system','new_abook_closeness',80); + $closeness = get_pconfig($channel['channel_id'], 'system', 'new_abook_closeness', 80); - // check if it is a sub-channel (collection) and auto-friend if it is + // check if it is a sub-channel (collection) and auto-friend if it is - $is_collection = false; + $is_collection = false; - $cl = q("select channel_id from channel where channel_hash = '%s' and channel_parent = '%s' and channel_account_id = %d limit 1", - dbesc($x['hash']), - dbesc($channel['channel_hash']), - intval($channel['channel_account_id']) - ); - if ($cl) { - $is_collection = true; - $automatic = true; - $closeness = 10; - } + $cl = q( + "select channel_id from channel where channel_hash = '%s' and channel_parent = '%s' and channel_account_id = %d limit 1", + dbesc($x['hash']), + dbesc($channel['channel_hash']), + intval($channel['channel_account_id']) + ); + if ($cl) { + $is_collection = true; + $automatic = true; + $closeness = 10; + } - $y = abook_store_lowlevel( - [ - 'abook_account' => intval($channel['channel_account_id']), - 'abook_channel' => intval($channel['channel_id']), - 'abook_closeness' => intval($closeness), - 'abook_xchan' => $x['hash'], - 'abook_profile' => $profile_assign, - 'abook_created' => datetime_convert(), - 'abook_updated' => datetime_convert(), - 'abook_dob' => $next_birthday, - 'abook_pending' => intval(($automatic) ? 0 : 1) - ] - ); + $y = abook_store_lowlevel( + [ + 'abook_account' => intval($channel['channel_account_id']), + 'abook_channel' => intval($channel['channel_id']), + 'abook_closeness' => intval($closeness), + 'abook_xchan' => $x['hash'], + 'abook_profile' => $profile_assign, + 'abook_created' => datetime_convert(), + 'abook_updated' => datetime_convert(), + 'abook_dob' => $next_birthday, + 'abook_pending' => intval(($automatic) ? 0 : 1) + ] + ); - if ($y) { - logger("New introduction received for {$channel['channel_name']}"); - $new_perms = get_all_perms($channel['channel_id'],$x['hash']); - - // Send a clone sync packet and a permissions update if permissions have changed + if ($y) { + logger("New introduction received for {$channel['channel_name']}"); + $new_perms = get_all_perms($channel['channel_id'], $x['hash']); - $new_connection = q("select * from abook left join xchan on abook_xchan = xchan_hash where abook_xchan = '%s' and abook_channel = %d and abook_self = 0 order by abook_created desc limit 1", - dbesc($x['hash']), - intval($channel['channel_id']) - ); + // Send a clone sync packet and a permissions update if permissions have changed - if ($new_connection) { - if (! Permissions::PermsCompare($new_perms,$previous_perms)) { - Run::Summon([ 'Notifier', 'permissions_create', $new_connection[0]['abook_id'] ]); - } + $new_connection = q( + "select * from abook left join xchan on abook_xchan = xchan_hash where abook_xchan = '%s' and abook_channel = %d and abook_self = 0 order by abook_created desc limit 1", + dbesc($x['hash']), + intval($channel['channel_id']) + ); - if (! $is_collection) { - Enotify::submit( - [ - 'type' => NOTIFY_INTRO, - 'from_xchan' => $x['hash'], - 'to_xchan' => $channel['channel_hash'], - 'link' => z_root() . '/connedit/' . $new_connection[0]['abook_id'] - ] - ); - } + if ($new_connection) { + if (!Permissions::PermsCompare($new_perms, $previous_perms)) { + Run::Summon(['Notifier', 'permissions_create', $new_connection[0]['abook_id']]); + } - if (intval($permissions['view_stream'])) { - if (intval(get_pconfig($channel['channel_id'],'perm_limits','send_stream') & PERMS_PENDING) - || (! intval($new_connection[0]['abook_pending']))) - Run::Summon([ 'Onepoll', $new_connection[0]['abook_id'] ]); - } + if (!$is_collection) { + Enotify::submit( + [ + 'type' => NOTIFY_INTRO, + 'from_xchan' => $x['hash'], + 'to_xchan' => $channel['channel_hash'], + 'link' => z_root() . '/connedit/' . $new_connection[0]['abook_id'] + ] + ); + } + + if (intval($permissions['view_stream'])) { + if ( + intval(get_pconfig($channel['channel_id'], 'perm_limits', 'send_stream') & PERMS_PENDING) + || (!intval($new_connection[0]['abook_pending'])) + ) { + Run::Summon(['Onepoll', $new_connection[0]['abook_id']]); + } + } - // If there is a default group for this channel, add this connection to it - // for pending connections this will happens at acceptance time. + // If there is a default group for this channel, add this connection to it + // for pending connections this will happens at acceptance time. - if (! intval($new_connection[0]['abook_pending'])) { - $default_group = $channel['channel_default_group']; - if ($default_group) { - $g = AccessList::rec_byhash($channel['channel_id'],$default_group); - if ($g) { - AccessList::member_add($channel['channel_id'],'',$x['hash'],$g['id']); - } - } - } + if (!intval($new_connection[0]['abook_pending'])) { + $default_group = $channel['channel_default_group']; + if ($default_group) { + $g = AccessList::rec_byhash($channel['channel_id'], $default_group); + if ($g) { + AccessList::member_add($channel['channel_id'], '', $x['hash'], $g['id']); + } + } + } - unset($new_connection[0]['abook_id']); - unset($new_connection[0]['abook_account']); - unset($new_connection[0]['abook_channel']); - $abconfig = load_abconfig($channel['channel_id'],$new_connection[0]['abook_xchan']); - if ($abconfig) { - $new_connection[0]['abconfig'] = $abconfig; - } - Libsync::build_sync_packet($channel['channel_id'], [ 'abook' => $new_connection ]); - } - } + unset($new_connection[0]['abook_id']); + unset($new_connection[0]['abook_account']); + unset($new_connection[0]['abook_channel']); + $abconfig = load_abconfig($channel['channel_id'], $new_connection[0]['abook_xchan']); + if ($abconfig) { + $new_connection[0]['abconfig'] = $abconfig; + } + Libsync::build_sync_packet($channel['channel_id'], ['abook' => $new_connection]); + } + } + } + return true; + } else { + return true; + } + return false; + } - } - return true; - } - else { - return true; - } - return false; - } + /** + * @brief Look up if channel is known and previously verified. + * + * A guid and a url, both signed by the sender, distinguish a known sender at a + * known location. + * This function looks these up to see if the channel is known and therefore + * previously verified. If not, we will need to verify it. + * + * @param array $arr an associative array which must contain: + * * \e string \b id => id of conversant + * * \e string \b id_sig => id signed with conversant's private key + * * \e string \b location => URL of the origination hub of this communication + * * \e string \b location_sig => URL signed with conversant's private key + * @param bool $multiple (optional) default false + * + * @return array|null + * * null if site is denied or not found + * * otherwise an array with an hubloc record + */ - /** - * @brief Look up if channel is known and previously verified. - * - * A guid and a url, both signed by the sender, distinguish a known sender at a - * known location. - * This function looks these up to see if the channel is known and therefore - * previously verified. If not, we will need to verify it. - * - * @param array $arr an associative array which must contain: - * * \e string \b id => id of conversant - * * \e string \b id_sig => id signed with conversant's private key - * * \e string \b location => URL of the origination hub of this communication - * * \e string \b location_sig => URL signed with conversant's private key - * @param boolean $multiple (optional) default false - * - * @return array|null - * * null if site is denied or not found - * * otherwise an array with an hubloc record - */ + public static function gethub($arr, $multiple = false) + { - static function gethub($arr, $multiple = false) { + if ($arr['id'] && $arr['id_sig'] && $arr['location'] && $arr['location_sig']) { + if (!check_siteallowed($arr['location'])) { + logger('denied site: ' . $arr['location']); + return null; + } - if ($arr['id'] && $arr['id_sig'] && $arr['location'] && $arr['location_sig']) { + $limit = (($multiple) ? '' : ' limit 1 '); - if (! check_siteallowed($arr['location'])) { - logger('denied site: ' . $arr['location']); - return null; - } - - $limit = (($multiple) ? '' : ' limit 1 '); - - $r = q("select hubloc.*, site.site_crypto from hubloc left join site on hubloc_url = site_url + $r = q( + "select hubloc.*, site.site_crypto from hubloc left join site on hubloc_url = site_url where hubloc_guid = '%s' and hubloc_guid_sig = '%s' and hubloc_url = '%s' and hubloc_url_sig = '%s' and hubloc_site_id = '%s' and hubloc_network in ('nomad','zot6') $limit", - dbesc($arr['id']), - dbesc($arr['id_sig']), - dbesc($arr['location']), - dbesc($arr['location_sig']), - dbesc($arr['site_id']) - ); - if ($r) { - logger('Found', LOGGER_DEBUG); - return (($multiple) ? $r : $r[0]); - } - } - logger('Not found: ' . print_r($arr,true), LOGGER_DEBUG); + dbesc($arr['id']), + dbesc($arr['id_sig']), + dbesc($arr['location']), + dbesc($arr['location_sig']), + dbesc($arr['site_id']) + ); + if ($r) { + logger('Found', LOGGER_DEBUG); + return (($multiple) ? $r : $r[0]); + } + } + logger('Not found: ' . print_r($arr, true), LOGGER_DEBUG); - return false; - } + return false; + } + public static function valid_hub($sender, $site_id) + { + $r = q( + "select hubloc.*, site.site_crypto from hubloc left join site on hubloc_url = site_url where hubloc_hash = '%s' and hubloc_site_id = '%s' limit 1", + dbesc($sender), + dbesc($site_id) + ); + if (!$r) { + return null; + } - static function valid_hub($sender,$site_id) { + if (!check_siteallowed($r[0]['hubloc_url'])) { + logger('denied site: ' . $r[0]['hubloc_url']); + return null; + } - $r = q("select hubloc.*, site.site_crypto from hubloc left join site on hubloc_url = site_url where hubloc_hash = '%s' and hubloc_site_id = '%s' limit 1", - dbesc($sender), - dbesc($site_id) - ); - if (! $r) { - return null; - } + if (!check_channelallowed($r[0]['hubloc_hash'])) { + logger('denied channel: ' . $r[0]['hubloc_hash']); + return null; + } - if (! check_siteallowed($r[0]['hubloc_url'])) { - logger('denied site: ' . $r[0]['hubloc_url']); - return null; - } + return $r[0]; + } - if (! check_channelallowed($r[0]['hubloc_hash'])) { - logger('denied channel: ' . $r[0]['hubloc_hash']); - return null; - } + /** + * @brief Registers an unknown hub. + * + * A communication has been received which has an unknown (to us) sender. + * Perform discovery based on our calculated hash of the sender at the + * origination address. This will fetch the discovery packet of the sender, + * which contains the public key we need to verify our guid and url signatures. + * + * @param array $arr an associative array which must contain: + * * \e string \b guid => guid of conversant + * * \e string \b guid_sig => guid signed with conversant's private key + * * \e string \b url => URL of the origination hub of this communication + * * \e string \b url_sig => URL signed with conversant's private key + * + * @return array An associative array with + * * \b success boolean true or false + * * \b message (optional) error string only if success is false + */ - return $r[0]; - } + public static function register_hub($id) + { - /** - * @brief Registers an unknown hub. - * - * A communication has been received which has an unknown (to us) sender. - * Perform discovery based on our calculated hash of the sender at the - * origination address. This will fetch the discovery packet of the sender, - * which contains the public key we need to verify our guid and url signatures. - * - * @param array $arr an associative array which must contain: - * * \e string \b guid => guid of conversant - * * \e string \b guid_sig => guid signed with conversant's private key - * * \e string \b url => URL of the origination hub of this communication - * * \e string \b url_sig => URL signed with conversant's private key - * - * @return array An associative array with - * * \b success boolean true or false - * * \b message (optional) error string only if success is false - */ + $id_hash = false; + $valid = false; + $hsig_valid = false; - static function register_hub($id) { + $result = ['success' => false]; - $id_hash = false; - $valid = false; - $hsig_valid = false; + if (!$id) { + return $result; + } - $result = [ 'success' => false ]; + $record = Zotfinger::exec($id); - if (! $id) { - return $result; - } + // Check the HTTP signature - $record = Zotfinger::exec($id); + $hsig = $record['signature']; + if ($hsig['signer'] === $id && $hsig['header_valid'] === true && $hsig['content_valid'] === true) { + $hsig_valid = true; + } + if (!$hsig_valid) { + logger('http signature not valid: ' . print_r($hsig, true)); + return $result; + } - // Check the HTTP signature + $c = self::import_xchan($record['data']); + if ($c['success']) { + $result['success'] = true; + } else { + logger('Failure to verify zot packet'); + } - $hsig = $record['signature']; - if ($hsig['signer'] === $id && $hsig['header_valid'] === true && $hsig['content_valid'] === true) { - $hsig_valid = true; - } - if (! $hsig_valid) { - logger('http signature not valid: ' . print_r($hsig,true)); - return $result; - } + return $result; + } - $c = self::import_xchan($record['data']); - if ($c['success']) { - $result['success'] = true; - } - else { - logger('Failure to verify zot packet'); - } + /** + * @brief Takes an associative array of a fetch discovery packet and updates + * all internal data structures which need to be updated as a result. + * + * @param array $arr => json_decoded discovery packet + * @param int $ud_flags + * Determines whether to create a directory update record if any changes occur, default is UPDATE_FLAGS_UPDATED + * $ud_flags = UPDATE_FLAGS_FORCED indicates a forced refresh where we unconditionally create a directory update record + * this typically occurs once a month for each channel as part of a scheduled ping to notify the directory + * that the channel still exists + * @param array $ud_arr + * If set [typically by update_directory_entry()] indicates a specific update table row and more particularly + * contains a particular address (ud_addr) which needs to be updated in that table. + * + * @return array An associative array with: + * * \e boolean \b success boolean true or false + * * \e string \b message (optional) error string only if success is false + */ - return $result; - } + public static function import_xchan($arr, $ud_flags = UPDATE_FLAGS_UPDATED, $ud_arr = null) + { - /** - * @brief Takes an associative array of a fetch discovery packet and updates - * all internal data structures which need to be updated as a result. - * - * @param array $arr => json_decoded discovery packet - * @param int $ud_flags - * Determines whether to create a directory update record if any changes occur, default is UPDATE_FLAGS_UPDATED - * $ud_flags = UPDATE_FLAGS_FORCED indicates a forced refresh where we unconditionally create a directory update record - * this typically occurs once a month for each channel as part of a scheduled ping to notify the directory - * that the channel still exists - * @param array $ud_arr - * If set [typically by update_directory_entry()] indicates a specific update table row and more particularly - * contains a particular address (ud_addr) which needs to be updated in that table. - * - * @return array An associative array with: - * * \e boolean \b success boolean true or false - * * \e string \b message (optional) error string only if success is false - */ + /** + * @hooks import_xchan + * Called when processing the result of zot_finger() to store the result + * * \e array + */ + call_hooks('import_xchan', $arr); - static function import_xchan($arr, $ud_flags = UPDATE_FLAGS_UPDATED, $ud_arr = null) { + $ret = array('success' => false); + $dirmode = intval(get_config('system', 'directory_mode')); - /** - * @hooks import_xchan - * Called when processing the result of zot_finger() to store the result - * * \e array - */ - call_hooks('import_xchan', $arr); + $changed = false; + $what = ''; - $ret = array('success' => false); - $dirmode = intval(get_config('system','directory_mode')); + if (!is_array($arr)) { + logger('Not an array: ' . print_r($arr, true), LOGGER_DEBUG); + return $ret; + } - $changed = false; - $what = ''; + if (!($arr['id'] && $arr['id_sig'])) { + logger('No identity information provided. ' . print_r($arr, true)); + return $ret; + } - if (! is_array($arr)) { - logger('Not an array: ' . print_r($arr,true), LOGGER_DEBUG); - return $ret; - } + $xchan_hash = self::make_xchan_hash($arr['id'], $arr['public_key']); + $arr['hash'] = $xchan_hash; - if (! ($arr['id'] && $arr['id_sig'])) { - logger('No identity information provided. ' . print_r($arr,true)); - return $ret; - } + $import_photos = false; - $xchan_hash = self::make_xchan_hash($arr['id'],$arr['public_key']); - $arr['hash'] = $xchan_hash; + $sig_methods = ((array_key_exists('signing', $arr) && is_array($arr['signing'])) ? $arr['signing'] : ['sha256']); + $verified = false; - $import_photos = false; + if (!self::verify($arr['id'], $arr['id_sig'], $arr['public_key'])) { + logger('Unable to verify channel signature for ' . $arr['address']); + return $ret; + } else { + $verified = true; + } - $sig_methods = ((array_key_exists('signing',$arr) && is_array($arr['signing'])) ? $arr['signing'] : [ 'sha256' ]); - $verified = false; + if (!$verified) { + $ret['message'] = t('Unable to verify channel signature'); + return $ret; + } - if (! self::verify($arr['id'],$arr['id_sig'],$arr['public_key'])) { - logger('Unable to verify channel signature for ' . $arr['address']); - return $ret; - } - else { - $verified = true; - } + logger('import_xchan: ' . $xchan_hash, LOGGER_DEBUG); - if (! $verified) { - $ret['message'] = t('Unable to verify channel signature'); - return $ret; - } + $r = q( + "select * from xchan where xchan_hash = '%s' limit 1", + dbesc($xchan_hash) + ); - logger('import_xchan: ' . $xchan_hash, LOGGER_DEBUG); + if (!array_key_exists('connect_url', $arr)) { + $arr['connect_url'] = ''; + } - $r = q("select * from xchan where xchan_hash = '%s' limit 1", - dbesc($xchan_hash) - ); + if ($r) { + if ($arr['photo'] && array_key_exists('updated', $arr['photo']) && $arr['photo']['updated'] > $r[0]['xchan_photo_date']) { + $import_photos = true; + } - if (! array_key_exists('connect_url', $arr)) { - $arr['connect_url'] = ''; - } + // if we import an entry from a site that's not ours and either or both of us is off the grid - hide the entry. + /** @TODO: check if we're the same directory realm, which would mean we are allowed to see it */ - if ($r) { - if ($arr['photo'] && array_key_exists('updated',$arr['photo']) && $arr['photo']['updated'] > $r[0]['xchan_photo_date']) { - $import_photos = true; - } + $dirmode = get_config('system', 'directory_mode'); - // if we import an entry from a site that's not ours and either or both of us is off the grid - hide the entry. - /** @TODO: check if we're the same directory realm, which would mean we are allowed to see it */ + if ((($arr['site']['directory_mode'] === 'standalone') || ($dirmode & DIRECTORY_MODE_STANDALONE)) && ($arr['site']['url'] != z_root())) { + $arr['searchable'] = false; + } - $dirmode = get_config('system','directory_mode'); + $hidden = (1 - intval($arr['searchable'])); - if ((($arr['site']['directory_mode'] === 'standalone') || ($dirmode & DIRECTORY_MODE_STANDALONE)) && ($arr['site']['url'] != z_root())) { - $arr['searchable'] = false; - } + $hidden_changed = $adult_changed = $deleted_changed = $type_changed = 0; - $hidden = (1 - intval($arr['searchable'])); + if (intval($r[0]['xchan_hidden']) != (1 - intval($arr['searchable']))) { + $hidden_changed = 1; + } + if (intval($r[0]['xchan_selfcensored']) != intval($arr['adult_content'])) { + $adult_changed = 1; + } + if (isset($arr['deleted']) && intval($r[0]['xchan_deleted']) != intval($arr['deleted'])) { + $deleted_changed = 1; + } - $hidden_changed = $adult_changed = $deleted_changed = $type_changed = 0; - - if (intval($r[0]['xchan_hidden']) != (1 - intval($arr['searchable']))) { - $hidden_changed = 1; - } - if (intval($r[0]['xchan_selfcensored']) != intval($arr['adult_content'])) { - $adult_changed = 1; - } - if (isset($arr['deleted']) && intval($r[0]['xchan_deleted']) != intval($arr['deleted'])) { - $deleted_changed = 1; - } - - if ($arr['channel_type'] === 'collection') { - $px = 2; - } - elseif ($arr['channel_type'] === 'group') { - $px = 1; - } - else { - $px = 0; - } - if (array_key_exists('public_forum',$arr) && intval($arr['public_forum'])) { - $px = 1; - } - - if (intval($r[0]['xchan_type']) !== $px) { - $type_changed = true; - } - - if ($arr['protocols']) { - $protocols = implode(',',$arr['protocols']); - if ($protocols) { - set_xconfig($xchan_hash,'system','protocols',$protocols); - } - else { - del_xconfig($xchan_hash,'system','protocols'); + $px = 0; + if (isset($arr['channel_type'])) { + if ($arr['channel_type'] === 'collection') { + $px = 2; + } elseif ($arr['channel_type'] === 'group') { + $px = 1; } } - $collections = []; - if (isset($arr['primary_location']['following'])) { - $collections['following'] = $arr['primary_location']['following']; - } - if (isset($arr['primary_location']['followers'])) { - $collections['followers'] = $arr['primary_location']['followers']; - } - if (isset($arr['primary_location']['wall'])) { - $collections['wall'] = $arr['primary_location']['wall']; - } - if ($collections) { - set_xconfig($xchan_hash,'activitypub','collections',$collections); - } + if (array_key_exists('public_forum', $arr) && intval($arr['public_forum'])) { + $px = 1; + } - if (isset($arr['cover_photo']) && isset($arr['cover_photo']['url']) && strlen($arr['cover_photo']['url'])) { - set_xconfig($xchan_hash,'system','cover_photo',$arr['cover_photo']['url']); - } + if (intval($r[0]['xchan_type']) !== $px) { + $type_changed = true; + } - if (isset($arr['signing_algorithm']) && strlen($arr['signing_algorithm'])) { - set_xconfig($xchan_hash,'system','signing_algorithm',$arr['signing_algorithm']); - } + if ($arr['protocols']) { + set_xconfig($xchan_hash, 'system', 'protocols', implode(',',$arr['protocols'])); + } + $collections = []; + if (isset($arr['primary_location']['following'])) { + $collections['following'] = $arr['primary_location']['following']; + } + if (isset($arr['primary_location']['followers'])) { + $collections['followers'] = $arr['primary_location']['followers']; + } + if (isset($arr['primary_location']['wall'])) { + $collections['wall'] = $arr['primary_location']['wall']; + } + if ($collections) { + set_xconfig($xchan_hash, 'activitypub', 'collections', $collections); + } - // upgrade zot6 connections to nomad if applicable - - if ($r[0]['xchan_network'] === 'zot6' && intval($arr['site']['protocol_version']) > 10) { - q("update xchan set xchan_network = 'nomad' where xchan_hash = '%s'", - dbesc($xchan_hash) - ); - } + if (isset($arr['cover_photo']) && isset($arr['cover_photo']['url']) && strlen($arr['cover_photo']['url'])) { + set_xconfig($xchan_hash, 'system', 'cover_photo', $arr['cover_photo']['url']); + } + + if (isset($arr['signing_algorithm']) && strlen($arr['signing_algorithm'])) { + set_xconfig($xchan_hash, 'system', 'signing_algorithm', $arr['signing_algorithm']); + } - if (($r[0]['xchan_name_date'] != $arr['name_updated']) - || ($r[0]['xchan_connurl'] != $arr['primary_location']['connections_url']) - || ($r[0]['xchan_addr'] != $arr['primary_location']['address']) - || ($r[0]['xchan_follow'] != $arr['primary_location']['follow_url']) - || ($r[0]['xchan_connpage'] != $arr['connect_url']) - || ($r[0]['xchan_url'] != $arr['primary_location']['url']) - || ($r[0]['xchan_updated'] < datetime_convert('UTC','UTC','now - 7 days')) - || $hidden_changed || $adult_changed || $deleted_changed || $type_changed ) { - $rup = q("update xchan set xchan_updated = '%s', xchan_name = '%s', xchan_name_date = '%s', xchan_connurl = '%s', xchan_follow = '%s', + if ( + ($r[0]['xchan_name_date'] != $arr['name_updated']) + || ($r[0]['xchan_connurl'] != $arr['primary_location']['connections_url']) + || ($r[0]['xchan_addr'] != $arr['primary_location']['address']) + || ($r[0]['xchan_follow'] != $arr['primary_location']['follow_url']) + || ($r[0]['xchan_connpage'] != $arr['connect_url']) + || ($r[0]['xchan_url'] != $arr['primary_location']['url']) + || ($r[0]['xchan_updated'] < datetime_convert('UTC', 'UTC', 'now - 7 days')) + || $hidden_changed || $adult_changed || $deleted_changed || $type_changed + ) { + $rup = q( + "update xchan set xchan_updated = '%s', xchan_name = '%s', xchan_name_date = '%s', xchan_connurl = '%s', xchan_follow = '%s', xchan_connpage = '%s', xchan_hidden = %d, xchan_selfcensored = %d, xchan_deleted = %d, xchan_type = %d, xchan_addr = '%s', xchan_url = '%s' where xchan_hash = '%s'", - dbesc(datetime_convert()), - dbesc(($arr['name']) ? escape_tags($arr['name']) : '-'), - dbesc($arr['name_updated']), - dbesc($arr['primary_location']['connections_url']), - dbesc($arr['primary_location']['follow_url']), - dbesc($arr['primary_location']['connect_url']), - intval(1 - intval($arr['searchable'])), - intval($arr['adult_content']), - intval($arr['deleted']), - intval($px), - dbesc(escape_tags($arr['primary_location']['address'])), - dbesc(escape_tags($arr['primary_location']['url'])), - dbesc($xchan_hash) - ); + dbesc(datetime_convert()), + dbesc(($arr['name']) ? escape_tags($arr['name']) : '-'), + dbesc($arr['name_updated']), + dbesc($arr['primary_location']['connections_url']), + dbesc($arr['primary_location']['follow_url']), + dbesc($arr['primary_location']['connect_url']), + intval(1 - intval($arr['searchable'])), + intval($arr['adult_content']), + intval($arr['deleted']), + intval($px), + dbesc(escape_tags($arr['primary_location']['address'])), + dbesc(escape_tags($arr['primary_location']['url'])), + dbesc($xchan_hash) + ); - logger('Update: existing: ' . print_r($r[0],true), LOGGER_DATA, LOG_DEBUG); - logger('Update: new: ' . print_r($arr,true), LOGGER_DATA, LOG_DEBUG); - $what .= 'xchan '; - $changed = true; - } - } - else { - $import_photos = true; + logger('Update: existing: ' . print_r($r[0], true), LOGGER_DATA, LOG_DEBUG); + logger('Update: new: ' . print_r($arr, true), LOGGER_DATA, LOG_DEBUG); + $what .= 'xchan '; + $changed = true; + } + } else { + $import_photos = true; - if ((($arr['site']['directory_mode'] === 'standalone') - || ($dirmode & DIRECTORY_MODE_STANDALONE)) - && ($arr['site']['url'] != z_root())) { - $arr['searchable'] = false; - } + if ( + (($arr['site']['directory_mode'] === 'standalone') + || ($dirmode & DIRECTORY_MODE_STANDALONE)) + && ($arr['site']['url'] != z_root()) + ) { + $arr['searchable'] = false; + } - if ($arr['channel_type'] === 'collection') { - $px = 2; - } - elseif ($arr['channel_type'] === 'group') { - $px = 1; - } - else { - $px = 0; - } + if ($arr['channel_type'] === 'collection') { + $px = 2; + } elseif ($arr['channel_type'] === 'group') { + $px = 1; + } else { + $px = 0; + } - if (array_key_exists('public_forum',$arr) && intval($arr['public_forum'])) { - $px = 1; - } + if (array_key_exists('public_forum', $arr) && intval($arr['public_forum'])) { + $px = 1; + } $network = isset($arr['site']['protocol_version']) && intval($arr['site']['protocol_version']) > 10 ? 'nomad' : 'zot6'; - $x = xchan_store_lowlevel( - [ - 'xchan_hash' => $xchan_hash, - 'xchan_guid' => $arr['id'], - 'xchan_guid_sig' => $arr['id_sig'], - 'xchan_pubkey' => $arr['public_key'], - 'xchan_photo_mimetype' => $arr['photo']['type'], - 'xchan_photo_l' => $arr['photo']['url'], - 'xchan_addr' => escape_tags($arr['primary_location']['address']), - 'xchan_url' => escape_tags($arr['primary_location']['url']), - 'xchan_connurl' => $arr['primary_location']['connections_url'], - 'xchan_follow' => $arr['primary_location']['follow_url'], - 'xchan_connpage' => $arr['connect_url'], - 'xchan_name' => (($arr['name']) ? escape_tags($arr['name']) : '-'), - 'xchan_network' => $network, - 'xchan_updated' => datetime_convert(), - 'xchan_photo_date' => $arr['photo']['updated'], - 'xchan_name_date' => $arr['name_updated'], - 'xchan_hidden' => intval(1 - intval($arr['searchable'])), - 'xchan_selfcensored' => $arr['adult_content'], - 'xchan_deleted' => $arr['deleted'], - 'xchan_type' => $px - ] - ); - $what .= 'new_xchan'; - $changed = true; - } + $x = xchan_store_lowlevel( + [ + 'xchan_hash' => $xchan_hash, + 'xchan_guid' => $arr['id'], + 'xchan_guid_sig' => $arr['id_sig'], + 'xchan_pubkey' => $arr['public_key'], + 'xchan_photo_mimetype' => $arr['photo']['type'], + 'xchan_photo_l' => $arr['photo']['url'], + 'xchan_addr' => escape_tags($arr['primary_location']['address']), + 'xchan_url' => escape_tags($arr['primary_location']['url']), + 'xchan_connurl' => $arr['primary_location']['connections_url'], + 'xchan_follow' => $arr['primary_location']['follow_url'], + 'xchan_connpage' => $arr['connect_url'], + 'xchan_name' => (($arr['name']) ? escape_tags($arr['name']) : '-'), + 'xchan_network' => $network, + 'xchan_updated' => datetime_convert(), + 'xchan_photo_date' => $arr['photo']['updated'], + 'xchan_name_date' => $arr['name_updated'], + 'xchan_hidden' => intval(1 - intval($arr['searchable'])), + 'xchan_selfcensored' => $arr['adult_content'], + 'xchan_deleted' => $arr['deleted'], + 'xchan_type' => $px + ] + ); - if (isset($arr['cover_photo']) && isset($arr['cover_photo']['url']) && strlen($arr['cover_photo']['url'])) { - set_xconfig($xchan_hash,'system','cover_photo',$arr['cover_photo']['url']); - } + $what .= 'new_xchan'; + $changed = true; + } - if ($import_photos) { + if (isset($arr['cover_photo']) && isset($arr['cover_photo']['url']) && strlen($arr['cover_photo']['url'])) { + set_xconfig($xchan_hash, 'system', 'cover_photo', $arr['cover_photo']['url']); + } - require_once('include/photo_factory.php'); + if ($import_photos) { + require_once('include/photo_factory.php'); - // see if this is a channel clone that's hosted locally - which we treat different from other xchans/connections + // see if this is a channel clone that's hosted locally - which we treat different from other xchans/connections - $local = q("select channel_account_id, channel_id from channel where channel_hash = '%s' limit 1", - dbesc($xchan_hash) - ); - if ($local) { - $ph = false; - if (strpos($arr['photo']['url'], z_root()) === false) { - $ph = z_fetch_url($arr['photo']['url'], true); - } - if ($ph && $ph['success']) { - $hash = import_channel_photo($ph['body'], $arr['photo']['type'], $local[0]['channel_account_id'], $local[0]['channel_id']); + $local = q( + "select channel_account_id, channel_id from channel where channel_hash = '%s' limit 1", + dbesc($xchan_hash) + ); + if ($local) { + $ph = false; + if (strpos($arr['photo']['url'], z_root()) === false) { + $ph = z_fetch_url($arr['photo']['url'], true); + } + if ($ph && $ph['success']) { + $hash = import_channel_photo($ph['body'], $arr['photo']['type'], $local[0]['channel_account_id'], $local[0]['channel_id']); - if ($hash) { - // unless proven otherwise - $is_default_profile = 1; + if ($hash) { + // unless proven otherwise + $is_default_profile = 1; - $profile = q("select is_default from profile where aid = %d and uid = %d limit 1", - intval($local[0]['channel_account_id']), - intval($local[0]['channel_id']) - ); - if ($profile) { - if (! intval($profile[0]['is_default'])) { - $is_default_profile = 0; - } - } + $profile = q( + "select is_default from profile where aid = %d and uid = %d limit 1", + intval($local[0]['channel_account_id']), + intval($local[0]['channel_id']) + ); + if ($profile) { + if (!intval($profile[0]['is_default'])) { + $is_default_profile = 0; + } + } - // If setting for the default profile, unset the profile photo flag from any other photos I own - if ($is_default_profile) { - q("UPDATE photo SET photo_usage = %d WHERE photo_usage = %d AND resource_id != '%s' AND aid = %d AND uid = %d", - intval(PHOTO_NORMAL), - intval(PHOTO_PROFILE), - dbesc($hash), - intval($local[0]['channel_account_id']), - intval($local[0]['channel_id']) - ); - } - } + // If setting for the default profile, unset the profile photo flag from any other photos I own + if ($is_default_profile) { + q( + "UPDATE photo SET photo_usage = %d WHERE photo_usage = %d AND resource_id != '%s' AND aid = %d AND uid = %d", + intval(PHOTO_NORMAL), + intval(PHOTO_PROFILE), + dbesc($hash), + intval($local[0]['channel_account_id']), + intval($local[0]['channel_id']) + ); + } + } - // reset the names in case they got messed up when we had a bug in this function - $photos = array( - z_root() . '/photo/profile/l/' . $local[0]['channel_id'], - z_root() . '/photo/profile/m/' . $local[0]['channel_id'], - z_root() . '/photo/profile/s/' . $local[0]['channel_id'], - $arr['photo_mimetype'], - false - ); - } - } - else { - $photos = import_remote_xchan_photo($arr['photo']['url'], $xchan_hash); - } - if ($photos) { - if ($photos[4]) { - // importing the photo failed somehow. Leave the photo_date alone so we can try again at a later date. - // This often happens when somebody joins the matrix with a bad cert. - $r = q("update xchan set xchan_updated = '%s', xchan_photo_l = '%s', xchan_photo_m = '%s', xchan_photo_s = '%s', xchan_photo_mimetype = '%s' + // reset the names in case they got messed up when we had a bug in this function + $photos = array( + z_root() . '/photo/profile/l/' . $local[0]['channel_id'], + z_root() . '/photo/profile/m/' . $local[0]['channel_id'], + z_root() . '/photo/profile/s/' . $local[0]['channel_id'], + $arr['photo_mimetype'], + false + ); + } + } else { + $photos = import_remote_xchan_photo($arr['photo']['url'], $xchan_hash); + } + if ($photos) { + if ($photos[4]) { + // importing the photo failed somehow. Leave the photo_date alone so we can try again at a later date. + // This often happens when somebody joins the matrix with a bad cert. + $r = q( + "update xchan set xchan_updated = '%s', xchan_photo_l = '%s', xchan_photo_m = '%s', xchan_photo_s = '%s', xchan_photo_mimetype = '%s' where xchan_hash = '%s'", - dbesc(datetime_convert()), - dbesc($photos[0]), - dbesc($photos[1]), - dbesc($photos[2]), - dbesc($photos[3]), - dbesc($xchan_hash) - ); - } - else { - $r = q("update xchan set xchan_updated = '%s', xchan_photo_date = '%s', xchan_photo_l = '%s', xchan_photo_m = '%s', xchan_photo_s = '%s', xchan_photo_mimetype = '%s' + dbesc(datetime_convert()), + dbesc($photos[0]), + dbesc($photos[1]), + dbesc($photos[2]), + dbesc($photos[3]), + dbesc($xchan_hash) + ); + } else { + $r = q( + "update xchan set xchan_updated = '%s', xchan_photo_date = '%s', xchan_photo_l = '%s', xchan_photo_m = '%s', xchan_photo_s = '%s', xchan_photo_mimetype = '%s' where xchan_hash = '%s'", - dbesc(datetime_convert()), - dbesc(datetime_convert('UTC','UTC',((isset($arr['photo_updated'])) ? $arr['photo_updated'] : 'now'))), - dbesc($photos[0]), - dbesc($photos[1]), - dbesc($photos[2]), - dbesc($photos[3]), - dbesc($xchan_hash) - ); - } - $what .= 'photo '; - $changed = true; - } - } - - // what we are missing for true hub independence is for any changes in the primary hub to - // get reflected not only in the hublocs, but also to update the URLs and addr in the appropriate xchan - - $s = Libsync::sync_locations($arr, $arr); - - if ($s) { - if (isset($s['change_message']) && $s['change_message']) { - $what .= $s['change_message']; - } - if (isset($s['changed']) && $s['changed']) { - $changed = $s['changed']; - } - if (isset($s['message']) && $s['message']) { - $ret['message'] .= $s['message']; - } - } - - // Which entries in the update table are we interested in updating? - $address = ((isset($arr['address']) && $arr['address']) ? $arr['address'] : EMPTY_STR); - if (isset($ud_arr) && isset($ud_arr['ud_addr'])) { - $address = $ud_arr['ud_addr']; - } - - // Are we a directory server of some kind? - - $other_realm = false; - -// $realm = get_directory_realm(); -// if (array_key_exists('site',$arr) -// && array_key_exists('realm',$arr['site']) -// && (strpos($arr['site']['realm'],$realm) === false)) -// $other_realm = true; - - -// if ($dirmode != DIRECTORY_MODE_NORMAL) { - - // We're some kind of directory server. However we can only add directory information - // if the entry is in the same realm (or is a sub-realm). Sub-realms are denoted by - // including the parent realm in the name. e.g. 'RED_GLOBAL:foo' would allow an entry to - // be in directories for the local realm (foo) and also the RED_GLOBAL realm. - - if (array_key_exists('profile',$arr) && is_array($arr['profile']) && (! $other_realm)) { - $profile_changed = Libzotdir::import_directory_profile($xchan_hash,$arr['profile'],$address,$ud_flags, 1); - if ($profile_changed) { - $what .= 'profile '; - $changed = true; - } - } - else { - logger('Profile not available - hiding'); - // they may have made it private - $r = q("delete from xprof where xprof_hash = '%s'", - dbesc($xchan_hash) - ); - $r = q("delete from xtag where xtag_hash = '%s' and xtag_flags = 0", - dbesc($xchan_hash) - ); - } -// } - - if (array_key_exists('site',$arr) && is_array($arr['site'])) { - $profile_changed = self::import_site($arr['site']); - if ($profile_changed) { - $what .= 'site '; - $changed = true; - } - } - - if (($changed) || ($ud_flags == UPDATE_FLAGS_FORCED)) { - $guid = random_string() . '@' . App::get_hostname(); - Libzotdir::update_modtime($xchan_hash,$guid,$address,$ud_flags); - logger('Changed: ' . $what,LOGGER_DEBUG); - } - elseif (! $ud_flags) { - // nothing changed but we still need to update the updates record - q("update updates set ud_flags = ( ud_flags | %d ) where ud_addr = '%s' and not (ud_flags & %d) > 0 ", - intval(UPDATE_FLAGS_UPDATED), - dbesc($address), - intval(UPDATE_FLAGS_UPDATED) - ); - } - - if (! x($ret,'message')) { - $ret['success'] = true; - $ret['hash'] = $xchan_hash; - } - - logger('Result: ' . print_r($ret,true), LOGGER_DATA, LOG_DEBUG); - return $ret; - } - - /** - * @brief Called immediately after sending a zot message which is using queue processing. - * - * Updates the queue item according to the response result and logs any information - * returned to aid communications troubleshooting. - * - * @param string $hub - url of site we just contacted - * @param array $arr - output of z_post_url() - * @param array $outq - The queue structure attached to this request - */ - - static function process_response($hub, $arr, $outq) { - - logger('remote: ' . print_r($arr,true),LOGGER_DATA); - - if (! $arr['success']) { - logger('Failed: ' . $hub); - return; - } - - $x = json_decode($arr['body'], true); - - if (! $x) { - logger('No json from ' . $hub); - logger('Headers: ' . print_r($arr['header'], true), LOGGER_DATA, LOG_DEBUG); - } - - $x = Crypto::unencapsulate($x, get_config('system','prvkey')); - if (! is_array($x)) { - $x = json_decode($x,true); - } - - if (! is_array($x)) { - logger('no useful response: ' . $x); - } - - if ($x) { - if (! $x['success']) { - - // handle remote validation issues - - $b = q("update dreport set dreport_result = '%s', dreport_time = '%s' where dreport_queue = '%s'", - dbesc(($x['message']) ? $x['message'] : 'unknown delivery error'), - dbesc(datetime_convert()), - dbesc($outq['outq_hash']) - ); - } - - if (is_array($x) && array_key_exists('delivery_report',$x) && is_array($x['delivery_report'])) { - foreach ($x['delivery_report'] as $xx) { - call_hooks('dreport_process',$xx); - if (is_array($xx) && array_key_exists('message_id',$xx) && DReport::is_storable($xx)) { - q("insert into dreport ( dreport_mid, dreport_site, dreport_recip, dreport_name, dreport_result, dreport_time, dreport_xchan, dreport_log ) values ( '%s', '%s', '%s','%s','%s','%s','%s','%s' ) ", - dbesc($xx['message_id']), - dbesc($xx['location']), - dbesc($xx['recipient']), - dbesc($xx['name']), - dbesc($xx['status']), - dbesc(datetime_convert('UTC','UTC',$xx['date'])), - dbesc($xx['sender']), - dbesc(EMPTY_STR) - ); - } - } - - // we have a more descriptive delivery report, so discard the per hub 'queue' report. - - q("delete from dreport where dreport_queue = '%s' ", - dbesc($outq['outq_hash']) - ); - } - } - // update the timestamp for this site - - q("update site set site_dead = 0, site_update = '%s' where site_url = '%s'", - dbesc(datetime_convert()), - dbesc(dirname($hub)) - ); - - // synchronous message types are handled immediately - // async messages remain in the queue until processed. - - if (intval($outq['outq_async'])) { - Queue::remove($outq['outq_hash'],$outq['outq_channel']); - } - - logger('zot_process_response: ' . print_r($x,true), LOGGER_DEBUG); - } - - /** - * @brief - * - * We received a notification packet (in mod_post) that a message is waiting for us, and we've verified the sender. - * Check if the site is using zot6 delivery and includes a verified HTTP Signature, signed content, and a 'msg' field, - * and also that the signer and the sender match. - * If that happens, we do not need to fetch/pickup the message - we have it already and it is verified. - * Translate it into the form we need for zot_import() and import it. - * - * Otherwise send back a pickup message, using our message tracking ID ($arr['secret']), which we will sign with our site - * private key. - * The entire pickup message is encrypted with the remote site's public key. - * If everything checks out on the remote end, we will receive back a packet containing one or more messages, - * which will be processed and delivered before this function ultimately returns. - * - * @see zot_import() - * - * @param array $arr - * decrypted and json decoded notify packet from remote site - * @return array from zot_import() - */ - - static function fetch($arr,$hub = null) { - - logger('zot_fetch: ' . print_r($arr,true), LOGGER_DATA, LOG_DEBUG); - - return self::import($arr,$hub); - - } - - /** - * @brief Process incoming messages. - * - * Process incoming messages and import, update, delete as directed - * - * The message types handled here are 'activity' (e.g. posts), and 'sync'. - * - * @param array $arr - * 'pickup' structure returned from remote site - * @param string $sender_url - * the url specified by the sender in the initial communication. - * We will verify the sender and url in each returned message structure and - * also verify that all the messages returned match the site url that we are - * currently processing. - * - * @returns array - * Suitable for logging remotely, enumerating the processing results of each message/recipient combination - * * [0] => \e string $channel_hash - * * [1] => \e string $delivery_status - * * [2] => \e string $address - */ - - static function import($arr,$hub = null) { - - $env = $arr; - $private = false; - $return = []; - - $result = null; - - logger('Notify: ' . print_r($env,true), LOGGER_DATA, LOG_DEBUG); - - if (! is_array($env)) { - logger('decode error'); - return; - } - - $message_request = false; - - $has_data = array_key_exists('data',$env) && $env['data']; - $data = (($has_data) ? $env['data'] : false); - - $AS = null; - - if ($env['encoding'] === 'activitystreams') { - - $AS = new ActivityStreams($data); - if ($AS->is_valid() && $AS->type === 'Announce' && is_array($AS->obj) - && array_key_exists('object',$AS->obj) && array_key_exists('actor',$AS->obj)) { - // This is a relayed/forwarded Activity (as opposed to a shared/boosted object) - // Reparse the encapsulated Activity and use that instead - logger('relayed activity',LOGGER_DEBUG); - $AS = new ActivityStreams($AS->obj); - } - - if (! $AS->is_valid()) { - logger('Activity rejected: ' . print_r($data,true)); - return; - } - - // compatibility issue with like of Hubzilla "new friend" activities which is very difficult to fix - - if ($AS->implied_create && is_array($AS->obj) && array_key_exists('type',$AS->obj) && ActivityStreams::is_an_actor($AS->obj['type'])) { - logger('create/person activity rejected. ' . print_r($data,true)); - return false; - } - - if (is_array($AS->obj)) { - $arr = Activity::decode_note($AS); - } - else { - $arr = []; - } - - logger($AS->debug(), LOGGER_DATA); - } - - // There is nothing inherently wrong with getting a message-id which isn't a canonical URI/URL, but - // at the present time (2019/02) during the Hubzilla transition to zot6 it is likely to cause lots of duplicates for - // messages arriving from different protocols and sources with different message-id semantics. This - // restriction can be relaxed once most Hubzilla sites are upgraded to > 4.0. - // Don't check sync packets since they have a different encoding - - if ($arr && $env['type'] !== 'sync') { - if (strpos($arr['mid'],'http') === false && strpos($arr['mid'],'x-zot') === false) { - if (strpos($arr['mid'],'bear:') === false) { - logger('activity rejected: legacy message-id'); - return; - } - } - - if ($arr['verb'] === 'Create' && ActivityStreams::is_an_actor($arr['obj_type'])) { - logger('activity rejected: create actor'); - return; - } - - } - - $deliveries = null; - - if (array_key_exists('recipients',$env) && count($env['recipients'])) { - logger('specific recipients'); - logger('recipients: ' . print_r($env['recipients'],true),LOGGER_DEBUG); - - $recip_arr = []; - foreach ($env['recipients'] as $recip) { - $recip_arr[] = $recip; - } - - $r = false; - if ($recip_arr) { - stringify_array_elms($recip_arr,true); - $recips = implode(',',$recip_arr); - $r = q("select channel_hash as hash from channel where channel_hash in ( " . $recips . " ) and channel_removed = 0 "); - } - - if (! $r) { - logger('recips: no recipients on this site'); - return; - } - - // Response messages will inherit the privacy of the parent - - if ($env['type'] !== 'response') { - $private = true; - } - - $deliveries = ids_to_array($r,'hash'); - - // We found somebody on this site that's in the recipient list. - } - else { - - logger('public post'); - - - // Public post. look for any site members who are or may be accepting posts from this sender - // and who are allowed to see them based on the sender's permissions - // @fixme; - - $deliveries = self::public_recips($env,$AS); - } - - $deliveries = array_unique($deliveries); - - if (! $deliveries) { - logger('No deliveries on this site'); - return; - } - - if ($has_data) { - - if (in_array($env['type'],['activity','response'])) { - - if (! (is_array($AS->actor) && isset($AS->actor['id']))) { - logger('No author!'); - return; - } - - $r = q("select hubloc_hash, hubloc_network, hubloc_url from hubloc where hubloc_id_url = '%s'", - dbesc($AS->actor['id']) - ); - - if ($r) { - $r = self::zot_record_preferred($r); - $arr['author_xchan'] = $r['hubloc_hash']; - } - - if (! $arr['author_xchan']) { - logger('No author!'); - return; - } - - $s = q("select hubloc_hash, hubloc_url from hubloc where hubloc_id_url = '%s' and hubloc_network in ('nomad','zot6') limit 1", - dbesc($env['sender']) - ); - - // in individual delivery, change owner if needed - if ($s) { - $arr['owner_xchan'] = $s[0]['hubloc_hash']; - } - else { - $arr['owner_xchan'] = $env['sender']; - } - - if ($private && (! intval($arr['item_private']))) { - $arr['item_private'] = 1; - } - if ($arr['mid'] === $arr['parent_mid']) { - if (is_array($AS->obj) && array_key_exists('commentPolicy',$AS->obj)) { - $p = strstr($AS->obj['commentPolicy'],'until='); - - // if until= is the same as the creation date, set the item_nocomment flag - // as comments were already closed before the post was even sent. - - if($p !== false) { - $comments_closed_at = datetime_convert('UTC','UTC',substr($p,6)); - if ($comments_closed_at === $arr['created']) { - $arr['item_nocomment'] = 1; - } - else { - $arr['comments_closed'] = $comments_closed_at; - $arr['comment_policy'] = trim(str_replace($p,'',$AS->obj['commentPolicy'])); - } - } - else { - $arr['comment_policy'] = $AS->obj['commentPolicy']; - } - } - } - if ($AS->data['hubloc']) { - $arr['item_verified'] = true; - - if (! array_key_exists('comment_policy',$arr)) { - // set comment policy depending on source hub. Unknown or osada is ActivityPub. - // Anything else we'll say is zot - which could have a range of project names - $s = q("select site_project from site where site_url = '%s' limit 1", - dbesc($r[0]['hubloc_url']) - ); - - if ((! $s) || (in_array($s[0]['site_project'],[ '', 'osada' ]))) { - $arr['comment_policy'] = 'authenticated'; - } - else { - $arr['comment_policy'] = 'contacts'; - } - } - } - if ($AS->data['signed_data']) { - IConfig::Set($arr,'activitypub','signed_data',$AS->data['signed_data'],false); - $j = json_decode($AS->data['signed_data'],true); - if ($j) { - IConfig::Set($arr,'activitypub','rawmsg',json_encode(JSalmon::unpack($j['data'])),true); - } - } - - logger('Activity received: ' . print_r($arr,true), LOGGER_DATA, LOG_DEBUG); - logger('Activity recipients: ' . print_r($deliveries,true), LOGGER_DATA, LOG_DEBUG); - - $relay = (($env['type'] === 'response') ? true : false ); - - $result = self::process_delivery($env['sender'],$AS,$arr,$deliveries,$relay,false,$message_request); - } - elseif ($env['type'] === 'sync') { - - $arr = json_decode($data,true); - - logger('Channel sync received: ' . print_r($arr,true), LOGGER_DATA, LOG_DEBUG); - logger('Channel sync recipients: ' . print_r($deliveries,true), LOGGER_DATA, LOG_DEBUG); - - if ($env['encoding'] === 'red') { - $result = Libsync::process_channel_sync_delivery($env['sender'],$arr,$deliveries); - } - else { - logger('unsupported sync packet encoding ignored.'); - } - } - } - if ($result) { - $return = array_merge($return, $result); - } - return $return; - } - - - static function is_top_level($env,$act) { - if ($env['encoding'] === 'zot' && array_key_exists('flags',$env) && in_array('thread_parent', $env['flags'])) { - return true; - } - if ($act) { - if (in_array($act->type, ['Like','Dislike'])) { - return false; - } - $x = self::find_parent($env,$act); - if ($x === $act->id || (is_array($act->obj) && array_key_exists('id',$act->obj) && $x === $act->obj['id'])) { - return true; - } - } - return false; - } - - - static function find_parent($env,$act) { - if ($act) { - if (in_array($act->type, ['Like','Dislike']) && is_array($act->obj)) { - return $act->obj['id']; - } - if ($act->parent_id) { - return $act->parent_id; - } - } - return false; - } - - - /** - * @brief - * - * A public message with no listed recipients can be delivered to anybody who - * has PERMS_NETWORK for that type of post, PERMS_AUTHED (in-network senders are - * by definition authenticated) or PERMS_SITE and is one the same site, - * or PERMS_SPECIFIC and the sender is a contact who is granted permissions via - * their connection permissions in the address book. - * Here we take a given message and construct a list of hashes of everybody - * on the site that we should try and deliver to. - * Some of these will be rejected, but this gives us a place to start. - * - * @param array $msg - * @return NULL|array - */ - - static function public_recips($msg, $act) { - - $check_mentions = false; - $include_sys = false; - - if ($msg['type'] === 'activity') { - $public_stream_mode = intval(get_config('system','public_stream_mode',PUBLIC_STREAM_NONE)); - if ($public_stream_mode === PUBLIC_STREAM_FULL) { - $include_sys = true; - } - - $perm = 'send_stream'; - - if (self::is_top_level($msg,$act)) { - $check_mentions = true; - } - } - elseif ($msg['type'] === 'mail') { - $perm = 'post_mail'; - } - - // channels which we will deliver this post to - $r = []; - - $c = q("select channel_id, channel_hash from channel where channel_removed = 0"); - - if ($c) { - foreach ($c as $cc) { - - // top level activity sent to ourself: ignore. Clones will get a sync activity - // which is a true clone of the original item. Everything else is a duplicate. - - if ($check_mentions && $cc['channel_hash'] === $msg['sender']) { - continue; - } - - if (perm_is_allowed($cc['channel_id'],$msg['sender'],$perm)) { - $r[] = $cc['channel_hash']; - } - } - } - - if ($include_sys) { - $sys = get_sys_channel(); - if ($sys) { - $r[] = $sys['channel_hash']; - } - } - - // add channels that are following tags - // these will be enumerated and validated in tgroup_check() - - $ft = q("select channel_hash as hash from channel left join pconfig on pconfig.uid = channel_id where cat = 'system' and k = 'followed_tags' and channel_hash != '%s' and channel_removed = 0", - dbesc($msg['sender']) - ); - if ($ft ) { - foreach ($ft as $t) { - $r[] = $t['hash']; - } - } - - // look for any public mentions on this site - // They will get filtered by tgroup_check() so we don't need to check permissions now - - if ($check_mentions) { - // It's a top level post. Look at the tags. See if any of them are mentions and are on this hub. - if ($act && $act->obj) { - if (is_array($act->obj['tag']) && $act->obj['tag']) { - foreach ($act->obj['tag'] as $tag) { - if ($tag['type'] === 'Mention' && (strpos($tag['href'],z_root()) !== false)) { - $address = basename($tag['href']); - if ($address) { - $z = q("select channel_hash as hash from channel where channel_address = '%s' + dbesc(datetime_convert()), + dbesc(datetime_convert('UTC', 'UTC', ((isset($arr['photo_updated'])) ? $arr['photo_updated'] : 'now'))), + dbesc($photos[0]), + dbesc($photos[1]), + dbesc($photos[2]), + dbesc($photos[3]), + dbesc($xchan_hash) + ); + } + $what .= 'photo '; + $changed = true; + } + } + + // what we are missing for true hub independence is for any changes in the primary hub to + // get reflected not only in the hublocs, but also to update the URLs and addr in the appropriate xchan + + $s = Libsync::sync_locations($arr, $arr); + + if ($s) { + if (isset($s['change_message']) && $s['change_message']) { + $what .= $s['change_message']; + } + if (isset($s['changed']) && $s['changed']) { + $changed = $s['changed']; + } + if (isset($s['message']) && $s['message']) { + $ret['message'] .= $s['message']; + } + } + + // Which entries in the update table are we interested in updating? + $address = ((isset($arr['address']) && $arr['address']) ? $arr['address'] : EMPTY_STR); + if (isset($ud_arr) && isset($ud_arr['ud_addr'])) { + $address = $ud_arr['ud_addr']; + } + + // Are we a directory server of some kind? + + $other_realm = false; + +// $realm = get_directory_realm(); +// if (array_key_exists('site',$arr) +// && array_key_exists('realm',$arr['site']) +// && (strpos($arr['site']['realm'],$realm) === false)) +// $other_realm = true; + + +// if ($dirmode != DIRECTORY_MODE_NORMAL) { + + // We're some kind of directory server. However we can only add directory information + // if the entry is in the same realm (or is a sub-realm). Sub-realms are denoted by + // including the parent realm in the name. e.g. 'RED_GLOBAL:foo' would allow an entry to + // be in directories for the local realm (foo) and also the RED_GLOBAL realm. + + if (array_key_exists('profile', $arr) && is_array($arr['profile']) && (!$other_realm)) { + $profile_changed = Libzotdir::import_directory_profile($xchan_hash, $arr['profile'], $address, $ud_flags, 1); + if ($profile_changed) { + $what .= 'profile '; + $changed = true; + } + } else { + logger('Profile not available - hiding'); + // they may have made it private + $r = q( + "delete from xprof where xprof_hash = '%s'", + dbesc($xchan_hash) + ); + $r = q( + "delete from xtag where xtag_hash = '%s' and xtag_flags = 0", + dbesc($xchan_hash) + ); + } +// } + + if (array_key_exists('site', $arr) && is_array($arr['site'])) { + $profile_changed = self::import_site($arr['site']); + if ($profile_changed) { + $what .= 'site '; + $changed = true; + } + } + + if (($changed) || ($ud_flags == UPDATE_FLAGS_FORCED)) { + $guid = random_string() . '@' . App::get_hostname(); + Libzotdir::update_modtime($xchan_hash, $guid, $address, $ud_flags); + logger('Changed: ' . $what, LOGGER_DEBUG); + } elseif (!$ud_flags) { + // nothing changed but we still need to update the updates record + q( + "update updates set ud_flags = ( ud_flags | %d ) where ud_addr = '%s' and not (ud_flags & %d) > 0 ", + intval(UPDATE_FLAGS_UPDATED), + dbesc($address), + intval(UPDATE_FLAGS_UPDATED) + ); + } + + if (!x($ret, 'message')) { + $ret['success'] = true; + $ret['hash'] = $xchan_hash; + } + + logger('Result: ' . print_r($ret, true), LOGGER_DATA, LOG_DEBUG); + return $ret; + } + + /** + * @brief Called immediately after sending a zot message which is using queue processing. + * + * Updates the queue item according to the response result and logs any information + * returned to aid communications troubleshooting. + * + * @param string $hub - url of site we just contacted + * @param array $arr - output of z_post_url() + * @param array $outq - The queue structure attached to this request + */ + + public static function process_response($hub, $arr, $outq) + { + + logger('remote: ' . print_r($arr, true), LOGGER_DATA); + + if (!$arr['success']) { + logger('Failed: ' . $hub); + return; + } + + $x = json_decode($arr['body'], true); + + if (!$x) { + logger('No json from ' . $hub); + logger('Headers: ' . print_r($arr['header'], true), LOGGER_DATA, LOG_DEBUG); + } + + $x = Crypto::unencapsulate($x, get_config('system', 'prvkey')); + if (!is_array($x)) { + $x = json_decode($x, true); + } + + if (!is_array($x)) { + logger('no useful response: ' . $x); + } + + if ($x) { + if (!$x['success']) { + // handle remote validation issues + + $b = q( + "update dreport set dreport_result = '%s', dreport_time = '%s' where dreport_queue = '%s'", + dbesc(($x['message']) ? $x['message'] : 'unknown delivery error'), + dbesc(datetime_convert()), + dbesc($outq['outq_hash']) + ); + } + + if (is_array($x) && array_key_exists('delivery_report', $x) && is_array($x['delivery_report'])) { + foreach ($x['delivery_report'] as $xx) { + call_hooks('dreport_process', $xx); + if (is_array($xx) && array_key_exists('message_id', $xx) && DReport::is_storable($xx)) { + q( + "insert into dreport ( dreport_mid, dreport_site, dreport_recip, dreport_name, dreport_result, dreport_time, dreport_xchan, dreport_log ) values ( '%s', '%s', '%s','%s','%s','%s','%s','%s' ) ", + dbesc($xx['message_id']), + dbesc($xx['location']), + dbesc($xx['recipient']), + dbesc($xx['name']), + dbesc($xx['status']), + dbesc(datetime_convert('UTC', 'UTC', $xx['date'])), + dbesc($xx['sender']), + dbesc(EMPTY_STR) + ); + } + } + + // we have a more descriptive delivery report, so discard the per hub 'queue' report. + + q( + "delete from dreport where dreport_queue = '%s' ", + dbesc($outq['outq_hash']) + ); + } + } + // update the timestamp for this site + + q( + "update site set site_dead = 0, site_update = '%s' where site_url = '%s'", + dbesc(datetime_convert()), + dbesc(dirname($hub)) + ); + + // synchronous message types are handled immediately + // async messages remain in the queue until processed. + + if (intval($outq['outq_async'])) { + Queue::remove($outq['outq_hash'], $outq['outq_channel']); + } + + logger('zot_process_response: ' . print_r($x, true), LOGGER_DEBUG); + } + + /** + * @brief + * + * We received a notification packet (in mod_post) that a message is waiting for us, and we've verified the sender. + * Check if the site is using zot6 delivery and includes a verified HTTP Signature, signed content, and a 'msg' field, + * and also that the signer and the sender match. + * If that happens, we do not need to fetch/pickup the message - we have it already and it is verified. + * Translate it into the form we need for zot_import() and import it. + * + * Otherwise send back a pickup message, using our message tracking ID ($arr['secret']), which we will sign with our site + * private key. + * The entire pickup message is encrypted with the remote site's public key. + * If everything checks out on the remote end, we will receive back a packet containing one or more messages, + * which will be processed and delivered before this function ultimately returns. + * + * @param array $arr + * decrypted and json decoded notify packet from remote site + * @return array from zot_import() + * @see zot_import() + * + */ + + public static function fetch($arr, $hub = null) + { + + logger('zot_fetch: ' . print_r($arr, true), LOGGER_DATA, LOG_DEBUG); + + return self::import($arr, $hub); + } + + /** + * @brief Process incoming messages. + * + * Process incoming messages and import, update, delete as directed + * + * The message types handled here are 'activity' (e.g. posts), and 'sync'. + * + * @param array $arr + * 'pickup' structure returned from remote site + * @param string $sender_url + * the url specified by the sender in the initial communication. + * We will verify the sender and url in each returned message structure and + * also verify that all the messages returned match the site url that we are + * currently processing. + * + * @returns array + * Suitable for logging remotely, enumerating the processing results of each message/recipient combination + * * [0] => \e string $channel_hash + * * [1] => \e string $delivery_status + * * [2] => \e string $address + */ + + public static function import($arr, $hub = null) + { + + $env = $arr; + $private = false; + $return = []; + + $result = null; + + logger('Notify: ' . print_r($env, true), LOGGER_DATA, LOG_DEBUG); + + if (!is_array($env)) { + logger('decode error'); + return; + } + + $message_request = false; + + $has_data = array_key_exists('data', $env) && $env['data']; + $data = (($has_data) ? $env['data'] : false); + + $AS = null; + + if ($env['encoding'] === 'activitystreams') { + $AS = new ActivityStreams($data); + if ( + $AS->is_valid() && $AS->type === 'Announce' && is_array($AS->obj) + && array_key_exists('object', $AS->obj) && array_key_exists('actor', $AS->obj) + ) { + // This is a relayed/forwarded Activity (as opposed to a shared/boosted object) + // Reparse the encapsulated Activity and use that instead + logger('relayed activity', LOGGER_DEBUG); + $AS = new ActivityStreams($AS->obj); + } + + if (!$AS->is_valid()) { + logger('Activity rejected: ' . print_r($data, true)); + return; + } + + // compatibility issue with like of Hubzilla "new friend" activities which is very difficult to fix + + if ($AS->implied_create && is_array($AS->obj) && array_key_exists('type', $AS->obj) && ActivityStreams::is_an_actor($AS->obj['type'])) { + logger('create/person activity rejected. ' . print_r($data, true)); + return false; + } + + if (is_array($AS->obj)) { + $arr = Activity::decode_note($AS); + } else { + $arr = []; + } + + logger($AS->debug(), LOGGER_DATA); + } + + // There is nothing inherently wrong with getting a message-id which isn't a canonical URI/URL, but + // at the present time (2019/02) during the Hubzilla transition to zot6 it is likely to cause lots of duplicates for + // messages arriving from different protocols and sources with different message-id semantics. This + // restriction can be relaxed once most Hubzilla sites are upgraded to > 4.0. + // Don't check sync packets since they have a different encoding + + if ($arr && $env['type'] !== 'sync') { + if (strpos($arr['mid'], 'http') === false && strpos($arr['mid'], 'x-zot') === false) { + if (strpos($arr['mid'], 'bear:') === false) { + logger('activity rejected: legacy message-id'); + return; + } + } + + if ($arr['verb'] === 'Create' && ActivityStreams::is_an_actor($arr['obj_type'])) { + logger('activity rejected: create actor'); + return; + } + } + + $deliveries = null; + + if (array_key_exists('recipients', $env) && count($env['recipients'])) { + logger('specific recipients'); + logger('recipients: ' . print_r($env['recipients'], true), LOGGER_DEBUG); + + $recip_arr = []; + foreach ($env['recipients'] as $recip) { + $recip_arr[] = $recip; + } + + $r = false; + if ($recip_arr) { + stringify_array_elms($recip_arr, true); + $recips = implode(',', $recip_arr); + $r = q("select channel_hash as hash from channel where channel_hash in ( " . $recips . " ) and channel_removed = 0 "); + } + + if (!$r) { + logger('recips: no recipients on this site'); + return; + } + + // Response messages will inherit the privacy of the parent + + if ($env['type'] !== 'response') { + $private = true; + } + + $deliveries = ids_to_array($r, 'hash'); + + // We found somebody on this site that's in the recipient list. + } else { + logger('public post'); + + + // Public post. look for any site members who are or may be accepting posts from this sender + // and who are allowed to see them based on the sender's permissions + // @fixme; + + $deliveries = self::public_recips($env, $AS); + } + + $deliveries = array_unique($deliveries); + + if (!$deliveries) { + logger('No deliveries on this site'); + return; + } + + if ($has_data) { + if (in_array($env['type'], ['activity', 'response'])) { + if (!(is_array($AS->actor) && isset($AS->actor['id']))) { + logger('No author!'); + return; + } + + $r = q( + "select hubloc_hash, hubloc_network, hubloc_url from hubloc where hubloc_id_url = '%s'", + dbesc($AS->actor['id']) + ); + + if ($r) { + $r = self::zot_record_preferred($r); + $arr['author_xchan'] = $r['hubloc_hash']; + } + + if (!$arr['author_xchan']) { + logger('No author!'); + return; + } + + $s = q( + "select hubloc_hash, hubloc_url from hubloc where hubloc_id_url = '%s' and hubloc_network in ('zot6','nomad') limit 1", + dbesc($env['sender']) + ); + + // in individual delivery, change owner if needed + if ($s) { + $arr['owner_xchan'] = $s[0]['hubloc_hash']; + } else { + $arr['owner_xchan'] = $env['sender']; + } + + if ($private && (!intval($arr['item_private']))) { + $arr['item_private'] = 1; + } + if ($arr['mid'] === $arr['parent_mid']) { + if (is_array($AS->obj) && array_key_exists('commentPolicy', $AS->obj)) { + $p = strstr($AS->obj['commentPolicy'], 'until='); + + // if until= is the same as the creation date, set the item_nocomment flag + // as comments were already closed before the post was even sent. + + if ($p !== false) { + $comments_closed_at = datetime_convert('UTC', 'UTC', substr($p, 6)); + if ($comments_closed_at === $arr['created']) { + $arr['item_nocomment'] = 1; + } else { + $arr['comments_closed'] = $comments_closed_at; + $arr['comment_policy'] = trim(str_replace($p, '', $AS->obj['commentPolicy'])); + } + } else { + $arr['comment_policy'] = $AS->obj['commentPolicy']; + } + } + } + if ($AS->data['hubloc']) { + $arr['item_verified'] = true; + + if (!array_key_exists('comment_policy', $arr)) { + // set comment policy based on type of site. + $s = q( + "select site_type from site where site_url = '%s' limit 1", + dbesc($r[0]['hubloc_url']) + ); + + if ($s && intval($s[0]['site_type']) === SITE_TYPE_ZOT) { + $arr['comment_policy'] = 'contacts'; + } else { + $arr['comment_policy'] = 'authenticated'; + } + } + } + if ($AS->data['signed_data']) { + IConfig::Set($arr, 'activitypub', 'signed_data', $AS->data['signed_data'], false); + $j = json_decode($AS->data['signed_data'], true); + if ($j) { + IConfig::Set($arr, 'activitypub', 'rawmsg', json_encode(JSalmon::unpack($j['data'])), true); + } + } + + logger('Activity received: ' . print_r($arr, true), LOGGER_DATA, LOG_DEBUG); + logger('Activity recipients: ' . print_r($deliveries, true), LOGGER_DATA, LOG_DEBUG); + + $relay = (($env['type'] === 'response') ? true : false); + + $result = self::process_delivery($env['sender'], $AS, $arr, $deliveries, $relay, false, $message_request); + } elseif ($env['type'] === 'sync') { + $arr = json_decode($data, true); + + logger('Channel sync received: ' . print_r($arr, true), LOGGER_DATA, LOG_DEBUG); + logger('Channel sync recipients: ' . print_r($deliveries, true), LOGGER_DATA, LOG_DEBUG); + + if ($env['encoding'] === 'red') { + $result = Libsync::process_channel_sync_delivery($env['sender'], $arr, $deliveries); + } else { + logger('unsupported sync packet encoding ignored.'); + } + } + } + if ($result) { + $return = array_merge($return, $result); + } + return $return; + } + + + public static function is_top_level($env, $act) + { + if ($env['encoding'] === 'zot' && array_key_exists('flags', $env) && in_array('thread_parent', $env['flags'])) { + return true; + } + if ($act) { + if (in_array($act->type, ['Like', 'Dislike'])) { + return false; + } + $x = self::find_parent($env, $act); + if ($x === $act->id || (is_array($act->obj) && array_key_exists('id', $act->obj) && $x === $act->obj['id'])) { + return true; + } + } + return false; + } + + + public static function find_parent($env, $act) + { + if ($act) { + if (in_array($act->type, ['Like', 'Dislike']) && is_array($act->obj)) { + return $act->obj['id']; + } + if ($act->parent_id) { + return $act->parent_id; + } + } + return false; + } + + + /** + * @brief + * + * A public message with no listed recipients can be delivered to anybody who + * has PERMS_NETWORK for that type of post, PERMS_AUTHED (in-network senders are + * by definition authenticated) or PERMS_SITE and is one the same site, + * or PERMS_SPECIFIC and the sender is a contact who is granted permissions via + * their connection permissions in the address book. + * Here we take a given message and construct a list of hashes of everybody + * on the site that we should try and deliver to. + * Some of these will be rejected, but this gives us a place to start. + * + * @param array $msg + * @return NULL|array + */ + + public static function public_recips($msg, $act) + { + + $check_mentions = false; + $include_sys = false; + + if ($msg['type'] === 'activity') { + $public_stream_mode = intval(get_config('system', 'public_stream_mode', PUBLIC_STREAM_NONE)); + if ($public_stream_mode === PUBLIC_STREAM_FULL) { + $include_sys = true; + } + + $perm = 'send_stream'; + + if (self::is_top_level($msg, $act)) { + $check_mentions = true; + } + } elseif ($msg['type'] === 'mail') { + $perm = 'post_mail'; + } + + // channels which we will deliver this post to + $r = []; + + $c = q("select channel_id, channel_hash from channel where channel_removed = 0"); + + if ($c) { + foreach ($c as $cc) { + // top level activity sent to ourself: ignore. Clones will get a sync activity + // which is a true clone of the original item. Everything else is a duplicate. + + if ($check_mentions && $cc['channel_hash'] === $msg['sender']) { + continue; + } + + if (perm_is_allowed($cc['channel_id'], $msg['sender'], $perm)) { + $r[] = $cc['channel_hash']; + } + } + } + + if ($include_sys) { + $sys = get_sys_channel(); + if ($sys) { + $r[] = $sys['channel_hash']; + } + } + + // add channels that are following tags + // these will be enumerated and validated in tgroup_check() + + $ft = q( + "select channel_hash as hash from channel left join pconfig on pconfig.uid = channel_id where cat = 'system' and k = 'followed_tags' and channel_hash != '%s' and channel_removed = 0", + dbesc($msg['sender']) + ); + if ($ft) { + foreach ($ft as $t) { + $r[] = $t['hash']; + } + } + + // look for any public mentions on this site + // They will get filtered by tgroup_check() so we don't need to check permissions now + + if ($check_mentions) { + // It's a top level post. Look at the tags. See if any of them are mentions and are on this hub. + if ($act && $act->obj) { + if (is_array($act->obj['tag']) && $act->obj['tag']) { + foreach ($act->obj['tag'] as $tag) { + if ($tag['type'] === 'Mention' && (strpos($tag['href'], z_root()) !== false)) { + $address = basename($tag['href']); + if ($address) { + $z = q( + "select channel_hash as hash from channel where channel_address = '%s' and channel_hash != '%s' and channel_removed = 0 limit 1", - dbesc($address), - dbesc($msg['sender']) - ); - if ($z) { - $r[] = $z[0]['hash']; - } - } - } - if ($tag['type'] === 'topicalCollection' && strpos($tag['name'],App::get_hostname())) { - $address = substr($tag['name'],0,strpos($tag['name'],'@')); - if ($address) { - $z = q("select channel_hash as hash from channel where channel_address = '%s' + dbesc($address), + dbesc($msg['sender']) + ); + if ($z) { + $r[] = $z[0]['hash']; + } + } + } + if ($tag['type'] === 'topicalCollection' && strpos($tag['name'], App::get_hostname())) { + $address = substr($tag['name'], 0, strpos($tag['name'], '@')); + if ($address) { + $z = q( + "select channel_hash as hash from channel where channel_address = '%s' and channel_hash != '%s' and channel_removed = 0 limit 1", - dbesc($address), - dbesc($msg['sender']) - ); - if ($z) { - $r[] = $z[0]['hash']; - } - } - } - } - } - } - } - else { - // This is a comment. We need to find any parent with ITEM_UPLINK set. But in fact, let's just return - // everybody that stored a copy of the parent. This way we know we're covered. We'll check the - // comment permissions when we deliver them. - - $thread_parent = self::find_parent($msg,$act); - - if ($thread_parent) { - $z = q("select channel_hash as hash from channel left join item on channel.channel_id = item.uid where ( item.thr_parent = '%s' OR item.parent_mid = '%s' ) ", - dbesc($thread_parent), - dbesc($thread_parent) - ); - if ($z) { - foreach ($z as $zv) { - $r[] = $zv['hash']; - } - } - } - } - - // There are probably a lot of duplicates in $r at this point. We need to filter those out. - - if ($r) { - $r = array_values(array_unique($r)); - } - - logger('public_recips: ' . print_r($r,true), LOGGER_DATA, LOG_DEBUG); - return $r; - } - - - /** - * @brief - * - * @param array $sender - * @param ActivityStreams object $act - * @param array $msg_arr - * @param array $deliveries - * @param boolean $relay - * @param boolean $public (optional) default false - * @param boolean $request (optional) default false - * @return array - */ - - static function process_delivery($sender, $act, $msg_arr, $deliveries, $relay, $public = false, $request = false) { - - $result = []; - - // logger('msg_arr: ' . print_r($msg_arr,true),LOGGER_ALL); - - // If an upstream hop used ActivityPub, set the identities to zot6 nomadic identities where applicable - // else things could easily get confused - - $msg_arr['author_xchan'] = Activity::find_best_identity($msg_arr['author_xchan']); - $msg_arr['owner_xchan'] = Activity::find_best_identity($msg_arr['owner_xchan']); - - // We've validated the sender. Now make sure that the sender is the owner or author - - if (! $public) { - if ($sender != $msg_arr['owner_xchan'] && $sender != $msg_arr['author_xchan']) { - logger("Sender $sender is not owner {$msg_arr['owner_xchan']} or author {$msg_arr['author_xchan']} - mid {$msg_arr['mid']}"); - return; - } - } - - if ($act->implied_create) { - logger('implied create activity. Not delivering/storing.'); - return; - } - - foreach ($deliveries as $d) { - - $local_public = $public; - - // if any further changes are to be made, change a copy and not the original - $arr = $msg_arr; - -// if (! $msg_arr['mid']) { -// logger('no mid2: ' . print_r($msg_arr,true)); -// logger('recip: ' . $d); -// } - - - $DR = new DReport(z_root(),$sender,$d,$arr['mid']); - - $channel = channelx_by_hash($d); - - if (! $channel) { - $DR->update('recipient not found'); - $result[] = $DR->get(); - continue; - } - - $DR->set_name($channel['channel_name'] . ' <' . channel_reddress($channel) . '>'); - -// if ($act->type === 'Tombstone') { -// $r = q("select * from item where mid in ( '%s', '%s' ) and uid = %d", -// dbesc($act->id), -// dbesc(str_replace('/activity/','/item/',$act->id)) -// intval($channel['channel_id']) -// ); -// if ($r) { -// if (($r[0]['author_xchan'] === $sender) || ($r[0]['owner_xchan'] === $sender)) { -// drop_item($r[0]['id'],false); -// } -// $DR->update('item deleted'); -// $result[] = $DR->get(); -// continue; -// } -// $DR->update('deleted item not found'); -// $result[] = $DR->get(); -// continue; -// } - - if (($act) && ($act->obj) && (! is_array($act->obj))) { - - // The initial object fetch failed using the sys channel credentials. - // Try again using the delivery channel credentials. - // We will also need to re-parse the $item array, - // but preserve any values that were set during anonymous parsing. - - $o = Activity::fetch($act->obj,$channel); - if ($o) { - $act->obj = $o; - $arr = array_merge(Activity::decode_note($act),$arr); - } - else { - $DR->update('Incomplete or corrupt activity'); - $result[] = $DR->get(); - continue; - } - } - - /** - * We need to block normal top-level message delivery from our clones, as the delivered - * message doesn't have ACL information in it as the cloned copy does. That copy - * will normally arrive first via sync delivery, but this isn't guaranteed. - * There's a chance the current delivery could take place before the cloned copy arrives - * hence the item could have the wrong ACL and *could* be used in subsequent deliveries or - * access checks. - */ - - if ($sender === $channel['channel_hash'] && $arr['author_xchan'] === $channel['channel_hash'] && $arr['mid'] === $arr['parent_mid']) { - $DR->update('self delivery ignored'); - $result[] = $DR->get(); - continue; - } - - // allow public postings to the sys channel regardless of permissions, but not - // for comments travelling upstream. Wait and catch them on the way down. - // They may have been blocked by the owner. - - if (intval($channel['channel_system']) && (! $arr['item_private']) && (! $relay)) { - $local_public = true; - - if (! check_pubstream_channelallowed($sender)) { - $local_public = false; - continue; - } - - // don't allow pubstream posts if the sender even has a clone on a pubstream denied site - - $siteallowed = true; - $h = q("select hubloc_url from hubloc where hubloc_hash = '%s'", - dbesc($sender) - ); - if ($h) { - foreach ($h as $hub) { - if (! check_pubstream_siteallowed($hub['hubloc_url'])) { - $siteallowed = false; - break; - } - } - } - if (! $siteallowed) { - $local_public = false; - continue; - } - - $r = q("select xchan_selfcensored from xchan where xchan_hash = '%s' limit 1", - dbesc($sender) - ); - // don't import sys channel posts from selfcensored authors - if ($r && (intval($r[0]['xchan_selfcensored']))) { - $local_public = false; - continue; - } - if (! MessageFilter::evaluate($arr,get_config('system','pubstream_incl'),get_config('system','pubstream_excl'))) { - $local_public = false; - continue; - } - } - - // perform pre-storage check to see if it's "likely" that this is a group or collection post - - $tag_delivery = tgroup_check($channel['channel_id'],$arr); - - $perm = 'send_stream'; - if (($arr['mid'] !== $arr['parent_mid']) && ($relay)) - $perm = 'post_comments'; - - // This is our own post, possibly coming from a channel clone - - if ($arr['owner_xchan'] == $d) { - $arr['item_wall'] = 1; - } - else { - $arr['item_wall'] = 0; - } - - $friendofriend = false; - - if ((! $tag_delivery) && (! $local_public)) { - $allowed = (perm_is_allowed($channel['channel_id'],$sender,$perm)); - - $blocked = LibBlock::fetch($channel['channel_id'],BLOCKTYPE_SERVER); - if ($blocked) { - $h = q("select hubloc_url from hubloc where hubloc_hash = '%s'", - dbesc($sender) - ); - if ($h) { - foreach ($h as $hub) { - foreach($blocked as $b) { - if (strpos($hub['hubloc_url'],$b['block_entity']) !== false) { - $allowed = false; - } - } - } - } - } - - $permit_mentions = intval(PConfig::Get($channel['channel_id'], 'system','permit_all_mentions') && i_am_mentioned($channel,$arr)); - - if (! $allowed) { - if ($perm === 'post_comments') { - $parent = q("select * from item where mid = '%s' and uid = %d limit 1", - dbesc($arr['parent_mid']), - intval($channel['channel_id']) - ); - if ($parent) { - $allowed = can_comment_on_post($sender,$parent[0]); - } - if ((! $allowed) && $permit_mentions) { - if ($parent && $parent[0]['owner_xchan'] === $channel['channel_hash']) { - $allowed = false; - } - else { - $allowed = true; - } - } - if ($parent && absolutely_no_comments($parent[0])) { - $allowed = false; - } - - } - elseif ($permit_mentions) { - $allowed = true; - } - } - if ($request) { - - // Conversation fetches (e.g. $request == true) take place for - // a) new comments on expired posts - // b) hyperdrive (friend-of-friend) conversations - - - // over-ride normal connection permissions for hyperdrive (friend-of-friend) conversations - // (if hyperdrive is enabled). - // If $allowed is already true, this is probably the conversation of a direct friend or a - // conversation fetch for a new comment on an expired post - // Comments of all these activities are allowed and will only be rejected (later) if the parent - // doesn't exist. - - if ($perm === 'send_stream') { - if (get_pconfig($channel['channel_id'],'system','hyperdrive',true)) { - $allowed = true; - } - } - else { - $allowed = true; - } - - $friendofriend = true; - } - - if (intval($arr['item_private']) === 2) { - if (! perm_is_allowed($channel['channel_id'],$sender,'post_mail')) { - $allowed = false; - } - } - - if (get_abconfig($channel['channel_id'],$sender,'system','block_announce', false)) { - if ($arr['verb'] === 'Announce' || strpos($arr['body'],'[/share]')) { - $allowed = false; - } - } - - if (! $allowed) { - logger("permission denied for delivery to channel {$channel['channel_id']} {$channel['channel_address']}"); - $DR->update('permission denied'); - $result[] = $DR->get(); - continue; - } - } - - if ($arr['mid'] !== $arr['parent_mid']) { - - if (perm_is_allowed($channel['channel_id'],$sender,'moderated') && $relay) { - $arr['item_blocked'] = ITEM_MODERATED; - } - - // check source route. - // We are only going to accept comments from this sender if the comment has the same route as the top-level-post, - // this is so that permissions mismatches between senders apply to the entire conversation - // As a side effect we will also do a preliminary check that we have the top-level-post, otherwise - // processing it is pointless. - - // The original author won't have a token in their copy of the message - - $prnt = ((strpos($arr['parent_mid'],'token=') !== false) ? substr($arr['parent_mid'],0,strpos($arr['parent_mid'],'?')) : ''); - - $r = q("select route, id, parent_mid, mid, owner_xchan, item_private, obj_type from item where mid = '%s' and uid = %d limit 1", - dbesc($arr['parent_mid']), - intval($channel['channel_id']) - ); - if (! $r) { - $r = q("select route, id, parent_mid, mid, owner_xchan, item_private, obj_type from item where mid = '%s' and uid = %d limit 1", - dbesc($prnt), - intval($channel['channel_id']) - ); - } - - if ($r) { - // if this is a multi-threaded conversation, preserve the threading information - if ($r[0]['parent_mid'] !== $r[0]['mid']) { - $arr['thr_parent'] = $arr['parent_mid']; - $arr['parent_mid'] = $r[0]['parent_mid']; - if ($act->replyto) { - q("update item set replyto = '%s' where id = %d", - dbesc($act->replyto), - intval($r[0]['id']) - ); - } - } - - if ($r[0]['obj_type'] === 'Question') { - // route checking doesn't work correctly here because we've changed the privacy - $r[0]['route'] = EMPTY_STR; - // If this is a poll response, convert the obj_type to our (internal-only) "Answer" type - if ($arr['obj_type'] === 'Note' && $arr['title'] && (! $arr['content'])) { - $arr['obj_type'] = 'Answer'; - } - } - } - else { - - // We don't seem to have a copy of this conversation or at least the parent - // - so request a copy of the entire conversation to date. - // Don't do this if it's a relay post as we're the ones who are supposed to - // have the copy and we don't want the request to loop. - // Also don't do this if this comment came from a conversation request packet. - // It's possible that comments are allowed but posting isn't and that could - // cause a conversation fetch loop. - // We'll also check the send_stream permission - because if it isn't allowed, - // the top level post is unlikely to be imported and - // this is just an exercise in futility. - - if ((! $relay) && (! $request) && (! $local_public) - && perm_is_allowed($channel['channel_id'],$sender,'send_stream')) { - $reports = self::fetch_conversation($channel,$arr['mid']); - - // extract our delivery report from the fetched conversation - // if we can find it. - logger('fetch_report for ' . $arr['mid'], LOGGER_ALL); - logger('fetch_report: ' . print_r($reports,true), LOGGER_ALL); - - if ($reports && is_array($reports)) { - $found_report = false; - foreach ($reports as $report) { - if ($report['message_id'] === $arr['mid']) { - $found_report = true; - $DR->update($report['status']); - } - } - if (! $found_report) { - $DR->update('conversation fetch failed'); - } - } - else { - $DR->update('conversation fetch failed'); - } - } - else { - $DR->update('comment parent not found'); - } - $result[] = $DR->get(); - continue; - } - - - if ($relay || $friendofriend || (intval($r[0]['item_private']) === 0 && intval($arr['item_private']) === 0)) { - // reset the route in case it travelled a great distance upstream - // use our parent's route so when we go back downstream we'll match - // with whatever route our parent has. - // Also friend-of-friend conversations may have been imported without a route, - // but we are now getting comments via listener delivery - // and if there is no privacy on this or the parent, we don't care about the route, - // so just set the owner and route accordingly. - $arr['route'] = $r[0]['route']; - $arr['owner_xchan'] = $r[0]['owner_xchan']; - } - else { - - // going downstream check that we have the same upstream provider that - // sent it to us originally. Ignore it if it came from another source - // (with potentially different permissions). - // only compare the last hop since it could have arrived at the last location any number of ways. - // Always accept empty routes and firehose items (route contains 'undefined') . - - $existing_route = explode(',', $r[0]['route']); - $routes = count($existing_route); - if ($routes) { - $last_hop = array_pop($existing_route); - $last_prior_route = implode(',',$existing_route); - } - else { - $last_hop = ''; - $last_prior_route = ''; - } - - if (in_array('undefined',$existing_route) || $last_hop == 'undefined' || $sender == 'undefined') { - $last_hop = ''; - } - - $current_route = (($arr['route']) ? $arr['route'] . ',' : '') . $sender; - - if ($last_hop && $last_hop != $sender) { - logger('comment route mismatch: parent route = ' . $r[0]['route'] . ' expected = ' . $current_route, LOGGER_DEBUG); - logger('comment route mismatch: parent msg = ' . $r[0]['id'],LOGGER_DEBUG); - $DR->update('comment route mismatch'); - $result[] = $DR->get(); - continue; - } - - // we'll add sender onto this when we deliver it. $last_prior_route now has the previously stored route - // *except* for the sender which would've been the last hop before it got to us. - - $arr['route'] = $last_prior_route; - } - } - - // This is used to fetch allow/deny rules if either the sender - // or owner is a connection. post_is_importable() evaluates all of them - $abook = q("select * from abook where abook_channel = %d and ( abook_xchan = '%s' OR abook_xchan = '%s' )", - intval($channel['channel_id']), - dbesc($arr['owner_xchan']), - dbesc($arr['author_xchan']) - ); - - if (isset($arr['item_deleted']) && intval($arr['item_deleted'])) { - - // set these just in case we need to store a fresh copy of the deleted post. - // This could happen if the delete got here before the original post did. - - $arr['aid'] = $channel['channel_account_id']; - $arr['uid'] = $channel['channel_id']; - - $item_id = self::delete_imported_item($sender,$act,$arr,$channel['channel_id'],$relay); - $DR->update(($item_id) ? 'deleted' : 'delete_failed'); - $result[] = $DR->get(); - - if ($relay && $item_id) { - logger('process_delivery: invoking relay'); - Run::Summon([ 'Notifier', 'relay', intval($item_id) ]); - $DR->update('relayed'); - $result[] = $DR->get(); - } - continue; - } - - // reactions such as like and dislike could have an mid with /activity/ in it. - // Check for both forms in order to prevent duplicates. - - $r = q("select * from item where mid in ('%s','%s') and uid = %d limit 1", - dbesc($arr['mid']), - dbesc(str_replace(z_root() . '/activity/', z_root() . '/item/', $arr['mid'])), - intval($channel['channel_id']) - ); - - if ($r) { - // We already have this post. - $item_id = $r[0]['id']; - - if (intval($r[0]['item_deleted'])) { - // It was deleted locally. - $DR->update('update ignored'); - $result[] = $DR->get(); - - continue; - } - // Maybe it has been edited? - elseif ($arr['edited'] > $r[0]['edited']) { - $arr['id'] = $r[0]['id']; - $arr['uid'] = $channel['channel_id']; - if (post_is_importable($channel['channel_id'],$arr,$abook)) { - $item_result = self::update_imported_item($sender,$arr,$r[0],$channel['channel_id'],$tag_delivery); - $DR->update('updated'); - $result[] = $DR->get(); - if (! $relay) { - add_source_route($item_id,$sender); - } - } - else { - $DR->update('update ignored'); - $result[] = $DR->get(); - } - } - else { - $DR->update('update ignored'); - $result[] = $DR->get(); - - // We need this line to ensure wall-to-wall comments are relayed (by falling through to the relay bit), - // and at the same time not relay any other relayable posts more than once, because to do so is very wasteful. - if (! intval($r[0]['item_origin'])) { - continue; - } - } - } - else { - $arr['aid'] = $channel['channel_account_id']; - $arr['uid'] = $channel['channel_id']; - - // if it's a sourced post, call the post_local hooks as if it were - // posted locally so that crosspost connectors will be triggered. - - if (check_item_source($arr['uid'], $arr)) { - /** - * @hooks post_local - * Called when an item has been posted on this machine via mod/item.php (also via API). - * * \e array with an item - */ - call_hooks('post_local', $arr); - } - - $item_id = 0; - - Activity::rewrite_mentions($arr); - - - $maxlen = get_max_import_size(); - - if($maxlen && mb_strlen($arr['body']) > $maxlen) { - $arr['body'] = mb_substr($arr['body'],0,$maxlen,'UTF-8'); - logger('message length exceeds max_import_size: truncated'); - } - - if($maxlen && mb_strlen($arr['summary']) > $maxlen) { - $arr['summary'] = mb_substr($arr['summary'],0,$maxlen,'UTF-8'); - logger('message summary length exceeds max_import_size: truncated'); - } - - if (post_is_importable($arr['uid'],$arr,$abook)) { - - // Strip old-style hubzilla bookmarks - if (strpos($arr['body'],"#^[") !== false) { - $arr['body'] = str_replace("#^[","[",$arr['body']); - } - - $item_result = item_store($arr); - if ($item_result['success']) { - $item_id = $item_result['item_id']; - $parr = [ - 'item_id' => $item_id, - 'item' => $arr, - 'sender' => $sender, - 'channel' => $channel - ]; - /** - * @hooks activity_received - * Called when an activity (post, comment, like, etc.) has been received from a zot source. - * * \e int \b item_id - * * \e array \b item - * * \e array \b sender - * * \e array \b channel - */ - call_hooks('activity_received', $parr); - // don't add a source route if it's a relay or later recipients will get a route mismatch - if (! $relay) { - add_source_route($item_id,$sender); - } - } - $DR->update(($item_id) ? 'posted' : 'storage failed: ' . $item_result['message']); - $result[] = $DR->get(); - } - else { - $DR->update('post ignored'); - $result[] = $DR->get(); - } - } - - // preserve conversations with which you are involved from expiration - - $stored = (($item_result && $item_result['item']) ? $item_result['item'] : false); - if ((is_array($stored)) && ($stored['id'] != $stored['parent']) - && ($stored['author_xchan'] === $channel['channel_hash'])) { - retain_item($stored['item']['parent']); - } - - if ($relay && $item_id) { - logger('Invoking relay'); - Run::Summon([ 'Notifier', 'relay', intval($item_id) ]); - $DR->addto_update('relayed'); - $result[] = $DR->get(); - } - } - - if (! $deliveries) { - $result[] = array('', 'no recipients', '', $arr['mid']); - } - - logger('Local results: ' . print_r($result, true), LOGGER_DEBUG); - - return $result; - } - - static public function hyperdrive_enabled($channel,$item) { - - if (get_pconfig($channel['channel_id'],'system','hyperdrive',true)) { - return true; - } - return false; - } - - static public function fetch_conversation($channel,$mid) { - - // Use Zotfinger to create a signed request - - logger('fetching conversation: ' . $mid, LOGGER_DEBUG); - - $a = Zotfinger::exec($mid,$channel); - - logger('received conversation: ' . print_r($a,true), LOGGER_DATA); - - if (! $a) { - return false; - } - - if ($a['data']['type'] !== 'OrderedCollection') { - return false; - } - - $obj = new ASCollection($a['data'],$channel); - $items = $obj->get(); - - if (! $items) { - return false; - } - - $ret = []; - - - $signer = q("select hubloc_hash, hubloc_url from hubloc where hubloc_id_url = '%s' and hubloc_network in ('nomad','zot6') limit 1", - dbesc($a['signature']['signer']) - ); - - - foreach ($items as $activity) { - - $AS = new ActivityStreams($activity); - if ($AS->is_valid() && $AS->type === 'Announce' && is_array($AS->obj) - && array_key_exists('object',$AS->obj) && array_key_exists('actor',$AS->obj)) { - // This is a relayed/forwarded Activity (as opposed to a shared/boosted object) - // Reparse the encapsulated Activity and use that instead - logger('relayed activity',LOGGER_DEBUG); - $AS = new ActivityStreams($AS->obj); - } - - if (! $AS->is_valid()) { - logger('FOF Activity rejected: ' . print_r($activity,true)); - continue; - } - $arr = Activity::decode_note($AS); - - // logger($AS->debug()); - - $r = q("select hubloc_hash from hubloc where hubloc_id_url = '%s' or hubloc_hash = '%s' limit 1", - dbesc($AS->actor['id']), - dbesc($AS->actor['id']) - ); - - if (! $r) { - $y = import_author_xchan([ 'url' => $AS->actor['id'] ]); - if ($y) { - $r = q("select hubloc_hash from hubloc where hubloc_id_url = '%s' or hubloc_hash = '%s' limit 1", - dbesc($AS->actor['id']), - dbesc($AS->actor['id']) - ); - } - if (! $r) { - logger('FOF Activity: no actor'); - continue; - } - } - - if ($AS->obj['actor'] && $AS->obj['actor']['id'] && $AS->obj['actor']['id'] !== $AS->actor['id']) { - $y = import_author_xchan([ 'url' => $AS->obj['actor']['id'] ]); - if (! $y) { - logger('FOF Activity: no object actor'); - continue; - } - } - - - if ($r) { - $arr['author_xchan'] = $r[0]['hubloc_hash']; - } - - if ($signer) { - $arr['owner_xchan'] = $signer[0]['hubloc_hash']; - } - else { - $arr['owner_xchan'] = $a['signature']['signer']; - } - - if ($AS->data['hubloc'] || $arr['author_xchan'] === $arr['owner_xchan']) { - $arr['item_verified'] = true; - } - - if ($AS->data['signed_data']) { - IConfig::Set($arr,'activitystreams','signed_data',$AS->data['signed_data'],false); - } - - logger('FOF Activity received: ' . print_r($arr,true), LOGGER_DATA, LOG_DEBUG); - logger('FOF Activity recipient: ' . $channel['channel_hash'], LOGGER_DATA, LOG_DEBUG); - - $result = self::process_delivery($arr['owner_xchan'],$AS,$arr, [ $channel['channel_hash'] ],false,false,true); - if ($result) { - $ret = array_merge($ret, $result); - } - } - - return $ret; - } - - - /** - * @brief Remove community tag. - * - * @param array $sender an associative array with - * * \e string \b hash a xchan_hash - * @param array $arr an associative array - * * \e int \b verb - * * \e int \b obj_type - * * \e int \b mid - * @param int $uid - */ - - static function remove_community_tag($sender, $arr, $uid) { - - if (! (activity_match($arr['verb'], ACTIVITY_TAG) && ($arr['obj_type'] == ACTIVITY_OBJ_TAGTERM))) - return; - - logger('remove_community_tag: invoked'); - - if (! get_pconfig($uid,'system','blocktags')) { - logger('Permission denied.'); - return; - } - - $r = q("select * from item where mid = '%s' and uid = %d limit 1", - dbesc($arr['mid']), - intval($uid) - ); - if (! $r) { - logger('No item'); - return; - } - - if (($sender != $r[0]['owner_xchan']) && ($sender != $r[0]['author_xchan'])) { - logger('Sender not authorised.'); - return; - } - - $i = $r[0]; - - if ($i['target']) { - $i['target'] = json_decode($i['target'],true); - } - if ($i['object']) { - $i['object'] = json_decode($i['object'],true); - } - if (! ($i['target'] && $i['object'])) { - logger('No target/object'); - return; - } - - $message_id = $i['target']['id']; - - $r = q("select id from item where mid = '%s' and uid = %d limit 1", - dbesc($message_id), - intval($uid) - ); - if (! $r) { - logger('No parent message'); - return; - } - - q("delete from term where uid = %d and oid = %d and otype = %d and ttype in ( %d, %d ) and term = '%s' and url = '%s'", - intval($uid), - intval($r[0]['id']), - intval(TERM_OBJ_POST), - intval(TERM_HASHTAG), - intval(TERM_COMMUNITYTAG), - dbesc($i['object']['title']), - dbesc(get_rel_link($i['object']['link'],'alternate')) - ); - } - - /** - * @brief Updates an imported item. - * - * @see item_store_update() - * - * @param array $sender - * @param array $item - * @param array $orig - * @param int $uid - * @param boolean $tag_delivery - */ - - static function update_imported_item($sender, $item, $orig, $uid, $tag_delivery) { - - // If this is a comment being updated, remove any privacy information - // so that item_store_update will set it from the original. - - if ($item['mid'] !== $item['parent_mid']) { - unset($item['allow_cid']); - unset($item['allow_gid']); - unset($item['deny_cid']); - unset($item['deny_gid']); - unset($item['item_private']); - } - - // we need the tag_delivery check for downstream flowing posts as the stored post - // may have a different owner than the one being transmitted. - - if (($sender != $orig['owner_xchan'] && $sender != $orig['author_xchan']) && (! $tag_delivery)) { - logger('sender is not owner or author'); - return; - } - - - $x = item_store_update($item); - - // If we're updating an event that we've saved locally, we store the item info first - // because event_addtocal will parse the body to get the 'new' event details - - if ($orig['resource_type'] === 'event') { - $res = event_addtocal($orig['id'], $uid); - if (! $res) { - logger('update event: failed'); - } - } - - if (! $x['item_id']) { - logger('update_imported_item: failed: ' . $x['message']); - } - else { - logger('update_imported_item'); - } - - return $x; - } - - /** - * @brief Deletes an imported item. - * - * @param array $sender - * * \e string \b hash a xchan_hash - * @param array $item - * @param int $uid - * @param boolean $relay - * @return boolean|int post_id - */ - - static function delete_imported_item($sender, $act, $item, $uid, $relay) { - - logger('invoked', LOGGER_DEBUG); - - $ownership_valid = false; - $item_found = false; - $post_id = 0; - - if ($item['verb'] === 'Tombstone') { - // The id of the deleted thing is the item mid (activity id) - $mid = $item['mid']; - } - else { - // The id is the object id if the type is Undo or Delete - $mid = ((is_array($act->obj)) ? $act->obj['id'] : $act->obj); - } - - // we may have stored either the object id or the activity id if it was a response activity (like, dislike, etc.) - - $r = q("select * from item where ( author_xchan = '%s' or owner_xchan = '%s' or source_xchan = '%s' ) + dbesc($address), + dbesc($msg['sender']) + ); + if ($z) { + $r[] = $z[0]['hash']; + } + } + } + } + } + } + } else { + // This is a comment. We need to find any parent with ITEM_UPLINK set. But in fact, let's just return + // everybody that stored a copy of the parent. This way we know we're covered. We'll check the + // comment permissions when we deliver them. + + $thread_parent = self::find_parent($msg, $act); + + if ($thread_parent) { + $z = q( + "select channel_hash as hash from channel left join item on channel.channel_id = item.uid where ( item.thr_parent = '%s' OR item.parent_mid = '%s' ) ", + dbesc($thread_parent), + dbesc($thread_parent) + ); + if ($z) { + foreach ($z as $zv) { + $r[] = $zv['hash']; + } + } + } + } + + // There are probably a lot of duplicates in $r at this point. We need to filter those out. + + if ($r) { + $r = array_values(array_unique($r)); + } + + logger('public_recips: ' . print_r($r, true), LOGGER_DATA, LOG_DEBUG); + return $r; + } + + + /** + * @brief + * + * @param array $sender + * @param ActivityStreams object $act + * @param array $msg_arr + * @param array $deliveries + * @param bool $relay + * @param bool $public (optional) default false + * @param bool $request (optional) default false + * @return array + */ + + public static function process_delivery($sender, $act, $msg_arr, $deliveries, $relay, $public = false, $request = false) + { + + $result = []; + + // logger('msg_arr: ' . print_r($msg_arr,true),LOGGER_ALL); + + // If an upstream hop used ActivityPub, set the identities to zot6 nomadic identities where applicable + // else things could easily get confused + + $msg_arr['author_xchan'] = Activity::find_best_identity($msg_arr['author_xchan']); + $msg_arr['owner_xchan'] = Activity::find_best_identity($msg_arr['owner_xchan']); + + // We've validated the sender. Now make sure that the sender is the owner or author + + if (!$public) { + if ($sender != $msg_arr['owner_xchan'] && $sender != $msg_arr['author_xchan']) { + logger("Sender $sender is not owner {$msg_arr['owner_xchan']} or author {$msg_arr['author_xchan']} - mid {$msg_arr['mid']}"); + return; + } + } + + if ($act->implied_create) { + logger('implied create activity. Not delivering/storing.'); + return; + } + + foreach ($deliveries as $d) { + $local_public = $public; + + // if any further changes are to be made, change a copy and not the original + $arr = $msg_arr; + +// if (! $msg_arr['mid']) { +// logger('no mid2: ' . print_r($msg_arr,true)); +// logger('recip: ' . $d); +// } + + + $DR = new DReport(z_root(), $sender, $d, $arr['mid']); + + $channel = channelx_by_hash($d); + + if (!$channel) { + $DR->update('recipient not found'); + $result[] = $DR->get(); + continue; + } + + $DR->set_name($channel['channel_name'] . ' <' . channel_reddress($channel) . '>'); + +// if ($act->type === 'Tombstone') { +// $r = q("select * from item where mid in ( '%s', '%s' ) and uid = %d", +// dbesc($act->id), +// dbesc(str_replace('/activity/','/item/',$act->id)) +// intval($channel['channel_id']) +// ); +// if ($r) { +// if (($r[0]['author_xchan'] === $sender) || ($r[0]['owner_xchan'] === $sender)) { +// drop_item($r[0]['id'],false); +// } +// $DR->update('item deleted'); +// $result[] = $DR->get(); +// continue; +// } +// $DR->update('deleted item not found'); +// $result[] = $DR->get(); +// continue; +// } + + if (($act) && ($act->obj) && (!is_array($act->obj))) { + // The initial object fetch failed using the sys channel credentials. + // Try again using the delivery channel credentials. + // We will also need to re-parse the $item array, + // but preserve any values that were set during anonymous parsing. + + $o = Activity::fetch($act->obj, $channel); + if ($o) { + $act->obj = $o; + $arr = array_merge(Activity::decode_note($act), $arr); + } else { + $DR->update('Incomplete or corrupt activity'); + $result[] = $DR->get(); + continue; + } + } + + /** + * We need to block normal top-level message delivery from our clones, as the delivered + * message doesn't have ACL information in it as the cloned copy does. That copy + * will normally arrive first via sync delivery, but this isn't guaranteed. + * There's a chance the current delivery could take place before the cloned copy arrives + * hence the item could have the wrong ACL and *could* be used in subsequent deliveries or + * access checks. + */ + + if ($sender === $channel['channel_hash'] && $arr['author_xchan'] === $channel['channel_hash'] && $arr['mid'] === $arr['parent_mid']) { + $DR->update('self delivery ignored'); + $result[] = $DR->get(); + continue; + } + + // allow public postings to the sys channel regardless of permissions, but not + // for comments travelling upstream. Wait and catch them on the way down. + // They may have been blocked by the owner. + + if (intval($channel['channel_system']) && (!$arr['item_private']) && (!$relay)) { + $local_public = true; + + if (!check_pubstream_channelallowed($sender)) { + $local_public = false; + continue; + } + + // don't allow pubstream posts if the sender even has a clone on a pubstream denied site + + $siteallowed = true; + $h = q( + "select hubloc_url from hubloc where hubloc_hash = '%s'", + dbesc($sender) + ); + if ($h) { + foreach ($h as $hub) { + if (!check_pubstream_siteallowed($hub['hubloc_url'])) { + $siteallowed = false; + break; + } + } + } + if (!$siteallowed) { + $local_public = false; + continue; + } + + $r = q( + "select xchan_selfcensored from xchan where xchan_hash = '%s' limit 1", + dbesc($sender) + ); + // don't import sys channel posts from selfcensored authors + if ($r && (intval($r[0]['xchan_selfcensored']))) { + $local_public = false; + continue; + } + if (!MessageFilter::evaluate($arr, get_config('system', 'pubstream_incl'), get_config('system', 'pubstream_excl'))) { + $local_public = false; + continue; + } + } + + // perform pre-storage check to see if it's "likely" that this is a group or collection post + + $tag_delivery = tgroup_check($channel['channel_id'], $arr); + + $perm = 'send_stream'; + if (($arr['mid'] !== $arr['parent_mid']) && ($relay)) { + $perm = 'post_comments'; + } + + // This is our own post, possibly coming from a channel clone + + if ($arr['owner_xchan'] == $d) { + $arr['item_wall'] = 1; + } else { + $arr['item_wall'] = 0; + } + + $friendofriend = false; + + if ((!$tag_delivery) && (!$local_public)) { + $allowed = (perm_is_allowed($channel['channel_id'], $sender, $perm)); + + $blocked = LibBlock::fetch($channel['channel_id'], BLOCKTYPE_SERVER); + if ($blocked) { + $h = q( + "select hubloc_url from hubloc where hubloc_hash = '%s'", + dbesc($sender) + ); + if ($h) { + foreach ($h as $hub) { + foreach ($blocked as $b) { + if (strpos($hub['hubloc_url'], $b['block_entity']) !== false) { + $allowed = false; + } + } + } + } + } + + $permit_mentions = intval(PConfig::Get($channel['channel_id'], 'system', 'permit_all_mentions') && i_am_mentioned($channel, $arr)); + + if (!$allowed) { + if ($perm === 'post_comments') { + $parent = q( + "select * from item where mid = '%s' and uid = %d limit 1", + dbesc($arr['parent_mid']), + intval($channel['channel_id']) + ); + if ($parent) { + $allowed = can_comment_on_post($sender, $parent[0]); + } + if ((!$allowed) && $permit_mentions) { + if ($parent && $parent[0]['owner_xchan'] === $channel['channel_hash']) { + $allowed = false; + } else { + $allowed = true; + } + } + if ($parent && absolutely_no_comments($parent[0])) { + $allowed = false; + } + } elseif ($permit_mentions) { + $allowed = true; + } + } + if ($request) { + // Conversation fetches (e.g. $request == true) take place for + // a) new comments on expired posts + // b) hyperdrive (friend-of-friend) conversations + + + // over-ride normal connection permissions for hyperdrive (friend-of-friend) conversations + // (if hyperdrive is enabled). + // If $allowed is already true, this is probably the conversation of a direct friend or a + // conversation fetch for a new comment on an expired post + // Comments of all these activities are allowed and will only be rejected (later) if the parent + // doesn't exist. + + if ($perm === 'send_stream') { + if (get_pconfig($channel['channel_id'], 'system', 'hyperdrive', true)) { + $allowed = true; + } + } else { + $allowed = true; + } + + $friendofriend = true; + } + + if (intval($arr['item_private']) === 2) { + if (!perm_is_allowed($channel['channel_id'], $sender, 'post_mail')) { + $allowed = false; + } + } + + if (get_abconfig($channel['channel_id'], $sender, 'system', 'block_announce', false)) { + if ($arr['verb'] === 'Announce' || strpos($arr['body'], '[/share]')) { + $allowed = false; + } + } + + if (!$allowed) { + logger("permission denied for delivery to channel {$channel['channel_id']} {$channel['channel_address']}"); + $DR->update('permission denied'); + $result[] = $DR->get(); + continue; + } + } + + if ($arr['mid'] !== $arr['parent_mid']) { + if (perm_is_allowed($channel['channel_id'], $sender, 'moderated') && $relay) { + $arr['item_blocked'] = ITEM_MODERATED; + } + + // check source route. + // We are only going to accept comments from this sender if the comment has the same route as the top-level-post, + // this is so that permissions mismatches between senders apply to the entire conversation + // As a side effect we will also do a preliminary check that we have the top-level-post, otherwise + // processing it is pointless. + + // The original author won't have a token in their copy of the message + + $prnt = ((strpos($arr['parent_mid'], 'token=') !== false) ? substr($arr['parent_mid'], 0, strpos($arr['parent_mid'], '?')) : ''); + + $r = q( + "select route, id, parent_mid, mid, owner_xchan, item_private, obj_type from item where mid = '%s' and uid = %d limit 1", + dbesc($arr['parent_mid']), + intval($channel['channel_id']) + ); + if (!$r) { + $r = q( + "select route, id, parent_mid, mid, owner_xchan, item_private, obj_type from item where mid = '%s' and uid = %d limit 1", + dbesc($prnt), + intval($channel['channel_id']) + ); + } + + if ($r) { + // if this is a multi-threaded conversation, preserve the threading information + if ($r[0]['parent_mid'] !== $r[0]['mid']) { + $arr['thr_parent'] = $arr['parent_mid']; + $arr['parent_mid'] = $r[0]['parent_mid']; + if ($act->replyto) { + q( + "update item set replyto = '%s' where id = %d", + dbesc($act->replyto), + intval($r[0]['id']) + ); + } + } + + if ($r[0]['obj_type'] === 'Question') { + // route checking doesn't work correctly here because we've changed the privacy + $r[0]['route'] = EMPTY_STR; + // If this is a poll response, convert the obj_type to our (internal-only) "Answer" type + if ($arr['obj_type'] === 'Note' && $arr['title'] && (!$arr['content'])) { + $arr['obj_type'] = 'Answer'; + } + } + } else { + // We don't seem to have a copy of this conversation or at least the parent + // - so request a copy of the entire conversation to date. + // Don't do this if it's a relay post as we're the ones who are supposed to + // have the copy and we don't want the request to loop. + // Also don't do this if this comment came from a conversation request packet. + // It's possible that comments are allowed but posting isn't and that could + // cause a conversation fetch loop. + // We'll also check the send_stream permission - because if it isn't allowed, + // the top level post is unlikely to be imported and + // this is just an exercise in futility. + + if ( + (!$relay) && (!$request) && (!$local_public) + && perm_is_allowed($channel['channel_id'], $sender, 'send_stream') + ) { + $reports = self::fetch_conversation($channel, $arr['mid']); + + // extract our delivery report from the fetched conversation + // if we can find it. + logger('fetch_report for ' . $arr['mid'], LOGGER_ALL); + logger('fetch_report: ' . print_r($reports, true), LOGGER_ALL); + + if ($reports && is_array($reports)) { + $found_report = false; + foreach ($reports as $report) { + if ($report['message_id'] === $arr['mid']) { + $found_report = true; + $DR->update($report['status']); + } + } + if (!$found_report) { + $DR->update('conversation fetch failed'); + } + } else { + $DR->update('conversation fetch failed'); + } + } else { + $DR->update('comment parent not found'); + } + $result[] = $DR->get(); + continue; + } + + + if ($relay || $friendofriend || (intval($r[0]['item_private']) === 0 && intval($arr['item_private']) === 0)) { + // reset the route in case it travelled a great distance upstream + // use our parent's route so when we go back downstream we'll match + // with whatever route our parent has. + // Also friend-of-friend conversations may have been imported without a route, + // but we are now getting comments via listener delivery + // and if there is no privacy on this or the parent, we don't care about the route, + // so just set the owner and route accordingly. + $arr['route'] = $r[0]['route']; + $arr['owner_xchan'] = $r[0]['owner_xchan']; + } else { + // going downstream check that we have the same upstream provider that + // sent it to us originally. Ignore it if it came from another source + // (with potentially different permissions). + // only compare the last hop since it could have arrived at the last location any number of ways. + // Always accept empty routes and firehose items (route contains 'undefined') . + + $existing_route = explode(',', $r[0]['route']); + $routes = count($existing_route); + if ($routes) { + $last_hop = array_pop($existing_route); + $last_prior_route = implode(',', $existing_route); + } else { + $last_hop = ''; + $last_prior_route = ''; + } + + if (in_array('undefined', $existing_route) || $last_hop == 'undefined' || $sender == 'undefined') { + $last_hop = ''; + } + + $current_route = (($arr['route']) ? $arr['route'] . ',' : '') . $sender; + + if ($last_hop && $last_hop != $sender) { + logger('comment route mismatch: parent route = ' . $r[0]['route'] . ' expected = ' . $current_route, LOGGER_DEBUG); + logger('comment route mismatch: parent msg = ' . $r[0]['id'], LOGGER_DEBUG); + $DR->update('comment route mismatch'); + $result[] = $DR->get(); + continue; + } + + // we'll add sender onto this when we deliver it. $last_prior_route now has the previously stored route + // *except* for the sender which would've been the last hop before it got to us. + + $arr['route'] = $last_prior_route; + } + } + + // This is used to fetch allow/deny rules if either the sender + // or owner is a connection. post_is_importable() evaluates all of them + $abook = q( + "select * from abook where abook_channel = %d and ( abook_xchan = '%s' OR abook_xchan = '%s' )", + intval($channel['channel_id']), + dbesc($arr['owner_xchan']), + dbesc($arr['author_xchan']) + ); + + if (isset($arr['item_deleted']) && intval($arr['item_deleted'])) { + // set these just in case we need to store a fresh copy of the deleted post. + // This could happen if the delete got here before the original post did. + + $arr['aid'] = $channel['channel_account_id']; + $arr['uid'] = $channel['channel_id']; + + $item_id = self::delete_imported_item($sender, $act, $arr, $channel['channel_id'], $relay); + $DR->update(($item_id) ? 'deleted' : 'delete_failed'); + $result[] = $DR->get(); + + if ($relay && $item_id) { + logger('process_delivery: invoking relay'); + Run::Summon(['Notifier', 'relay', intval($item_id)]); + $DR->update('relayed'); + $result[] = $DR->get(); + } + continue; + } + + // reactions such as like and dislike could have an mid with /activity/ in it. + // Check for both forms in order to prevent duplicates. + + $r = q( + "select * from item where mid in ('%s','%s') and uid = %d limit 1", + dbesc($arr['mid']), + dbesc(str_replace(z_root() . '/activity/', z_root() . '/item/', $arr['mid'])), + intval($channel['channel_id']) + ); + + if ($r) { + // We already have this post. + $item_id = $r[0]['id']; + + if (intval($r[0]['item_deleted'])) { + // It was deleted locally. + $DR->update('update ignored'); + $result[] = $DR->get(); + + continue; + } // Maybe it has been edited? + elseif ($arr['edited'] > $r[0]['edited']) { + $arr['id'] = $r[0]['id']; + $arr['uid'] = $channel['channel_id']; + if (post_is_importable($channel['channel_id'], $arr, $abook)) { + $item_result = self::update_imported_item($sender, $arr, $r[0], $channel['channel_id'], $tag_delivery); + $DR->update('updated'); + $result[] = $DR->get(); + if (!$relay) { + add_source_route($item_id, $sender); + } + } else { + $DR->update('update ignored'); + $result[] = $DR->get(); + } + } else { + $DR->update('update ignored'); + $result[] = $DR->get(); + + // We need this line to ensure wall-to-wall comments are relayed (by falling through to the relay bit), + // and at the same time not relay any other relayable posts more than once, because to do so is very wasteful. + if (!intval($r[0]['item_origin'])) { + continue; + } + } + } else { + $arr['aid'] = $channel['channel_account_id']; + $arr['uid'] = $channel['channel_id']; + + // if it's a sourced post, call the post_local hooks as if it were + // posted locally so that crosspost connectors will be triggered. + + if (check_item_source($arr['uid'], $arr)) { + /** + * @hooks post_local + * Called when an item has been posted on this machine via mod/item.php (also via API). + * * \e array with an item + */ + call_hooks('post_local', $arr); + } + + $item_id = 0; + + Activity::rewrite_mentions($arr); + + + $maxlen = get_max_import_size(); + + if ($maxlen && mb_strlen($arr['body']) > $maxlen) { + $arr['body'] = mb_substr($arr['body'], 0, $maxlen, 'UTF-8'); + logger('message length exceeds max_import_size: truncated'); + } + + if ($maxlen && mb_strlen($arr['summary']) > $maxlen) { + $arr['summary'] = mb_substr($arr['summary'], 0, $maxlen, 'UTF-8'); + logger('message summary length exceeds max_import_size: truncated'); + } + + if (post_is_importable($arr['uid'], $arr, $abook)) { + // Strip old-style hubzilla bookmarks + if (strpos($arr['body'], "#^[") !== false) { + $arr['body'] = str_replace("#^[", "[", $arr['body']); + } + + $item_result = item_store($arr); + if ($item_result['success']) { + $item_id = $item_result['item_id']; + $parr = [ + 'item_id' => $item_id, + 'item' => $arr, + 'sender' => $sender, + 'channel' => $channel + ]; + /** + * @hooks activity_received + * Called when an activity (post, comment, like, etc.) has been received from a zot source. + * * \e int \b item_id + * * \e array \b item + * * \e array \b sender + * * \e array \b channel + */ + call_hooks('activity_received', $parr); + // don't add a source route if it's a relay or later recipients will get a route mismatch + if (!$relay) { + add_source_route($item_id, $sender); + } + } + $DR->update(($item_id) ? 'posted' : 'storage failed: ' . $item_result['message']); + $result[] = $DR->get(); + } else { + $DR->update('post ignored'); + $result[] = $DR->get(); + } + } + + // preserve conversations with which you are involved from expiration + + $stored = (($item_result && $item_result['item']) ? $item_result['item'] : false); + if ( + (is_array($stored)) && ($stored['id'] != $stored['parent']) + && ($stored['author_xchan'] === $channel['channel_hash']) + ) { + retain_item($stored['item']['parent']); + } + + if ($relay && $item_id) { + logger('Invoking relay'); + Run::Summon(['Notifier', 'relay', intval($item_id)]); + $DR->addto_update('relayed'); + $result[] = $DR->get(); + } + } + + if (!$deliveries) { + $result[] = array('', 'no recipients', '', $arr['mid']); + } + + logger('Local results: ' . print_r($result, true), LOGGER_DEBUG); + + return $result; + } + + public static function hyperdrive_enabled($channel, $item) + { + + if (get_pconfig($channel['channel_id'], 'system', 'hyperdrive', true)) { + return true; + } + return false; + } + + public static function fetch_conversation($channel, $mid) + { + + // Use Zotfinger to create a signed request + + logger('fetching conversation: ' . $mid, LOGGER_DEBUG); + + $a = Zotfinger::exec($mid, $channel); + + logger('received conversation: ' . print_r($a, true), LOGGER_DATA); + + if (!$a) { + return false; + } + + if ($a['data']['type'] !== 'OrderedCollection') { + return false; + } + + $obj = new ASCollection($a['data'], $channel); + $items = $obj->get(); + + if (!$items) { + return false; + } + + $ret = []; + + + $signer = q( + "select hubloc_hash, hubloc_url from hubloc where hubloc_id_url = '%s' and hubloc_network in ('zot6','nomad') limit 1", + dbesc($a['signature']['signer']) + ); + + + foreach ($items as $activity) { + $AS = new ActivityStreams($activity); + if ( + $AS->is_valid() && $AS->type === 'Announce' && is_array($AS->obj) + && array_key_exists('object', $AS->obj) && array_key_exists('actor', $AS->obj) + ) { + // This is a relayed/forwarded Activity (as opposed to a shared/boosted object) + // Reparse the encapsulated Activity and use that instead + logger('relayed activity', LOGGER_DEBUG); + $AS = new ActivityStreams($AS->obj); + } + + if (!$AS->is_valid()) { + logger('FOF Activity rejected: ' . print_r($activity, true)); + continue; + } + $arr = Activity::decode_note($AS); + + // logger($AS->debug()); + + $r = q( + "select hubloc_hash from hubloc where hubloc_id_url = '%s' or hubloc_hash = '%s' limit 1", + dbesc($AS->actor['id']), + dbesc($AS->actor['id']) + ); + + if (!$r) { + $y = import_author_xchan(['url' => $AS->actor['id']]); + if ($y) { + $r = q( + "select hubloc_hash from hubloc where hubloc_id_url = '%s' or hubloc_hash = '%s' limit 1", + dbesc($AS->actor['id']), + dbesc($AS->actor['id']) + ); + } + if (!$r) { + logger('FOF Activity: no actor'); + continue; + } + } + + if ($AS->obj['actor'] && $AS->obj['actor']['id'] && $AS->obj['actor']['id'] !== $AS->actor['id']) { + $y = import_author_xchan(['url' => $AS->obj['actor']['id']]); + if (!$y) { + logger('FOF Activity: no object actor'); + continue; + } + } + + + if ($r) { + $arr['author_xchan'] = $r[0]['hubloc_hash']; + } + + if ($signer) { + $arr['owner_xchan'] = $signer[0]['hubloc_hash']; + } else { + $arr['owner_xchan'] = $a['signature']['signer']; + } + + if ($AS->data['hubloc'] || $arr['author_xchan'] === $arr['owner_xchan']) { + $arr['item_verified'] = true; + } + + if ($AS->data['signed_data']) { + IConfig::Set($arr, 'activitystreams', 'signed_data', $AS->data['signed_data'], false); + } + + logger('FOF Activity received: ' . print_r($arr, true), LOGGER_DATA, LOG_DEBUG); + logger('FOF Activity recipient: ' . $channel['channel_hash'], LOGGER_DATA, LOG_DEBUG); + + $result = self::process_delivery($arr['owner_xchan'], $AS, $arr, [$channel['channel_hash']], false, false, true); + if ($result) { + $ret = array_merge($ret, $result); + } + } + + return $ret; + } + + + /** + * @brief Remove community tag. + * + * @param array $sender an associative array with + * * \e string \b hash a xchan_hash + * @param array $arr an associative array + * * \e int \b verb + * * \e int \b obj_type + * * \e int \b mid + * @param int $uid + */ + + public static function remove_community_tag($sender, $arr, $uid) + { + + if (!(activity_match($arr['verb'], ACTIVITY_TAG) && ($arr['obj_type'] == ACTIVITY_OBJ_TAGTERM))) { + return; + } + + logger('remove_community_tag: invoked'); + + if (!get_pconfig($uid, 'system', 'blocktags')) { + logger('Permission denied.'); + return; + } + + $r = q( + "select * from item where mid = '%s' and uid = %d limit 1", + dbesc($arr['mid']), + intval($uid) + ); + if (!$r) { + logger('No item'); + return; + } + + if (($sender != $r[0]['owner_xchan']) && ($sender != $r[0]['author_xchan'])) { + logger('Sender not authorised.'); + return; + } + + $i = $r[0]; + + if ($i['target']) { + $i['target'] = json_decode($i['target'], true); + } + if ($i['object']) { + $i['object'] = json_decode($i['object'], true); + } + if (!($i['target'] && $i['object'])) { + logger('No target/object'); + return; + } + + $message_id = $i['target']['id']; + + $r = q( + "select id from item where mid = '%s' and uid = %d limit 1", + dbesc($message_id), + intval($uid) + ); + if (!$r) { + logger('No parent message'); + return; + } + + q( + "delete from term where uid = %d and oid = %d and otype = %d and ttype in ( %d, %d ) and term = '%s' and url = '%s'", + intval($uid), + intval($r[0]['id']), + intval(TERM_OBJ_POST), + intval(TERM_HASHTAG), + intval(TERM_COMMUNITYTAG), + dbesc($i['object']['title']), + dbesc(get_rel_link($i['object']['link'], 'alternate')) + ); + } + + /** + * @brief Updates an imported item. + * + * @param array $sender + * @param array $item + * @param array $orig + * @param int $uid + * @param bool $tag_delivery + * @see item_store_update() + * + */ + + public static function update_imported_item($sender, $item, $orig, $uid, $tag_delivery) + { + + // If this is a comment being updated, remove any privacy information + // so that item_store_update will set it from the original. + + if ($item['mid'] !== $item['parent_mid']) { + unset($item['allow_cid']); + unset($item['allow_gid']); + unset($item['deny_cid']); + unset($item['deny_gid']); + unset($item['item_private']); + } + + // we need the tag_delivery check for downstream flowing posts as the stored post + // may have a different owner than the one being transmitted. + + if (($sender != $orig['owner_xchan'] && $sender != $orig['author_xchan']) && (!$tag_delivery)) { + logger('sender is not owner or author'); + return; + } + + + $x = item_store_update($item); + + // If we're updating an event that we've saved locally, we store the item info first + // because event_addtocal will parse the body to get the 'new' event details + + if ($orig['resource_type'] === 'event') { + $res = event_addtocal($orig['id'], $uid); + if (!$res) { + logger('update event: failed'); + } + } + + if (!$x['item_id']) { + logger('update_imported_item: failed: ' . $x['message']); + } else { + logger('update_imported_item'); + } + + return $x; + } + + /** + * @brief Deletes an imported item. + * + * @param array $sender + * * \e string \b hash a xchan_hash + * @param array $item + * @param int $uid + * @param bool $relay + * @return bool|int post_id + */ + + public static function delete_imported_item($sender, $act, $item, $uid, $relay) + { + + logger('invoked', LOGGER_DEBUG); + + $ownership_valid = false; + $item_found = false; + $post_id = 0; + + if ($item['verb'] === 'Tombstone') { + // The id of the deleted thing is the item mid (activity id) + $mid = $item['mid']; + } else { + // The id is the object id if the type is Undo or Delete + $mid = ((is_array($act->obj)) ? $act->obj['id'] : $act->obj); + } + + // we may have stored either the object id or the activity id if it was a response activity (like, dislike, etc.) + + $r = q( + "select * from item where ( author_xchan = '%s' or owner_xchan = '%s' or source_xchan = '%s' ) and mid in ('%s','%s') and uid = %d limit 1", - dbesc($sender), - dbesc($sender), - dbesc($sender), - dbesc($mid), - dbesc(str_replace('/activity/','/item/',$mid)), - intval($uid) - ); + dbesc($sender), + dbesc($sender), + dbesc($sender), + dbesc($mid), + dbesc(str_replace('/activity/', '/item/', $mid)), + intval($uid) + ); - if ($r) { - $stored = $r[0]; - // we proved ownership in the sql query - $ownership_valid = true; + if ($r) { + $stored = $r[0]; + // we proved ownership in the sql query + $ownership_valid = true; - $post_id = $stored['id']; - $item_found = true; - } - else { - // this will fail with an ownership issue, so explain the real reason - logger('delete received for non-existent item or not owned by sender - ignoring.'); + $post_id = $stored['id']; + $item_found = true; + } else { + // this will fail with an ownership issue, so explain the real reason + logger('delete received for non-existent item or not owned by sender - ignoring.'); + } - } + if ($ownership_valid === false) { + logger('delete_imported_item: failed: ownership issue'); + return false; + } - if ($ownership_valid === false) { - logger('delete_imported_item: failed: ownership issue'); - return false; - } + if ($stored['resource_type'] === 'event') { + $i = q( + "SELECT * FROM event WHERE event_hash = '%s' AND uid = %d LIMIT 1", + dbesc($stored['resource_id']), + intval($uid) + ); + if ($i) { + if ($i[0]['event_xchan'] === $sender) { + q( + "delete from event where event_hash = '%s' and uid = %d", + dbesc($stored['resource_id']), + intval($uid) + ); + } else { + logger('delete linked event: not owner'); + return; + } + } + } + if ($item_found) { + if (intval($stored['item_deleted'])) { + logger('delete_imported_item: item was already deleted'); + if (!$relay) { + return false; + } - if ($stored['resource_type'] === 'event') { - $i = q("SELECT * FROM event WHERE event_hash = '%s' AND uid = %d LIMIT 1", - dbesc($stored['resource_id']), - intval($uid) - ); - if ($i) { - if ($i[0]['event_xchan'] === $sender) { - q("delete from event where event_hash = '%s' and uid = %d", - dbesc($stored['resource_id']), - intval($uid) - ); - } - else { - logger('delete linked event: not owner'); - return; - } - } - } - if ($item_found) { - if (intval($stored['item_deleted'])) { - logger('delete_imported_item: item was already deleted'); - if (! $relay) { - return false; - } - - // This is a bit hackish, but may have to suffice until the notification/delivery loop is optimised - // a bit further. We're going to strip the ITEM_ORIGIN on this item if it's a comment, because - // it was already deleted, and we're already relaying, and this ensures that no other process or - // code path downstream can relay it again (causing a loop). Since it's already gone it's not coming - // back, and we aren't going to (or shouldn't at any rate) delete it again in the future - so losing - // this information from the metadata should have no other discernible impact. + // This is a bit hackish, but may have to suffice until the notification/delivery loop is optimised + // a bit further. We're going to strip the ITEM_ORIGIN on this item if it's a comment, because + // it was already deleted, and we're already relaying, and this ensures that no other process or + // code path downstream can relay it again (causing a loop). Since it's already gone it's not coming + // back, and we aren't going to (or shouldn't at any rate) delete it again in the future - so losing + // this information from the metadata should have no other discernible impact. - if (($stored['id'] != $stored['parent']) && intval($stored['item_origin'])) { - q("update item set item_origin = 0 where id = %d and uid = %d", - intval($stored['id']), - intval($stored['uid']) - ); - } - } - else { - if ($stored['id'] !== $stored['parent']) { - q("update item set commented = '%s', changed = '%s' where id = %d", - dbesc(datetime_convert()), - dbesc(datetime_convert()), - intval($stored['parent']) - ); - } - } + if (($stored['id'] != $stored['parent']) && intval($stored['item_origin'])) { + q( + "update item set item_origin = 0 where id = %d and uid = %d", + intval($stored['id']), + intval($stored['uid']) + ); + } + } else { + if ($stored['id'] !== $stored['parent']) { + q( + "update item set commented = '%s', changed = '%s' where id = %d", + dbesc(datetime_convert()), + dbesc(datetime_convert()), + intval($stored['parent']) + ); + } + } - // Use phased deletion to set the deleted flag, call both tag_deliver and the notifier to notify downstream channels - // and then clean up after ourselves with a cron job after several days to do the delete_item_lowlevel() (DROPITEM_PHASE2). + // Use phased deletion to set the deleted flag, call both tag_deliver and the notifier to notify downstream channels + // and then clean up after ourselves with a cron job after several days to do the delete_item_lowlevel() (DROPITEM_PHASE2). - drop_item($post_id, false, DROPITEM_PHASE1); - tag_deliver($uid, $post_id); - } + drop_item($post_id, false, DROPITEM_PHASE1); + tag_deliver($uid, $post_id); + } - return $post_id; - } + return $post_id; + } - /** - * @brief Processes delivery of profile. - * - * @see import_directory_profile() - * @param array $sender an associative array - * * \e string \b hash a xchan_hash - * @param array $arr - * @param array $deliveries (unused) - */ + /** + * @brief Processes delivery of profile. + * + * @param array $sender an associative array + * * \e string \b hash a xchan_hash + * @param array $arr + * @param array $deliveries (unused) + * @see import_directory_profile() + */ - static function process_profile_delivery($sender, $arr, $deliveries) { + public static function process_profile_delivery($sender, $arr, $deliveries) + { - logger('process_profile_delivery', LOGGER_DEBUG); + logger('process_profile_delivery', LOGGER_DEBUG); - $r = q("select xchan_addr from xchan where xchan_hash = '%s' limit 1", - dbesc($sender['hash']) - ); - if ($r) { - Libzotdir::import_directory_profile($sender, $arr, $r[0]['xchan_addr'], UPDATE_FLAGS_UPDATED, 0); - } - } + $r = q( + "select xchan_addr from xchan where xchan_hash = '%s' limit 1", + dbesc($sender['hash']) + ); + if ($r) { + Libzotdir::import_directory_profile($sender, $arr, $r[0]['xchan_addr'], UPDATE_FLAGS_UPDATED, 0); + } + } - /** - * @brief Checks for a moved channel and sets the channel_moved flag. - * - * Currently the effect of this flag is to turn the channel into 'read-only' mode. - * New content will not be processed (there was still an issue with blocking the - * ability to post comments as of 10-Mar-2016). - * We do not physically remove the channel at this time. The hub admin may choose - * to do so, but is encouraged to allow a grace period of several days in case there - * are any issues migrating content. This packet will generally be received by the - * original site when the basic channel import has been processed. - * - * This will only be executed on the old location - * if a new location is reported and there is only one location record. - * The rest of the hubloc syncronisation will be handled within - * sync_locations - * - * @param string $sender_hash A channel hash - * @param array $locations - */ + /** + * @brief + * + * @param array $sender an associative array + * * \e string \b hash a xchan_hash + * @param array $arr + * @param array $deliveries (unused) deliveries is irrelevant + */ + public static function process_location_delivery($sender, $arr, $deliveries) + { - static function check_location_move($sender_hash, $locations) { + // deliveries is irrelevant + logger('process_location_delivery', LOGGER_DEBUG); - if (! $locations) { - return; - } + $r = q( + "select * from xchan where xchan_hash = '%s' limit 1", + dbesc($sender) + ); + if ($r) { + $xchan = ['id' => $r[0]['xchan_guid'], 'id_sig' => $r[0]['xchan_guid_sig'], + 'hash' => $r[0]['xchan_hash'], 'public_key' => $r[0]['xchan_pubkey']]; + } + if (array_key_exists('locations', $arr) && $arr['locations']) { + $x = Libsync::sync_locations($xchan, $arr, true); + logger('results: ' . print_r($x, true), LOGGER_DEBUG); + if ($x['changed']) { + $guid = random_string() . '@' . App::get_hostname(); + Libzotdir::update_modtime($sender, $r[0]['xchan_guid'], $arr['locations'][0]['address'], UPDATE_FLAGS_UPDATED); + } + } + } - if (count($locations) != 1) { - return; - } + /** + * @brief Checks for a moved channel and sets the channel_moved flag. + * + * Currently the effect of this flag is to turn the channel into 'read-only' mode. + * New content will not be processed (there was still an issue with blocking the + * ability to post comments as of 10-Mar-2016). + * We do not physically remove the channel at this time. The hub admin may choose + * to do so, but is encouraged to allow a grace period of several days in case there + * are any issues migrating content. This packet will generally be received by the + * original site when the basic channel import has been processed. + * + * This will only be executed on the old location + * if a new location is reported and there is only one location record. + * The rest of the hubloc syncronisation will be handled within + * sync_locations + * + * @param string $sender_hash A channel hash + * @param array $locations + */ - $loc = $locations[0]; + public static function check_location_move($sender_hash, $locations) + { - $r = q("select * from channel where channel_hash = '%s' limit 1", - dbesc($sender_hash) - ); + if (!$locations) { + return; + } - if (! $r) { - return; - } + if (count($locations) != 1) { + return; + } - if ($loc['url'] !== z_root()) { - $x = q("update channel set channel_moved = '%s' where channel_hash = '%s' limit 1", - dbesc($loc['url']), - dbesc($sender_hash) - ); + $loc = $locations[0]; - // federation plugins may wish to notify connections - // of the move on singleton networks + $r = q( + "select * from channel where channel_hash = '%s' limit 1", + dbesc($sender_hash) + ); - $arr = [ - 'channel' => $r[0], - 'locations' => $locations - ]; - /** - * @hooks location_move - * Called when a new location has been provided to a UNO channel (indicating a move rather than a clone). - * * \e array \b channel - * * \e array \b locations - */ - call_hooks('location_move', $arr); - } - } + if (!$r) { + return; + } + + if ($loc['url'] !== z_root()) { + $x = q( + "update channel set channel_moved = '%s' where channel_hash = '%s' limit 1", + dbesc($loc['url']), + dbesc($sender_hash) + ); + + // federation plugins may wish to notify connections + // of the move on singleton networks + + $arr = [ + 'channel' => $r[0], + 'locations' => $locations + ]; + /** + * @hooks location_move + * Called when a new location has been provided to a UNO channel (indicating a move rather than a clone). + * * \e array \b channel + * * \e array \b locations + */ + call_hooks('location_move', $arr); + } + } + /** + * @brief Returns an array with all known distinct hubs for this channel. + * + * @param array $channel an associative array which must contain + * * \e string \b channel_hash the hash of the channel + * @return array an array with associative arrays + * @see self::get_hublocs() + */ - /** - * @brief Returns an array with all known distinct hubs for this channel. - * - * @see self::get_hublocs() - * @param array $channel an associative array which must contain - * * \e string \b channel_hash the hash of the channel - * @return array an array with associative arrays - */ + public static function encode_locations($channel) + { + $ret = []; - static function encode_locations($channel) { - $ret = []; + $x = self::get_hublocs($channel['channel_hash']); - $x = self::get_hublocs($channel['channel_hash']); + if ($x && count($x)) { + foreach ($x as $hub) { + // if this is a local channel that has been deleted, the hubloc is no good + // - make sure it is marked deleted so that nobody tries to use it. - if ($x && count($x)) { - foreach ($x as $hub) { + if (intval($channel['channel_removed']) && $hub['hubloc_url'] === z_root()) { + $hub['hubloc_deleted'] = 1; + } - // if this is a local channel that has been deleted, the hubloc is no good - // - make sure it is marked deleted so that nobody tries to use it. + $ret[] = [ + 'host' => $hub['hubloc_host'], + 'address' => $hub['hubloc_addr'], + 'id_url' => $hub['hubloc_id_url'], + 'primary' => (intval($hub['hubloc_primary']) ? true : false), + 'url' => $hub['hubloc_url'], + 'url_sig' => $hub['hubloc_url_sig'], + 'site_id' => $hub['hubloc_site_id'], + 'callback' => $hub['hubloc_callback'], + 'sitekey' => $hub['hubloc_sitekey'], + 'deleted' => (intval($hub['hubloc_deleted']) ? true : false) + ]; + } + } - if (intval($channel['channel_removed']) && $hub['hubloc_url'] === z_root()) { - $hub['hubloc_deleted'] = 1; - } - - $ret[] = [ - 'host' => $hub['hubloc_host'], - 'address' => $hub['hubloc_addr'], - 'id_url' => $hub['hubloc_id_url'], - 'primary' => (intval($hub['hubloc_primary']) ? true : false), - 'url' => $hub['hubloc_url'], - 'url_sig' => $hub['hubloc_url_sig'], - 'site_id' => $hub['hubloc_site_id'], - 'callback' => $hub['hubloc_callback'], - 'sitekey' => $hub['hubloc_sitekey'], - 'driver' => $hub['hubloc_network'], - 'deleted' => (intval($hub['hubloc_deleted']) ? true : false) - ]; - } - } - - return $ret; - } + return $ret; + } - /** - * @brief - * - * @param array $arr - * @param string $pubkey - * @return boolean true if updated or inserted - */ - - static function import_site($arr) { + /** + * @brief + * + * @param array $arr + * @param string $pubkey + * @return bool true if updated or inserted + */ - if ( (! is_array($arr)) || (! $arr['url']) || (! $arr['site_sig'])) { - return false; - } + public static function import_site($arr) + { - if (! self::verify($arr['url'], $arr['site_sig'], $arr['sitekey'])) { - logger('Bad url_sig'); - return false; - } + if ((!is_array($arr)) || (!$arr['url']) || (!$arr['site_sig'])) { + return false; + } - $update = false; - $exists = false; + if (!self::verify($arr['url'], $arr['site_sig'], $arr['sitekey'])) { + logger('Bad url_sig'); + return false; + } - $r = q("select * from site where site_url = '%s' limit 1", - dbesc($arr['url']) - ); - if ($r) { - $exists = true; - $siterecord = $r[0]; - } + $update = false; + $exists = false; - $site_directory = 0; - if ($arr['directory_mode'] == 'normal') { - $site_directory = DIRECTORY_MODE_NORMAL; - } - if ($arr['directory_mode'] == 'primary') { - $site_directory = DIRECTORY_MODE_PRIMARY; - } - if ($arr['directory_mode'] == 'secondary') { - $site_directory = DIRECTORY_MODE_SECONDARY; - } - if ($arr['directory_mode'] == 'standalone') { - $site_directory = DIRECTORY_MODE_STANDALONE; - } + $r = q( + "select * from site where site_url = '%s' limit 1", + dbesc($arr['url']) + ); + if ($r) { + $exists = true; + $siterecord = $r[0]; + } - $register_policy = 0; - if ($arr['register_policy'] == 'closed') { - $register_policy = REGISTER_CLOSED; - } - if ($arr['register_policy'] == 'open') { - $register_policy = REGISTER_OPEN; - } - if ($arr['register_policy'] == 'approve') { - $register_policy = REGISTER_APPROVE; - } + $site_directory = 0; + if ($arr['directory_mode'] == 'normal') { + $site_directory = DIRECTORY_MODE_NORMAL; + } + if ($arr['directory_mode'] == 'primary') { + $site_directory = DIRECTORY_MODE_PRIMARY; + } + if ($arr['directory_mode'] == 'secondary') { + $site_directory = DIRECTORY_MODE_SECONDARY; + } + if ($arr['directory_mode'] == 'standalone') { + $site_directory = DIRECTORY_MODE_STANDALONE; + } - $access_policy = 0; - if (array_key_exists('access_policy',$arr)) { - if ($arr['access_policy'] === 'private') { - $access_policy = ACCESS_PRIVATE; - } - if ($arr['access_policy'] === 'paid') { - $access_policy = ACCESS_PAID; - } - if ($arr['access_policy'] === 'free') { - $access_policy = ACCESS_FREE; - } - if ($arr['access_policy'] === 'tiered') { - $access_policy = ACCESS_TIERED; - } - } + $register_policy = 0; + if ($arr['register_policy'] == 'closed') { + $register_policy = REGISTER_CLOSED; + } + if ($arr['register_policy'] == 'open') { + $register_policy = REGISTER_OPEN; + } + if ($arr['register_policy'] == 'approve') { + $register_policy = REGISTER_APPROVE; + } - // don't let insecure sites register as public hubs + $access_policy = 0; + if (array_key_exists('access_policy', $arr)) { + if ($arr['access_policy'] === 'private') { + $access_policy = ACCESS_PRIVATE; + } + if ($arr['access_policy'] === 'paid') { + $access_policy = ACCESS_PAID; + } + if ($arr['access_policy'] === 'free') { + $access_policy = ACCESS_FREE; + } + if ($arr['access_policy'] === 'tiered') { + $access_policy = ACCESS_TIERED; + } + } - if (strpos($arr['url'],'https://') === false) { - $access_policy = ACCESS_PRIVATE; - } + // don't let insecure sites register as public hubs - if ($access_policy != ACCESS_PRIVATE) { - $x = z_fetch_url($arr['url'] . '/siteinfo.json'); - if (! $x['success']) - $access_policy = ACCESS_PRIVATE; - } + if (strpos($arr['url'], 'https://') === false) { + $access_policy = ACCESS_PRIVATE; + } - $site_about = EMPTY_STR; - $site_logo = EMPTY_STR; - $sitename = EMPTY_STR; + if ($access_policy != ACCESS_PRIVATE) { + $x = z_fetch_url($arr['url'] . '/siteinfo.json'); + if (!$x['success']) { + $access_policy = ACCESS_PRIVATE; + } + } - $directory_url = htmlspecialchars(isset($arr['directory_url']) ? $arr['directory_url'] : EMPTY_STR,ENT_COMPAT,'UTF-8',false); - $url = htmlspecialchars(strtolower($arr['url']),ENT_COMPAT,'UTF-8',false); - $sellpage = htmlspecialchars($arr['sellpage'],ENT_COMPAT,'UTF-8',false); - $site_location = htmlspecialchars($arr['location'],ENT_COMPAT,'UTF-8',false); - $site_realm = htmlspecialchars($arr['realm'],ENT_COMPAT,'UTF-8',false); - $sitename = htmlspecialchars($arr['sitename'],ENT_COMPAT,'UTF-8',false); - $site_project = htmlspecialchars($arr['project'],ENT_COMPAT,'UTF-8',false); - $site_crypto = ((array_key_exists('encryption',$arr) && is_array($arr['encryption'])) ? htmlspecialchars(implode(',',$arr['encryption']),ENT_COMPAT,'UTF-8',false) : ''); - $site_version = ((array_key_exists('version',$arr)) ? htmlspecialchars($arr['version'],ENT_COMPAT,'UTF-8',false) : ''); - if (array_key_exists('about',$arr) && $arr['about']) { - $site_about = html2bbcode(purify_html($arr['about'])); - } - if (array_key_exists('logo',$arr) && $arr['logo']) { - $site_logo = $arr['logo']; - } - elseif (file_exists('images/' . strtolower($site_project) . '.png')) { - $site_logo = z_root() . '/images/' . strtolower($site_project) . '.png'; - } - else { - $site_logo = z_root() . '/images/default_profile_photos/red_koala_trans/300.png'; - } - - set_sconfig($url,'system','about', $site_about); - set_sconfig($url,'system','logo', $site_logo); - set_sconfig($url,'system','sitename', $sitename); - - $site_flags = $site_directory; + $site_about = EMPTY_STR; + $site_logo = EMPTY_STR; + $sitename = EMPTY_STR; - if (array_key_exists('zot',$arr)) { - set_sconfig($arr['url'],'system','zot_version',$arr['zot']); - } + $directory_url = htmlspecialchars(isset($arr['directory_url']) ? $arr['directory_url'] : EMPTY_STR, ENT_COMPAT, 'UTF-8', false); + $url = htmlspecialchars(strtolower($arr['url']), ENT_COMPAT, 'UTF-8', false); + $sellpage = htmlspecialchars($arr['sellpage'], ENT_COMPAT, 'UTF-8', false); + $site_location = htmlspecialchars($arr['location'], ENT_COMPAT, 'UTF-8', false); + $site_realm = htmlspecialchars($arr['realm'], ENT_COMPAT, 'UTF-8', false); + $sitename = htmlspecialchars($arr['sitename'], ENT_COMPAT, 'UTF-8', false); + $site_project = htmlspecialchars($arr['project'], ENT_COMPAT, 'UTF-8', false); + $site_crypto = ((array_key_exists('encryption', $arr) && is_array($arr['encryption'])) ? htmlspecialchars(implode(',', $arr['encryption']), ENT_COMPAT, 'UTF-8', false) : ''); + $site_version = ((array_key_exists('version', $arr)) ? htmlspecialchars($arr['version'], ENT_COMPAT, 'UTF-8', false) : ''); + if (array_key_exists('about', $arr) && $arr['about']) { + $site_about = html2bbcode(purify_html($arr['about'])); + } + if (array_key_exists('logo', $arr) && $arr['logo']) { + $site_logo = $arr['logo']; + } elseif (file_exists('images/' . strtolower($site_project) . '.png')) { + $site_logo = z_root() . '/images/' . strtolower($site_project) . '.png'; + } else { + $site_logo = z_root() . '/images/default_profile_photos/red_koala_trans/300.png'; + } - if ($exists) { - if (($siterecord['site_flags'] != $site_flags) - || ($siterecord['site_access'] != $access_policy) - || ($siterecord['site_directory'] != $directory_url) - || ($siterecord['site_sellpage'] != $sellpage) - || ($siterecord['site_location'] != $site_location) - || ($siterecord['site_register'] != $register_policy) - || ($siterecord['site_project'] != $site_project) - || ($siterecord['site_realm'] != $site_realm) - || ($siterecord['site_crypto'] != $site_crypto) - || ($siterecord['site_version'] != $site_version) ) { + set_sconfig($url, 'system', 'about', $site_about); + set_sconfig($url, 'system', 'logo', $site_logo); + set_sconfig($url, 'system', 'sitename', $sitename); - $update = true; + $site_flags = $site_directory; - // logger('import_site: input: ' . print_r($arr,true)); - // logger('import_site: stored: ' . print_r($siterecord,true)); + if (array_key_exists('zot', $arr)) { + set_sconfig($arr['url'], 'system', 'zot_version', $arr['zot']); + } - $r = q("update site set site_dead = 0, site_location = '%s', site_flags = %d, site_access = %d, site_directory = '%s', site_register = %d, site_update = '%s', site_sellpage = '%s', site_realm = '%s', site_type = %d, site_project = '%s', site_version = '%s', site_crypto = '%s' + if ($exists) { + if ( + ($siterecord['site_flags'] != $site_flags) + || ($siterecord['site_access'] != $access_policy) + || ($siterecord['site_directory'] != $directory_url) + || ($siterecord['site_sellpage'] != $sellpage) + || ($siterecord['site_location'] != $site_location) + || ($siterecord['site_register'] != $register_policy) + || ($siterecord['site_project'] != $site_project) + || ($siterecord['site_realm'] != $site_realm) + || ($siterecord['site_crypto'] != $site_crypto) + || ($siterecord['site_version'] != $site_version) + ) { + $update = true; + + // logger('import_site: input: ' . print_r($arr,true)); + // logger('import_site: stored: ' . print_r($siterecord,true)); + + $r = q( + "update site set site_dead = 0, site_location = '%s', site_flags = %d, site_access = %d, site_directory = '%s', site_register = %d, site_update = '%s', site_sellpage = '%s', site_realm = '%s', site_type = %d, site_project = '%s', site_version = '%s', site_crypto = '%s' where site_url = '%s'", - dbesc($site_location), - intval($site_flags), - intval($access_policy), - dbesc($directory_url), - intval($register_policy), - dbesc(datetime_convert()), - dbesc($sellpage), - dbesc($site_realm), - intval(SITE_TYPE_ZOT), - dbesc($site_project), - dbesc($site_version), - dbesc($site_crypto), - dbesc($url) - ); - if (! $r) { - logger('Update failed. ' . print_r($arr,true)); - } - } - else { - // update the timestamp to indicate we communicated with this site - q("update site set site_dead = 0, site_update = '%s' where site_url = '%s'", - dbesc(datetime_convert()), - dbesc($url) - ); - } - } - else { - $update = true; + dbesc($site_location), + intval($site_flags), + intval($access_policy), + dbesc($directory_url), + intval($register_policy), + dbesc(datetime_convert()), + dbesc($sellpage), + dbesc($site_realm), + intval(SITE_TYPE_ZOT), + dbesc($site_project), + dbesc($site_version), + dbesc($site_crypto), + dbesc($url) + ); + if (!$r) { + logger('Update failed. ' . print_r($arr, true)); + } + } else { + // update the timestamp to indicate we communicated with this site + q( + "update site set site_dead = 0, site_update = '%s' where site_url = '%s'", + dbesc(datetime_convert()), + dbesc($url) + ); + } + } else { + $update = true; - $r = site_store_lowlevel( - [ - 'site_location' => $site_location, - 'site_url' => $url, - 'site_access' => intval($access_policy), - 'site_flags' => intval($site_flags), - 'site_update' => datetime_convert(), - 'site_directory' => $directory_url, - 'site_register' => intval($register_policy), - 'site_sellpage' => $sellpage, - 'site_realm' => $site_realm, - 'site_type' => intval(SITE_TYPE_ZOT), - 'site_project' => $site_project, - 'site_version' => $site_version, - 'site_crypto' => $site_crypto - ] - ); + $r = site_store_lowlevel( + [ + 'site_location' => $site_location, + 'site_url' => $url, + 'site_access' => intval($access_policy), + 'site_flags' => intval($site_flags), + 'site_update' => datetime_convert(), + 'site_directory' => $directory_url, + 'site_register' => intval($register_policy), + 'site_sellpage' => $sellpage, + 'site_realm' => $site_realm, + 'site_type' => intval(SITE_TYPE_ZOT), + 'site_project' => $site_project, + 'site_version' => $site_version, + 'site_crypto' => $site_crypto + ] + ); - if (! $r) { - logger('Record create failed. ' . print_r($arr,true)); - } - } + if (!$r) { + logger('Record create failed. ' . print_r($arr, true)); + } + } - return $update; - } + return $update; + } - /** - * @brief Returns path to /rpost - * - * @todo We probably should make rpost discoverable. - * - * @param array $observer - * * \e string \b xchan_url - * @return string - */ - static function get_rpost_path($observer) { - if (! $observer) { - return EMPTY_STR; - } + /** + * @brief Returns path to /rpost + * + * @param array $observer + * * \e string \b xchan_url + * @return string + * @todo We probably should make rpost discoverable. + * + */ + public static function get_rpost_path($observer) + { + if (!$observer) { + return EMPTY_STR; + } - $parsed = parse_url($observer['xchan_url']); + $parsed = parse_url($observer['xchan_url']); - return $parsed['scheme'] . '://' . $parsed['host'] . (($parsed['port']) ? ':' . $parsed['port'] : '') . '/rpost?f='; - } + return $parsed['scheme'] . '://' . $parsed['host'] . (($parsed['port']) ? ':' . $parsed['port'] : '') . '/rpost?f='; + } - /** - * @brief - * - * @param array $x - * @return boolean|string return false or a hash - */ + /** + * @brief + * + * @param array $x + * @return bool|string return false or a hash + */ - static function import_author_zot($x) { + public static function import_author_zot($x) + { - // Check that we have both a hubloc and xchan record - as occasionally storage calls will fail and - // we may only end up with one; which results in posts with no author name or photo and are a bit - // of a hassle to repair. If either or both are missing, do a full discovery probe. + // Check that we have both a hubloc and xchan record - as occasionally storage calls will fail and + // we may only end up with one; which results in posts with no author name or photo and are a bit + // of a hassle to repair. If either or both are missing, do a full discovery probe. - if (! array_key_exists('id',$x)) { - return import_author_activitypub($x); - } + if (!array_key_exists('id', $x)) { + return import_author_activitypub($x); + } - $hash = self::make_xchan_hash($x['id'],$x['key']); + $hash = self::make_xchan_hash($x['id'], $x['key']); - $desturl = $x['url']; + $desturl = $x['url']; - $found_primary = false; + $found_primary = false; - $r1 = q("select hubloc_url, hubloc_updated, site_dead from hubloc left join site on + $r1 = q( + "select hubloc_url, hubloc_updated, site_dead from hubloc left join site on hubloc_url = site_url where hubloc_guid = '%s' and hubloc_guid_sig = '%s' and hubloc_primary = 1 limit 1", - dbesc($x['id']), - dbesc($x['id_sig']) - ); - if ($r1) { - $found_primary = true; - } + dbesc($x['id']), + dbesc($x['id_sig']) + ); + if ($r1) { + $found_primary = true; + } - $r2 = q("select xchan_hash from xchan where xchan_guid = '%s' and xchan_guid_sig = '%s' limit 1", - dbesc($x['id']), - dbesc($x['id_sig']) - ); + $r2 = q( + "select xchan_hash from xchan where xchan_guid = '%s' and xchan_guid_sig = '%s' limit 1", + dbesc($x['id']), + dbesc($x['id_sig']) + ); - $primary_dead = false; + $primary_dead = false; - if ($r1 && intval($r1[0]['site_dead'])) { - $primary_dead = true; - } + if ($r1 && intval($r1[0]['site_dead'])) { + $primary_dead = true; + } - // We have valid and somewhat fresh information. Always true if it is our own site. + // We have valid and somewhat fresh information. Always true if it is our own site. - if ($r1 && $r2 && ( $r1[0]['hubloc_updated'] > datetime_convert('UTC','UTC','now - 1 week') || $r1[0]['hubloc_url'] === z_root() ) ) { - logger('in cache', LOGGER_DEBUG); - return $hash; - } + if ($r1 && $r2 && ($r1[0]['hubloc_updated'] > datetime_convert('UTC', 'UTC', 'now - 1 week') || $r1[0]['hubloc_url'] === z_root())) { + logger('in cache', LOGGER_DEBUG); + return $hash; + } - logger('not in cache or cache stale - probing: ' . print_r($x,true), LOGGER_DEBUG,LOG_INFO); + logger('not in cache or cache stale - probing: ' . print_r($x, true), LOGGER_DEBUG, LOG_INFO); - // The primary hub may be dead. Try to find another one associated with this identity that is - // still alive. If we find one, use that url for the discovery/refresh probe. Otherwise, the dead site - // is all we have and there is no point probing it. Just return the hash indicating we have a - // cached entry and the identity is valid. It's just unreachable until they bring back their - // server from the grave or create another clone elsewhere. + // The primary hub may be dead. Try to find another one associated with this identity that is + // still alive. If we find one, use that url for the discovery/refresh probe. Otherwise, the dead site + // is all we have and there is no point probing it. Just return the hash indicating we have a + // cached entry and the identity is valid. It's just unreachable until they bring back their + // server from the grave or create another clone elsewhere. - if ($primary_dead || ! $found_primary) { - logger('dead or site - ignoring', LOGGER_DEBUG,LOG_INFO); + if ($primary_dead || !$found_primary) { + logger('dead or site - ignoring', LOGGER_DEBUG, LOG_INFO); - $r = q("select hubloc_id_url from hubloc left join site on hubloc_url = site_url + $r = q( + "select hubloc_id_url from hubloc left join site on hubloc_url = site_url where hubloc_hash = '%s' and site_dead = 0", - dbesc($hash) - ); - if ($r) { - logger('found another site that is not dead: ' . $r[0]['hubloc_id_url'], LOGGER_DEBUG,LOG_INFO); - $desturl = $r[0]['hubloc_id_url']; - } - else { - return $hash; - } - } + dbesc($hash) + ); + if ($r) { + logger('found another site that is not dead: ' . $r[0]['hubloc_id_url'], LOGGER_DEBUG, LOG_INFO); + $desturl = $r[0]['hubloc_id_url']; + } else { + return $hash; + } + } - $them = [ 'hubloc_id_url' => $desturl ]; - if (self::refresh($them)) { - return $hash; - } + $them = ['hubloc_id_url' => $desturl]; + if (self::refresh($them)) { + return $hash; + } - return false; - } + return false; + } - static function zotinfo($arr) { + public static function zotinfo($arr) + { - $ret = []; + $ret = []; - $zhash = ((x($arr,'guid_hash')) ? $arr['guid_hash'] : ''); - $zguid = ((x($arr,'guid')) ? $arr['guid'] : ''); - $zguid_sig = ((x($arr,'guid_sig')) ? $arr['guid_sig'] : ''); - $zaddr = ((x($arr,'address')) ? $arr['address'] : ''); - $ztarget = ((x($arr,'target_url')) ? $arr['target_url'] : ''); - $zsig = ((x($arr,'target_sig')) ? $arr['target_sig'] : ''); - $zkey = ((x($arr,'key')) ? $arr['key'] : ''); - $mindate = ((x($arr,'mindate')) ? $arr['mindate'] : ''); - $token = ((x($arr,'token')) ? $arr['token'] : ''); - $feed = ((x($arr,'feed')) ? intval($arr['feed']) : 0); + $zhash = ((x($arr, 'guid_hash')) ? $arr['guid_hash'] : ''); + $zguid = ((x($arr, 'guid')) ? $arr['guid'] : ''); + $zguid_sig = ((x($arr, 'guid_sig')) ? $arr['guid_sig'] : ''); + $zaddr = ((x($arr, 'address')) ? $arr['address'] : ''); + $ztarget = ((x($arr, 'target_url')) ? $arr['target_url'] : ''); + $zsig = ((x($arr, 'target_sig')) ? $arr['target_sig'] : ''); + $zkey = ((x($arr, 'key')) ? $arr['key'] : ''); + $mindate = ((x($arr, 'mindate')) ? $arr['mindate'] : ''); + $token = ((x($arr, 'token')) ? $arr['token'] : ''); + $feed = ((x($arr, 'feed')) ? intval($arr['feed']) : 0); - if ($ztarget) { - $t = q("select * from hubloc where hubloc_id_url = '%s' and hubloc_network in ('nomad','zot6') limit 1", - dbesc($ztarget) - ); - if ($t) { - $ztarget_hash = $t[0]['hubloc_hash']; - } - else { - - // should probably perform discovery of the requestor (target) but if they actually had - // permissions we would know about them and we only want to know who they are to - // enumerate their specific permissions - - $ztarget_hash = EMPTY_STR; - } - } + if ($ztarget) { + $t = q("select * from hubloc where hubloc_id_url = '%s' and hubloc_network in ('nomad','zot6') limit 1", + dbesc($ztarget) + ); + if ($t) { + $ztarget_hash = $t[0]['hubloc_hash']; + } else { + // should probably perform discovery of the requestor (target) but if they actually had + // permissions we would know about them and we only want to know who they are to + // enumerate their specific permissions - $r = null; + $ztarget_hash = EMPTY_STR; + } + } - if (strlen($zhash)) { - $r = q("select channel.*, xchan.* from channel left join xchan on channel_hash = xchan_hash + $r = null; + + if (strlen($zhash)) { + $r = q( + "select channel.*, xchan.* from channel left join xchan on channel_hash = xchan_hash where channel_hash = '%s' limit 1", - dbesc($zhash) - ); - } - elseif (strlen($zguid) && strlen($zguid_sig)) { - $r = q("select channel.*, xchan.* from channel left join xchan on channel_hash = xchan_hash + dbesc($zhash) + ); + } elseif (strlen($zguid) && strlen($zguid_sig)) { + $r = q( + "select channel.*, xchan.* from channel left join xchan on channel_hash = xchan_hash where channel_guid = '%s' and channel_guid_sig = '%s' limit 1", - dbesc($zguid), - dbesc($zguid_sig) - ); - } - elseif (strlen($zaddr)) { - if (strpos($zaddr,'[system]') === false) { /* normal address lookup */ - $r = q("select channel.*, xchan.* from channel left join xchan on channel_hash = xchan_hash + dbesc($zguid), + dbesc($zguid_sig) + ); + } elseif (strlen($zaddr)) { + if (strpos($zaddr, '[system]') === false) { /* normal address lookup */ + $r = q( + "select channel.*, xchan.* from channel left join xchan on channel_hash = xchan_hash where ( channel_address = '%s' or xchan_addr = '%s' ) limit 1", - dbesc($zaddr), - dbesc($zaddr) - ); - } - else { + dbesc($zaddr), + dbesc($zaddr) + ); + } else { - /** - * The special address '[system]' will return a system channel if one has been defined, - * Or the first valid channel we find if there are no system channels. - * - * This is used by magic-auth if we have no prior communications with this site - and - * returns an identity on this site which we can use to create a valid hub record so that - * we can exchange signed messages. The precise identity is irrelevant. It's the hub - * information that we really need at the other end - and this will return it. - * - */ + /** + * The special address '[system]' will return a system channel if one has been defined, + * Or the first valid channel we find if there are no system channels. + * + * This is used by magic-auth if we have no prior communications with this site - and + * returns an identity on this site which we can use to create a valid hub record so that + * we can exchange signed messages. The precise identity is irrelevant. It's the hub + * information that we really need at the other end - and this will return it. + * + */ - $r = q("select channel.*, xchan.* from channel left join xchan on channel_hash = xchan_hash + $r = q("select channel.*, xchan.* from channel left join xchan on channel_hash = xchan_hash where channel_system = 1 order by channel_id limit 1"); - if (! $r) { - $r = q("select channel.*, xchan.* from channel left join xchan on channel_hash = xchan_hash + if (!$r) { + $r = q("select channel.*, xchan.* from channel left join xchan on channel_hash = xchan_hash where channel_removed = 0 order by channel_id limit 1"); - } - } - } - else { - $ret['message'] = 'Invalid request'; - return($ret); - } - - if (! $r) { - $ret['message'] = 'Item not found.'; - return($ret); - } - - $e = $r[0]; - - $id = $e['channel_id']; - - $sys_channel = (intval($e['channel_system']) ? true : false); - $special_channel = (($e['channel_pageflags'] & PAGE_PREMIUM) ? true : false); - $adult_channel = (($e['channel_pageflags'] & PAGE_ADULT) ? true : false); - $censored = (($e['channel_pageflags'] & PAGE_CENSORED) ? true : false); - $searchable = (($e['channel_pageflags'] & PAGE_HIDDEN) ? false : true); - $deleted = (intval($e['xchan_deleted']) ? true : false); - - if ($deleted || $censored || $sys_channel) { - $searchable = false; - } - - // now all forums (public, restricted, and private) set the public_forum flag. So it really means "is a group" - // and has nothing to do with accessibility. - - $role = get_pconfig($e['channel_id'],'system','permissions_role'); - $rolesettings = PermissionRoles::role_perms($role); - - $channel_type = isset($rolesettings['channel_type']) ? $rolesettings['channel_type'] : 'normal'; - - // This is for birthdays and keywords, but must check access permissions - $p = q("select * from profile where uid = %d and is_default = 1", - intval($e['channel_id']) - ); - - $profile = []; - - if ($p) { - - if (! intval($p[0]['publish'])) - $searchable = false; - - $profile['description'] = $p[0]['pdesc']; - $profile['birthday'] = $p[0]['dob']; - if (($profile['birthday'] != '0000-00-00') && (($bd = z_birthday($p[0]['dob'],$e['channel_timezone'])) !== '')) { - $profile['next_birthday'] = $bd; - } - - if ($age = age($p[0]['dob'],$e['channel_timezone'],'')) { - $profile['age'] = $age; - } - $profile['gender'] = $p[0]['gender']; - $profile['marital'] = $p[0]['marital']; - $profile['sexual'] = $p[0]['sexual']; - $profile['locale'] = $p[0]['locality']; - $profile['region'] = $p[0]['region']; - $profile['postcode'] = $p[0]['postal_code']; - $profile['country'] = $p[0]['country_name']; - $profile['about'] = ((is_sys_channel($e['channel_id'])) ? get_config('system','siteinfo') : $p[0]['about']); - $profile['homepage'] = $p[0]['homepage']; - $profile['hometown'] = $p[0]['hometown']; - - if ($p[0]['keywords']) { - $tags = []; - $k = explode(' ',$p[0]['keywords']); - if ($k) { - foreach ($k as $kk) { - if (trim($kk," \t\n\r\0\x0B,")) { - $tags[] = trim($kk," \t\n\r\0\x0B,"); - } - } - } - if ($tags) { - $profile['keywords'] = $tags; - } - } - } - - $cover_photo = get_cover_photo($e['channel_id'],'array'); - - // Communication details - - $ret['id'] = $e['xchan_guid']; - $ret['id_sig'] = self::sign($e['xchan_guid'], $e['channel_prvkey']); - - $ret['primary_location'] = [ - 'address' => $e['xchan_addr'], - 'url' => $e['xchan_url'], - 'connections_url' => $e['xchan_connurl'], - 'follow_url' => $e['xchan_follow'], - 'wall' => z_root() . '/outbox/' . $e['channel_address'], - 'followers' => z_root() . '/followers/' . $e['channel_address'], - 'following' => z_root() . '/following/' . $e['channel_address'] - ]; - - $ret['public_key'] = $e['xchan_pubkey']; - $ret['signing_algorithm'] = 'rsa-sha256'; - $ret['username'] = $e['channel_address']; - $ret['name'] = $e['xchan_name']; - $ret['name_updated'] = $e['xchan_name_date']; - $ret['photo'] = [ - 'url' => $e['xchan_photo_l'], - 'type' => $e['xchan_photo_mimetype'], - 'updated' => $e['xchan_photo_date'] - ]; - - if ($cover_photo) { - $ret['cover_photo'] = [ - 'url' => $cover_photo['url'], - 'type' => $cover_photo['type'], - 'updated' => $cover_photo['updated'] - ]; - } - - $ret['channel_role'] = get_pconfig($e['channel_id'],'system','permissions_role','custom'); - $ret['channel_type'] = $channel_type; - $ret['protocols'] = [ 'nomad', 'zot6' ]; - if (get_pconfig($e['channel_id'],'system','activitypub',get_config('system','activitypub', ACTIVITYPUB_ENABLED))) { - $ret['protocols'][] = 'activitypub'; - } - $ret['searchable'] = $searchable; - $ret['adult_content'] = $adult_channel; - - $ret['comments'] = map_scope(PermissionLimits::Get($e['channel_id'],'post_comments')); - - if ($deleted) { - $ret['deleted'] = $deleted; - } - - if (intval($e['channel_removed'])) { - $ret['deleted_locally'] = true; - } - - // premium or other channel desiring some contact with potential followers before connecting. - // This is a template - %s will be replaced with the follow_url we discover for the return channel. - - if ($special_channel) { - $ret['connect_url'] = (($e['xchan_connpage']) ? $e['xchan_connpage'] : z_root() . '/connect/' . $e['channel_address']); - } - - // This is a template for our follow url, %s will be replaced with a webbie - if (! isset($ret['follow_url'])) { - $ret['follow_url'] = z_root() . '/follow?f=&url=%s'; - } - - $permissions = get_all_perms($e['channel_id'],$ztarget_hash,false); - - if ($ztarget_hash) { - $permissions['connected'] = false; - $b = q("select * from abook where abook_xchan = '%s' and abook_channel = %d limit 1", - dbesc($ztarget_hash), - intval($e['channel_id']) - ); - if ($b) { - $permissions['connected'] = true; - } - } - - if ($permissions['view_profile']) { - $ret['profile'] = $profile; - } - - - $concise_perms = []; - if ($permissions) { - foreach ($permissions as $k => $v) { - if ($v) { - $concise_perms[] = $k; - } - } - $permissions = implode(',',$concise_perms); - } - - $ret['permissions'] = $permissions; - $ret['permissions_for'] = $ztarget; - - - // array of (verified) hubs this channel uses - - $x = self::encode_locations($e); - if ($x) { - $ret['locations'] = $x; - } - $ret['site'] = self::site_info(); - - call_hooks('zotinfo',$ret); - - return($ret); - - } - - - static function site_info($force = false) { - - $signing_key = get_config('system','prvkey'); - $sig_method = get_config('system','signature_algorithm','sha256'); - - $ret = []; - $ret['site'] = []; - $ret['site']['url'] = z_root(); - $ret['site']['site_sig'] = self::sign(z_root(), $signing_key); - $ret['site']['post'] = z_root() . '/zot'; - $ret['site']['openWebAuth'] = z_root() . '/owa'; - $ret['site']['authRedirect'] = z_root() . '/magic'; - $ret['site']['sitekey'] = get_config('system','pubkey'); - - $dirmode = get_config('system','directory_mode'); - if (($dirmode === false) || ($dirmode == DIRECTORY_MODE_NORMAL)) { - $ret['site']['directory_mode'] = 'normal'; - } - - if ($dirmode == DIRECTORY_MODE_PRIMARY) { - $ret['site']['directory_mode'] = 'primary'; - } - elseif ($dirmode == DIRECTORY_MODE_SECONDARY) { - $ret['site']['directory_mode'] = 'secondary'; - } - elseif ($dirmode == DIRECTORY_MODE_STANDALONE) { - $ret['site']['directory_mode'] = 'standalone'; - } - if ($dirmode != DIRECTORY_MODE_NORMAL) { - $ret['site']['directory_url'] = z_root() . '/dirsearch'; - } - - - $ret['site']['encryption'] = Crypto::methods(); - - // hide detailed site information if you're off the grid - - if ($dirmode != DIRECTORY_MODE_STANDALONE || $force) { - - $register_policy = intval(get_config('system','register_policy')); - - if ($register_policy == REGISTER_CLOSED) { - $ret['site']['register_policy'] = 'closed'; - } - if ($register_policy == REGISTER_APPROVE) { - $ret['site']['register_policy'] = 'approve'; - } - if ($register_policy == REGISTER_OPEN) { - $ret['site']['register_policy'] = 'open'; - } - - $access_policy = intval(get_config('system','access_policy')); - - if ($access_policy == ACCESS_PRIVATE) { - $ret['site']['access_policy'] = 'private'; - } - if ($access_policy == ACCESS_PAID) { - $ret['site']['access_policy'] = 'paid'; - } - if ($access_policy == ACCESS_FREE) { - $ret['site']['access_policy'] = 'free'; - } - if ($access_policy == ACCESS_TIERED) { - $ret['site']['access_policy'] = 'tiered'; - } - - $ret['site']['admin'] = get_config('system','admin_email'); - - $visible_plugins = []; - if (is_array(App::$plugins) && count(App::$plugins)) { - $r = q("select * from addon where hidden = 0"); - if ($r) { - foreach ($r as $rr) { - $visible_plugins[] = $rr['aname']; - } - } - } - - $ret['site']['about'] = bbcode(get_config('system','siteinfo'), [ 'export' => true ]); - $ret['site']['plugins'] = $visible_plugins; - $ret['site']['sitehash'] = get_config('system','location_hash'); - $ret['site']['sellpage'] = get_config('system','sellpage'); - $ret['site']['location'] = get_config('system','site_location'); - $ret['site']['sitename'] = System::get_site_name(); - $ret['site']['logo'] = System::get_site_icon(); - $ret['site']['project'] = System::get_platform_name(); - $ret['site']['version'] = System::get_project_version(); - $ret['site']['protocol_version'] = ZOT_REVISION; - } - - return $ret['site']; - - } - - /** - * @brief - * - * @param array $hub - * @param string $sitekey (optional, default empty) - * - * @return string hubloc_url - */ - - static function update_hub_connected($hub, $site_id = '') { - - if ($site_id) { - - /* - * This hub has now been proven to be valid. - * Any hub with the same URL and a different sitekey cannot be valid. - * Get rid of them (mark them deleted). There's a good chance they were re-installs. - */ - - q("update hubloc set hubloc_deleted = 1, hubloc_error = 1 where hubloc_hash = '%s' and hubloc_url = '%s' and hubloc_site_id != '%s' ", - dbesc($hub['hubloc_hash']), - dbesc($hub['hubloc_url']), - dbesc($site_id) - ); - - } - else { - $site_id = $hub['hubloc_site_id']; - } - - // $sender['sitekey'] is a new addition to the protocol to distinguish - // hublocs coming from re-installed sites. Older sites will not provide - // this field and we have to still mark them valid, since we can't tell - // if this hubloc has the same sitekey as the packet we received. - // Update our DB to show when we last communicated successfully with this hub - // This will allow us to prune dead hubs from using up resources - - $t = datetime_convert('UTC', 'UTC', 'now - 15 minutes'); - - $r = q("update hubloc set hubloc_connected = '%s' where hubloc_id = %d and hubloc_site_id = '%s' and hubloc_connected < '%s' ", - dbesc(datetime_convert()), - intval($hub['hubloc_id']), - dbesc($site_id), - dbesc($t) - ); - - // a dead hub came back to life - reset any tombstones we might have - - if (intval($hub['hubloc_error']) || intval($hub['hubloc_deleted'])) { - q("update hubloc set hubloc_error = 0, hubloc_deleted = 0 where hubloc_id = %d and hubloc_site_id = '%s' ", - intval($hub['hubloc_id']), - dbesc($site_id) - ); - if (intval($hub['hubloc_orphancheck'])) { - q("update hubloc set hubloc_orphancheck = 0 where hubloc_id = %d and hubloc_site_id = '%s' ", - intval($hub['hubloc_id']), - dbesc($site_id) - ); - } - q("update xchan set xchan_orphan = 0 where xchan_orphan = 1 and xchan_hash = '%s'", - dbesc($hub['hubloc_hash']) - ); - } - - // this site obviously isn't dead because they are trying to communicate with us. - q("update site set site_dead = 0 where site_dead = 1 and site_url = '%s' ", - dbesc($hub['hubloc_url']) - ); - - return $hub['hubloc_url']; - } - - - static function sign($data,$key,$alg = 'sha256') { - if (! $key) { - return 'no key'; - } - $sig = ''; - openssl_sign($data,$sig,$key,$alg); - return $alg . '.' . base64url_encode($sig); - } - - static function verify($data,$sig,$key) { - - $verify = 0; - - $x = explode('.',$sig,2); - - if ($key && count($x) === 2) { - $alg = $x[0]; - $signature = base64url_decode($x[1]); - - $verify = @openssl_verify($data,$signature,$key,$alg); - - if ($verify === (-1)) { - while ($msg = openssl_error_string()) { - logger('openssl_verify: ' . $msg,LOGGER_NORMAL,LOG_ERR); - } - btlogger('openssl_verify: key: ' . $key, LOGGER_DEBUG, LOG_ERR); - } - } - return(($verify > 0) ? true : false); - } - - - - static function is_zot_request() { - + } + } + } else { + $ret['message'] = 'Invalid request'; + return ($ret); + } + + if (!$r) { + $ret['message'] = 'Item not found.'; + return ($ret); + } + + $e = $r[0]; + + $id = $e['channel_id']; + + $sys_channel = (intval($e['channel_system']) ? true : false); + $special_channel = (($e['channel_pageflags'] & PAGE_PREMIUM) ? true : false); + $adult_channel = (($e['channel_pageflags'] & PAGE_ADULT) ? true : false); + $censored = (($e['channel_pageflags'] & PAGE_CENSORED) ? true : false); + $searchable = (($e['channel_pageflags'] & PAGE_HIDDEN) ? false : true); + $deleted = (intval($e['xchan_deleted']) ? true : false); + + if ($deleted || $censored || $sys_channel) { + $searchable = false; + } + + // now all forums (public, restricted, and private) set the public_forum flag. So it really means "is a group" + // and has nothing to do with accessibility. + + $role = get_pconfig($e['channel_id'], 'system', 'permissions_role'); + $rolesettings = PermissionRoles::role_perms($role); + + $channel_type = isset($rolesettings['channel_type']) ? $rolesettings['channel_type'] : 'normal'; + + // This is for birthdays and keywords, but must check access permissions + $p = q( + "select * from profile where uid = %d and is_default = 1", + intval($e['channel_id']) + ); + + $profile = []; + + if ($p) { + if (!intval($p[0]['publish'])) { + $searchable = false; + } + + $profile['description'] = $p[0]['pdesc']; + $profile['birthday'] = $p[0]['dob']; + if (($profile['birthday'] != '0000-00-00') && (($bd = z_birthday($p[0]['dob'], $e['channel_timezone'])) !== '')) { + $profile['next_birthday'] = $bd; + } + + if ($age = age($p[0]['dob'], $e['channel_timezone'], '')) { + $profile['age'] = $age; + } + $profile['gender'] = $p[0]['gender']; + $profile['marital'] = $p[0]['marital']; + $profile['sexual'] = $p[0]['sexual']; + $profile['locale'] = $p[0]['locality']; + $profile['region'] = $p[0]['region']; + $profile['postcode'] = $p[0]['postal_code']; + $profile['country'] = $p[0]['country_name']; + $profile['about'] = ((is_sys_channel($e['channel_id'])) ? get_config('system', 'siteinfo') : $p[0]['about']); + $profile['homepage'] = $p[0]['homepage']; + $profile['hometown'] = $p[0]['hometown']; + + if ($p[0]['keywords']) { + $tags = []; + $k = explode(' ', $p[0]['keywords']); + if ($k) { + foreach ($k as $kk) { + if (trim($kk, " \t\n\r\0\x0B,")) { + $tags[] = trim($kk, " \t\n\r\0\x0B,"); + } + } + } + if ($tags) { + $profile['keywords'] = $tags; + } + } + } + + $cover_photo = get_cover_photo($e['channel_id'], 'array'); + + // Communication details + + $ret['id'] = $e['xchan_guid']; + $ret['id_sig'] = self::sign($e['xchan_guid'], $e['channel_prvkey']); + + $ret['primary_location'] = [ + 'address' => $e['xchan_addr'], + 'url' => $e['xchan_url'], + 'connections_url' => $e['xchan_connurl'], + 'follow_url' => $e['xchan_follow'], + 'wall' => z_root() . '/outbox/' . $e['channel_address'], + 'followers' => z_root() . '/followers/' . $e['channel_address'], + 'following' => z_root() . '/following/' . $e['channel_address'] + ]; + + $ret['public_key'] = $e['xchan_pubkey']; + $ret['signing_algorithm'] = 'rsa-sha256'; + $ret['username'] = $e['channel_address']; + $ret['name'] = $e['xchan_name']; + $ret['name_updated'] = $e['xchan_name_date']; + $ret['photo'] = [ + 'url' => $e['xchan_photo_l'], + 'type' => $e['xchan_photo_mimetype'], + 'updated' => $e['xchan_photo_date'] + ]; + + if ($cover_photo) { + $ret['cover_photo'] = [ + 'url' => $cover_photo['url'], + 'type' => $cover_photo['type'], + 'updated' => $cover_photo['updated'] + ]; + } + + $ret['channel_role'] = get_pconfig($e['channel_id'], 'system', 'permissions_role', 'custom'); + $ret['channel_type'] = $channel_type; + $ret['protocols'] = ['nomad','zot6']; + if (get_pconfig($e['channel_id'], 'system', 'activitypub', get_config('system', 'activitypub', ACTIVITYPUB_ENABLED))) { + $ret['protocols'][] = 'activitypub'; + } + $ret['searchable'] = $searchable; + $ret['adult_content'] = $adult_channel; + + $ret['comments'] = map_scope(PermissionLimits::Get($e['channel_id'], 'post_comments')); + + if ($deleted) { + $ret['deleted'] = $deleted; + } + + if (intval($e['channel_removed'])) { + $ret['deleted_locally'] = true; + } + + // premium or other channel desiring some contact with potential followers before connecting. + // This is a template - %s will be replaced with the follow_url we discover for the return channel. + + if ($special_channel) { + $ret['connect_url'] = (($e['xchan_connpage']) ? $e['xchan_connpage'] : z_root() . '/connect/' . $e['channel_address']); + } + + // This is a template for our follow url, %s will be replaced with a webbie + if (!isset($ret['follow_url'])) { + $ret['follow_url'] = z_root() . '/follow?f=&url=%s'; + } + + $permissions = get_all_perms($e['channel_id'], $ztarget_hash, false); + + if ($ztarget_hash) { + $permissions['connected'] = false; + $b = q( + "select * from abook where abook_xchan = '%s' and abook_channel = %d limit 1", + dbesc($ztarget_hash), + intval($e['channel_id']) + ); + if ($b) { + $permissions['connected'] = true; + } + } + + if ($permissions['view_profile']) { + $ret['profile'] = $profile; + } + + + $concise_perms = []; + if ($permissions) { + foreach ($permissions as $k => $v) { + if ($v) { + $concise_perms[] = $k; + } + } + $permissions = implode(',', $concise_perms); + } + + $ret['permissions'] = $permissions; + $ret['permissions_for'] = $ztarget; + + + // array of (verified) hubs this channel uses + + $x = self::encode_locations($e); + if ($x) { + $ret['locations'] = $x; + } + $ret['site'] = self::site_info(); + + call_hooks('zotinfo', $ret); + + return ($ret); + } + + + public static function site_info($force = false) + { + + $signing_key = get_config('system', 'prvkey'); + $sig_method = get_config('system', 'signature_algorithm', 'sha256'); + + $ret = []; + $ret['site'] = []; + $ret['site']['url'] = z_root(); + $ret['site']['site_sig'] = self::sign(z_root(), $signing_key); + $ret['site']['post'] = z_root() . '/zot'; + $ret['site']['openWebAuth'] = z_root() . '/owa'; + $ret['site']['authRedirect'] = z_root() . '/magic'; + $ret['site']['sitekey'] = get_config('system', 'pubkey'); + + $dirmode = get_config('system', 'directory_mode'); + if (($dirmode === false) || ($dirmode == DIRECTORY_MODE_NORMAL)) { + $ret['site']['directory_mode'] = 'normal'; + } + + if ($dirmode == DIRECTORY_MODE_PRIMARY) { + $ret['site']['directory_mode'] = 'primary'; + } elseif ($dirmode == DIRECTORY_MODE_SECONDARY) { + $ret['site']['directory_mode'] = 'secondary'; + } elseif ($dirmode == DIRECTORY_MODE_STANDALONE) { + $ret['site']['directory_mode'] = 'standalone'; + } + if ($dirmode != DIRECTORY_MODE_NORMAL) { + $ret['site']['directory_url'] = z_root() . '/dirsearch'; + } + + + $ret['site']['encryption'] = Crypto::methods(); + $ret['site']['zot'] = System::get_zot_revision(); + + // hide detailed site information if you're off the grid + + if ($dirmode != DIRECTORY_MODE_STANDALONE || $force) { + $register_policy = intval(get_config('system', 'register_policy')); + + if ($register_policy == REGISTER_CLOSED) { + $ret['site']['register_policy'] = 'closed'; + } + if ($register_policy == REGISTER_APPROVE) { + $ret['site']['register_policy'] = 'approve'; + } + if ($register_policy == REGISTER_OPEN) { + $ret['site']['register_policy'] = 'open'; + } + + $access_policy = intval(get_config('system', 'access_policy')); + + if ($access_policy == ACCESS_PRIVATE) { + $ret['site']['access_policy'] = 'private'; + } + if ($access_policy == ACCESS_PAID) { + $ret['site']['access_policy'] = 'paid'; + } + if ($access_policy == ACCESS_FREE) { + $ret['site']['access_policy'] = 'free'; + } + if ($access_policy == ACCESS_TIERED) { + $ret['site']['access_policy'] = 'tiered'; + } + + $ret['site']['admin'] = get_config('system', 'admin_email'); + + $visible_plugins = []; + if (is_array(App::$plugins) && count(App::$plugins)) { + $r = q("select * from addon where hidden = 0"); + if ($r) { + foreach ($r as $rr) { + $visible_plugins[] = $rr['aname']; + } + } + } + + $ret['site']['about'] = bbcode(get_config('system', 'siteinfo'), ['export' => true]); + $ret['site']['plugins'] = $visible_plugins; + $ret['site']['sitehash'] = get_config('system', 'location_hash'); + $ret['site']['sellpage'] = get_config('system', 'sellpage'); + $ret['site']['location'] = get_config('system', 'site_location'); + $ret['site']['realm'] = get_directory_realm(); + $ret['site']['sitename'] = System::get_site_name(); + $ret['site']['logo'] = System::get_site_icon(); + $ret['site']['project'] = System::get_platform_name(); + $ret['site']['version'] = System::get_project_version(); + } + + return $ret['site']; + } + + /** + * @brief + * + * @param array $hub + * @param string $sitekey (optional, default empty) + * + * @return string hubloc_url + */ + + public static function update_hub_connected($hub, $site_id = '') + { + + if ($site_id) { + /* + * This hub has now been proven to be valid. + * Any hub with the same URL and a different sitekey cannot be valid. + * Get rid of them (mark them deleted). There's a good chance they were re-installs. + */ + + q( + "update hubloc set hubloc_deleted = 1, hubloc_error = 1 where hubloc_hash = '%s' and hubloc_url = '%s' and hubloc_site_id != '%s' ", + dbesc($hub['hubloc_hash']), + dbesc($hub['hubloc_url']), + dbesc($site_id) + ); + } else { + $site_id = $hub['hubloc_site_id']; + } + + // $sender['sitekey'] is a new addition to the protocol to distinguish + // hublocs coming from re-installed sites. Older sites will not provide + // this field and we have to still mark them valid, since we can't tell + // if this hubloc has the same sitekey as the packet we received. + // Update our DB to show when we last communicated successfully with this hub + // This will allow us to prune dead hubs from using up resources + + $t = datetime_convert('UTC', 'UTC', 'now - 15 minutes'); + + $r = q( + "update hubloc set hubloc_connected = '%s' where hubloc_id = %d and hubloc_site_id = '%s' and hubloc_connected < '%s' ", + dbesc(datetime_convert()), + intval($hub['hubloc_id']), + dbesc($site_id), + dbesc($t) + ); + + // a dead hub came back to life - reset any tombstones we might have + + if (intval($hub['hubloc_error']) || intval($hub['hubloc_deleted'])) { + q( + "update hubloc set hubloc_error = 0, hubloc_deleted = 0 where hubloc_id = %d and hubloc_site_id = '%s' ", + intval($hub['hubloc_id']), + dbesc($site_id) + ); + if (intval($hub['hubloc_orphancheck'])) { + q( + "update hubloc set hubloc_orphancheck = 0 where hubloc_id = %d and hubloc_site_id = '%s' ", + intval($hub['hubloc_id']), + dbesc($site_id) + ); + } + q( + "update xchan set xchan_orphan = 0 where xchan_orphan = 1 and xchan_hash = '%s'", + dbesc($hub['hubloc_hash']) + ); + } + + // this site obviously isn't dead because they are trying to communicate with us. + q( + "update site set site_dead = 0 where site_dead = 1 and site_url = '%s' ", + dbesc($hub['hubloc_url']) + ); + + return $hub['hubloc_url']; + } + + + public static function sign($data, $key, $alg = 'sha256') + { + if (!$key) { + return 'no key'; + } + $sig = ''; + openssl_sign($data, $sig, $key, $alg); + return $alg . '.' . base64url_encode($sig); + } + + public static function verify($data, $sig, $key) + { + + $verify = 0; + + $x = explode('.', $sig, 2); + + if ($key && count($x) === 2) { + $alg = $x[0]; + $signature = base64url_decode($x[1]); + + $verify = @openssl_verify($data, $signature, $key, $alg); + + if ($verify === (-1)) { + while ($msg = openssl_error_string()) { + logger('openssl_verify: ' . $msg, LOGGER_NORMAL, LOG_ERR); + } + btlogger('openssl_verify: key: ' . $key, LOGGER_DEBUG, LOG_ERR); + } + } + return (($verify > 0) ? true : false); + } + + + public static function is_zot_request() + { $x = getBestSupportedMimeType([ 'application/x-zot+json', 'application/x-nomad+json' ]); - return(($x) ? true : false); - } + return (($x) ? true : false); + } - static public function zot_record_preferred($arr, $check = 'hubloc_network') { - - if (! $arr) { - return $arr; - } + public static function zot_record_preferred($arr, $check = 'hubloc_network') + { + if (!$arr) { + return $arr; + } foreach ($arr as $v) { if($v[$check] === 'nomad') { return $v; } } - foreach ($arr as $v) { - if($v[$check] === 'zot6') { - return $v; - } - } - return $arr[0]; - } - - static function update_cached_hubloc($hubloc) { - if ($hubloc['hubloc_updated'] > datetime_convert('UTC','UTC','now - 1 week') || $hubloc['hubloc_url'] === z_root()) { - return; - } - self::refresh( [ 'hubloc_id_url' => $hubloc['hubloc_id_url'] ] ); - } + foreach ($arr as $v) { + if ($v[$check] === 'zot6') { + return $v; + } + } + return $arr[0]; + } + public static function update_cached_hubloc($hubloc) + { + if ($hubloc['hubloc_updated'] > datetime_convert('UTC', 'UTC', 'now - 1 week') || $hubloc['hubloc_url'] === z_root()) { + return; + } + self::refresh(['hubloc_id_url' => $hubloc['hubloc_id_url']]); + } } diff --git a/Zotlabs/Lib/Libzotdir.php b/Zotlabs/Lib/Libzotdir.php index 89beaca0d..cad64cbfd 100644 --- a/Zotlabs/Lib/Libzotdir.php +++ b/Zotlabs/Lib/Libzotdir.php @@ -2,6 +2,7 @@ namespace Zotlabs\Lib; +use App; use Zotlabs\Lib\Libzot; use Zotlabs\Lib\Webfinger; use Zotlabs\Lib\Zotfinger; @@ -9,332 +10,359 @@ use Zotlabs\Lib\Zotfinger; require_once('include/permissions.php'); -class Libzotdir { +class Libzotdir +{ - /** - * Directories may come and go over time. We will need to check that our - * directory server is still valid occasionally, and reset to something that - * is if our directory has gone offline for any reason - */ + /** + * Directories may come and go over time. We will need to check that our + * directory server is still valid occasionally, and reset to something that + * is if our directory has gone offline for any reason + */ - static function check_upstream_directory() { + public static function check_upstream_directory() + { - $directory = get_config('system', 'directory_server'); + $directory = get_config('system', 'directory_server'); - // it's possible there is no directory server configured and the local hub is being used. - // If so, default to preserving the absence of a specific server setting. + // it's possible there is no directory server configured and the local hub is being used. + // If so, default to preserving the absence of a specific server setting. - $isadir = true; + $isadir = true; - if ($directory) { - $j = Zotfinger::exec($directory); - if (array_path_exists('data/directory_mode',$j)) { - if ($j['data']['directory_mode'] === 'normal') { - $isadir = false; - } - } + if ($directory) { + $j = Zotfinger::exec($directory); + if (array_path_exists('data/directory_mode', $j)) { + if ($j['data']['directory_mode'] === 'normal') { + $isadir = false; + } + } + } + + if (!$isadir) { + set_config('system', 'directory_server', ''); + } + } + + + public static function get_directory_setting($observer, $setting) + { + + + if ($observer) { + $ret = get_xconfig($observer, 'directory', $setting); + } else { + $ret = ((array_key_exists($setting, $_SESSION)) ? intval($_SESSION[$setting]) : false); + } + + if ($ret === false) { + $ret = get_config('directory', $setting); + if ($ret === false) { + $ret = (in_array($setting, ['globaldir', 'safemode', 'activedir']) ? 1 : 0); + } + } + + if ($setting === 'globaldir' && intval(get_config('system', 'localdir_hide'))) { + $ret = 1; + } + + return $ret; + } + + /** + * @brief Called by the directory_sort widget. + */ + public static function dir_sort_links() + { + + $safe_mode = 1; + + $observer = get_observer_hash(); + + $safe_mode = self::get_directory_setting($observer, 'safemode'); + $globaldir = self::get_directory_setting($observer, 'globaldir'); + $pubforums = self::get_directory_setting($observer, 'chantype'); + $activedir = self::get_directory_setting($observer, 'activedir'); + + $hide_local = intval(get_config('system', 'localdir_hide')); + if ($hide_local) { + $globaldir = 1; + } + + // Build urls without order and pubforums so it's easy to tack on the changed value + // Probably there's an easier way to do this + + $directory_sort_order = get_config('system', 'directory_sort_order'); + if (!$directory_sort_order) { + $directory_sort_order = 'date'; + } + + $current_order = (($_REQUEST['order']) ? $_REQUEST['order'] : $directory_sort_order); + $suggest = (($_REQUEST['suggest']) ? '&suggest=' . $_REQUEST['suggest'] : ''); + + $url = 'directory?f='; + + $tmp = array_merge($_GET, $_POST); + unset($tmp['suggest']); + unset($tmp['pubforums']); + unset($tmp['type']); + unset($tmp['global']); + unset($tmp['safe']); + unset($tmp['active']); + unset($tmp['req']); + unset($tmp['f']); + $q = http_build_query($tmp); + $forumsurl = $url . (($q) ? '&' . $q : '') . $suggest; + + $o = replace_macros(get_markup_template('dir_sort_links.tpl'), [ + '$header' => t('Directory Options'), + '$forumsurl' => $forumsurl, + '$safemode' => array('safemode', t('Safe Mode'), $safe_mode, '', array(t('No'), t('Yes')), ' onchange=\'window.location.href="' . $forumsurl . '&safe="+(this.checked ? 1 : 0)\''), + '$pubforums' => array('pubforums', t('Groups Only'), (($pubforums == 1) ? true : false), '', array(t('No'), t('Yes')), ' onchange=\'window.location.href="' . $forumsurl . '&type="+(this.checked ? 1 : 0)\''), +// '$collections' => array('collections', t('Collections Only'),(($pubforums == 2) ? true : false),'',array(t('No'), t('Yes')),' onchange=\'window.location.href="' . $forumsurl . '&type="+(this.checked ? 2 : 0)\''), + '$hide_local' => $hide_local, + '$globaldir' => array('globaldir', t('This Website Only'), 1 - intval($globaldir), '', array(t('No'), t('Yes')), ' onchange=\'window.location.href="' . $forumsurl . '&global="+(this.checked ? 0 : 1)\''), + '$activedir' => array('activedir', t('Recently Updated'), intval($activedir), '', array(t('No'), t('Yes')), ' onchange=\'window.location.href="' . $forumsurl . '&active="+(this.checked ? 1 : 0)\''), + ]); + + return $o; + } + + /** + * @brief + * + * Given an update record, probe the channel, grab a zot-info packet and refresh/sync the data. + * + * Ignore updating records marked as deleted. + * + * If successful, sets ud_last in the DB to the current datetime for this + * reddress/webbie. + * + * @param array $ud Entry from update table + */ + + public static function update_directory_entry($ud) + { + + logger('update_directory_entry: ' . print_r($ud, true), LOGGER_DATA); + + if ($ud['ud_addr'] && (!($ud['ud_flags'] & UPDATE_FLAGS_DELETED))) { + $success = false; + + $href = Webfinger::zot_url(punify($ud['ud_addr'])); + if ($href) { + $zf = Zotfinger::exec($href); + } + if (is_array($zf) && array_path_exists('signature/signer', $zf) && $zf['signature']['signer'] === $href && intval($zf['signature']['header_valid'])) { + $xc = Libzot::import_xchan($zf['data'], 0, $ud); + } else { + q( + "update updates set ud_last = '%s' where ud_addr = '%s'", + dbesc(datetime_convert()), + dbesc($ud['ud_addr']) + ); + } + } + } + + + /** + * @brief Push local channel updates to a local directory server. + * + * This is called from Zotlabs/Daemon/Directory.php if a profile is to be pushed to the + * directory and the local hub in this case is any kind of directory server. + * + * @param int $uid + * @param bool $force + */ + + public static function local_dir_update($uid, $force) + { + + + logger('local_dir_update: uid: ' . $uid, LOGGER_DEBUG); + + $p = q( + "select channel_hash, channel_address, channel_timezone, profile.* from profile left join channel on channel_id = uid where uid = %d and is_default = 1", + intval($uid) + ); + + $profile = []; + $profile['encoding'] = 'zot'; + + if ($p) { + $hash = $p[0]['channel_hash']; + + $profile['description'] = $p[0]['pdesc']; + $profile['birthday'] = $p[0]['dob']; + if ($age = age($p[0]['dob'], $p[0]['channel_timezone'], '')) { + $profile['age'] = $age; + } + + $profile['gender'] = $p[0]['gender']; + $profile['marital'] = $p[0]['marital']; + $profile['sexual'] = $p[0]['sexual']; + $profile['locale'] = $p[0]['locality']; + $profile['region'] = $p[0]['region']; + $profile['postcode'] = $p[0]['postal_code']; + $profile['country'] = $p[0]['country_name']; + $profile['about'] = $p[0]['about']; + $profile['homepage'] = $p[0]['homepage']; + $profile['hometown'] = $p[0]['hometown']; + + if ($p[0]['keywords']) { + $tags = []; + $k = explode(' ', $p[0]['keywords']); + if ($k) { + foreach ($k as $kk) { + if (trim($kk)) { + $tags[] = trim($kk); + } + } + } + + if ($tags) { + $profile['keywords'] = $tags; + } + } + + $hidden = (1 - intval($p[0]['publish'])); + + // logger('hidden: ' . $hidden); + + $r = q( + "select xchan_hidden from xchan where xchan_hash = '%s' limit 1", + dbesc($p[0]['channel_hash']) + ); + + if (intval($r[0]['xchan_hidden']) != $hidden) { + $r = q( + "update xchan set xchan_hidden = %d where xchan_hash = '%s'", + intval($hidden), + dbesc($p[0]['channel_hash']) + ); + } + + $arr = ['channel_id' => $uid, 'hash' => $hash, 'profile' => $profile]; + call_hooks('local_dir_update', $arr); + + $address = channel_reddress($p[0]); + + if (perm_is_allowed($uid, '', 'view_profile')) { + self::import_directory_profile($hash, $arr['profile'], $address, 0); + } else { + // they may have made it private + $r = q( + "delete from xprof where xprof_hash = '%s'", + dbesc($hash) + ); + $r = q( + "delete from xtag where xtag_hash = '%s'", + dbesc($hash) + ); + } + } + + $ud_hash = random_string() . '@' . App::get_hostname(); + self::update_modtime($hash, $ud_hash, channel_reddress($p[0]), (($force) ? UPDATE_FLAGS_FORCED : UPDATE_FLAGS_UPDATED)); + } + + + /** + * @brief Imports a directory profile. + * + * @param string $hash + * @param array $profile + * @param string $addr + * @param number $ud_flags (optional) UPDATE_FLAGS_UPDATED + * @param number $suppress_update (optional) default 0 + * @return bool $updated if something changed + */ + + public static function import_directory_profile($hash, $profile, $addr, $ud_flags = UPDATE_FLAGS_UPDATED, $suppress_update = 0) + { + + logger('import_directory_profile', LOGGER_DEBUG); + if (!$hash) { + return false; + } + + + $maxlen = get_max_import_size(); + + if ($maxlen && mb_strlen($profile['about']) > $maxlen) { + $profile['about'] = mb_substr($profile['about'], 0, $maxlen, 'UTF-8'); + } + + $arr = []; + + $arr['xprof_hash'] = $hash; + if (isset($profile['birthday'])) { + $arr['xprof_dob'] = (($profile['birthday'] === '0000-00-00') + ? $profile['birthday'] + : datetime_convert('', '', $profile['birthday'], 'Y-m-d')); // !!!! check this for 0000 year } + $arr['xprof_age'] = (isset($profile['age']) ? intval($profile['age']) : 0); + $arr['xprof_desc'] = ((isset($profile['description']) && $profile['description']) ? htmlspecialchars($profile['description'], ENT_COMPAT, 'UTF-8', false) : ''); + $arr['xprof_gender'] = ((isset($profile['gender']) && $profile['gender']) ? htmlspecialchars($profile['gender'], ENT_COMPAT, 'UTF-8', false) : ''); + $arr['xprof_marital'] = ((isset($profile['marital']) && $profile['marital']) ? htmlspecialchars($profile['marital'], ENT_COMPAT, 'UTF-8', false) : ''); + $arr['xprof_sexual'] = ((isset($profile['sexual']) && $profile['sexual']) ? htmlspecialchars($profile['sexual'], ENT_COMPAT, 'UTF-8', false) : ''); + $arr['xprof_locale'] = ((isset($profile['locale']) && $profile['locale']) ? htmlspecialchars($profile['locale'], ENT_COMPAT, 'UTF-8', false) : ''); + $arr['xprof_region'] = ((isset($profile['region']) && $profile['region']) ? htmlspecialchars($profile['region'], ENT_COMPAT, 'UTF-8', false) : ''); + $arr['xprof_postcode'] = ((isset($profile['postcode']) && $profile['postcode']) ? htmlspecialchars($profile['postcode'], ENT_COMPAT, 'UTF-8', false) : ''); + $arr['xprof_country'] = ((isset($profile['country']) && $profile['country']) ? htmlspecialchars($profile['country'], ENT_COMPAT, 'UTF-8', false) : ''); + $arr['xprof_about'] = ((isset($profile['about']) && $profile['about']) ? htmlspecialchars($profile['about'], ENT_COMPAT, 'UTF-8', false) : ''); + $arr['xprof_pronouns'] = ((isset($profile['pronouns']) && $profile['pronouns']) ? htmlspecialchars($profile['pronouns'], ENT_COMPAT, 'UTF-8', false) : ''); + $arr['xprof_homepage'] = ((isset($profile['homepage']) && $profile['homepage']) ? htmlspecialchars($profile['homepage'], ENT_COMPAT, 'UTF-8', false) : ''); + $arr['xprof_hometown'] = ((isset($profile['hometown']) && $profile['hometown']) ? htmlspecialchars($profile['hometown'], ENT_COMPAT, 'UTF-8', false) : ''); - if (! $isadir) - set_config('system', 'directory_server', ''); - } + $clean = []; + if (array_key_exists('keywords', $profile) and is_array($profile['keywords'])) { + self::import_directory_keywords($hash, $profile['keywords']); + foreach ($profile['keywords'] as $kw) { + $kw = trim(htmlspecialchars($kw, ENT_COMPAT, 'UTF-8', false)); + $kw = trim($kw, ','); + $clean[] = $kw; + } + } + + $arr['xprof_keywords'] = implode(' ', $clean); + + // Self censored, make it so + // These are not translated, so the German "erwachsenen" keyword will not censor the directory profile. Only the English form - "adult". - static function get_directory_setting($observer, $setting) { + if (in_arrayi('nsfw', $clean) || in_arrayi('adult', $clean)) { + q( + "update xchan set xchan_selfcensored = 1 where xchan_hash = '%s'", + dbesc($hash) + ); + } + $r = q( + "select * from xprof where xprof_hash = '%s' limit 1", + dbesc($hash) + ); - if ($observer) - $ret = get_xconfig($observer, 'directory', $setting); - else - $ret = ((array_key_exists($setting,$_SESSION)) ? intval($_SESSION[$setting]) : false); + if ($arr['xprof_age'] > 150) { + $arr['xprof_age'] = 150; + } + if ($arr['xprof_age'] < 0) { + $arr['xprof_age'] = 0; + } - if($ret === false) { - $ret = get_config('directory', $setting); - if($ret === false) { - $ret = (in_array($setting,[ 'globaldir','safemode', 'activedir' ]) ? 1 : 0); - } - } - - if($setting === 'globaldir' && intval(get_config('system','localdir_hide'))) - $ret = 1; - - return $ret; - } - - /** - * @brief Called by the directory_sort widget. - */ - static function dir_sort_links() { - - $safe_mode = 1; - - $observer = get_observer_hash(); - - $safe_mode = self::get_directory_setting($observer, 'safemode'); - $globaldir = self::get_directory_setting($observer, 'globaldir'); - $pubforums = self::get_directory_setting($observer, 'chantype'); - $activedir = self::get_directory_setting($observer, 'activedir'); - - $hide_local = intval(get_config('system','localdir_hide')); - if ($hide_local) { - $globaldir = 1; - } - - // Build urls without order and pubforums so it's easy to tack on the changed value - // Probably there's an easier way to do this - - $directory_sort_order = get_config('system','directory_sort_order'); - if (! $directory_sort_order) { - $directory_sort_order = 'date'; - } - - $current_order = (($_REQUEST['order']) ? $_REQUEST['order'] : $directory_sort_order); - $suggest = (($_REQUEST['suggest']) ? '&suggest=' . $_REQUEST['suggest'] : ''); - - $url = 'directory?f='; - - $tmp = array_merge($_GET,$_POST); - unset($tmp['suggest']); - unset($tmp['pubforums']); - unset($tmp['type']); - unset($tmp['global']); - unset($tmp['safe']); - unset($tmp['active']); - unset($tmp['req']); - unset($tmp['f']); - $q = http_build_query($tmp); - $forumsurl = $url . (($q) ? '&' . $q : '') . $suggest; - - $o = replace_macros(get_markup_template('dir_sort_links.tpl'), [ - '$header' => t('Directory Options'), - '$forumsurl' => $forumsurl, - '$safemode' => array('safemode', t('Safe Mode'),$safe_mode,'',array(t('No'), t('Yes')),' onchange=\'window.location.href="' . $forumsurl . '&safe="+(this.checked ? 1 : 0)\''), - '$pubforums' => array('pubforums', t('Groups Only'),(($pubforums == 1) ? true : false),'',array(t('No'), t('Yes')),' onchange=\'window.location.href="' . $forumsurl . '&type="+(this.checked ? 1 : 0)\''), -// '$collections' => array('collections', t('Collections Only'),(($pubforums == 2) ? true : false),'',array(t('No'), t('Yes')),' onchange=\'window.location.href="' . $forumsurl . '&type="+(this.checked ? 2 : 0)\''), - '$hide_local' => $hide_local, - '$globaldir' => array('globaldir', t('This Website Only'), 1-intval($globaldir),'',array(t('No'), t('Yes')),' onchange=\'window.location.href="' . $forumsurl . '&global="+(this.checked ? 0 : 1)\''), - '$activedir' => array('activedir', t('Recently Updated'), intval($activedir),'',array(t('No'), t('Yes')),' onchange=\'window.location.href="' . $forumsurl . '&active="+(this.checked ? 1 : 0)\''), - ]); - - return $o; - } - - /** - * @brief - * - * Given an update record, probe the channel, grab a zot-info packet and refresh/sync the data. - * - * Ignore updating records marked as deleted. - * - * If successful, sets ud_last in the DB to the current datetime for this - * reddress/webbie. - * - * @param array $ud Entry from update table - */ - - static function update_directory_entry($ud) { - - logger('update_directory_entry: ' . print_r($ud,true), LOGGER_DATA); - - if ($ud['ud_addr'] && (! ($ud['ud_flags'] & UPDATE_FLAGS_DELETED))) { - $success = false; - - $href = Webfinger::zot_url(punify($ud['ud_addr'])); - if($href) { - $zf = Zotfinger::exec($href); - } - if(is_array($zf) && array_path_exists('signature/signer',$zf) && $zf['signature']['signer'] === $href && intval($zf['signature']['header_valid'])) { - $xc = Libzot::import_xchan($zf['data'], 0, $ud); - } - else { - q("update updates set ud_last = '%s' where ud_addr = '%s'", - dbesc(datetime_convert()), - dbesc($ud['ud_addr']) - ); - } - } - } - - - /** - * @brief Push local channel updates to a local directory server. - * - * This is called from Zotlabs/Daemon/Directory.php if a profile is to be pushed to the - * directory and the local hub in this case is any kind of directory server. - * - * @param int $uid - * @param boolean $force - */ - - static function local_dir_update($uid, $force) { - - - logger('local_dir_update: uid: ' . $uid, LOGGER_DEBUG); - - $p = q("select channel_hash, channel_address, channel_timezone, profile.* from profile left join channel on channel_id = uid where uid = %d and is_default = 1", - intval($uid) - ); - - $profile = []; - $profile['encoding'] = 'zot'; - - if ($p) { - $hash = $p[0]['channel_hash']; - - $profile['description'] = $p[0]['pdesc']; - $profile['birthday'] = $p[0]['dob']; - if ($age = age($p[0]['dob'],$p[0]['channel_timezone'],'')) - $profile['age'] = $age; - - $profile['gender'] = $p[0]['gender']; - $profile['marital'] = $p[0]['marital']; - $profile['sexual'] = $p[0]['sexual']; - $profile['locale'] = $p[0]['locality']; - $profile['region'] = $p[0]['region']; - $profile['postcode'] = $p[0]['postal_code']; - $profile['country'] = $p[0]['country_name']; - $profile['about'] = $p[0]['about']; - $profile['homepage'] = $p[0]['homepage']; - $profile['hometown'] = $p[0]['hometown']; - - if ($p[0]['keywords']) { - $tags = []; - $k = explode(' ', $p[0]['keywords']); - if ($k) - foreach ($k as $kk) - if (trim($kk)) - $tags[] = trim($kk); - - if ($tags) - $profile['keywords'] = $tags; - } - - $hidden = (1 - intval($p[0]['publish'])); - - // logger('hidden: ' . $hidden); - - $r = q("select xchan_hidden from xchan where xchan_hash = '%s' limit 1", - dbesc($p[0]['channel_hash']) - ); - - if(intval($r[0]['xchan_hidden']) != $hidden) { - $r = q("update xchan set xchan_hidden = %d where xchan_hash = '%s'", - intval($hidden), - dbesc($p[0]['channel_hash']) - ); - } - - $arr = [ 'channel_id' => $uid, 'hash' => $hash, 'profile' => $profile ]; - call_hooks('local_dir_update', $arr); - - $address = channel_reddress($p[0]); - - if (perm_is_allowed($uid, '', 'view_profile')) { - self::import_directory_profile($hash, $arr['profile'], $address, 0); - } - else { - // they may have made it private - $r = q("delete from xprof where xprof_hash = '%s'", - dbesc($hash) - ); - $r = q("delete from xtag where xtag_hash = '%s'", - dbesc($hash) - ); - } - - } - - $ud_hash = random_string() . '@' . \App::get_hostname(); - self::update_modtime($hash, $ud_hash, channel_reddress($p[0]),(($force) ? UPDATE_FLAGS_FORCED : UPDATE_FLAGS_UPDATED)); - } - - - - /** - * @brief Imports a directory profile. - * - * @param string $hash - * @param array $profile - * @param string $addr - * @param number $ud_flags (optional) UPDATE_FLAGS_UPDATED - * @param number $suppress_update (optional) default 0 - * @return boolean $updated if something changed - */ - - static function import_directory_profile($hash, $profile, $addr, $ud_flags = UPDATE_FLAGS_UPDATED, $suppress_update = 0) { - - logger('import_directory_profile', LOGGER_DEBUG); - if (! $hash) - return false; - - - $maxlen = get_max_import_size(); - - if($maxlen && mb_strlen($profile['about']) > $maxlen) { - $profile['about'] = mb_substr($profile['about'],0,$maxlen,'UTF-8'); - } - - $arr = []; - - $arr['xprof_hash'] = $hash; - $arr['xprof_dob'] = ((isset($profile['birthday']) && $profile['birthday'] === '0000-00-00') ? $profile['birthday'] : datetime_convert('','',$profile['birthday'],'Y-m-d')); // !!!! check this for 0000 year - $arr['xprof_age'] = (isset($profile['age']) ? intval($profile['age']) : 0); - $arr['xprof_desc'] = ((isset($profile['description']) && $profile['description']) ? htmlspecialchars($profile['description'], ENT_COMPAT,'UTF-8',false) : ''); - $arr['xprof_gender'] = ((isset($profile['gender']) && $profile['gender']) ? htmlspecialchars($profile['gender'], ENT_COMPAT,'UTF-8',false) : ''); - $arr['xprof_marital'] = ((isset($profile['marital']) && $profile['marital']) ? htmlspecialchars($profile['marital'], ENT_COMPAT,'UTF-8',false) : ''); - $arr['xprof_sexual'] = ((isset($profile['sexual']) && $profile['sexual']) ? htmlspecialchars($profile['sexual'], ENT_COMPAT,'UTF-8',false) : ''); - $arr['xprof_locale'] = ((isset($profile['locale']) && $profile['locale']) ? htmlspecialchars($profile['locale'], ENT_COMPAT,'UTF-8',false) : ''); - $arr['xprof_region'] = ((isset($profile['region']) && $profile['region']) ? htmlspecialchars($profile['region'], ENT_COMPAT,'UTF-8',false) : ''); - $arr['xprof_postcode'] = ((isset($profile['postcode']) && $profile['postcode']) ? htmlspecialchars($profile['postcode'], ENT_COMPAT,'UTF-8',false) : ''); - $arr['xprof_country'] = ((isset($profile['country']) && $profile['country']) ? htmlspecialchars($profile['country'], ENT_COMPAT,'UTF-8',false) : ''); - $arr['xprof_about'] = ((isset($profile['about']) && $profile['about']) ? htmlspecialchars($profile['about'], ENT_COMPAT,'UTF-8',false) : ''); - $arr['xprof_pronouns'] = ((isset($profile['pronouns']) && $profile['pronouns']) ? htmlspecialchars($profile['pronouns'], ENT_COMPAT,'UTF-8',false) : ''); - $arr['xprof_homepage'] = ((isset($profile['homepage']) && $profile['homepage']) ? htmlspecialchars($profile['homepage'], ENT_COMPAT,'UTF-8',false) : ''); - $arr['xprof_hometown'] = ((isset($profile['hometown']) && $profile['hometown']) ? htmlspecialchars($profile['hometown'], ENT_COMPAT,'UTF-8',false) : ''); - - $clean = []; - if (array_key_exists('keywords', $profile) and is_array($profile['keywords'])) { - self::import_directory_keywords($hash,$profile['keywords']); - foreach ($profile['keywords'] as $kw) { - $kw = trim(htmlspecialchars($kw,ENT_COMPAT, 'UTF-8', false)); - $kw = trim($kw, ','); - $clean[] = $kw; - } - } - - $arr['xprof_keywords'] = implode(' ',$clean); - - // Self censored, make it so - // These are not translated, so the German "erwachsenen" keyword will not censor the directory profile. Only the English form - "adult". - - - if(in_arrayi('nsfw',$clean) || in_arrayi('adult',$clean)) { - q("update xchan set xchan_selfcensored = 1 where xchan_hash = '%s'", - dbesc($hash) - ); - } - - $r = q("select * from xprof where xprof_hash = '%s' limit 1", - dbesc($hash) - ); - - if ($arr['xprof_age'] > 150) - $arr['xprof_age'] = 150; - if ($arr['xprof_age'] < 0) - $arr['xprof_age'] = 0; - - if ($r) { - $update = false; - foreach ($r[0] as $k => $v) { - if ((array_key_exists($k,$arr)) && ($arr[$k] != $v)) { - logger('import_directory_profile: update ' . $k . ' => ' . $arr[$k]); - $update = true; - break; - } - } - if ($update) { - q("update xprof set + if ($r) { + $update = false; + foreach ($r[0] as $k => $v) { + if ((array_key_exists($k, $arr)) && ($arr[$k] != $v)) { + logger('import_directory_profile: update ' . $k . ' => ' . $arr[$k]); + $update = true; + break; + } + } + if ($update) { + q( + "update xprof set xprof_desc = '%s', xprof_dob = '%s', xprof_age = %d, @@ -351,156 +379,161 @@ class Libzotdir { xprof_keywords = '%s', xprof_pronouns = '%s' where xprof_hash = '%s'", - dbesc($arr['xprof_desc']), - dbesc($arr['xprof_dob']), - intval($arr['xprof_age']), - dbesc($arr['xprof_gender']), - dbesc($arr['xprof_marital']), - dbesc($arr['xprof_sexual']), - dbesc($arr['xprof_locale']), - dbesc($arr['xprof_region']), - dbesc($arr['xprof_postcode']), - dbesc($arr['xprof_country']), - dbesc($arr['xprof_about']), - dbesc($arr['xprof_homepage']), - dbesc($arr['xprof_hometown']), - dbesc($arr['xprof_keywords']), - dbesc($arr['xprof_pronouns']), - dbesc($arr['xprof_hash']) - ); - } - } else { - $update = true; - logger('New profile'); - q("insert into xprof (xprof_hash, xprof_desc, xprof_dob, xprof_age, xprof_gender, xprof_marital, xprof_sexual, xprof_locale, xprof_region, xprof_postcode, xprof_country, xprof_about, xprof_homepage, xprof_hometown, xprof_keywords, xprof_pronouns) values ('%s', '%s', '%s', %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s') ", - dbesc($arr['xprof_hash']), - dbesc($arr['xprof_desc']), - dbesc($arr['xprof_dob']), - intval($arr['xprof_age']), - dbesc($arr['xprof_gender']), - dbesc($arr['xprof_marital']), - dbesc($arr['xprof_sexual']), - dbesc($arr['xprof_locale']), - dbesc($arr['xprof_region']), - dbesc($arr['xprof_postcode']), - dbesc($arr['xprof_country']), - dbesc($arr['xprof_about']), - dbesc($arr['xprof_homepage']), - dbesc($arr['xprof_hometown']), - dbesc($arr['xprof_keywords']), - dbesc($arr['xprof_pronouns']) - ); - } + dbesc($arr['xprof_desc']), + dbesc($arr['xprof_dob']), + intval($arr['xprof_age']), + dbesc($arr['xprof_gender']), + dbesc($arr['xprof_marital']), + dbesc($arr['xprof_sexual']), + dbesc($arr['xprof_locale']), + dbesc($arr['xprof_region']), + dbesc($arr['xprof_postcode']), + dbesc($arr['xprof_country']), + dbesc($arr['xprof_about']), + dbesc($arr['xprof_homepage']), + dbesc($arr['xprof_hometown']), + dbesc($arr['xprof_keywords']), + dbesc($arr['xprof_pronouns']), + dbesc($arr['xprof_hash']) + ); + } + } else { + $update = true; + logger('New profile'); + q( + "insert into xprof (xprof_hash, xprof_desc, xprof_dob, xprof_age, xprof_gender, xprof_marital, xprof_sexual, xprof_locale, xprof_region, xprof_postcode, xprof_country, xprof_about, xprof_homepage, xprof_hometown, xprof_keywords, xprof_pronouns) values ('%s', '%s', '%s', %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s') ", + dbesc($arr['xprof_hash']), + dbesc($arr['xprof_desc']), + dbesc($arr['xprof_dob']), + intval($arr['xprof_age']), + dbesc($arr['xprof_gender']), + dbesc($arr['xprof_marital']), + dbesc($arr['xprof_sexual']), + dbesc($arr['xprof_locale']), + dbesc($arr['xprof_region']), + dbesc($arr['xprof_postcode']), + dbesc($arr['xprof_country']), + dbesc($arr['xprof_about']), + dbesc($arr['xprof_homepage']), + dbesc($arr['xprof_hometown']), + dbesc($arr['xprof_keywords']), + dbesc($arr['xprof_pronouns']) + ); + } - $d = [ - 'xprof' => $arr, - 'profile' => $profile, - 'update' => $update - ]; + $d = [ + 'xprof' => $arr, + 'profile' => $profile, + 'update' => $update + ]; - /** - * @hooks import_directory_profile - * Called when processing delivery of a profile structure from an external source (usually for directory storage). - * * \e array \b xprof - * * \e array \b profile - * * \e boolean \b update - */ + /** + * @hooks import_directory_profile + * Called when processing delivery of a profile structure from an external source (usually for directory storage). + * * \e array \b xprof + * * \e array \b profile + * * \e boolean \b update + */ - call_hooks('import_directory_profile', $d); + call_hooks('import_directory_profile', $d); - if (($d['update']) && (! $suppress_update)) { - self::update_modtime($arr['xprof_hash'], new_uuid(), $addr, $ud_flags); - } + if (($d['update']) && (!$suppress_update)) { + self::update_modtime($arr['xprof_hash'], new_uuid(), $addr, $ud_flags); + } - q("update xchan set xchan_updated = '%s' where xchan_hash = '%s'", - dbesc(datetime_convert()), - dbesc($arr['xprof_hash']) - ); + q( + "update xchan set xchan_updated = '%s' where xchan_hash = '%s'", + dbesc(datetime_convert()), + dbesc($arr['xprof_hash']) + ); - return $d['update']; - } + return $d['update']; + } - /** - * @brief - * - * @param string $hash An xtag_hash - * @param array $keywords - */ + /** + * @brief + * + * @param string $hash An xtag_hash + * @param array $keywords + */ - static function import_directory_keywords($hash, $keywords) { + public static function import_directory_keywords($hash, $keywords) + { - $existing = []; - $r = q("select * from xtag where xtag_hash = '%s' and xtag_flags = 0", - dbesc($hash) - ); + $existing = []; + $r = q( + "select * from xtag where xtag_hash = '%s' and xtag_flags = 0", + dbesc($hash) + ); - if($r) { - foreach($r as $rr) - $existing[] = $rr['xtag_term']; - } + if ($r) { + foreach ($r as $rr) { + $existing[] = $rr['xtag_term']; + } + } - $clean = []; - foreach($keywords as $kw) { - $kw = trim(htmlspecialchars($kw,ENT_COMPAT, 'UTF-8', false)); - $kw = trim($kw, ','); - $clean[] = $kw; - } + $clean = []; + foreach ($keywords as $kw) { + $kw = trim(htmlspecialchars($kw, ENT_COMPAT, 'UTF-8', false)); + $kw = trim($kw, ','); + $clean[] = $kw; + } - foreach($existing as $x) { - if(! in_array($x, $clean)) - $r = q("delete from xtag where xtag_hash = '%s' and xtag_term = '%s' and xtag_flags = 0", - dbesc($hash), - dbesc($x) - ); - } - foreach($clean as $x) { - if(! in_array($x, $existing)) { - $r = q("insert into xtag ( xtag_hash, xtag_term, xtag_flags) values ( '%s' ,'%s', 0 )", - dbesc($hash), - dbesc($x) - ); - } - } - } + foreach ($existing as $x) { + if (!in_array($x, $clean)) { + $r = q( + "delete from xtag where xtag_hash = '%s' and xtag_term = '%s' and xtag_flags = 0", + dbesc($hash), + dbesc($x) + ); + } + } + foreach ($clean as $x) { + if (!in_array($x, $existing)) { + $r = q( + "insert into xtag ( xtag_hash, xtag_term, xtag_flags) values ( '%s' ,'%s', 0 )", + dbesc($hash), + dbesc($x) + ); + } + } + } - /** - * @brief - * - * @param string $hash - * @param string $guid - * @param string $addr - * @param int $flags (optional) default 0 - */ + /** + * @brief + * + * @param string $hash + * @param string $guid + * @param string $addr + * @param int $flags (optional) default 0 + */ - static function update_modtime($hash, $guid, $addr, $flags = 0) { + public static function update_modtime($hash, $guid, $addr, $flags = 0) + { - $dirmode = intval(get_config('system', 'directory_mode')); + $dirmode = intval(get_config('system', 'directory_mode')); - if($dirmode == DIRECTORY_MODE_NORMAL) - return; + if ($dirmode == DIRECTORY_MODE_NORMAL) { + return; + } - if($flags) { - q("insert into updates (ud_hash, ud_guid, ud_date, ud_flags, ud_addr ) values ( '%s', '%s', '%s', %d, '%s' )", - dbesc($hash), - dbesc($guid), - dbesc(datetime_convert()), - intval($flags), - dbesc($addr) - ); - } - else { - q("update updates set ud_flags = ( ud_flags | %d ) where ud_addr = '%s' and (ud_flags & %d) = 0 ", - intval(UPDATE_FLAGS_UPDATED), - dbesc($addr), - intval(UPDATE_FLAGS_UPDATED) - ); - } - } - - - - - - -} \ No newline at end of file + if ($flags) { + q( + "insert into updates (ud_hash, ud_guid, ud_date, ud_flags, ud_addr ) values ( '%s', '%s', '%s', %d, '%s' )", + dbesc($hash), + dbesc($guid), + dbesc(datetime_convert()), + intval($flags), + dbesc($addr) + ); + } else { + q( + "update updates set ud_flags = ( ud_flags | %d ) where ud_addr = '%s' and (ud_flags & %d) = 0 ", + intval(UPDATE_FLAGS_UPDATED), + dbesc($addr), + intval(UPDATE_FLAGS_UPDATED) + ); + } + } +} diff --git a/Zotlabs/Lib/Markdown.php b/Zotlabs/Lib/Markdown.php index 03ff41bd5..59ae117a5 100644 --- a/Zotlabs/Lib/Markdown.php +++ b/Zotlabs/Lib/Markdown.php @@ -1,4 +1,5 @@ $s, - 'zrl' => $use_zrl, - 'options' => $options - ]; - - /** - * @hooks markdown_to_bb_init - * * \e string \b text - The message as Markdown and what will get returned - * * \e boolean \b zrl - * * \e array \b options - */ - call_hooks('markdown_to_bb_init', $x); - - $s = $x['text']; - - // Escaping the hash tags - $s = preg_replace('/\#([^\s\#])/','#$1',$s); - - $s = MarkdownExtra::defaultTransform($s); - - if($options && $options['preserve_lf']) { - $s = str_replace(["\r","\n"],["",'
'],$s); - } - else { - $s = str_replace("\r","",$s); - } - - $s = str_replace('#','#',$s); - - $s = html2bbcode($s); - - // Convert everything that looks like a link to a link - if($use_zrl) { - if (strpos($s,'[/img]') !== false) { - $s = preg_replace_callback("/\[img\](.*?)\[\/img\]/ism", [ '\\Zotlabs\\Lib\\Markdown', 'use_zrl_cb_img'], $s); - $s = preg_replace_callback("/\[img\=([0-9]*)x([0-9]*)\](.*?)\[\/img\]/ism", [ '\\Zotlabs\\Lib\\Markdown', 'use_zrl_cb_img_x' ], $s); - } - $s = preg_replace_callback("/([^\]\=\{\/]|^)(https?\:\/\/)([a-zA-Z0-9\pL\:\/\-\?\&\;\.\=\_\~\#\%\$\!\+\,\@\(\)]+)/ismu", [ '\\Zotlabs\\Lib\\Markdown', 'use_zrl_cb_link'] ,$s); - } - else { - $s = preg_replace("/([^\]\=\{\/]|^)(https?\:\/\/)([a-zA-Z0-9\pL\:\/\-\?\&\;\.\=\_\~\#\%\$\!\+\,\@\(\)]+)/ismu", '$1[url=$2$3]$2$3[/url]',$s); - } - - // remove duplicate adjacent code tags - $s = preg_replace("/(\[code\])+(.*?)(\[\/code\])+/ism","[code]$2[/code]", $s); - - /** - * @hooks markdown_to_bb - * * \e string - The already converted message as bbcode - */ - call_hooks('markdown_to_bb', $s); - - return $s; - } - - static function use_zrl_cb_link($match) { - $res = ''; - $is_zid = is_matrix_url(trim($match[0])); - - if($is_zid) - $res = $match[1] . '[zrl=' . $match[2] . $match[3] . ']' . $match[2] . $match[3] . '[/zrl]'; - else - $res = $match[1] . '[url=' . $match[2] . $match[3] . ']' . $match[2] . $match[3] . '[/url]'; - - return $res; - } - - static function use_zrl_cb_img($match) { - $res = ''; - $is_zid = is_matrix_url(trim($match[1])); - - if($is_zid) - $res = '[zmg]' . $match[1] . '[/zmg]'; - else - $res = $match[0]; - - return $res; - } - - static function use_zrl_cb_img_x($match) { - $res = ''; - $is_zid = is_matrix_url(trim($match[3])); +class Markdown +{ + + /** + * @brief Convert Markdown to bbcode. + * + * We don't want to support a bbcode specific markdown interpreter + * and the markdown library we have is pretty good, but provides HTML output. + * So we'll use that to convert to HTML, then convert the HTML back to bbcode, + * and then clean up a few Diaspora specific constructs. + * + * @param string $s The message as Markdown + * @param bool $use_zrl default false + * @param array $options default empty + * @return string The message converted to bbcode + */ + + public static function to_bbcode($s, $use_zrl = false, $options = []) + { + + if (is_array($s)) { + btlogger('markdown_to_bb called with array. ' . print_r($s, true), LOGGER_NORMAL, LOG_WARNING); + return ''; + } + + $s = str_replace(" ", "\r", $s); + $s = str_replace(" \n>", "", $s); + + $s = html_entity_decode($s, ENT_COMPAT, 'UTF-8'); + + // if empty link text replace with the url + $s = preg_replace("/\[\]\((.*?)\)/ism", '[$1]($1)', $s); + + $x = [ + 'text' => $s, + 'zrl' => $use_zrl, + 'options' => $options + ]; + + /** + * @hooks markdown_to_bb_init + * * \e string \b text - The message as Markdown and what will get returned + * * \e boolean \b zrl + * * \e array \b options + */ + call_hooks('markdown_to_bb_init', $x); + + $s = $x['text']; + + // Escaping the hash tags + $s = preg_replace('/\#([^\s\#])/', '#$1', $s); + + $s = MarkdownExtra::defaultTransform($s); + + if ($options && $options['preserve_lf']) { + $s = str_replace(["\r", "\n"], ["", '
'], $s); + } else { + $s = str_replace("\r", "", $s); + } + + $s = str_replace('#', '#', $s); + + $s = html2bbcode($s); + + // Convert everything that looks like a link to a link + if ($use_zrl) { + if (strpos($s, '[/img]') !== false) { + $s = preg_replace_callback("/\[img\](.*?)\[\/img\]/ism", ['\\Zotlabs\\Lib\\Markdown', 'use_zrl_cb_img'], $s); + $s = preg_replace_callback("/\[img\=([0-9]*)x([0-9]*)\](.*?)\[\/img\]/ism", ['\\Zotlabs\\Lib\\Markdown', 'use_zrl_cb_img_x'], $s); + } + $s = preg_replace_callback("/([^\]\=\{\/]|^)(https?\:\/\/)([a-zA-Z0-9\pL\:\/\-\?\&\;\.\=\_\~\#\%\$\!\+\,\@\(\)]+)/ismu", ['\\Zotlabs\\Lib\\Markdown', 'use_zrl_cb_link'], $s); + } else { + $s = preg_replace("/([^\]\=\{\/]|^)(https?\:\/\/)([a-zA-Z0-9\pL\:\/\-\?\&\;\.\=\_\~\#\%\$\!\+\,\@\(\)]+)/ismu", '$1[url=$2$3]$2$3[/url]', $s); + } + + // remove duplicate adjacent code tags + $s = preg_replace("/(\[code\])+(.*?)(\[\/code\])+/ism", "[code]$2[/code]", $s); + + /** + * @hooks markdown_to_bb + * * \e string - The already converted message as bbcode + */ + call_hooks('markdown_to_bb', $s); + + return $s; + } + + public static function use_zrl_cb_link($match) + { + $res = ''; + $is_zid = is_matrix_url(trim($match[0])); + + if ($is_zid) { + $res = $match[1] . '[zrl=' . $match[2] . $match[3] . ']' . $match[2] . $match[3] . '[/zrl]'; + } else { + $res = $match[1] . '[url=' . $match[2] . $match[3] . ']' . $match[2] . $match[3] . '[/url]'; + } + + return $res; + } + + public static function use_zrl_cb_img($match) + { + $res = ''; + $is_zid = is_matrix_url(trim($match[1])); + + if ($is_zid) { + $res = '[zmg]' . $match[1] . '[/zmg]'; + } else { + $res = $match[0]; + } + + return $res; + } + + public static function use_zrl_cb_img_x($match) + { + $res = ''; + $is_zid = is_matrix_url(trim($match[3])); + + if ($is_zid) { + $res = '[zmg=' . $match[1] . 'x' . $match[2] . ']' . $match[3] . '[/zmg]'; + } else { + $res = $match[0]; + } + + return $res; + } + + /** + * @brief + * + * @param array $match + * @return string + */ + + public static function from_bbcode_share($match) + { + + $matches = []; + $attributes = $match[1]; + + $author = ""; + preg_match("/author='(.*?)'/ism", $attributes, $matches); + if ($matches[1] != "") { + $author = urldecode($matches[1]); + } + + $link = ""; + preg_match("/link='(.*?)'/ism", $attributes, $matches); + if ($matches[1] != "") { + $link = $matches[1]; + } + + $avatar = ""; + preg_match("/avatar='(.*?)'/ism", $attributes, $matches); + if ($matches[1] != "") { + $avatar = $matches[1]; + } + + $profile = ""; + preg_match("/profile='(.*?)'/ism", $attributes, $matches); + if ($matches[1] != "") { + $profile = $matches[1]; + } + + $posted = ""; + preg_match("/posted='(.*?)'/ism", $attributes, $matches); + if ($matches[1] != "") { + $posted = $matches[1]; + } - if($is_zid) - $res = '[zmg=' . $match[1] . 'x' . $match[2] . ']' . $match[3] . '[/zmg]'; - else - $res = $match[0]; + // message_id is never used, do we still need it? + $message_id = ""; + preg_match("/message_id='(.*?)'/ism", $attributes, $matches); + if ($matches[1] != "") { + $message_id = $matches[1]; + } - return $res; - } + if (!$message_id) { + preg_match("/guid='(.*?)'/ism", $attributes, $matches); + if ($matches[1] != "") { + $message_id = $matches[1]; + } + } - /** - * @brief - * - * @param array $match - * @return string - */ - static public function from_bbcode_share($match) { + $reldate = datetime_convert('UTC', date_default_timezone_get(), $posted, 'r'); - $matches = []; - $attributes = $match[1]; + $headline = ''; - $author = ""; - preg_match("/author='(.*?)'/ism", $attributes, $matches); - if ($matches[1] != "") - $author = urldecode($matches[1]); + if ($avatar != "") { + $headline .= '[url=' . zid($profile) . '][img]' . $avatar . '[/img][/url]'; + } - $link = ""; - preg_match("/link='(.*?)'/ism", $attributes, $matches); - if ($matches[1] != "") - $link = $matches[1]; + // Bob Smith wrote the following post 2 hours ago - $avatar = ""; - preg_match("/avatar='(.*?)'/ism", $attributes, $matches); - if ($matches[1] != "") - $avatar = $matches[1]; + $fmt = sprintf( + t('%1$s wrote the following %2$s %3$s'), + '[url=' . zid($profile) . ']' . $author . '[/url]', + '[url=' . zid($link) . ']' . t('post') . '[/url]', + $reldate + ); + + $headline .= $fmt . "\n\n"; + + $text = $headline . trim($match[2]); + + return $text; + } + + + /** + * @brief Convert bbcode to Markdown. + * + * @param string $Text The message as bbcode + * @param array $options default empty + * @return string The message converted to Markdown + */ + + public static function from_bbcode($Text, $options = []) + { + + /* + * Transform #tags, strip off the [url] and replace spaces with underscore + */ + + $Text = preg_replace_callback( + '/#\[([zu])rl\=(.*?)\](.*?)\[\/[(zu)]rl\]/i', + create_function('$match', 'return \'#\'. str_replace(\' \', \'_\', $match[3]);'), + $Text + ); + + $Text = preg_replace('/#\^\[([zu])rl\=(.*?)\](.*?)\[\/([zu])rl\]/i', '[$1rl=$2]$3[/$4rl]', $Text); + + // Converting images with size parameters to simple images. Markdown doesn't know it. + $Text = preg_replace("/\[img\=([0-9]*)x([0-9]*)\](.*?)\[\/img\]/ism", '[img]$3[/img]', $Text); + + $Text = preg_replace_callback("/\[share(.*?)\](.*?)\[\/share\]/ism", ['\\Zotlabs\\Lib\\Markdown', 'from_bbcode_share'], $Text); + + $x = ['bbcode' => $Text, 'options' => $options]; + + /** + * @hooks bb_to_markdown_bb + * * \e string \b bbcode - The message as bbcode and what will get returned + * * \e array \b options + */ + call_hooks('bb_to_markdown_bb', $x); + + $Text = $x['bbcode']; + + // Convert it to HTML - don't try oembed + $Text = bbcode($Text, ['tryoembed' => false]); - $profile = ""; - preg_match("/profile='(.*?)'/ism", $attributes, $matches); - if ($matches[1] != "") - $profile = $matches[1]; + // Now convert HTML to Markdown + + $Text = self::from_html($Text); - $posted = ""; - preg_match("/posted='(.*?)'/ism", $attributes, $matches); - if ($matches[1] != "") - $posted = $matches[1]; + //html2markdown adds backslashes infront of hashes after a new line. remove them + $Text = str_replace("\n\#", "\n#", $Text); - // message_id is never used, do we still need it? - $message_id = ""; - preg_match("/message_id='(.*?)'/ism", $attributes, $matches); - if ($matches[1] != "") - $message_id = $matches[1]; - - if(! $message_id) { - preg_match("/guid='(.*?)'/ism", $attributes, $matches); - if ($matches[1] != "") - $message_id = $matches[1]; - } + // If the text going into bbcode() has a plain URL in it, i.e. + // with no [url] tags around it, it will come out of parseString() + // looking like: , which gets removed by strip_tags(). + // So take off the angle brackets of any such URL + $Text = preg_replace("//is", "http$1", $Text); - $reldate = datetime_convert('UTC', date_default_timezone_get(), $posted, 'r'); + // Remove empty zrl links + $Text = preg_replace("/\[zrl\=\].*?\[\/zrl\]/is", "", $Text); - $headline = ''; + $Text = trim($Text); - if ($avatar != "") - $headline .= '[url=' . zid($profile) . '][img]' . $avatar . '[/img][/url]'; + /** + * @hooks bb_to_markdown + * * \e string - The already converted message as bbcode and what will get returned + */ + call_hooks('bb_to_markdown', $Text); - // Bob Smith wrote the following post 2 hours ago + return $Text; + } - $fmt = sprintf( t('%1$s wrote the following %2$s %3$s'), - '[url=' . zid($profile) . ']' . $author . '[/url]', - '[url=' . zid($link) . ']' . t('post') . '[/url]', - $reldate - ); - $headline .= $fmt . "\n\n"; + /** + * @brief Convert a HTML text into Markdown. + * + * This function uses the library league/html-to-markdown for this task. + * + * If the HTML text can not get parsed it will return an empty string. + * + * @param string $html The HTML code to convert + * @return string Markdown representation of the given HTML text, empty on error + */ - $text = $headline . trim($match[2]); + public static function from_html($html, $options = []) + { + $markdown = ''; - return $text; - } + if (!$options) { + $options = [ + 'header_style' => 'setext', // Set to 'atx' to output H1 and H2 headers as # Header1 and ## Header2 + 'suppress_errors' => true, // Set to false to show warnings when loading malformed HTML + 'strip_tags' => false, // Set to true to strip tags that don't have markdown equivalents. N.B. Strips tags, not their content. Useful to clean MS Word HTML output. + 'bold_style' => '**', // DEPRECATED: Set to '__' if you prefer the underlined style + 'italic_style' => '*', // DEPRECATED: Set to '_' if you prefer the underlined style + 'remove_nodes' => '', // space-separated list of dom nodes that should be removed. example: 'meta style script' + 'hard_break' => false, // Set to true to turn
into `\n` instead of ` \n` + 'list_item_style' => '-', // Set the default character for each
  • in a
      . Can be '-', '*', or '+' + ]; + } + $environment = Environment::createDefaultEnvironment($options); + $environment->addConverter(new TableConverter()); + $converter = new HtmlConverter($environment); + + try { + $markdown = $converter->convert($html); + } catch (InvalidArgumentException $e) { + logger("Invalid HTML. HTMLToMarkdown library threw an exception."); + } - /** - * @brief Convert bbcode to Markdown. - * - * @param string $Text The message as bbcode - * @param array $options default empty - * @return string The message converted to Markdown - */ - - static public function from_bbcode($Text, $options = []) { - - /* - * Transform #tags, strip off the [url] and replace spaces with underscore - */ - - $Text = preg_replace_callback('/#\[([zu])rl\=(.*?)\](.*?)\[\/[(zu)]rl\]/i', - create_function('$match', 'return \'#\'. str_replace(\' \', \'_\', $match[3]);'), $Text); - - $Text = preg_replace('/#\^\[([zu])rl\=(.*?)\](.*?)\[\/([zu])rl\]/i', '[$1rl=$2]$3[/$4rl]', $Text); - - // Converting images with size parameters to simple images. Markdown doesn't know it. - $Text = preg_replace("/\[img\=([0-9]*)x([0-9]*)\](.*?)\[\/img\]/ism", '[img]$3[/img]', $Text); - - $Text = preg_replace_callback("/\[share(.*?)\](.*?)\[\/share\]/ism", [ '\\Zotlabs\\Lib\\Markdown', 'from_bbcode_share'], $Text); - - $x = [ 'bbcode' => $Text, 'options' => $options ]; - - /** - * @hooks bb_to_markdown_bb - * * \e string \b bbcode - The message as bbcode and what will get returned - * * \e array \b options - */ - call_hooks('bb_to_markdown_bb', $x); - - $Text = $x['bbcode']; - - // Convert it to HTML - don't try oembed - $Text = bbcode($Text, [ 'tryoembed' => false ]); - - // Now convert HTML to Markdown - - $Text = self::from_html($Text); - - //html2markdown adds backslashes infront of hashes after a new line. remove them - $Text = str_replace("\n\#", "\n#", $Text); - - - // If the text going into bbcode() has a plain URL in it, i.e. - // with no [url] tags around it, it will come out of parseString() - // looking like: , which gets removed by strip_tags(). - // So take off the angle brackets of any such URL - $Text = preg_replace("//is", "http$1", $Text); - - // Remove empty zrl links - $Text = preg_replace("/\[zrl\=\].*?\[\/zrl\]/is", "", $Text); - - $Text = trim($Text); - - /** - * @hooks bb_to_markdown - * * \e string - The already converted message as bbcode and what will get returned - */ - call_hooks('bb_to_markdown', $Text); - - return $Text; - } - - - /** - * @brief Convert a HTML text into Markdown. - * - * This function uses the library league/html-to-markdown for this task. - * - * If the HTML text can not get parsed it will return an empty string. - * - * @param string $html The HTML code to convert - * @return string Markdown representation of the given HTML text, empty on error - */ - - static public function from_html($html,$options = []) { - $markdown = ''; - - if(! $options) { - $options = [ - 'header_style' => 'setext', // Set to 'atx' to output H1 and H2 headers as # Header1 and ## Header2 - 'suppress_errors' => true, // Set to false to show warnings when loading malformed HTML - 'strip_tags' => false, // Set to true to strip tags that don't have markdown equivalents. N.B. Strips tags, not their content. Useful to clean MS Word HTML output. - 'bold_style' => '**', // DEPRECATED: Set to '__' if you prefer the underlined style - 'italic_style' => '*', // DEPRECATED: Set to '_' if you prefer the underlined style - 'remove_nodes' => '', // space-separated list of dom nodes that should be removed. example: 'meta style script' - 'hard_break' => false, // Set to true to turn
      into `\n` instead of ` \n` - 'list_item_style' => '-', // Set the default character for each
    • in a
        . Can be '-', '*', or '+' - ]; - } - - $environment = Environment::createDefaultEnvironment($options); - $environment->addConverter(new TableConverter()); - $converter = new HtmlConverter($environment); - - try { - $markdown = $converter->convert($html); - } catch (InvalidArgumentException $e) { - logger("Invalid HTML. HTMLToMarkdown library threw an exception."); - } - - return $markdown; - } + return $markdown; + } } // Tables are not an official part of the markdown specification. @@ -330,64 +351,64 @@ class Markdown { class TableConverter implements ConverterInterface { - /** - * @param ElementInterface $element - * - * @return string - */ - public function convert(ElementInterface $element) - { - switch ($element->getTagName()) { - case 'tr': - $line = []; - $i = 1; - foreach ($element->getChildren() as $td) { - $i++; - $v = $td->getValue(); - $v = trim($v); - if ($i % 2 === 0 || $v !== '') { - $line[] = $v; - } - } - return '| ' . implode(' | ', $line) . " |\n"; - case 'td': - case 'th': - return trim($element->getValue()); - case 'tbody': - return trim($element->getValue()); - case 'thead': - $headerLine = reset($element->getChildren())->getValue(); - $headers = explode(' | ', trim(trim($headerLine, "\n"), '|')); - $hr = []; - foreach ($headers as $td) { - $length = strlen(trim($td)) + 2; - $hr[] = str_repeat('-', $length > 3 ? $length : 3); - } - $hr = '|' . implode('|', $hr) . '|'; - return $headerLine . $hr . "\n"; - case 'table': - $inner = $element->getValue(); - if (strpos($inner, '-----') === false) { - $inner = explode("\n", $inner); - $single = explode(' | ', trim($inner[0], '|')); - $hr = []; - foreach ($single as $td) { - $length = strlen(trim($td)) + 2; - $hr[] = str_repeat('-', $length > 3 ? $length : 3); - } - $hr = '|' . implode('|', $hr) . '|'; - array_splice($inner, 1, 0, $hr); - $inner = implode("\n", $inner); - } - return trim($inner) . "\n\n"; - } - return $element->getValue(); - } - /** - * @return string[] - */ - public function getSupportedTags() - { - return array('table', 'tr', 'thead', 'td', 'tbody'); - } + /** + * @param ElementInterface $element + * + * @return string + */ + public function convert(ElementInterface $element) + { + switch ($element->getTagName()) { + case 'tr': + $line = []; + $i = 1; + foreach ($element->getChildren() as $td) { + $i++; + $v = $td->getValue(); + $v = trim($v); + if ($i % 2 === 0 || $v !== '') { + $line[] = $v; + } + } + return '| ' . implode(' | ', $line) . " |\n"; + case 'td': + case 'th': + return trim($element->getValue()); + case 'tbody': + return trim($element->getValue()); + case 'thead': + $headerLine = reset($element->getChildren())->getValue(); + $headers = explode(' | ', trim(trim($headerLine, "\n"), '|')); + $hr = []; + foreach ($headers as $td) { + $length = strlen(trim($td)) + 2; + $hr[] = str_repeat('-', $length > 3 ? $length : 3); + } + $hr = '|' . implode('|', $hr) . '|'; + return $headerLine . $hr . "\n"; + case 'table': + $inner = $element->getValue(); + if (strpos($inner, '-----') === false) { + $inner = explode("\n", $inner); + $single = explode(' | ', trim($inner[0], '|')); + $hr = []; + foreach ($single as $td) { + $length = strlen(trim($td)) + 2; + $hr[] = str_repeat('-', $length > 3 ? $length : 3); + } + $hr = '|' . implode('|', $hr) . '|'; + array_splice($inner, 1, 0, $hr); + $inner = implode("\n", $inner); + } + return trim($inner) . "\n\n"; + } + return $element->getValue(); + } + /** + * @return string[] + */ + public function getSupportedTags() + { + return array('table', 'tr', 'thead', 'td', 'tbody'); + } } diff --git a/Zotlabs/Lib/MarkdownSoap.php b/Zotlabs/Lib/MarkdownSoap.php index a58a5753a..304f0cd02 100644 --- a/Zotlabs/Lib/MarkdownSoap.php +++ b/Zotlabs/Lib/MarkdownSoap.php @@ -22,111 +22,126 @@ namespace Zotlabs\Lib; * $html = \Michelf\MarkdownExtra::DefaultTransform($markdown); * @endcode */ -class MarkdownSoap { +class MarkdownSoap +{ - /** - * @var string - */ - private $str; - /** - * @var string - */ - private $token; + /** + * @var string + */ + private $str; + /** + * @var string + */ + private $token; - function __construct($s) { - $this->str = $s; - $this->token = random_string(20); - } + public function __construct($s) + { + $this->str = $s; + $this->token = random_string(20); + } - function clean() { + public function clean() + { - $x = $this->extract_code($this->str); + $x = $this->extract_code($this->str); - $x = $this->purify($x); + $x = $this->purify($x); - $x = $this->putback_code($x); + $x = $this->putback_code($x); - $x = $this->escape($x); + $x = $this->escape($x); - return $x; - } + return $x; + } - /** - * @brief Extracts code blocks and privately escapes them from processing. - * - * @see encode_code() - * @see putback_code() - * - * @param string $s - * @return string - */ - function extract_code($s) { + /** + * @brief Extracts code blocks and privately escapes them from processing. + * + * @param string $s + * @return string + * @see encode_code() + * @see putback_code() + * + */ + public function extract_code($s) + { - $text = preg_replace_callback('{ + $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 + [ ]{' . '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 + ((?=^[ ]{0,' . '4' . '}\S)|\Z) # Lookahead for non-space at line-start, or end of doc }xm', - [ $this , 'encode_code' ], $s); + [$this, 'encode_code'], + $s + ); - return $text; - } + return $text; + } - function encode_code($matches) { - return $this->token . ';' . base64_encode($matches[0]) . ';' ; - } + public function encode_code($matches) + { + return $this->token . ';' . base64_encode($matches[0]) . ';'; + } - function decode_code($matches) { - return base64_decode($matches[1]); - } + public function decode_code($matches) + { + return base64_decode($matches[1]); + } - /** - * @brief Put back the code blocks. - * - * @see extract_code() - * @see decode_code() - * - * @param string $s - * @return string - */ - function putback_code($s) { - $text = preg_replace_callback('{' . $this->token . '\;(.*?)\;}xm', [ $this, 'decode_code' ], $s); - return $text; - } + /** + * @brief Put back the code blocks. + * + * @param string $s + * @return string + * @see extract_code() + * @see decode_code() + * + */ + public function putback_code($s) + { + $text = preg_replace_callback('{' . $this->token . '\;(.*?)\;}xm', [$this, 'decode_code'], $s); + return $text; + } - function purify($s) { - $s = $this->protect_autolinks($s); - $s = purify_html($s); - $s = $this->unprotect_autolinks($s); - return $s; - } + public function purify($s) + { + $s = $this->protect_autolinks($s); + $s = purify_html($s); + $s = $this->unprotect_autolinks($s); + return $s; + } - function protect_autolinks($s) { - $s = preg_replace('/\<(https?\:\/\/)(.*?)\>/', '[$1$2]($1$2)', $s); - return $s; - } + public function protect_autolinks($s) + { + $s = preg_replace('/\<(https?\:\/\/)(.*?)\>/', '[$1$2]($1$2)', $s); + return $s; + } - function unprotect_autolinks($s) { - return $s; - } + public function unprotect_autolinks($s) + { + return $s; + } - function escape($s) { - return htmlspecialchars($s, ENT_QUOTES, 'UTF-8', false); - } + public function escape($s) + { + return htmlspecialchars($s, ENT_QUOTES, 'UTF-8', false); + } - /** - * @brief Converts special HTML entities back to characters. - * - * @param string $s - * @return string - */ - static public function unescape($s) { - return htmlspecialchars_decode($s, ENT_QUOTES); - } + /** + * @brief Converts special HTML entities back to characters. + * + * @param string $s + * @return string + */ + public static function unescape($s) + { + return htmlspecialchars_decode($s, ENT_QUOTES); + } } diff --git a/Zotlabs/Lib/MastAPI.php b/Zotlabs/Lib/MastAPI.php index 7bfd7191a..99c5dfddc 100644 --- a/Zotlabs/Lib/MastAPI.php +++ b/Zotlabs/Lib/MastAPI.php @@ -5,99 +5,104 @@ namespace Zotlabs\Lib; use App; use Zotlabs\Lib\PConfig; +class MastAPI +{ -class MastAPI { + public static function format_channel($channel) + { + $p = q( + "select * from profile where uid = %d and is_default = 1", + intval($channel['channel_id']) + ); - static function format_channel($channel) { - $p = q("select * from profile where uid = %d and is_default = 1", - intval($channel['channel_id']) - ); + $a = q( + "select * from account where account_id = %d", + intval($channel['channel_account_id']) + ); - $a = q("select * from account where account_id = %d", - intval($channel['channel_account_id']) - ); - - $followers = q("select count(xchan_hash) as total from xchan left join abconfig on abconfig.xchan = xchan_hash left join abook on abook_xchan = xchan_hash where abook_channel = %d and abconfig.chan = %d and abconfig.cat = 'system' and abconfig.k = 'their_perms' and abconfig.v like '%%send_stream%%' and xchan_hash != '%s' and xchan_orphan = 0 and xchan_deleted = 0 and abook_hidden = 0 and abook_pending = 0 and abook_self = 0 ", + $followers = q( + "select count(xchan_hash) as total from xchan left join abconfig on abconfig.xchan = xchan_hash left join abook on abook_xchan = xchan_hash where abook_channel = %d and abconfig.chan = %d and abconfig.cat = 'system' and abconfig.k = 'their_perms' and abconfig.v like '%%send_stream%%' and xchan_hash != '%s' and xchan_orphan = 0 and xchan_deleted = 0 and abook_hidden = 0 and abook_pending = 0 and abook_self = 0 ", intval($channel['channel_id']), intval($channel['channel_id']), dbesc($channel['channel_hash']) ); - $following = q("select count(xchan_hash) as total from xchan left join abconfig on abconfig.xchan = xchan_hash left join abook on abook_xchan = xchan_hash where abook_channel = %d and abconfig.chan = %d and abconfig.cat = 'system' and abconfig.k = 'my_perms' and abconfig.v like '%%send_stream%%' and xchan_hash != '%s' and xchan_orphan = 0 and xchan_deleted = 0 and abook_hidden = 0 and abook_pending = 0 and abook_self = 0", - intval($channel['channel_id']), - intval($channel['channel_id']), - dbesc($channel['channel_hash']) - ); + $following = q( + "select count(xchan_hash) as total from xchan left join abconfig on abconfig.xchan = xchan_hash left join abook on abook_xchan = xchan_hash where abook_channel = %d and abconfig.chan = %d and abconfig.cat = 'system' and abconfig.k = 'my_perms' and abconfig.v like '%%send_stream%%' and xchan_hash != '%s' and xchan_orphan = 0 and xchan_deleted = 0 and abook_hidden = 0 and abook_pending = 0 and abook_self = 0", + intval($channel['channel_id']), + intval($channel['channel_id']), + dbesc($channel['channel_hash']) + ); - $cover_photo = get_cover_photo($channel['channel_id'],'array'); + $cover_photo = get_cover_photo($channel['channel_id'], 'array'); $item_normal = item_normal(); // count posts/comments - $statuses = q("SELECT COUNT(id) as total FROM item + $statuses = q( + "SELECT COUNT(id) as total FROM item WHERE uid = %d AND author_xchan = '%s' $item_normal ", intval($channel['channel_id']), - dbesc($channel['channel_hash']) + dbesc($channel['channel_hash']) ); - $ret = []; - $ret['id'] = (string) $channel['channel_id']; - $ret['username'] = $channel['channel_address']; - $ret['acct'] = $channel['channel_address']; - $ret['display_name'] = $channel['channel_name']; - $ret['locked'] = ((intval(PConfig::Get($channel['channel_id'],'system','autoperms'))) ? false : true ); - $ret['discoverable'] = ((1 - intval($channel['xchan_hidden'])) ? true : false); - $ret['created_at'] = datetime_convert('UTC','UTC', $a[0]['account_created'], ATOM_TIME); - $ret['note'] = bbcode($p[0]['about'], [ 'export' => true ]); - $ret['url'] = channel_url($channel); - $ret['avatar'] = $channel['xchan_photo_l']; - $ret['avatar_static'] = $channel['xchan_photo_l']; - if ($cover_photo) { - $ret['header'] = $cover_photo['url']; - $ret['header_static'] = $cover_photo['url']; - } - $ret['followers_count'] = intval($followers[0]['total']); - $ret['following_count'] = intval($following[0]['total']); - $ret['statuses_count'] = intval($statuses[0]['total']); - $ret['last_status_at'] = datetime_convert('UTC','UTC', $channel['lastpost'], ATOM_TIME); + $ret = []; + $ret['id'] = (string)$channel['channel_id']; + $ret['username'] = $channel['channel_address']; + $ret['acct'] = $channel['channel_address']; + $ret['display_name'] = $channel['channel_name']; + $ret['locked'] = ((intval(PConfig::Get($channel['channel_id'], 'system', 'autoperms'))) ? false : true); + $ret['discoverable'] = ((1 - intval($channel['xchan_hidden'])) ? true : false); + $ret['created_at'] = datetime_convert('UTC', 'UTC', $a[0]['account_created'], ATOM_TIME); + $ret['note'] = bbcode($p[0]['about'], ['export' => true]); + $ret['url'] = channel_url($channel); + $ret['avatar'] = $channel['xchan_photo_l']; + $ret['avatar_static'] = $channel['xchan_photo_l']; + if ($cover_photo) { + $ret['header'] = $cover_photo['url']; + $ret['header_static'] = $cover_photo['url']; + } + $ret['followers_count'] = intval($followers[0]['total']); + $ret['following_count'] = intval($following[0]['total']); + $ret['statuses_count'] = intval($statuses[0]['total']); + $ret['last_status_at'] = datetime_convert('UTC', 'UTC', $channel['lastpost'], ATOM_TIME); - return $ret; - } + return $ret; + } - static function format_site() { + public static function format_site() + { - $register = intval(get_config('system','register_policy')); + $register = intval(get_config('system', 'register_policy')); - $u = q("select count(channel_id) as total from channel where channel_removed = 0"); - $i = q("select count(id) as total from item where item_origin = 1"); - $s = q("select count(site_url) as total from site"); + $u = q("select count(channel_id) as total from channel where channel_removed = 0"); + $i = q("select count(id) as total from item where item_origin = 1"); + $s = q("select count(site_url) as total from site"); - $admins = q("select * from channel left join account on account_id = channel_account_id where ( account_roles & 4096 ) > 0 and account_default_channel = channel_id"); - $adminsx = channelx_by_n($admins[0]['channel_id']); + $admins = q("select * from channel left join account on account_id = channel_account_id where ( account_roles & 4096 ) > 0 and account_default_channel = channel_id"); + $adminsx = channelx_by_n($admins[0]['channel_id']); - $ret = []; - $ret['uri'] = z_root(); - $ret['title'] = System::get_site_name(); - $ret['description'] = bbcode(get_config('system','siteinfo',''), [ 'export' => true ] ); - $ret['email'] = get_config('system','admin_email'); - $ret['version'] = System::get_project_version(); - $ret['registrations'] = (($register) ? true : false); - $ret['approval_required'] = (($register === REGISTER_APPROVE) ? true : false); - $ret['invites_enabled'] = false; - $ret['urls'] = []; - $ret['stats'] = [ - 'user_count' => intval($u[0]['total']), - 'status_count' => intval($i[0]['total']), - 'domain_count' => intval($s[0]['total']), - ]; + $ret = []; + $ret['uri'] = z_root(); + $ret['title'] = System::get_site_name(); + $ret['description'] = bbcode(get_config('system', 'siteinfo', ''), ['export' => true]); + $ret['email'] = get_config('system', 'admin_email'); + $ret['version'] = System::get_project_version(); + $ret['registrations'] = (($register) ? true : false); + $ret['approval_required'] = (($register === REGISTER_APPROVE) ? true : false); + $ret['invites_enabled'] = false; + $ret['urls'] = []; + $ret['stats'] = [ + 'user_count' => intval($u[0]['total']), + 'status_count' => intval($i[0]['total']), + 'domain_count' => intval($s[0]['total']), + ]; - $ret['contact_account'] = self::format_channel($adminsx); + $ret['contact_account'] = self::format_channel($adminsx); - return $ret; - - } - -} \ No newline at end of file + return $ret; + } +} diff --git a/Zotlabs/Lib/MessageFilter.php b/Zotlabs/Lib/MessageFilter.php index 6941c2e45..f61e240aa 100644 --- a/Zotlabs/Lib/MessageFilter.php +++ b/Zotlabs/Lib/MessageFilter.php @@ -3,88 +3,94 @@ namespace Zotlabs\Lib; - -class MessageFilter { +class MessageFilter +{ - static public function evaluate($item,$incl,$excl) { + public static function evaluate($item, $incl, $excl) + { - require_once('include/html2plain.php'); + require_once('include/html2plain.php'); $text = prepare_text($item['body'],((isset($item['mimetype'])) ? $item['mimetype'] : 'text/x-multicode')); - $text = html2plain(($item['title']) ? $item['title'] . ' ' . $text : $text); + $text = html2plain(($item['title']) ? $item['title'] . ' ' . $text : $text); - $lang = null; + $lang = null; - if((strpos($incl,'lang=') !== false) || (strpos($excl,'lang=') !== false) || (strpos($incl,'lang!=') !== false) || (strpos($excl,'lang!=') !== false)) { - $lang = detect_language($text); - } + if ((strpos($incl, 'lang=') !== false) || (strpos($excl, 'lang=') !== false) || (strpos($incl, 'lang!=') !== false) || (strpos($excl, 'lang!=') !== false)) { + $lang = detect_language($text); + } - $tags = ((isset($item['term']) && is_array($item['term']) && count($item['term'])) ? $item['term'] : false); + $tags = ((isset($item['term']) && is_array($item['term']) && count($item['term'])) ? $item['term'] : false); - // exclude always has priority + // exclude always has priority - $exclude = (($excl) ? explode("\n",$excl) : null); + $exclude = (($excl) ? explode("\n", $excl) : null); - if($exclude) { - foreach($exclude as $word) { - $word = trim($word); - if(! $word) - continue; - if(substr($word,0,1) === '#' && $tags) { - foreach($tags as $t) - if((($t['ttype'] == TERM_HASHTAG) || ($t['ttype'] == TERM_COMMUNITYTAG)) && (($t['term'] === substr($word,1)) || (substr($word,1) === '*'))) - return false; - } - elseif(substr($word,0,1) === '$' && $tags) { - foreach($tags as $t) - if(($t['ttype'] == TERM_CATEGORY) && (($t['term'] === substr($word,1)) || (substr($word,1) === '*'))) - return false; - } - elseif((strpos($word,'/') === 0) && preg_match($word,$text)) - return false; - elseif((strpos($word,'lang=') === 0) && ($lang) && (strcasecmp($lang,trim(substr($word,5))) == 0)) - return false; - elseif((strpos($word,'lang!=') === 0) && ($lang) && (strcasecmp($lang,trim(substr($word,6))) != 0)) - return false; - elseif(stristr($text,$word) !== false) - return false; - } - } + if ($exclude) { + foreach ($exclude as $word) { + $word = trim($word); + if (! $word) { + continue; + } + if (substr($word, 0, 1) === '#' && $tags) { + foreach ($tags as $t) { + if ((($t['ttype'] == TERM_HASHTAG) || ($t['ttype'] == TERM_COMMUNITYTAG)) && (($t['term'] === substr($word, 1)) || (substr($word, 1) === '*'))) { + return false; + } + } + } elseif (substr($word, 0, 1) === '$' && $tags) { + foreach ($tags as $t) { + if (($t['ttype'] == TERM_CATEGORY) && (($t['term'] === substr($word, 1)) || (substr($word, 1) === '*'))) { + return false; + } + } + } elseif ((strpos($word, '/') === 0) && preg_match($word, $text)) { + return false; + } elseif ((strpos($word, 'lang=') === 0) && ($lang) && (strcasecmp($lang, trim(substr($word, 5))) == 0)) { + return false; + } elseif ((strpos($word, 'lang!=') === 0) && ($lang) && (strcasecmp($lang, trim(substr($word, 6))) != 0)) { + return false; + } elseif (stristr($text, $word) !== false) { + return false; + } + } + } - $include = (($incl) ? explode("\n",$incl) : null); - - if($include) { - foreach($include as $word) { - $word = trim($word); - if(! $word) - continue; - if(substr($word,0,1) === '#' && $tags) { - foreach($tags as $t) - if((($t['ttype'] == TERM_HASHTAG) || ($t['ttype'] == TERM_COMMUNITYTAG)) && (($t['term'] === substr($word,1)) || (substr($word,1) === '*'))) - return true; - } - elseif(substr($word,0,1) === '$' && $tags) { - foreach($tags as $t) - if(($t['ttype'] == TERM_CATEGORY) && (($t['term'] === substr($word,1)) || (substr($word,1) === '*'))) - return true; - } - elseif((strpos($word,'/') === 0) && preg_match($word,$text)) - return true; - elseif((strpos($word,'lang=') === 0) && ($lang) && (strcasecmp($lang,trim(substr($word,5))) == 0)) - return true; - elseif((strpos($word,'lang!=') === 0) && ($lang) && (strcasecmp($lang,trim(substr($word,6))) != 0)) - return true; - elseif(stristr($text,$word) !== false) - return true; - } - } - else { - return true; - } - - return false; - } + $include = (($incl) ? explode("\n", $incl) : null); + if ($include) { + foreach ($include as $word) { + $word = trim($word); + if (! $word) { + continue; + } + if (substr($word, 0, 1) === '#' && $tags) { + foreach ($tags as $t) { + if ((($t['ttype'] == TERM_HASHTAG) || ($t['ttype'] == TERM_COMMUNITYTAG)) && (($t['term'] === substr($word, 1)) || (substr($word, 1) === '*'))) { + return true; + } + } + } elseif (substr($word, 0, 1) === '$' && $tags) { + foreach ($tags as $t) { + if (($t['ttype'] == TERM_CATEGORY) && (($t['term'] === substr($word, 1)) || (substr($word, 1) === '*'))) { + return true; + } + } + } elseif ((strpos($word, '/') === 0) && preg_match($word, $text)) { + return true; + } elseif ((strpos($word, 'lang=') === 0) && ($lang) && (strcasecmp($lang, trim(substr($word, 5))) == 0)) { + return true; + } elseif ((strpos($word, 'lang!=') === 0) && ($lang) && (strcasecmp($lang, trim(substr($word, 6))) != 0)) { + return true; + } elseif (stristr($text, $word) !== false) { + return true; + } + } + } else { + return true; + } + return false; + } } diff --git a/Zotlabs/Lib/Nodeinfo.php b/Zotlabs/Lib/Nodeinfo.php index 62efd7583..65d5c78a5 100644 --- a/Zotlabs/Lib/Nodeinfo.php +++ b/Zotlabs/Lib/Nodeinfo.php @@ -2,43 +2,40 @@ namespace Zotlabs\Lib; +class Nodeinfo +{ - -class Nodeinfo { - - static public function fetch($url) { - $href = EMPTY_STR; - $m = parse_url($url); - if ($m['scheme'] && $m['host']) { - $s = $m['scheme'] . '://' . $m['host'] . '/.well-known/nodeinfo'; - $n = z_fetch_url($s); - if ($n['success']) { - $j = json_decode($n['body'], true); - if ($j && $j['links']) { - // lemmy just sends one result - if (isset($j['links']['rel'])) { - if ($j['links']['rel'] === 'http://nodeinfo.diaspora.software/ns/schema/2.0' && isset($j['links']['href'])) { - $href = $j['links']['href']; - } - } - else { - foreach ($j['links'] as $l) { - if (isset($l['rel']) && $l['rel'] === 'http://nodeinfo.diaspora.software/ns/schema/2.0' && isset($l['href'])) { - $href = $l['href']; - } - } - } - } - } - } - if ($href) { - $n = z_fetch_url($href); - if ($n['success']) { - return json_decode($n['body'],true); - } - } - return []; - - } - -} \ No newline at end of file + public static function fetch($url) + { + $href = EMPTY_STR; + $m = parse_url($url); + if ($m['scheme'] && $m['host']) { + $s = $m['scheme'] . '://' . $m['host'] . '/.well-known/nodeinfo'; + $n = z_fetch_url($s); + if ($n['success']) { + $j = json_decode($n['body'], true); + if ($j && $j['links']) { + // lemmy just sends one result + if (isset($j['links']['rel'])) { + if ($j['links']['rel'] === 'http://nodeinfo.diaspora.software/ns/schema/2.0' && isset($j['links']['href'])) { + $href = $j['links']['href']; + } + } else { + foreach ($j['links'] as $l) { + if (isset($l['rel']) && $l['rel'] === 'http://nodeinfo.diaspora.software/ns/schema/2.0' && isset($l['href'])) { + $href = $l['href']; + } + } + } + } + } + } + if ($href) { + $n = z_fetch_url($href); + if ($n['success']) { + return json_decode($n['body'], true); + } + } + return []; + } +} diff --git a/Zotlabs/Lib/PConfig.php b/Zotlabs/Lib/PConfig.php index efc5698c2..b456be5ca 100644 --- a/Zotlabs/Lib/PConfig.php +++ b/Zotlabs/Lib/PConfig.php @@ -2,6 +2,8 @@ namespace Zotlabs\Lib; +use App; + /** * @brief Class for handling channel specific configurations. * @@ -16,193 +18,212 @@ namespace Zotlabs\Lib; * The old (deprecated?) way to access a PConfig value is: * @code{.php}$var = get_pconfig(local_channel(), 'category', 'key');@endcode */ -class PConfig { +class PConfig +{ - /** - * @brief Loads all configuration values of a channel into a cached storage. - * - * All configuration values of the given channel are stored in global cache - * which is available under the global variable App::$config[$uid]. - * - * @param string $uid - * The channel_id - * @return void|false Nothing or false if $uid is null or false - */ - static public function Load($uid) { - if(is_null($uid) || $uid === false) - return false; + /** + * @brief Loads all configuration values of a channel into a cached storage. + * + * All configuration values of the given channel are stored in global cache + * which is available under the global variable App::$config[$uid]. + * + * @param string $uid + * The channel_id + * @return void|false Nothing or false if $uid is null or false + */ + public static function Load($uid) + { + if (is_null($uid) || $uid === false) { + return false; + } - if(! is_array(\App::$config)) { - btlogger('App::$config not an array'); - } + if (! is_array(App::$config)) { + btlogger('App::$config not an array'); + } - if(! array_key_exists($uid, \App::$config)) { - \App::$config[$uid] = []; - } + if (! array_key_exists($uid, App::$config)) { + App::$config[$uid] = []; + } - if(! is_array(\App::$config[$uid])) { - btlogger('App::$config[$uid] not an array: ' . $uid); - } + if (! is_array(App::$config[$uid])) { + btlogger('App::$config[$uid] not an array: ' . $uid); + } - $r = q("SELECT * FROM pconfig WHERE uid = %d", - intval($uid) - ); + $r = q( + "SELECT * FROM pconfig WHERE uid = %d", + intval($uid) + ); - if($r) { - foreach($r as $rr) { - $k = $rr['k']; - $c = $rr['cat']; - if(! array_key_exists($c, \App::$config[$uid])) { - \App::$config[$uid][$c] = []; - \App::$config[$uid][$c]['config_loaded'] = true; - } - \App::$config[$uid][$c][$k] = $rr['v']; - } - } - } + if ($r) { + foreach ($r as $rr) { + $k = $rr['k']; + $c = $rr['cat']; + if (! array_key_exists($c, App::$config[$uid])) { + App::$config[$uid][$c] = []; + App::$config[$uid][$c]['config_loaded'] = true; + } + App::$config[$uid][$c][$k] = $rr['v']; + } + } + } - /** - * @brief Get a particular channel's config variable given the category name - * ($family) and a key. - * - * Get a particular channel's config value from the given category ($family) - * and the $key from a cached storage in App::$config[$uid]. - * - * Returns false if not set. - * - * @param string $uid - * The channel_id - * @param string $family - * The category of the configuration value - * @param string $key - * The configuration key to query - * @param mixed $default (optional, default false) - * Default value to return if key does not exist - * @return mixed Stored value or false if it does not exist - */ - static public function Get($uid, $family, $key, $default = false) { + /** + * @brief Get a particular channel's config variable given the category name + * ($family) and a key. + * + * Get a particular channel's config value from the given category ($family) + * and the $key from a cached storage in App::$config[$uid]. + * + * Returns false if not set. + * + * @param string $uid + * The channel_id + * @param string $family + * The category of the configuration value + * @param string $key + * The configuration key to query + * @param mixed $default (optional, default false) + * Default value to return if key does not exist + * @return mixed Stored value or false if it does not exist + */ + public static function Get($uid, $family, $key, $default = false) + { - if(is_null($uid) || $uid === false) - return $default; + if (is_null($uid) || $uid === false) { + return $default; + } - if(! array_key_exists($uid, \App::$config)) - self::Load($uid); + 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 $default; + if ((! array_key_exists($family, App::$config[$uid])) || (! array_key_exists($key, App::$config[$uid][$family]))) { + return $default; + } - return unserialise(\App::$config[$uid][$family][$key]); - } + return unserialise(App::$config[$uid][$family][$key]); + } - /** - * @brief Sets a configuration value for a channel. - * - * Stores a config value ($value) in the category ($family) under the key ($key) - * for the channel_id $uid. - * - * @param string $uid - * The channel_id - * @param string $family - * The category of the configuration value - * @param string $key - * The configuration key to set - * @param string $value - * The value to store - * @return mixed Stored $value or false - */ - static public function Set($uid, $family, $key, $value) { + /** + * @brief Sets a configuration value for a channel. + * + * Stores a config value ($value) in the category ($family) under the key ($key) + * for the channel_id $uid. + * + * @param string $uid + * The channel_id + * @param string $family + * The category of the configuration value + * @param string $key + * The configuration key to set + * @param string $value + * The value to store + * @return mixed Stored $value or false + */ + public static function Set($uid, $family, $key, $value) + { - // this catches subtle errors where this function has been called - // with local_channel() when not logged in (which returns false) - // and throws an error in array_key_exists below. - // we provide a function backtrace in the logs so that we can find - // and fix the calling function. + // this catches subtle errors where this function has been called + // with local_channel() when not logged in (which returns false) + // and throws an error in array_key_exists below. + // we provide a function backtrace in the logs so that we can find + // and fix the calling function. - if(is_null($uid) || $uid === false) { - btlogger('UID is FALSE!', LOGGER_NORMAL, LOG_ERR); - return; - } + if (is_null($uid) || $uid === false) { + btlogger('UID is FALSE!', LOGGER_NORMAL, LOG_ERR); + return; + } - // manage array value - $dbvalue = ((is_array($value)) ? serialise($value) : $value); - $dbvalue = ((is_bool($dbvalue)) ? intval($dbvalue) : $dbvalue); + // manage array value + $dbvalue = ((is_array($value)) ? serialise($value) : $value); + $dbvalue = ((is_bool($dbvalue)) ? intval($dbvalue) : $dbvalue); - if(self::Get($uid, $family, $key) === false) { - if(! array_key_exists($uid, \App::$config)) - \App::$config[$uid] = []; - if(! array_key_exists($family, \App::$config[$uid])) - \App::$config[$uid][$family] = []; + if (self::Get($uid, $family, $key) === false) { + if (! array_key_exists($uid, App::$config)) { + App::$config[$uid] = []; + } + if (! array_key_exists($family, App::$config[$uid])) { + App::$config[$uid][$family] = []; + } - $ret = q("INSERT INTO pconfig ( uid, cat, k, v ) VALUES ( %d, '%s', '%s', '%s' ) ", - intval($uid), - dbesc($family), - dbesc($key), - dbesc($dbvalue) - ); - } - else { + $ret = q( + "INSERT INTO pconfig ( uid, cat, k, v ) VALUES ( %d, '%s', '%s', '%s' ) ", + intval($uid), + dbesc($family), + dbesc($key), + dbesc($dbvalue) + ); + } else { + $ret = q( + "UPDATE pconfig SET v = '%s' WHERE uid = %d and cat = '%s' AND k = '%s'", + dbesc($dbvalue), + intval($uid), + dbesc($family), + dbesc($key) + ); + } - $ret = q("UPDATE pconfig SET v = '%s' WHERE uid = %d and cat = '%s' AND k = '%s'", - dbesc($dbvalue), - intval($uid), - dbesc($family), - dbesc($key) - ); - } + // keep a separate copy for all variables which were + // set in the life of this page. We need this to + // synchronise channel clones. - // keep a separate copy for all variables which were - // set in the life of this page. We need this to - // synchronise channel clones. + if (! array_key_exists('transient', App::$config[$uid])) { + App::$config[$uid]['transient'] = []; + } + if (! array_key_exists($family, App::$config[$uid]['transient'])) { + App::$config[$uid]['transient'][$family] = []; + } - if(! array_key_exists('transient', \App::$config[$uid])) - \App::$config[$uid]['transient'] = []; - if(! array_key_exists($family, \App::$config[$uid]['transient'])) - \App::$config[$uid]['transient'][$family] = []; + App::$config[$uid][$family][$key] = $value; + App::$config[$uid]['transient'][$family][$key] = $value; - \App::$config[$uid][$family][$key] = $value; - \App::$config[$uid]['transient'][$family][$key] = $value; + if ($ret) { + return $value; + } - if($ret) - return $value; - - return $ret; - } + return $ret; + } - /** - * @brief Deletes the given key from the channel's configuration. - * - * Removes the configured value from the stored cache in App::$config[$uid] - * and removes it from the database. - * - * @param string $uid - * The channel_id - * @param string $family - * The category of the configuration value - * @param string $key - * The configuration key to delete - * @return mixed - */ - static public function Delete($uid, $family, $key) { + /** + * @brief Deletes the given key from the channel's configuration. + * + * Removes the configured value from the stored cache in App::$config[$uid] + * and removes it from the database. + * + * @param string $uid + * The channel_id + * @param string $family + * The category of the configuration value + * @param string $key + * The configuration key to delete + * @return mixed + */ + public static function Delete($uid, $family, $key) + { - if(is_null($uid) || $uid === false) - return false; + if (is_null($uid) || $uid === false) { + return false; + } - $ret = false; + $ret = false; - if(array_key_exists($uid,\App::$config) - && is_array(\App::$config['uid']) - && array_key_exists($family,\App::$config['uid']) - && array_key_exists($key, \App::$config[$uid][$family])) - unset(\App::$config[$uid][$family][$key]); + if ( + array_key_exists($uid, App::$config) + && is_array(App::$config['uid']) + && array_key_exists($family, App::$config['uid']) + && array_key_exists($key, App::$config[$uid][$family]) + ) { + unset(App::$config[$uid][$family][$key]); + } - $ret = q("DELETE FROM pconfig WHERE uid = %d AND cat = '%s' AND k = '%s'", - intval($uid), - dbesc($family), - dbesc($key) - ); - - return $ret; - } + $ret = q( + "DELETE FROM pconfig WHERE uid = %d AND cat = '%s' AND k = '%s'", + intval($uid), + dbesc($family), + dbesc($key) + ); + return $ret; + } } diff --git a/Zotlabs/Lib/Permcat.php b/Zotlabs/Lib/Permcat.php index 92f69cdbd..c94061586 100644 --- a/Zotlabs/Lib/Permcat.php +++ b/Zotlabs/Lib/Permcat.php @@ -21,157 +21,168 @@ use Zotlabs\Access\Permissions; * These answer the question "Can Joe view *this* album/photo?". */ -class Permcat { +class Permcat +{ - /** - * @var array - */ - private $permcats = []; + /** + * @var array + */ + private $permcats = []; - /** - * @brief Permcat constructor. - * - * @param int $channel_id - */ - public function __construct($channel_id) { + /** + * @brief Permcat constructor. + * + * @param int $channel_id + */ + public function __construct($channel_id) + { - $perms = []; + $perms = []; - // first check role perms for a perms_connect setting + // first check role perms for a perms_connect setting - $role = get_pconfig($channel_id,'system','permissions_role'); - if($role) { - $x = PermissionRoles::role_perms($role); - if($x['perms_connect']) { - $perms = Permissions::FilledPerms($x['perms_connect']); - } - } + $role = get_pconfig($channel_id, 'system', 'permissions_role'); + if ($role) { + $x = PermissionRoles::role_perms($role); + if ($x['perms_connect']) { + $perms = Permissions::FilledPerms($x['perms_connect']); + } + } - // if no role perms it may be a custom role, see if there any autoperms + // if no role perms it may be a custom role, see if there any autoperms - if(! $perms) { - $perms = Permissions::FilledAutoPerms($channel_id); - } + if (! $perms) { + $perms = Permissions::FilledAutoPerms($channel_id); + } - // if no autoperms it may be a custom role with manual perms + // if no autoperms it may be a custom role with manual perms - if(! $perms) { - $c = channelx_by_n($channel_id); - if($c) { - $perms = Permissions::FilledPerms(get_abconfig($channel_id,$c['channel_hash'],'system','my_perms',EMPTY_STR)); - } - } + if (! $perms) { + $c = channelx_by_n($channel_id); + if ($c) { + $perms = Permissions::FilledPerms(get_abconfig($channel_id, $c['channel_hash'], 'system', 'my_perms', EMPTY_STR)); + } + } - // nothing was found - create a filled permission array where all permissions are 0 + // nothing was found - create a filled permission array where all permissions are 0 - if(! $perms) { - $perms = Permissions::FilledPerms([]); - } + if (! $perms) { + $perms = Permissions::FilledPerms([]); + } - $this->permcats[] = [ - 'name' => 'default', - 'localname' => t('default','permcat'), - 'perms' => Permissions::Operms($perms), - 'system' => 1 - ]; + $this->permcats[] = [ + 'name' => 'default', + 'localname' => t('default', 'permcat'), + 'perms' => Permissions::Operms($perms), + 'system' => 1 + ]; - $p = $this->load_permcats($channel_id); - if($p) { - for($x = 0; $x < count($p); $x++) { - $this->permcats[] = [ - 'name' => $p[$x][0], - 'localname' => $p[$x][1], - 'perms' => Permissions::Operms(Permissions::FilledPerms($p[$x][2])), - 'system' => intval($p[$x][3]) - ]; - } - } - } + $p = $this->load_permcats($channel_id); + if ($p) { + for ($x = 0; $x < count($p); $x++) { + $this->permcats[] = [ + 'name' => $p[$x][0], + 'localname' => $p[$x][1], + 'perms' => Permissions::Operms(Permissions::FilledPerms($p[$x][2])), + 'system' => intval($p[$x][3]) + ]; + } + } + } - /** - * @brief Return array with permcats. - * - * @return array - */ - public function listing() { - return $this->permcats; - } + /** + * @brief Return array with permcats. + * + * @return array + */ + public function listing() + { + return $this->permcats; + } - /** - * @brief - * - * @param string $name - * @return array - * * \e array with permcats - * * \e bool \b error if $name not found in permcats true - */ - public function fetch($name) { - if($name && $this->permcats) { - foreach($this->permcats as $permcat) { - if(strcasecmp($permcat['name'], $name) === 0) { - return $permcat; - } - } - } + /** + * @brief + * + * @param string $name + * @return array + * * \e array with permcats + * * \e bool \b error if $name not found in permcats true + */ + public function fetch($name) + { + if ($name && $this->permcats) { + foreach ($this->permcats as $permcat) { + if (strcasecmp($permcat['name'], $name) === 0) { + return $permcat; + } + } + } - return ['error' => true]; - } + return ['error' => true]; + } - public function load_permcats($uid) { + public function load_permcats($uid) + { - $permcats = [ - [ 'follower', t('follower','permcat'), - [ 'view_stream','view_profile','view_contacts','view_storage','view_pages','view_wiki', - 'post_like' ], 1 - ], - [ 'contributor', t('contributor','permcat'), - [ 'view_stream','view_profile','view_contacts','view_storage','view_pages','view_wiki', - 'post_wall','post_comments','write_wiki','post_like','tag_deliver','chat' ], 1 - ], - [ 'publisher', t('publisher','permcat'), - [ 'view_stream','view_profile','view_contacts','view_storage','view_pages', - 'write_storage','post_wall','write_pages','write_wiki','post_comments','post_like','tag_deliver', - 'chat', 'republish' ], 1 - ] - ]; + $permcats = [ + [ 'follower', t('follower', 'permcat'), + [ 'view_stream','view_profile','view_contacts','view_storage','view_pages','view_wiki', + 'post_like' ], 1 + ], + [ 'contributor', t('contributor', 'permcat'), + [ 'view_stream','view_profile','view_contacts','view_storage','view_pages','view_wiki', + 'post_wall','post_comments','write_wiki','post_like','tag_deliver','chat' ], 1 + ], + [ 'publisher', t('publisher', 'permcat'), + [ 'view_stream','view_profile','view_contacts','view_storage','view_pages', + 'write_storage','post_wall','write_pages','write_wiki','post_comments','post_like','tag_deliver', + 'chat', 'republish' ], 1 + ] + ]; - if($uid) { - $x = q("select * from pconfig where uid = %d and cat = 'permcat'", - intval($uid) - ); - if($x) { - foreach($x as $xv) { - $value = ((preg_match('|^a:[0-9]+:{.*}$|s', $xv['v'])) ? unserialize($xv['v']) : $xv['v']); - $permcats[] = [ $xv['k'], $xv['k'], $value, 0 ]; - } - } - } + if ($uid) { + $x = q( + "select * from pconfig where uid = %d and cat = 'permcat'", + intval($uid) + ); + if ($x) { + foreach ($x as $xv) { + $value = ((preg_match('|^a:[0-9]+:{.*}$|s', $xv['v'])) ? unserialize($xv['v']) : $xv['v']); + $permcats[] = [ $xv['k'], $xv['k'], $value, 0 ]; + } + } + } - /** - * @hooks permcats - * * \e array - */ - call_hooks('permcats', $permcats); + /** + * @hooks permcats + * * \e array + */ + call_hooks('permcats', $permcats); - return $permcats; - } + return $permcats; + } - static public function find_permcat($arr, $name) { - if((! $arr) || (! $name)) - return false; + public static function find_permcat($arr, $name) + { + if ((! $arr) || (! $name)) { + return false; + } - foreach($arr as $p) - if($p['name'] == $name) - return $p['value']; - } + foreach ($arr as $p) { + if ($p['name'] == $name) { + return $p['value']; + } + } + } - static public function update($channel_id, $name, $permarr) { - PConfig::Set($channel_id, 'permcat', $name, $permarr); - } + public static function update($channel_id, $name, $permarr) + { + PConfig::Set($channel_id, 'permcat', $name, $permarr); + } - static public function delete($channel_id, $name) { - PConfig::Delete($channel_id, 'permcat', $name); - } - -} \ No newline at end of file + public static function delete($channel_id, $name) + { + PConfig::Delete($channel_id, 'permcat', $name); + } +} diff --git a/Zotlabs/Lib/PermissionDescription.php b/Zotlabs/Lib/PermissionDescription.php index 51d5f890d..58d2ee7f3 100644 --- a/Zotlabs/Lib/PermissionDescription.php +++ b/Zotlabs/Lib/PermissionDescription.php @@ -2,6 +2,10 @@ namespace Zotlabs\Lib; +use App; +use Zotlabs\Access\PermissionLimits; +use Zotlabs\Access\Permissions; + require_once("include/permissions.php"); require_once("include/language.php"); require_once("include/text.php"); @@ -12,148 +16,178 @@ require_once("include/text.php"); * permission settings for an item with an empty ACL. * i.e the caption, icon, and tooltip for the no-ACL option in the ACL dialog. */ -class PermissionDescription { +class PermissionDescription +{ - private $global_perm; - private $channel_perm; - private $fallback_description; + private $global_perm; + private $channel_perm; + private $fallback_description; - /** - * Constructor is private. - * Use static methods fromGlobalPermission(), fromStandalonePermission(), - * or fromDescription() to create instances. - * - * @internal - * @param int $global_perm - * @param int $channel_perm - * @param string $description (optional) default empty - */ - private function __construct($global_perm, $channel_perm, $description = '') { - $this->global_perm = $global_perm; - $this->channel_perm = $channel_perm; - $this->fallback_description = ($description == '') ? t('Visible to your default audience') : $description; - } + /** + * Constructor is private. + * Use static methods fromGlobalPermission(), fromStandalonePermission(), + * or fromDescription() to create instances. + * + * @internal + * @param int $global_perm + * @param int $channel_perm + * @param string $description (optional) default empty + */ + private function __construct($global_perm, $channel_perm, $description = '') + { + $this->global_perm = $global_perm; + $this->channel_perm = $channel_perm; + $this->fallback_description = ($description == '') ? t('Visible to your default audience') : $description; + } - /** - * If the interpretation of an empty ACL can't be summarised with a global default permission - * or a specific permission setting then use this method and describe what it means instead. - * Remember to localize the description first. - * - * @param string $description - the localized caption for the no-ACL option in the ACL dialog. - * @return a new instance of PermissionDescription - */ - public static function fromDescription($description) { - return new PermissionDescription('', 0x80000, $description); - } + /** + * If the interpretation of an empty ACL can't be summarised with a global default permission + * or a specific permission setting then use this method and describe what it means instead. + * Remember to localize the description first. + * + * @param string $description - the localized caption for the no-ACL option in the ACL dialog. + * @return a new instance of PermissionDescription + */ + public static function fromDescription($description) + { + return new PermissionDescription('', 0x80000, $description); + } - /** - * Use this method only if the interpretation of an empty ACL doesn't fall back to a global - * default permission. You should pass one of the constants from boot.php - PERMS_PUBLIC, - * PERMS_NETWORK etc. - * - * @param integer $perm - a single enumerated constant permission - PERMS_PUBLIC, PERMS_NETWORK etc. - * @return a new instance of PermissionDescription - */ - public static function fromStandalonePermission($perm) { + /** + * Use this method only if the interpretation of an empty ACL doesn't fall back to a global + * default permission. You should pass one of the constants from boot.php - PERMS_PUBLIC, + * PERMS_NETWORK etc. + * + * @param int $perm - a single enumerated constant permission - PERMS_PUBLIC, PERMS_NETWORK etc. + * @return a new instance of PermissionDescription + */ + public static function fromStandalonePermission($perm) + { - $result = new PermissionDescription('', $perm); + $result = new PermissionDescription('', $perm); - $checkPerm = $result->get_permission_description(); - if($checkPerm == $result->fallback_description) { - $result = null; - logger('null PermissionDescription from unknown standalone permission: ' . $perm, LOGGER_DEBUG, LOG_ERR); - } + $checkPerm = $result->get_permission_description(); + if ($checkPerm == $result->fallback_description) { + $result = null; + logger('null PermissionDescription from unknown standalone permission: ' . $perm, LOGGER_DEBUG, LOG_ERR); + } - return $result; - } + return $result; + } - /** - * This is the preferred way to create a PermissionDescription, as it provides the most details. - * Use this method if you know an empty ACL will result in one of the global default permissions - * being used, such as channel_r_stream (for which you would pass 'view_stream'). - * - * @param string $permname - a key for the global perms array from get_perms() in permissions.php, - * e.g. 'view_stream', 'view_profile', etc. - * @return a new instance of PermissionDescription - */ - public static function fromGlobalPermission($permname) { + /** + * This is the preferred way to create a PermissionDescription, as it provides the most details. + * Use this method if you know an empty ACL will result in one of the global default permissions + * being used, such as channel_r_stream (for which you would pass 'view_stream'). + * + * @param string $permname - a key for the global perms array from get_perms() in permissions.php, + * e.g. 'view_stream', 'view_profile', etc. + * @return a new instance of PermissionDescription + */ + public static function fromGlobalPermission($permname) + { - $result = null; + $result = null; - $global_perms = \Zotlabs\Access\Permissions::Perms(); + $global_perms = Permissions::Perms(); - if(array_key_exists($permname, $global_perms)) { + if (array_key_exists($permname, $global_perms)) { + $channelPerm = PermissionLimits::Get(App::$channel['channel_id'], $permname); - $channelPerm = \Zotlabs\Access\PermissionLimits::Get(\App::$channel['channel_id'], $permname); + $result = new PermissionDescription('', $channelPerm); + } else { + // The acl dialog can handle null arguments, but it shouldn't happen + logger('null PermissionDescription from unknown global permission: ' . $permname, LOGGER_DEBUG, LOG_ERR); + } - $result = new PermissionDescription('', $channelPerm); - } else { - // The acl dialog can handle null arguments, but it shouldn't happen - logger('null PermissionDescription from unknown global permission: ' . $permname, LOGGER_DEBUG, LOG_ERR); - } + return $result; + } - return $result; - } + /** + * Gets a localized description of the permission, or a generic message if the permission + * is unknown. + * + * @return string description + */ + public function get_permission_description() + { - /** - * Gets a localized description of the permission, or a generic message if the permission - * is unknown. - * - * @return string description - */ - public function get_permission_description() { + switch ($this->channel_perm) { + case 0: + return t('Only me'); + case PERMS_PUBLIC: + return t('Public'); + case PERMS_NETWORK: + return t('Anybody in the $Projectname network'); + case PERMS_SITE: + return sprintf(t('Any account on %s'), App::get_hostname()); + case PERMS_CONTACTS: + return t('Any of my connections'); + case PERMS_SPECIFIC: + return t('Only connections I specifically allow'); + case PERMS_AUTHED: + return t('Anybody authenticated (could include visitors from other networks)'); + case PERMS_PENDING: + return t('Any connections including those who haven\'t yet been approved'); + default: + return $this->fallback_description; + } + } - switch($this->channel_perm) { - case 0: return t('Only me'); - case PERMS_PUBLIC: return t('Public'); - case PERMS_NETWORK: return t('Anybody in the $Projectname network'); - case PERMS_SITE: return sprintf(t('Any account on %s'), \App::get_hostname()); - case PERMS_CONTACTS: return t('Any of my connections'); - case PERMS_SPECIFIC: return t('Only connections I specifically allow'); - case PERMS_AUTHED: return t('Anybody authenticated (could include visitors from other networks)'); - case PERMS_PENDING: return t('Any connections including those who haven\'t yet been approved'); - default: return $this->fallback_description; - } - } + /** + * Returns an icon css class name if an appropriate one is available, e.g. "fa-globe" for Public, + * otherwise returns empty string. + * + * @return string icon css class name (often FontAwesome) + */ + public function get_permission_icon() + { - /** - * Returns an icon css class name if an appropriate one is available, e.g. "fa-globe" for Public, - * otherwise returns empty string. - * - * @return string icon css class name (often FontAwesome) - */ - public function get_permission_icon() { + switch ($this->channel_perm) { + case 0: + return 'fa-eye-slash'; + case PERMS_PUBLIC: + return 'fa-globe'; + case PERMS_NETWORK: + return 'fa-share-alt-square'; // fa-share-alt-square is very similiar to the hubzilla logo, but we should create our own logo class to use + case PERMS_SITE: + return 'fa-sitemap'; + case PERMS_CONTACTS: + return 'fa-group'; + case PERMS_SPECIFIC: + return 'fa-list'; + case PERMS_AUTHED: + return ''; + case PERMS_PENDING: + return ''; + default: + return ''; + } + } - switch($this->channel_perm) { - case 0:/* only me */ return 'fa-eye-slash'; - case PERMS_PUBLIC: return 'fa-globe'; - case PERMS_NETWORK: return 'fa-share-alt-square'; // fa-share-alt-square is very similiar to the hubzilla logo, but we should create our own logo class to use - case PERMS_SITE: return 'fa-sitemap'; - case PERMS_CONTACTS: return 'fa-group'; - case PERMS_SPECIFIC: return 'fa-list'; - case PERMS_AUTHED: return ''; - case PERMS_PENDING: return ''; - default: return ''; - } - } - - /** - * Returns a localized description of where the permission came from, if this is known. - * If it's not know, or if the permission is standalone and didn't come from a default - * permission setting, then empty string is returned. - * - * @return string description or empty string - */ - public function get_permission_origin_description() { - - switch($this->global_perm) { - case PERMS_R_STREAM: return t('This is your default setting for the audience of your normal stream, and posts.'); - case PERMS_R_PROFILE: return t('This is your default setting for who can view your default channel profile'); - case PERMS_R_ABOOK: return t('This is your default setting for who can view your connections'); - case PERMS_R_STORAGE: return t('This is your default setting for who can view your file storage and photos'); - case PERMS_R_PAGES: return t('This is your default setting for the audience of your webpages'); - default: return ''; - } - } + /** + * Returns a localized description of where the permission came from, if this is known. + * If it's not know, or if the permission is standalone and didn't come from a default + * permission setting, then empty string is returned. + * + * @return string description or empty string + */ + public function get_permission_origin_description() + { + switch ($this->global_perm) { + case PERMS_R_STREAM: + return t('This is your default setting for the audience of your normal stream, and posts.'); + case PERMS_R_PROFILE: + return t('This is your default setting for who can view your default channel profile'); + case PERMS_R_ABOOK: + return t('This is your default setting for who can view your connections'); + case PERMS_R_STORAGE: + return t('This is your default setting for who can view your file storage and photos'); + case PERMS_R_PAGES: + return t('This is your default setting for the audience of your webpages'); + default: + return ''; + } + } } diff --git a/Zotlabs/Lib/Queue.php b/Zotlabs/Lib/Queue.php index 02b4f9038..1ff8d18a8 100644 --- a/Zotlabs/Lib/Queue.php +++ b/Zotlabs/Lib/Queue.php @@ -1,4 +1,6 @@ - $base, + 'site_update' => datetime_convert(), + 'site_dead' => 0, + 'site_type' => ((in_array($outq['outq_driver'], ['post', 'activitypub'])) ? SITE_TYPE_NOTZOT : SITE_TYPE_UNKNOWN), + 'site_crypto' => '' + ] + ); + } + } - site_store_lowlevel( - [ - 'site_url' => $base, - 'site_update' => datetime_convert(), - 'site_dead' => 0, - 'site_type' => ((in_array($outq['outq_driver'], [ 'post', 'activitypub' ])) ? SITE_TYPE_NOTZOT : SITE_TYPE_UNKNOWN), - 'site_crypto' => '' - ] - ); - } - } + $arr = array('outq' => $outq, 'base' => $base, 'handled' => false, 'immediate' => $immediate); + call_hooks('queue_deliver', $arr); + if ($arr['handled']) { + return; + } - $arr = array('outq' => $outq, 'base' => $base, 'handled' => false, 'immediate' => $immediate); - call_hooks('queue_deliver',$arr); - if($arr['handled']) - return; + // "post" queue driver - used for diaspora and friendica-over-diaspora communications. - // "post" queue driver - used for diaspora and friendica-over-diaspora communications. + if ($outq['outq_driver'] === 'post') { + $result = z_post_url($outq['outq_posturl'], $outq['outq_msg']); + if ($result['success'] && $result['return_code'] < 300) { + logger('deliver: queue post success to ' . $outq['outq_posturl'], LOGGER_DEBUG); + if ($base) { + q( + "update site set site_update = '%s', site_dead = 0 where site_url = '%s' ", + dbesc(datetime_convert()), + dbesc($base) + ); + } + q( + "update dreport set dreport_result = '%s', dreport_time = '%s' where dreport_queue = '%s'", + dbesc('accepted for delivery'), + dbesc(datetime_convert()), + dbesc($outq['outq_hash']) + ); + self::remove($outq['outq_hash']); - if($outq['outq_driver'] === 'post') { - $result = z_post_url($outq['outq_posturl'],$outq['outq_msg']); - if($result['success'] && $result['return_code'] < 300) { - logger('deliver: queue post success to ' . $outq['outq_posturl'], LOGGER_DEBUG); - if($base) { - q("update site set site_update = '%s', site_dead = 0 where site_url = '%s' ", - dbesc(datetime_convert()), - dbesc($base) - ); - } - q("update dreport set dreport_result = '%s', dreport_time = '%s' where dreport_queue = '%s'", - dbesc('accepted for delivery'), - dbesc(datetime_convert()), - dbesc($outq['outq_hash']) - ); - self::remove($outq['outq_hash']); + // server is responding - see if anything else is going to this destination and is piled up + // and try to send some more. We're relying on the fact that do_delivery() results in an + // immediate delivery otherwise we could get into a queue loop. - // server is responding - see if anything else is going to this destination and is piled up - // and try to send some more. We're relying on the fact that do_delivery() results in an - // immediate delivery otherwise we could get into a queue loop. + if (!$immediate) { + $x = q( + "select outq_hash from outq where outq_posturl = '%s' and outq_delivered = 0", + dbesc($outq['outq_posturl']) + ); - if(! $immediate) { - $x = q("select outq_hash from outq where outq_posturl = '%s' and outq_delivered = 0", - dbesc($outq['outq_posturl']) - ); - - $piled_up = []; - if($x) { - foreach($x as $xx) { - $piled_up[] = $xx['outq_hash']; - } - } - if($piled_up) { - // call do_delivery() with the force flag - do_delivery($piled_up, true); - } - } - } - else { - logger('deliver: queue post returned ' . $result['return_code'] - . ' from ' . $outq['outq_posturl'],LOGGER_DEBUG); - self::update($outq['outq_hash'],10); - } - return; - } + $piled_up = []; + if ($x) { + foreach ($x as $xx) { + $piled_up[] = $xx['outq_hash']; + } + } + if ($piled_up) { + // call do_delivery() with the force flag + do_delivery($piled_up, true); + } + } + } else { + logger('deliver: queue post returned ' . $result['return_code'] + . ' from ' . $outq['outq_posturl'], LOGGER_DEBUG); + self::update($outq['outq_hash'], 10); + } + return; + } - if ($outq['outq_driver'] === 'asfetch') { + if ($outq['outq_driver'] === 'asfetch') { + $channel = channelx_by_n($outq['outq_channel']); + if (!$channel) { + logger('missing channel: ' . $outq['outq_channel']); + return; + } - $channel = channelx_by_n($outq['outq_channel']); - if (! $channel) { - logger('missing channel: ' . $outq['outq_channel']); - return; - } + if (!ActivityStreams::is_url($outq['outq_posturl'])) { + logger('fetch item is not url: ' . $outq['outq_posturl']); + self::remove($outq['outq_hash']); + return; + } - if (! ActivityStreams::is_url($outq['outq_posturl'])) { - logger('fetch item is not url: ' . $outq['outq_posturl']); - self::remove($outq['outq_hash']); - return; - } + $j = Activity::fetch($outq['outq_posturl'], $channel); + if ($j) { + $AS = new ActivityStreams($j, null, true); + if ($AS->is_valid() && isset($AS->data['type'])) { + if (ActivityStreams::is_an_actor($AS->data['type'])) { + Activity::actor_store($AS->data['id'], $AS->data); + } + if (strpos($AS->data['type'], 'Collection') !== false) { + // we are probably fetching a collection already - and do not support collection recursion at this time + self::remove($outq['outq_hash']); + return; + } + $item = Activity::decode_note($AS, true); + if ($item) { + Activity::store($channel, $channel['channnel_hash'], $AS, $item, true, true); + } + } + logger('deliver: queue fetch success from ' . $outq['outq_posturl'], LOGGER_DEBUG); + self::remove($outq['outq_hash']); - $j = Activity::fetch($outq['outq_posturl'],$channel); - if ($j) { - $AS = new ActivityStreams($j, null, true); - if ($AS->is_valid() && isset($AS->data['type'])) { - if (ActivityStreams::is_an_actor($AS->data['type'])) { - Activity::actor_store($AS->data['id'],$AS->data); - } - if (strpos($AS->data['type'],'Collection') !== false) { - // we are probably fetching a collection already - and do not support collection recursion at this time - self::remove($outq['outq_hash']); - return; - } - $item = Activity::decode_note($AS,true); - if ($item) { - Activity::store($channel,$channel['channnel_hash'],$AS,$item,true,true); - } - } - logger('deliver: queue fetch success from ' . $outq['outq_posturl'], LOGGER_DEBUG); - self::remove($outq['outq_hash']); + // server is responding - see if anything else is going to this destination and is piled up + // and try to send some more. We're relying on the fact that do_delivery() results in an + // immediate delivery otherwise we could get into a queue loop. - // server is responding - see if anything else is going to this destination and is piled up - // and try to send some more. We're relying on the fact that do_delivery() results in an - // immediate delivery otherwise we could get into a queue loop. + if (!$immediate) { + $x = q( + "select outq_hash from outq where outq_driver = 'asfetch' and outq_channel = %d and outq_delivered = 0", + dbesc($outq['outq_channel']) + ); - if (! $immediate) { - $x = q("select outq_hash from outq where outq_driver = 'asfetch' and outq_channel = %d and outq_delivered = 0", - dbesc($outq['outq_channel']) - ); + $piled_up = []; + if ($x) { + foreach ($x as $xx) { + $piled_up[] = $xx['outq_hash']; + } + } + if ($piled_up) { + do_delivery($piled_up, true); + } + } + } else { + logger('deliver: queue fetch failed' . ' from ' . $outq['outq_posturl'], LOGGER_DEBUG); + self::update($outq['outq_hash'], 10); + } + return; + } - $piled_up = []; - if ($x) { - foreach ($x as $xx) { - $piled_up[] = $xx['outq_hash']; - } - } - if ($piled_up) { - do_delivery($piled_up,true); - } - } - } - else { - logger('deliver: queue fetch failed' . ' from ' . $outq['outq_posturl'],LOGGER_DEBUG); - self::update($outq['outq_hash'],10); - } - return; - } - - if($outq['outq_driver'] === 'activitypub') { - - $channel = channelx_by_n($outq['outq_channel']); - if (! $channel) { - logger('missing channel: ' . $outq['outq_channel']); - return; - } + if ($outq['outq_driver'] === 'activitypub') { + $channel = channelx_by_n($outq['outq_channel']); + if (!$channel) { + logger('missing channel: ' . $outq['outq_channel']); + return; + } - $retries = 0; - $m = parse_url($outq['outq_posturl']); - - $headers = []; - $headers['Content-Type'] = 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"' ; - $ret = $outq['outq_msg']; - logger('ActivityPub send: ' . jindent($ret), LOGGER_DATA); - $headers['Date'] = datetime_convert('UTC','UTC', 'now', 'D, d M Y H:i:s \\G\\M\\T'); - $headers['Digest'] = HTTPSig::generate_digest_header($ret); - $headers['Host'] = $m['host']; - $headers['(request-target)'] = 'post ' . get_request_string($outq['outq_posturl']); + $retries = 0; + $m = parse_url($outq['outq_posturl']); - $xhead = HTTPSig::create_sig($headers,$channel['channel_prvkey'],channel_url($channel)); - if(strpos($outq['outq_posturl'],'http') !== 0) { - logger('bad url: ' . $outq['outq_posturl']); - self::remove($outq['outq_hash']); - } + $headers = []; + $headers['Content-Type'] = 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"'; + $ret = $outq['outq_msg']; + logger('ActivityPub send: ' . jindent($ret), LOGGER_DATA); + $headers['Date'] = datetime_convert('UTC', 'UTC', 'now', 'D, d M Y H:i:s \\G\\M\\T'); + $headers['Digest'] = HTTPSig::generate_digest_header($ret); + $headers['Host'] = $m['host']; + $headers['(request-target)'] = 'post ' . get_request_string($outq['outq_posturl']); - $result = z_post_url($outq['outq_posturl'],$outq['outq_msg'],$retries,[ 'headers' => $xhead ]); + $xhead = HTTPSig::create_sig($headers, $channel['channel_prvkey'], channel_url($channel)); + if (strpos($outq['outq_posturl'], 'http') !== 0) { + logger('bad url: ' . $outq['outq_posturl']); + self::remove($outq['outq_hash']); + } - if($result['success'] && $result['return_code'] < 300) { - logger('deliver: queue post success to ' . $outq['outq_posturl'], LOGGER_DEBUG); - if($base) { - q("update site set site_update = '%s', site_dead = 0 where site_url = '%s' ", - dbesc(datetime_convert()), - dbesc($base) - ); - } - q("update dreport set dreport_result = '%s', dreport_time = '%s' where dreport_queue = '%s'", - dbesc('accepted for delivery'), - dbesc(datetime_convert()), - dbesc($outq['outq_hash']) - ); - self::remove($outq['outq_hash']); + $result = z_post_url($outq['outq_posturl'], $outq['outq_msg'], $retries, ['headers' => $xhead]); - // server is responding - see if anything else is going to this destination and is piled up - // and try to send some more. We're relying on the fact that do_delivery() results in an - // immediate delivery otherwise we could get into a queue loop. + if ($result['success'] && $result['return_code'] < 300) { + logger('deliver: queue post success to ' . $outq['outq_posturl'], LOGGER_DEBUG); + if ($base) { + q( + "update site set site_update = '%s', site_dead = 0 where site_url = '%s' ", + dbesc(datetime_convert()), + dbesc($base) + ); + } + q( + "update dreport set dreport_result = '%s', dreport_time = '%s' where dreport_queue = '%s'", + dbesc('accepted for delivery'), + dbesc(datetime_convert()), + dbesc($outq['outq_hash']) + ); + self::remove($outq['outq_hash']); - if(! $immediate) { - $x = q("select outq_hash from outq where outq_posturl = '%s' and outq_delivered = 0", - dbesc($outq['outq_posturl']) - ); + // server is responding - see if anything else is going to this destination and is piled up + // and try to send some more. We're relying on the fact that do_delivery() results in an + // immediate delivery otherwise we could get into a queue loop. - $piled_up = []; - if($x) { - foreach($x as $xx) { - $piled_up[] = $xx['outq_hash']; - } - } - if($piled_up) { - do_delivery($piled_up,true); - } - } - } - else { - if ($result['return_code'] >= 300) { - q("update dreport set dreport_result = '%s', dreport_time = '%s' where dreport_queue = '%s'", - dbesc('delivery rejected' . ' ' . $result['return_code']), - dbesc(datetime_convert()), - dbesc($outq['outq_hash']) - ); - } - else { - $dr = q("select * from dreport where dreport_queue = '%s'", - dbesc($outq['outq_hash']) - ); - if ($dr) { - // update every queue entry going to this site with the most recent communication error - q("update dreport set dreport_log = '%s' where dreport_site = '%s'", - dbesc(z_curl_error($result)), - dbesc($dr[0]['dreport_site']) - ); - } - } - logger('deliver: queue post returned ' . $result['return_code'] . ' from ' . $outq['outq_posturl'],LOGGER_DEBUG); - self::update($outq['outq_hash'],10); - } - return; - } + if (!$immediate) { + $x = q( + "select outq_hash from outq where outq_posturl = '%s' and outq_delivered = 0", + dbesc($outq['outq_posturl']) + ); - // normal zot delivery + $piled_up = []; + if ($x) { + foreach ($x as $xx) { + $piled_up[] = $xx['outq_hash']; + } + } + if ($piled_up) { + do_delivery($piled_up, true); + } + } + } else { + if ($result['return_code'] >= 300) { + q( + "update dreport set dreport_result = '%s', dreport_time = '%s' where dreport_queue = '%s'", + dbesc('delivery rejected' . ' ' . $result['return_code']), + dbesc(datetime_convert()), + dbesc($outq['outq_hash']) + ); + } else { + $dr = q( + "select * from dreport where dreport_queue = '%s'", + dbesc($outq['outq_hash']) + ); + if ($dr) { + // update every queue entry going to this site with the most recent communication error + q( + "update dreport set dreport_log = '%s' where dreport_site = '%s'", + dbesc(z_curl_error($result)), + dbesc($dr[0]['dreport_site']) + ); + } + } + logger('deliver: queue post returned ' . $result['return_code'] . ' from ' . $outq['outq_posturl'], LOGGER_DEBUG); + self::update($outq['outq_hash'], 10); + } + return; + } - logger('deliver: dest: ' . $outq['outq_posturl'], LOGGER_DEBUG); + // normal zot delivery + + logger('deliver: dest: ' . $outq['outq_posturl'], LOGGER_DEBUG); - if($outq['outq_posturl'] === z_root() . '/zot') { - // local delivery - $zot = new \Zotlabs\Zot6\Receiver(new \Zotlabs\Zot6\Zot6Handler(),$outq['outq_notify']); - $result = $zot->run(); - logger('returned_json: ' . json_encode($result,JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES), LOGGER_DATA); - logger('deliver: local zot delivery succeeded to ' . $outq['outq_posturl']); - Libzot::process_response($outq['outq_posturl'],[ 'success' => true, 'body' => json_encode($result) ], $outq); + if ($outq['outq_posturl'] === z_root() . '/zot') { + // local delivery + $zot = new Receiver(new Zot6Handler(), $outq['outq_notify']); + $result = $zot->run(); + logger('returned_json: ' . json_encode($result, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES), LOGGER_DATA); + logger('deliver: local zot delivery succeeded to ' . $outq['outq_posturl']); + Libzot::process_response($outq['outq_posturl'], ['success' => true, 'body' => json_encode($result)], $outq); - if(! $immediate) { - $x = q("select outq_hash from outq where outq_posturl = '%s' and outq_delivered = 0", - dbesc($outq['outq_posturl']) - ); + if (!$immediate) { + $x = q( + "select outq_hash from outq where outq_posturl = '%s' and outq_delivered = 0", + dbesc($outq['outq_posturl']) + ); - $piled_up = []; - if($x) { - foreach($x as $xx) { - $piled_up[] = $xx['outq_hash']; - } - } - if($piled_up) { - do_delivery($piled_up,true); - } - } - } - else { - logger('remote'); - $channel = null; + $piled_up = []; + if ($x) { + foreach ($x as $xx) { + $piled_up[] = $xx['outq_hash']; + } + } + if ($piled_up) { + do_delivery($piled_up, true); + } + } + } else { + logger('remote'); + $channel = null; - if($outq['outq_channel']) { - $channel = channelx_by_n($outq['outq_channel'],true); - } + if ($outq['outq_channel']) { + $channel = channelx_by_n($outq['outq_channel'], true); + } - $host_crypto = null; + $host_crypto = null; - if($channel && $base) { - $h = q("select hubloc_sitekey, site_crypto from hubloc left join site on hubloc_url = site_url where site_url = '%s' and hubloc_network in ('nomad','zot6') order by hubloc_id desc limit 1", - dbesc($base) - ); - if($h) { - $host_crypto = $h[0]; - } - } - - $msg = $outq['outq_notify']; + if ($channel && $base) { + $h = q( + "select hubloc_sitekey, site_crypto from hubloc left join site on hubloc_url = site_url where site_url = '%s' and hubloc_network in ('zot6','nomad') order by hubloc_id desc limit 1", + dbesc($base) + ); + if ($h) { + $host_crypto = $h[0]; + } + } + $msg = $outq['outq_notify']; if ($outq['outq_driver'] === 'nomad') { $result = Libzot::nomad($outq['outq_posturl'],$msg,$channel,$host_crypto); @@ -471,27 +491,27 @@ class Queue { $result = Libzot::zot($outq['outq_posturl'],$msg,$channel,$host_crypto); } - if($result['success']) { - logger('deliver: remote nomad/zot delivery succeeded to ' . $outq['outq_posturl']); - Libzot::process_response($outq['outq_posturl'],$result, $outq); - } - else { - $dr = q("select * from dreport where dreport_queue = '%s'", - dbesc($outq['outq_hash']) - ); + if ($result['success']) { + logger('deliver: remote nomad/zot delivery succeeded to ' . $outq['outq_posturl']); + Libzot::process_response($outq['outq_posturl'], $result, $outq); + } else { + $dr = q( + "select * from dreport where dreport_queue = '%s'", + dbesc($outq['outq_hash']) + ); - // update every queue entry going to this site with the most recent communication error - q("update dreport set dreport_log = '%s' where dreport_site = '%s'", - dbesc(z_curl_error($result)), - dbesc($dr[0]['dreport_site']) - ); - - logger('deliver: remote zot delivery failed to ' . $outq['outq_posturl']); - logger('deliver: remote zot delivery fail data: ' . print_r($result,true), LOGGER_DATA); - self::update($outq['outq_hash'],10); - } - } - return; - } + // update every queue entry going to this site with the most recent communication error + q( + "update dreport set dreport_log = '%s' where dreport_site = '%s'", + dbesc(z_curl_error($result)), + dbesc($dr[0]['dreport_site']) + ); + + logger('deliver: remote nomad/zot delivery failed to ' . $outq['outq_posturl']); + logger('deliver: remote nomad/zot delivery fail data: ' . print_r($result, true), LOGGER_DATA); + self::update($outq['outq_hash'], 10); + } + } + return; + } } - diff --git a/Zotlabs/Lib/SConfig.php b/Zotlabs/Lib/SConfig.php index 76e5b3af9..1d4e93a3f 100644 --- a/Zotlabs/Lib/SConfig.php +++ b/Zotlabs/Lib/SConfig.php @@ -7,23 +7,27 @@ namespace Zotlabs\Lib; * * @see XConfig */ - -class SConfig { - static public function Load($server_id) { - return XConfig::Load('s_' . $server_id); - } +class SConfig +{ - static public function Get($server_id,$family,$key,$default = false) { - return XConfig::Get('s_' . $server_id,$family,$key, $default); - } + public static function Load($server_id) + { + return XConfig::Load('s_' . $server_id); + } - static public function Set($server_id,$family,$key,$value) { - return XConfig::Set('s_' . $server_id,$family,$key,$value); - } + public static function Get($server_id, $family, $key, $default = false) + { + return XConfig::Get('s_' . $server_id, $family, $key, $default); + } - static public function Delete($server_id,$family,$key) { - return XConfig::Delete('s_' . $server_id,$family,$key); - } + public static function Set($server_id, $family, $key, $value) + { + return XConfig::Set('s_' . $server_id, $family, $key, $value); + } + public static function Delete($server_id, $family, $key) + { + return XConfig::Delete('s_' . $server_id, $family, $key); + } } diff --git a/Zotlabs/Lib/Share.php b/Zotlabs/Lib/Share.php index 4d7f36d4e..9de030273 100644 --- a/Zotlabs/Lib/Share.php +++ b/Zotlabs/Lib/Share.php @@ -6,222 +6,227 @@ use App; use Zotlabs\Daemon\Run; use Zotlabs\Lib\Libsync; -class Share { +class Share +{ - private $item = null; + private $item = null; - public function __construct($post_id) { - - if (! $post_id) { - return; - } - - if (is_array($post_id)) { - $this->item = $post_id; - return; - } - - if (! (local_channel() || remote_channel())) { - return; - } + public function __construct($post_id) + { - $r = q("SELECT * from item left join xchan on author_xchan = xchan_hash WHERE id = %d LIMIT 1", - intval($post_id) - ); - if (! $r) { - return; - } + if (! $post_id) { + return; + } - if (($r[0]['item_private']) && ($r[0]['xchan_network'] !== 'rss')) { - return; - } - - $sql_extra = item_permissions_sql($r[0]['uid']); - - $r = q("select * from item where id = %d $sql_extra", - intval($post_id) - ); - if (! $r) { - return; - } - - if (! in_array($r[0]['mimetype'], [ 'text/bbcode', 'text/x-multicode' ])) { - return; - } - - /** @FIXME eventually we want to post remotely via rpost on your home site */ - // When that works remove this next bit: - - if (! local_channel()) { - return; - } + if (is_array($post_id)) { + $this->item = $post_id; + return; + } - xchan_query($r); - - $this->item = array_shift($r); + if (! (local_channel() || remote_channel())) { + return; + } - $arr = []; + $r = q( + "SELECT * from item left join xchan on author_xchan = xchan_hash WHERE id = %d LIMIT 1", + intval($post_id) + ); + if (! $r) { + return; + } - $owner_uid = $this->item['uid']; - $owner_aid = $this->item['aid']; + if (($r[0]['item_private']) && ($r[0]['xchan_network'] !== 'rss')) { + return; + } - $channel = channelx_by_n($this->item['uid']); - $observer = App::get_observer(); + $sql_extra = item_permissions_sql($r[0]['uid']); - $can_comment = false; - if ((array_key_exists('owner',$this->item)) && intval($this->item['owner']['abook_self'])) { - $can_comment = perm_is_allowed($this->item['uid'],$observer['xchan_hash'],'post_comments'); - } - else { - $can_comment = can_comment_on_post($observer['xchan_hash'],$this->item); - } + $r = q( + "select * from item where id = %d $sql_extra", + intval($post_id) + ); + if (! $r) { + return; + } - if (! $can_comment) { - return; - } + if (! in_array($r[0]['mimetype'], [ 'text/bbcode', 'text/x-multicode' ])) { + return; + } - $r = q("select * from xchan where xchan_hash = '%s' limit 1", - dbesc($this->item['owner_xchan']) - ); + /** @FIXME eventually we want to post remotely via rpost on your home site */ + // When that works remove this next bit: - if ($r) { - $thread_owner = array_shift($r); - } - else { - return; - } - - $r = q("select * from xchan where xchan_hash = '%s' limit 1", - dbesc($this->item['author_xchan']) - ); - if ($r) { - $item_author = array_shift($r); - } - else { - return; - } + if (! local_channel()) { + return; + } - if ($item_author['network'] === 'activitypub') { + xchan_query($r); - // for Mastodon compatibility, send back an ActivityPub Announce activity. - // We don't need or want these on our own network as there is no mechanism for providing - // a fair-use defense to copyright claims and frivolous lawsuits. - - $arr['aid'] = $owner_aid; - $arr['uid'] = $owner_uid; + $this->item = array_shift($r); - $arr['item_origin'] = 1; - $arr['item_wall'] = $this->item['item_wall']; - $arr['uuid'] = new_uuid(); - $arr['mid'] = z_root() . '/item/' . $arr['uuid']; - $arr['mid'] = str_replace('/item/','/activity/',$arr['mid']); - $arr['parent_mid'] = $this->item['mid']; + $arr = []; - $mention = '@[zrl=' . $this->item['author']['xchan_url'] . ']' . $this->item['author']['xchan_name'] . '[/zrl]'; - $arr['body'] = sprintf( t('🔁 Repeated %1$s\'s %2$s'), $mention, $this->item['obj_type']); + $owner_uid = $this->item['uid']; + $owner_aid = $this->item['aid']; - $arr['author_xchan'] = $observer['xchan_hash']; - $arr['owner_xchan'] = $this->item['author_xchan']; - $arr['obj'] = $this->item['obj']; - $arr['obj_type'] = $this->item['obj_type']; - $arr['verb'] = 'Announce'; + $channel = channelx_by_n($this->item['uid']); + $observer = App::get_observer(); - $post = item_store($arr); + $can_comment = false; + if ((array_key_exists('owner', $this->item)) && intval($this->item['owner']['abook_self'])) { + $can_comment = perm_is_allowed($this->item['uid'], $observer['xchan_hash'], 'post_comments'); + } else { + $can_comment = can_comment_on_post($observer['xchan_hash'], $this->item); + } - $post_id = $post['item_id']; + if (! $can_comment) { + return; + } - $arr['id'] = $post_id; - - call_hooks('post_local_end', $arr); + $r = q( + "select * from xchan where xchan_hash = '%s' limit 1", + dbesc($this->item['owner_xchan']) + ); - $r = q("select * from item where id = %d", - intval($post_id) - ); - if ($r) { - xchan_query($r); - $sync_item = fetch_post_tags($r); - Libsync::build_sync_packet($channel['channel_id'], [ 'item' => [ encode_item($sync_item[0],true) ] ]); - } + if ($r) { + $thread_owner = array_shift($r); + } else { + return; + } - Run::Summon([ 'Notifier','like',$post_id ]); - } - - return; + $r = q( + "select * from xchan where xchan_hash = '%s' limit 1", + dbesc($this->item['author_xchan']) + ); + if ($r) { + $item_author = array_shift($r); + } else { + return; + } - } + if ($item_author['network'] === 'activitypub') { + // for Mastodon compatibility, send back an ActivityPub Announce activity. + // We don't need or want these on our own network as there is no mechanism for providing + // a fair-use defense to copyright claims and frivolous lawsuits. - public function obj() { - $obj = []; + $arr['aid'] = $owner_aid; + $arr['uid'] = $owner_uid; - if(! $this->item) - return $obj; + $arr['item_origin'] = 1; + $arr['item_wall'] = $this->item['item_wall']; + $arr['uuid'] = new_uuid(); + $arr['mid'] = z_root() . '/item/' . $arr['uuid']; + $arr['mid'] = str_replace('/item/', '/activity/', $arr['mid']); + $arr['parent_mid'] = $this->item['mid']; - $obj['type'] = $this->item['obj_type']; - $obj['id'] = $this->item['mid']; - $obj['content'] = bbcode($this->item['body']); - $obj['source'] = [ - 'mediaType' => $this->item['mimetype'], - 'content' => $this->item['body'] - ]; + $mention = '@[zrl=' . $this->item['author']['xchan_url'] . ']' . $this->item['author']['xchan_name'] . '[/zrl]'; + $arr['body'] = sprintf(t('🔁 Repeated %1$s\'s %2$s'), $mention, $this->item['obj_type']); - $obj['name'] = $this->item['title']; - $obj['published'] = $this->item['created']; - $obj['updated'] = $this->item['edited']; - $obj['attributedTo'] = ((strpos($this->item['author']['xchan_hash'],'http') === 0) - ? $this->item['author']['xchan_hash'] - : $this->item['author']['xchan_url']); + $arr['author_xchan'] = $observer['xchan_hash']; + $arr['owner_xchan'] = $this->item['author_xchan']; + $arr['obj'] = $this->item['obj']; + $arr['obj_type'] = $this->item['obj_type']; + $arr['verb'] = 'Announce'; - return $obj; - } + $post = item_store($arr); - public function bbcode() { - $bb = EMPTY_STR; + $post_id = $post['item_id']; - if (! $this->item) - return $bb; + $arr['id'] = $post_id; - if (! $this->item['author']) { - $author = q("select * from xchan where xchan_hash = '%s' limit 1", - dbesc($this->item['author_xchan']) - ); - if ($author) { - $this->item['author'] = array_shift($author); - } - } + call_hooks('post_local_end', $arr); - $special_object = (in_array($this->item['obj_type'], [ ACTIVITY_OBJ_PHOTO, 'Event', 'Question' ]) ? true : false); - if($special_object) { - $object = json_decode($this->item['obj'],true); - $special = (($object['source']) ? $object['source']['content'] : $object['body']); - } - - if (strpos($this->item['body'], "[/share]") !== false) { - $pos = strpos($this->item['body'], "[share"); - $bb = substr($this->item['body'], $pos); - } else { - $bb = "[share author='" . urlencode($this->item['author']['xchan_name']). - "' profile='" . $this->item['author']['xchan_url'] . - "' portable_id='" . $this->item['author']['xchan_hash'] . - "' avatar='" . $this->item['author']['xchan_photo_s'] . - "' link='" . $this->item['plink'] . + $r = q( + "select * from item where id = %d", + intval($post_id) + ); + if ($r) { + xchan_query($r); + $sync_item = fetch_post_tags($r); + Libsync::build_sync_packet($channel['channel_id'], [ 'item' => [ encode_item($sync_item[0], true) ] ]); + } + + Run::Summon([ 'Notifier','like',$post_id ]); + } + + return; + } + + public function obj() + { + $obj = []; + + if (! $this->item) { + return $obj; + } + + $obj['type'] = $this->item['obj_type']; + $obj['id'] = $this->item['mid']; + $obj['content'] = bbcode($this->item['body']); + $obj['source'] = [ + 'mediaType' => $this->item['mimetype'], + 'content' => $this->item['body'] + ]; + + $obj['name'] = $this->item['title']; + $obj['published'] = $this->item['created']; + $obj['updated'] = $this->item['edited']; + $obj['attributedTo'] = ((strpos($this->item['author']['xchan_hash'], 'http') === 0) + ? $this->item['author']['xchan_hash'] + : $this->item['author']['xchan_url']); + + return $obj; + } + + public function bbcode() + { + $bb = EMPTY_STR; + + if (! $this->item) { + return $bb; + } + + if (! $this->item['author']) { + $author = q( + "select * from xchan where xchan_hash = '%s' limit 1", + dbesc($this->item['author_xchan']) + ); + if ($author) { + $this->item['author'] = array_shift($author); + } + } + + $special_object = (in_array($this->item['obj_type'], [ ACTIVITY_OBJ_PHOTO, 'Event', 'Question' ]) ? true : false); + if ($special_object) { + $object = json_decode($this->item['obj'], true); + $special = (($object['source']) ? $object['source']['content'] : $object['body']); + } + + if (strpos($this->item['body'], "[/share]") !== false) { + $pos = strpos($this->item['body'], "[share"); + $bb = substr($this->item['body'], $pos); + } else { + $bb = "[share author='" . urlencode($this->item['author']['xchan_name']) . + "' profile='" . $this->item['author']['xchan_url'] . + "' portable_id='" . $this->item['author']['xchan_hash'] . + "' avatar='" . $this->item['author']['xchan_photo_s'] . + "' link='" . $this->item['plink'] . "' auth='" . (in_array($this->item['author']['network'],['nomad','zot6']) ? 'true' : 'false') . - "' posted='" . $this->item['created'] . - "' message_id='" . $this->item['mid'] . - "']"; - if ($this->item['title']) { - $bb .= '[b]'.$this->item['title'].'[/b]'."\r\n"; - } - if ($this->item['summary']) { - $bb .= $this->item['summary'] . "\r\n"; - } - - $bb .= (($special_object) ? $special . "\r\n" . $this->item['body'] : $this->item['body']); - $bb .= "[/share]"; - } + "' posted='" . $this->item['created'] . + "' message_id='" . $this->item['mid'] . + "']"; + if ($this->item['title']) { + $bb .= '[b]' . $this->item['title'] . '[/b]' . "\r\n"; + } + if ($this->item['summary']) { + $bb .= $this->item['summary'] . "\r\n"; + } - return $bb; - - } + $bb .= (($special_object) ? $special . "\r\n" . $this->item['body'] : $this->item['body']); + $bb .= "[/share]"; + } + return $bb; + } } diff --git a/Zotlabs/Lib/SvgSanitizer.php b/Zotlabs/Lib/SvgSanitizer.php index 906b4bb3e..5e21c4f12 100644 --- a/Zotlabs/Lib/SvgSanitizer.php +++ b/Zotlabs/Lib/SvgSanitizer.php @@ -7,150 +7,154 @@ use Zotlabs\Lib\Config; /** * SVGSantiizer - * + * * Allowlist-based PHP SVG sanitizer. - * + * * @link https://github.com/alister-/SVG-Sanitizer} * @author Alister Norris * @copyright Copyright (c) 2013 Alister Norris * @license http://opensource.org/licenses/mit-license.php The MIT License * @package svgsanitizer */ +class SvgSanitizer +{ -class SvgSanitizer { - - private $xmlDoc; // PHP XML DOMDocument + private $xmlDoc; // PHP XML DOMDocument - private $removedattrs = []; + private $removedattrs = []; - private static $allowed_functions = [ 'matrix', 'url', 'translate', 'rgb' ]; + private static $allowed_functions = ['matrix', 'url', 'translate', 'rgb']; - // defines the allowlist of elements and attributes allowed. - private static $allowlist = [ - 'a' => [ 'class', 'clip-path', 'clip-rule', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'id', 'mask', 'opacity', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'style', 'systemLanguage', 'transform', 'href', 'xlink:href', 'xlink:title' ], - 'circle' => [ 'class', 'clip-path', 'clip-rule', 'cx', 'cy', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'id', 'mask', 'opacity', 'r', 'requiredFeatures', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'style', 'systemLanguage', 'transform' ], - 'clipPath' => [ 'class', 'clipPathUnits', 'id' ], - 'defs' => [ ], - 'style' => [ 'type' ], - 'desc' => [ ], - 'ellipse' => [ 'class', 'clip-path', 'clip-rule', 'cx', 'cy', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'id', 'mask', 'opacity', 'requiredFeatures', 'rx', 'ry', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'style', 'systemLanguage', 'transform' ], - 'feGaussianBlur' => [ 'class', 'color-interpolation-filters', 'id', 'requiredFeatures', 'stdDeviation' ], - 'filter' => [ 'class', 'color-interpolation-filters', 'filterRes', 'filterUnits', 'height', 'id', 'primitiveUnits', 'requiredFeatures', 'width', 'x', 'xlink:href', 'y' ], - 'foreignObject' => [ 'class', 'font-size', 'height', 'id', 'opacity', 'requiredFeatures', 'style', 'transform', 'width', 'x', 'y' ], - 'g' => [ 'class', 'clip-path', 'clip-rule', 'id', 'display', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'mask', 'opacity', 'requiredFeatures', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'style', 'systemLanguage', 'transform', 'font-family', 'font-size', 'font-style', 'font-weight', 'text-anchor' ], - 'image' => [ 'class', 'clip-path', 'clip-rule', 'filter', 'height', 'id', 'mask', 'opacity', 'requiredFeatures', 'style', 'systemLanguage', 'transform', 'width', 'x', 'xlink:href', 'xlink:title', 'y' ], - 'line' => [ 'class', 'clip-path', 'clip-rule', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'id', 'marker-end', 'marker-mid', 'marker-start', 'mask', 'opacity', 'requiredFeatures', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'style', 'systemLanguage', 'transform', 'x1', 'x2', 'y1', 'y2' ], - 'linearGradient' => [ 'class', 'id', 'gradientTransform', 'gradientUnits', 'requiredFeatures', 'spreadMethod', 'systemLanguage', 'x1', 'x2', 'xlink:href', 'y1', 'y2' ], - 'marker' => [ 'id', 'class', 'markerHeight', 'markerUnits', 'markerWidth', 'orient', 'preserveAspectRatio', 'refX', 'refY', 'systemLanguage', 'viewBox' ], - 'mask' => [ 'class', 'height', 'id', 'maskContentUnits', 'maskUnits', 'width', 'x', 'y' ], - 'metadata' => [ 'class', 'id' ], - 'path' => [ 'class', 'clip-path', 'clip-rule', 'd', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'id', 'marker-end', 'marker-mid', 'marker-start', 'mask', 'opacity', 'requiredFeatures', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'style', 'systemLanguage', 'transform' ], - 'pattern' => [ 'class', 'height', 'id', 'patternContentUnits', 'patternTransform', 'patternUnits', 'requiredFeatures', 'style', 'systemLanguage', 'viewBox', 'width', 'x', 'xlink:href', 'y' ], - 'polygon' => [ 'class', 'clip-path', 'clip-rule', 'id', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'id', 'class', 'marker-end', 'marker-mid', 'marker-start', 'mask', 'opacity', 'points', 'requiredFeatures', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'style', 'systemLanguage', 'transform' ], - 'polyline' => [ 'class', 'clip-path', 'clip-rule', 'id', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'marker-end', 'marker-mid', 'marker-start', 'mask', 'opacity', 'points', 'requiredFeatures', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'style', 'systemLanguage', 'transform' ], - 'radialGradient' => [ 'class', 'cx', 'cy', 'fx', 'fy', 'gradientTransform', 'gradientUnits', 'id', 'r', 'requiredFeatures', 'spreadMethod', 'systemLanguage', 'xlink:href' ], - 'rect' => [ 'class', 'clip-path', 'clip-rule', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'height', 'id', 'mask', 'opacity', 'requiredFeatures', 'rx', 'ry', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'style', 'systemLanguage', 'transform', 'width', 'x', 'y' ], - 'stop' => [ 'class', 'id', 'offset', 'requiredFeatures', 'stop-color', 'stop-opacity', 'style', 'systemLanguage' ], - 'svg' => [ 'class', 'clip-path', 'clip-rule', 'filter', 'id', 'height', 'mask', 'preserveAspectRatio', 'requiredFeatures', 'style', 'systemLanguage', 'viewBox', 'width', 'x', 'xmlns', 'xmlns:se', 'xmlns:xlink', 'y' ], - 'switch' => [ 'class', 'id', 'requiredFeatures', 'systemLanguage' ], - 'symbol' => [ 'class', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'font-family', 'font-size', 'font-style', 'font-weight', 'id', 'opacity', 'preserveAspectRatio', 'requiredFeatures', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'style', 'systemLanguage', 'transform', 'viewBox' ], - 'text' => [ 'class', 'clip-path', 'clip-rule', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'font-family', 'font-size', 'font-style', 'font-weight', 'id', 'mask', 'opacity', 'requiredFeatures', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'style', 'systemLanguage', 'text-anchor', 'transform', 'x', 'xml:space', 'y' ], - 'textPath' => [ 'class', 'id', 'method', 'requiredFeatures', 'spacing', 'startOffset', 'style', 'systemLanguage', 'transform', 'xlink:href' ], - 'title' => [ ], - 'tspan' => [ 'class', 'clip-path', 'clip-rule', 'dx', 'dy', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'font-family', 'font-size', 'font-style', 'font-weight', 'id', 'mask', 'opacity', 'requiredFeatures', 'rotate', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'style', 'systemLanguage', 'text-anchor', 'textLength', 'transform', 'x', 'xml:space', 'y' ], - 'use' => [ 'class', 'clip-path', 'clip-rule', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'height', 'id', 'mask', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'style', 'transform', 'width', 'x', 'xlink:href', 'y' ], - ]; + // defines the allowlist of elements and attributes allowed. + private static $allowlist = [ + 'a' => ['class', 'clip-path', 'clip-rule', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'id', 'mask', 'opacity', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'style', 'systemLanguage', 'transform', 'href', 'xlink:href', 'xlink:title'], + 'circle' => ['class', 'clip-path', 'clip-rule', 'cx', 'cy', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'id', 'mask', 'opacity', 'r', 'requiredFeatures', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'style', 'systemLanguage', 'transform'], + 'clipPath' => ['class', 'clipPathUnits', 'id'], + 'defs' => [], + 'style' => ['type'], + 'desc' => [], + 'ellipse' => ['class', 'clip-path', 'clip-rule', 'cx', 'cy', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'id', 'mask', 'opacity', 'requiredFeatures', 'rx', 'ry', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'style', 'systemLanguage', 'transform'], + 'feGaussianBlur' => ['class', 'color-interpolation-filters', 'id', 'requiredFeatures', 'stdDeviation'], + 'filter' => ['class', 'color-interpolation-filters', 'filterRes', 'filterUnits', 'height', 'id', 'primitiveUnits', 'requiredFeatures', 'width', 'x', 'xlink:href', 'y'], + 'foreignObject' => ['class', 'font-size', 'height', 'id', 'opacity', 'requiredFeatures', 'style', 'transform', 'width', 'x', 'y'], + 'g' => ['class', 'clip-path', 'clip-rule', 'id', 'display', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'mask', 'opacity', 'requiredFeatures', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'style', 'systemLanguage', 'transform', 'font-family', 'font-size', 'font-style', 'font-weight', 'text-anchor'], + 'image' => ['class', 'clip-path', 'clip-rule', 'filter', 'height', 'id', 'mask', 'opacity', 'requiredFeatures', 'style', 'systemLanguage', 'transform', 'width', 'x', 'xlink:href', 'xlink:title', 'y'], + 'line' => ['class', 'clip-path', 'clip-rule', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'id', 'marker-end', 'marker-mid', 'marker-start', 'mask', 'opacity', 'requiredFeatures', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'style', 'systemLanguage', 'transform', 'x1', 'x2', 'y1', 'y2'], + 'linearGradient' => ['class', 'id', 'gradientTransform', 'gradientUnits', 'requiredFeatures', 'spreadMethod', 'systemLanguage', 'x1', 'x2', 'xlink:href', 'y1', 'y2'], + 'marker' => ['id', 'class', 'markerHeight', 'markerUnits', 'markerWidth', 'orient', 'preserveAspectRatio', 'refX', 'refY', 'systemLanguage', 'viewBox'], + 'mask' => ['class', 'height', 'id', 'maskContentUnits', 'maskUnits', 'width', 'x', 'y'], + 'metadata' => ['class', 'id'], + 'path' => ['class', 'clip-path', 'clip-rule', 'd', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'id', 'marker-end', 'marker-mid', 'marker-start', 'mask', 'opacity', 'requiredFeatures', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'style', 'systemLanguage', 'transform'], + 'pattern' => ['class', 'height', 'id', 'patternContentUnits', 'patternTransform', 'patternUnits', 'requiredFeatures', 'style', 'systemLanguage', 'viewBox', 'width', 'x', 'xlink:href', 'y'], + 'polygon' => ['class', 'clip-path', 'clip-rule', 'id', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'id', 'class', 'marker-end', 'marker-mid', 'marker-start', 'mask', 'opacity', 'points', 'requiredFeatures', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'style', 'systemLanguage', 'transform'], + 'polyline' => ['class', 'clip-path', 'clip-rule', 'id', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'marker-end', 'marker-mid', 'marker-start', 'mask', 'opacity', 'points', 'requiredFeatures', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'style', 'systemLanguage', 'transform'], + 'radialGradient' => ['class', 'cx', 'cy', 'fx', 'fy', 'gradientTransform', 'gradientUnits', 'id', 'r', 'requiredFeatures', 'spreadMethod', 'systemLanguage', 'xlink:href'], + 'rect' => ['class', 'clip-path', 'clip-rule', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'height', 'id', 'mask', 'opacity', 'requiredFeatures', 'rx', 'ry', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'style', 'systemLanguage', 'transform', 'width', 'x', 'y'], + 'stop' => ['class', 'id', 'offset', 'requiredFeatures', 'stop-color', 'stop-opacity', 'style', 'systemLanguage'], + 'svg' => ['class', 'clip-path', 'clip-rule', 'filter', 'id', 'height', 'mask', 'preserveAspectRatio', 'requiredFeatures', 'style', 'systemLanguage', 'viewBox', 'width', 'x', 'xmlns', 'xmlns:se', 'xmlns:xlink', 'y'], + 'switch' => ['class', 'id', 'requiredFeatures', 'systemLanguage'], + 'symbol' => ['class', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'font-family', 'font-size', 'font-style', 'font-weight', 'id', 'opacity', 'preserveAspectRatio', 'requiredFeatures', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'style', 'systemLanguage', 'transform', 'viewBox'], + 'text' => ['class', 'clip-path', 'clip-rule', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'font-family', 'font-size', 'font-style', 'font-weight', 'id', 'mask', 'opacity', 'requiredFeatures', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'style', 'systemLanguage', 'text-anchor', 'transform', 'x', 'xml:space', 'y'], + 'textPath' => ['class', 'id', 'method', 'requiredFeatures', 'spacing', 'startOffset', 'style', 'systemLanguage', 'transform', 'xlink:href'], + 'title' => [], + 'tspan' => ['class', 'clip-path', 'clip-rule', 'dx', 'dy', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'font-family', 'font-size', 'font-style', 'font-weight', 'id', 'mask', 'opacity', 'requiredFeatures', 'rotate', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'style', 'systemLanguage', 'text-anchor', 'textLength', 'transform', 'x', 'xml:space', 'y'], + 'use' => ['class', 'clip-path', 'clip-rule', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'height', 'id', 'mask', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'style', 'transform', 'width', 'x', 'xlink:href', 'y'], + ]; - function __construct() { - $this->xmlDoc = new DOMDocument('1.0','UTF-8'); - $this->xmlDoc->preserveWhiteSpace = false; - libxml_use_internal_errors(true); - } + public function __construct() + { + $this->xmlDoc = new DOMDocument('1.0', 'UTF-8'); + $this->xmlDoc->preserveWhiteSpace = false; + libxml_use_internal_errors(true); + } - // load XML SVG - function load($file) { - $this->xmlDoc->load($file); - } + // load XML SVG + public function load($file) + { + $this->xmlDoc->load($file); + } - function loadXML($str) { - if (! $str) { - logger('loadxml: empty input', LOGGER_DEBUG); - return false; - } - if (! $this->xmlDoc->loadXML($str)) { - logger('loadxml: ' . print_r(array_slice(libxml_get_errors(),0,Config::Get('system','svg_backtrace_limit',3)),true), LOGGER_DEBUG); - return false; - } - return true; - } + public function loadXML($str) + { + if (!$str) { + logger('loadxml: empty input', LOGGER_DEBUG); + return false; + } + if (!$this->xmlDoc->loadXML($str)) { + logger('loadxml: ' . print_r(array_slice(libxml_get_errors(), 0, Config::Get('system', 'svg_backtrace_limit', 3)), true), LOGGER_DEBUG); + return false; + } + return true; + } - function sanitize() - { - // all elements in xml doc - $allElements = $this->xmlDoc->getElementsByTagName('*'); + public function sanitize() + { + // all elements in xml doc + $allElements = $this->xmlDoc->getElementsByTagName('*'); - // loop through all elements - for($i = 0; $i < $allElements->length; $i++) - { - $this->removedattrs = []; - - $currentNode = $allElements->item($i); + // loop through all elements + for ($i = 0; $i < $allElements->length; $i++) { + $this->removedattrs = []; - // logger('current_node: ' . print_r($currentNode,true)); + $currentNode = $allElements->item($i); - // array of allowed attributes in specific element - $allowlist_attr_arr = self::$allowlist[$currentNode->tagName]; + // logger('current_node: ' . print_r($currentNode,true)); - // does element exist in allowlist? - if(isset($allowlist_attr_arr)) { - $total = $currentNode->attributes->length; - - for($x = 0; $x < $total; $x++) { + // array of allowed attributes in specific element + $allowlist_attr_arr = self::$allowlist[$currentNode->tagName]; - // get attributes name - $attrName = $currentNode->attributes->item($x)->nodeName; + // does element exist in allowlist? + if (isset($allowlist_attr_arr)) { + $total = $currentNode->attributes->length; - // logger('checking: ' . print_r($currentNode->attributes->item($x),true)); - $matches = false; - - // check if attribute isn't in allowlist - if(! in_array($attrName, $allowlist_attr_arr)) { - $this->removedattrs[] = $attrName; - } - // check for disallowed functions - elseif (preg_match_all('/([a-zA-Z0-9]+)[\s]*\(/', - $currentNode->attributes->item($x)->textContent,$matches,PREG_SET_ORDER)) { - if ($attrName === 'text') { - continue; - } - foreach ($matches as $match) { - if(! in_array($match[1],self::$allowed_functions)) { - logger('queue_remove_function: ' . $match[1],LOGGER_DEBUG); - $this->removedattrs[] = $attrName; - } - } - } - } - if ($this->removedattrs) { - foreach ($this->removedattrs as $attr) { - $currentNode->removeAttribute($attr); - logger('removed: ' . $attr, LOGGER_DEBUG); - } - } + for ($x = 0; $x < $total; $x++) { + // get attributes name + $attrName = $currentNode->attributes->item($x)->nodeName; - } + // logger('checking: ' . print_r($currentNode->attributes->item($x),true)); + $matches = false; - // else remove element - else { - logger('remove_node: ' . print_r($currentNode,true)); - $currentNode->parentNode->removeChild($currentNode); - } - } - return true; - } + // check if attribute isn't in allowlist + if (!in_array($attrName, $allowlist_attr_arr)) { + $this->removedattrs[] = $attrName; + } // check for disallowed functions + elseif ( + preg_match_all( + '/([a-zA-Z0-9]+)[\s]*\(/', + $currentNode->attributes->item($x)->textContent, + $matches, + PREG_SET_ORDER + ) + ) { + if ($attrName === 'text') { + continue; + } + foreach ($matches as $match) { + if (!in_array($match[1], self::$allowed_functions)) { + logger('queue_remove_function: ' . $match[1], LOGGER_DEBUG); + $this->removedattrs[] = $attrName; + } + } + } + } + if ($this->removedattrs) { + foreach ($this->removedattrs as $attr) { + $currentNode->removeAttribute($attr); + logger('removed: ' . $attr, LOGGER_DEBUG); + } + } + } // else remove element + else { + logger('remove_node: ' . print_r($currentNode, true)); + $currentNode->parentNode->removeChild($currentNode); + } + } + return true; + } - function saveSVG() { - $this->xmlDoc->formatOutput = true; - return($this->xmlDoc->saveXML()); - } + public function saveSVG() + { + $this->xmlDoc->formatOutput = true; + return ($this->xmlDoc->saveXML()); + } } diff --git a/Zotlabs/Lib/System.php b/Zotlabs/Lib/System.php index d316b13b6..0df1fb449 100644 --- a/Zotlabs/Lib/System.php +++ b/Zotlabs/Lib/System.php @@ -4,116 +4,145 @@ namespace Zotlabs\Lib; use App; -class System { +class System +{ - static public function get_platform_name() { - if(is_array(App::$config) && is_array(App::$config['system']) && array_key_exists('platform_name',App::$config['system'])) - return App::$config['system']['platform_name']; - return PLATFORM_NAME; - } + public static function get_platform_name() + { + if (is_array(App::$config) && is_array(App::$config['system']) && array_key_exists('platform_name', App::$config['system'])) { + return App::$config['system']['platform_name']; + } + return PLATFORM_NAME; + } - static public function get_site_name() { - if(is_array(App::$config) && is_array(App::$config['system']) && App::$config['system']['sitename']) - return App::$config['system']['sitename']; - return ''; - } + public static function get_site_name() + { + if (is_array(App::$config) && is_array(App::$config['system']) && App::$config['system']['sitename']) { + return App::$config['system']['sitename']; + } + return ''; + } - static public function get_banner() { + public static function get_banner() + { - if(is_array(App::$config) && is_array(App::$config['system']) && array_key_exists('banner',App::$config['system']) && App::$config['system']['banner']) { - return App::$config['system']['banner']; + if (is_array(App::$config) && is_array(App::$config['system']) && array_key_exists('banner', App::$config['system']) && App::$config['system']['banner']) { + return App::$config['system']['banner']; + } + return self::get_site_name(); + } + + public static function get_project_icon() + { + if (is_array(App::$config) && is_array(App::$config['system']) && array_key_exists('icon', App::$config['system'])) { + return App::$config['system']['icon']; + } + return z_root() . '/images/' . PLATFORM_NAME . '-64.png'; + } + + public static function get_project_favicon() + { + if (is_array(App::$config) && is_array(App::$config['system']) && array_key_exists('favicon', App::$config['system'])) { + return App::$config['system']['favicon']; + } + return z_root() . '/images/' . PLATFORM_NAME . '.ico'; + } + + + public static function get_project_version() + { + if (array_path_exists('system/hide_version', App::$config) && intval(App::$config['system']['hide_version'])) { + return ''; + } + if (is_array(App::$config) && is_array(App::$config['system']) && array_key_exists('std_version', App::$config['system'])) { + return App::$config['system']['std_version']; + } + + return self::get_std_version(); + } + + public static function get_update_version() + { + if (is_array(App::$config) && is_array(App::$config['system']) && App::$config['system']['hide_version']) { + return EMPTY_STR; + } + return DB_UPDATE_VERSION; + } + + + public static function get_notify_icon() + { + if (is_array(App::$config) && is_array(App::$config['system']) && App::$config['system']['email_notify_icon_url']) { + return App::$config['system']['email_notify_icon_url']; + } + return self::get_project_icon(); + } + + public static function get_site_icon() + { + if (is_array(App::$config) && is_array(App::$config['system']) && isset(App::$config['system']['site_icon_url']) && App::$config['system']['site_icon_url']) { + return App::$config['system']['site_icon_url']; + } + return self::get_project_icon(); + } + + public static function get_site_favicon() + { + if (is_array(App::$config) && is_array(App::$config['system']) && App::$config['system']['site_favicon_url']) { + return App::$config['system']['site_favicon_url']; + } + return self::get_project_favicon(); + } + + public static 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://zotlabs.com/' . PLATFORM_NAME; + } + + public static 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']; + } + if (PLATFORM_NAME === 'streams') { + return 'https://codeberg.org/streams/' . PLATFORM_NAME; } - return self::get_site_name(); - } - static public function get_project_icon() { - if(is_array(App::$config) && is_array(App::$config['system']) && array_key_exists('icon',App::$config['system'])) { - return App::$config['system']['icon']; - } - return z_root() . '/images/' . PLATFORM_NAME . '-64.png'; - } + return 'https://codeberg.org/zot/' . PLATFORM_NAME; + } - static public function get_project_favicon() { - if(is_array(App::$config) && is_array(App::$config['system']) && array_key_exists('favicon',App::$config['system'])) { - return App::$config['system']['favicon']; - } - return z_root() . '/images/' . PLATFORM_NAME . '.ico'; - } + public static function ebs() + { + if (defined('EBSSTATE')) { + return EBSSTATE; + } + return 'armed'; + } + public static function get_zot_revision() + { + $x = [ 'revision' => ZOT_REVISION ]; + call_hooks('zot_revision', $x); + return $x['revision']; + } - static public function get_project_version() { - if(array_path_exists('system/hide_version', App::$config) && intval(App::$config['system']['hide_version'])) - return ''; - if(is_array(App::$config) && is_array(App::$config['system']) && array_key_exists('std_version',App::$config['system'])) - return App::$config['system']['std_version']; + public static function get_std_version() + { + if (defined('STD_VERSION')) { + return STD_VERSION; + } + return '0.0.0'; + } - return self::get_std_version(); - } + public static function compatible_project($p) + { - static public function get_update_version() { - if(is_array(App::$config) && is_array(App::$config['system']) && App::$config['system']['hide_version']) - return EMPTY_STR; - return DB_UPDATE_VERSION; - } - - - static public function get_notify_icon() { - if(is_array(App::$config) && is_array(App::$config['system']) && App::$config['system']['email_notify_icon_url']) - return App::$config['system']['email_notify_icon_url']; - return self::get_project_icon(); - } - - static public function get_site_icon() { - if(is_array(App::$config) && is_array(App::$config['system']) && isset(App::$config['system']['site_icon_url']) && App::$config['system']['site_icon_url']) - return App::$config['system']['site_icon_url']; - return self::get_project_icon(); - } - - static public function get_site_favicon() { - if(is_array(App::$config) && is_array(App::$config['system']) && App::$config['system']['site_favicon_url']) - return App::$config['system']['site_favicon_url']; - return self::get_project_favicon(); - } - - 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://zotlabs.com/' . PLATFORM_NAME; - } - - 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']; - if (PLATFORM_NAME === 'streams') { - return 'https://codeberg.org/streams/' . PLATFORM_NAME; - } - return 'https://codeberg.org/zot/' . PLATFORM_NAME; - } - - static public function ebs() { - if(defined('EBSSTATE')) { - return EBSSTATE; - } - return 'armed'; - } - - static public function get_zot_revision() { - $x = [ 'revision' => ZOT_REVISION ]; - call_hooks('zot_revision',$x); - return $x['revision']; - } - - static public function get_std_version() { - if(defined('STD_VERSION')) - return STD_VERSION; - return '0.0.0'; - } - - static public function compatible_project($p) { - - if (in_array(strtolower($p),['hubzilla', 'zap', 'red', 'misty', 'mistpark', 'redmatrix', 'osada', 'roadhouse', 'streams'])) { - return true; - } - return false; - } + if (in_array(strtolower($p), ['hubzilla', 'zap', 'red', 'misty', 'mistpark', 'redmatrix', 'osada', 'roadhouse','streams'])) { + return true; + } + return false; + } } diff --git a/Zotlabs/Lib/ThreadItem.php b/Zotlabs/Lib/ThreadItem.php index 23cdf336d..c44ac923c 100644 --- a/Zotlabs/Lib/ThreadItem.php +++ b/Zotlabs/Lib/ThreadItem.php @@ -1,4 +1,6 @@ -data = $data; - $this->toplevel = ($this->get_id() == $this->get_data_value('parent')); - $this->threaded = get_config('system','thread_allow',true); + public function __construct($data) + { - $observer = App::get_observer(); + $this->data = $data; + $this->toplevel = ($this->get_id() == $this->get_data_value('parent')); + $this->threaded = get_config('system', 'thread_allow', true); - // Prepare the children - if($data['children']) { - foreach($data['children'] as $item) { + $observer = App::get_observer(); - /* - * Only add those that will be displayed - */ + // Prepare the children + if ($data['children']) { + foreach ($data['children'] as $item) { + /* + * Only add those that will be displayed + */ - if(! visible_activity($item)) { - continue; - } + if (! visible_activity($item)) { + continue; + } - // this is a quick hack to hide ActivityPub DMs that we should not be allowed to see - // but may have been forwarded as part of a conversation + // this is a quick hack to hide ActivityPub DMs that we should not be allowed to see + // but may have been forwarded as part of a conversation - if(intval($item['item_private']) && (intval($item['item_restrict']) & 1 ) && $item['mid'] !== $item['parent_mid']) { - if(! $observer) { - continue; - } - } + if (intval($item['item_private']) && (intval($item['item_restrict']) & 1 ) && $item['mid'] !== $item['parent_mid']) { + if (! $observer) { + continue; + } + } - $child = new ThreadItem($item); - $this->add_child($child); - } + $child = new ThreadItem($item); + $this->add_child($child); + } + } + + // allow a site to configure the order and content of the reaction emoji list + if ($this->toplevel) { + $x = get_config('system', 'reactions'); + if ($x && is_array($x) && count($x)) { + $this->reactions = $x; + } + } + } + + /** + * Get data in a form usable by a conversation template + * + * Returns: + * _ The data requested on success + * _ false on failure + */ + + public function get_template_data($conv_responses, $thread_level = 1) + { + + $result = []; + + $item = $this->get_data(); + + $commentww = ''; + $sparkle = ''; + $buttons = ''; + $dropping = false; + $star = false; + $isstarred = "unstarred fa-star-o"; + $is_comment = false; + $is_item = false; + $osparkle = ''; + $total_children = $this->count_descendants(); + $unseen_comments = ((isset($item['real_uid']) && $item['real_uid']) ? 0 : $this->count_unseen_descendants()); + $privacy_warning = false; + + $conv = $this->get_conversation(); + $observer = $conv->get_observer(); + + $lock = t('Public visibility'); + if (intval($item['item_private']) === 2) { + $lock = t('Direct message (private mail)'); + } + if (intval($item['item_private']) === 1) { + $lock = t('Restricted visibility'); } - // allow a site to configure the order and content of the reaction emoji list - if($this->toplevel) { - $x = get_config('system','reactions'); - if($x && is_array($x) && count($x)) { - $this->reactions = $x; - } - } - } - - /** - * Get data in a form usable by a conversation template - * - * Returns: - * _ The data requested on success - * _ false on failure - */ - - public function get_template_data($conv_responses, $thread_level = 1) { - - $result = []; - - $item = $this->get_data(); - - $commentww = ''; - $sparkle = ''; - $buttons = ''; - $dropping = false; - $star = false; - $isstarred = "unstarred fa-star-o"; - $is_comment = false; - $is_item = false; - $osparkle = ''; - $total_children = $this->count_descendants(); - $unseen_comments = ((isset($item['real_uid']) && $item['real_uid']) ? 0 : $this->count_unseen_descendants()); - $privacy_warning = false; - - $conv = $this->get_conversation(); - $observer = $conv->get_observer(); - - $lock = (((intval($item['item_private'])) || (($item['uid'] == local_channel()) && (strlen($item['allow_cid']) || strlen($item['allow_gid']) - || strlen($item['deny_cid']) || strlen($item['deny_gid'])))) - ? t('Private Message') - : false); - - $locktype = $item['item_private']; - - $shareable = ((($conv->get_profile_owner() == local_channel() && local_channel()) && (! intval($item['item_private']))) ? true : false); - - // allow an exemption for sharing stuff from your private feeds - if($item['author']['xchan_network'] === 'rss') - $shareable = true; - - // @fixme - // Have recently added code to properly handle polls in group reshares by redirecting all of the poll responses to the group. - // Sharing a poll using a regular embedded share is harder because the poll will need to fork. This is due to comment permissions. - // The original poll author may not accept responses from strangers. Forking the poll will receive responses from the sharer's - // followers, but there's no elegant way to merge these two sets of results together. For now, we'll disable sharing polls. - - if ($item['obj_type'] === 'Question') { - $shareable = false; - } - - - if ($item['item_restrict'] & 2) { - $privacy_warning = true; - $lock = t('This comment is part of a private conversation, yet was shared with the public. Discretion advised.'); - } - - $mode = $conv->get_mode(); - - $edlink = 'editpost'; - - if(local_channel() && $observer['xchan_hash'] === $item['author_xchan']) - $edpost = array(z_root() . '/' . $edlink . '/' . $item['id'], t('Edit')); - else - $edpost = false; - - if(local_channel() && $observer['xchan_hash'] === $item['owner_xchan']) - $myconv = true; - else - $myconv = false; - - - if($item['verb'] === 'Announce') { - $edpost = false; - } - - - if ($observer && $observer['xchan_hash'] - && ( $observer['xchan_hash'] == $this->get_data_value('author_xchan') - || $observer['xchan_hash'] == $this->get_data_value('owner_xchan') - || $observer['xchan_hash'] == $this->get_data_value('source_xchan') - || $this->get_data_value('uid') == local_channel())) - $dropping = true; - - - if(array_key_exists('real_uid',$item)) { - $edpost = false; - $dropping = false; - } - - - if($dropping) { - $drop = array( - 'dropping' => $dropping, - 'delete' => t('Delete'), - ); - } - elseif(is_site_admin()) { - $drop = [ 'dropping' => true, 'delete' => t('Admin Delete') ]; - } - - if(isset($observer_is_pageowner) && $observer_is_pageowner) { - $multidrop = array( - 'select' => t('Select'), - ); - } - - $filer = ((($conv->get_profile_owner() == local_channel()) && (! array_key_exists('real_uid',$item))) ? t('Save to Folder') : false); - - $profile_avatar = $item['author']['xchan_photo_m']; - $profile_link = chanlink_hash($item['author_xchan']); - $profile_name = $item['author']['xchan_name']; - - $profile_addr = $item['author']['xchan_addr'] ? $item['author']['xchan_addr'] : $item['author']['xchan_url']; - - $location = format_location($item); - $isevent = false; - $attend = null; - $canvote = false; - - // process action responses - e.g. like/dislike/attend/agree/whatever - $response_verbs = [ 'like', 'dislike' ]; - - if($item['obj_type'] === ACTIVITY_OBJ_EVENT) { - $response_verbs[] = 'attendyes'; - $response_verbs[] = 'attendno'; - $response_verbs[] = 'attendmaybe'; - if($this->is_commentable() && $observer) { - $isevent = true; - $attend = array( t('I will attend'), t('I will not attend'), t('I might attend')); - $undo_attend = t('Undo attendance'); - } - } - - $responses = get_responses($conv_responses,$response_verbs,$this,$item); - - $my_responses = []; - foreach($response_verbs as $v) { - $my_responses[$v] = ((isset($conv_responses[$v][$item['mid'] . '-m']) && $conv_responses[$v][$item['mid'] . '-m']) ? 1 : 0); - } - - $like_count = ((x($conv_responses['like'],$item['mid'])) ? $conv_responses['like'][$item['mid']] : ''); - $like_list = ((x($conv_responses['like'],$item['mid'])) ? $conv_responses['like'][$item['mid'] . '-l'] : ''); - if (($like_list) && (count($like_list) > MAX_LIKERS)) { - $like_list_part = array_slice($like_list, 0, MAX_LIKERS); - array_push($like_list_part, '' . t('View all') . ''); - } else { - $like_list_part = ''; - } - if(get_config('system','show_like_counts',true)) { - $like_button_label = tt('Like','Likes',$like_count,'noun'); - } - else { - $like_button_label = t('Likes','noun'); - } - - $dislike_count = ((x($conv_responses['dislike'],$item['mid'])) ? $conv_responses['dislike'][$item['mid']] : ''); - $dislike_list = ((x($conv_responses['dislike'],$item['mid'])) ? $conv_responses['dislike'][$item['mid'] . '-l'] : ''); - if(get_config('system','show_like_counts',true)) { - $dislike_button_label = tt('Dislike','Dislikes',$dislike_count,'noun'); - } - else { - $dislike_button_label = t('Dislikes','noun'); - } - - if (($dislike_list) && (count($dislike_list) > MAX_LIKERS)) { - $dislike_list_part = array_slice($dislike_list, 0, MAX_LIKERS); - array_push($dislike_list_part, '' . t('View all') . ''); - } else { - $dislike_list_part = ''; - } - - - $showlike = ((x($conv_responses['like'],$item['mid'])) ? format_like($conv_responses['like'][$item['mid']],$conv_responses['like'][$item['mid'] . '-l'],'like',$item['mid']) : ''); - $showdislike = ((x($conv_responses['dislike'],$item['mid'])) - ? format_like($conv_responses['dislike'][$item['mid']],$conv_responses['dislike'][$item['mid'] . '-l'],'dislike',$item['mid']) : ''); - - /* - * We should avoid doing this all the time, but it depends on the conversation mode - * And the conv mode may change when we change the conv, or it changes its mode - * Maybe we should establish a way to be notified about conversation changes - */ - - $this->check_wall_to_wall(); - - if($this->is_toplevel()) { - if(local_channel() && $conv->get_profile_owner() == local_channel() && (! array_key_exists('real_uid',$item))) { - $star = [ - 'toggle' => t('Save'), - 'isstarred' => ((intval($item['item_starred'])) ? true : false), - ]; - } - } - else { - $is_comment = true; - } - - - $verified = (intval($item['item_verified']) ? t('Message signature validated') : ''); - $forged = ((($item['sig']) && (! intval($item['item_verified']))) ? t('Message signature incorrect') : ''); - $unverified = '' ; // (($this->is_wall_to_wall() && (! intval($item['item_verified']))) ? t('Message cannot be verified') : ''); - - - if($conv->get_profile_owner() == local_channel()) { - $tagger = array( - 'tagit' => t("Add Tag"), - 'classtagger' => "", - ); - } - - $has_bookmarks = false; - if(isset($item['term']) && is_array($item['term'])) { - foreach($item['term'] as $t) { - if($t['ttype'] == TERM_BOOKMARK) - $has_bookmarks = true; - } - } - - $has_event = false; - if(($item['obj_type'] === ACTIVITY_OBJ_EVENT) && $conv->get_profile_owner() == local_channel()) - $has_event = true; - - if($this->is_commentable() && $observer) { - $like = array( t('I like this'), t('Undo like')); - $dislike = array( t('I don\'t like this'), t('Undo dislike') ); - } - - $share = $embed = EMPTY_STR; - - if ($shareable) { - $share = t('Repeat This'); - $embed = t('Share this'); - } - - $dreport = ''; - - $keep_reports = intval(get_config('system','expire_delivery_reports')); - if($keep_reports === 0) - $keep_reports = 10; - - if((! get_config('system','disable_dreport')) && strcmp(datetime_convert('UTC','UTC',$item['created']),datetime_convert('UTC','UTC',"now - $keep_reports days")) > 0) { - $dreport = t('Delivery Report'); - $dreport_link = gen_link_id($item['mid']); - } - $is_new = false; - - if (strcmp(datetime_convert('UTC','UTC',$item['created']),datetime_convert('UTC','UTC','now - 12 hours')) > 0) { - $is_new = true; - } - - localize_item($item); - - $opts = []; - if ($this->is_wall_to_wall()) { - if ($this->owner_censored) { - $opts['censored'] = true; - } - } - - $body = prepare_body($item,true,$opts); - - // $viewthread (below) is only valid in list mode. If this is a channel page, build the thread viewing link - // since we can't depend on llink or plink pointing to the right local location. - - $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=' . urlencode(gen_link_id($item['mid'])); - - $comment_count_txt = sprintf( tt('%d comment','%d comments',$total_children),$total_children ); - $list_unseen_txt = (($unseen_comments) ? sprintf( t('%d unseen'),$unseen_comments) : ''); - - $children = $this->get_children(); - - - $has_tags = (($body['tags'] || $body['categories'] || $body['mentions'] || $body['attachments'] || $body['folders']) ? true : false); - - $dropdown_extras_arr = [ 'item' => $item , 'dropdown_extras' => '' ]; - call_hooks('dropdown_extras',$dropdown_extras_arr); - $dropdown_extras = $dropdown_extras_arr['dropdown_extras']; - - // Pinned item processing - $allowed_type = (in_array($item['item_type'], get_config('system', 'pin_types', [ ITEM_TYPE_POST ])) ? true : false); - $pinned_items = ($allowed_type ? get_pconfig($item['uid'], 'pinned', $item['item_type'], []) : []); - $pinned = ((! empty($pinned_items) && in_array($item['mid'], $pinned_items)) ? true : false); - - $tmp_item = array( - 'template' => $this->get_template(), - 'mode' => $mode, - 'item_type' => intval($item['item_type']), - 'comment_order' => $item['comment_order'], - 'parent' => $this->get_data_value('parent'), - 'collapsed' => ((intval($item['comment_order']) > 3) ? true : false), - 'type' => implode("",array_slice(explode("/",$item['verb']),-1)), - 'body' => $body['html'], - 'tags' => $body['tags'], - 'categories' => $body['categories'], - 'mentions' => $body['mentions'], - 'attachments' => $body['attachments'], - 'folders' => $body['folders'], - 'text' => strip_tags($body['html']), - 'id' => $this->get_id(), - 'mid' => $item['mid'], - 'isevent' => $isevent, - 'attend' => $attend, - 'undo_attend' => $undo_attend, - 'consensus' => '', - 'conlabels' => '', - 'canvote' => $canvote, - 'linktitle' => sprintf( t('View %s\'s profile - %s'), $profile_name, (($item['author']['xchan_addr']) ? $item['author']['xchan_addr'] : $item['author']['xchan_url'])), - 'olinktitle' => sprintf( t('View %s\'s profile - %s'), $this->get_owner_name(), (($item['owner']['xchan_addr']) ? $item['owner']['xchan_addr'] : $item['owner']['xchan_url'])), - 'llink' => $item['llink'], - 'viewthread' => $viewthread, - 'to' => t('to'), - 'via' => t('via'), - '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()), - 'dreport' => $dreport, - 'dreport_link' => ((isset($dreport_link) && $dreport_link) ? $dreport_link : EMPTY_STR), - 'myconv' => $myconv, - 'name' => $profile_name, - 'thumb' => $profile_avatar, - 'osparkle' => $osparkle, - 'sparkle' => $sparkle, - 'title' => $item['title'], - 'title_tosource' => get_pconfig($conv->get_profile_owner(),'system','title_tosource'), - 'ago' => relative_date($item['created']), - 'app' => $item['app'], - 'str_app' => sprintf( t('from %s'), $item['app']), - 'isotime' => datetime_convert('UTC', date_default_timezone_get(), $item['created'], 'c'), - 'localtime' => datetime_convert('UTC', date_default_timezone_get(), $item['created'], 'r'), - 'editedtime' => (($item['edited'] != $item['created']) ? sprintf( t('last edited: %s'), datetime_convert('UTC', date_default_timezone_get(), $item['edited'], 'r')) : ''), - 'expiretime' => (($item['expires'] > NULL_DATE) ? sprintf( t('Expires: %s'), datetime_convert('UTC', date_default_timezone_get(), $item['expires'], 'r')):''), - 'lock' => $lock, - 'locktype' => $locktype, - 'delayed' => $item['item_delayed'], - 'privacy_warning' => $privacy_warning, - 'verified' => $verified, - 'unverified' => $unverified, - 'forged' => $forged, - 'location' => $location, - 'divider' => get_pconfig($conv->get_profile_owner(),'system','item_divider'), - 'attend_label' => t('Attend'), - 'attend_title' => t('Attendance Options'), - 'vote_label' => t('Vote'), - 'vote_title' => t('Voting Options'), - 'comment_lbl' => (($this->is_commentable() && $observer) ? t('Reply') : ''), - 'is_comment' => $is_comment, - 'is_new' => $is_new, - 'mod_display' => ((argv(0) === 'display') ? true : false), // comments are not collapsed when using mod_display - 'owner_url' => $this->get_owner_url(), - 'owner_photo' => $this->get_owner_photo(), - 'owner_name' => $this->get_owner_name(), - 'photo' => $body['photo'], - 'event' => $body['event'], - 'has_tags' => $has_tags, - 'reactions' => $this->reactions, - - // Item toolbar buttons - - 'emojis' => '', // deprecated - use your operating system or a browser plugin - 'like' => $like, - 'dislike' => $dislike, - 'share' => $share, - 'embed' => $embed, - 'rawmid' => $item['mid'], - 'plink' => get_plink($item), - 'edpost' => $edpost, // ((feature_enabled($conv->get_profile_owner(),'edit_posts')) ? $edpost : ''), - 'star' => $star, - 'tagger' => ((feature_enabled($conv->get_profile_owner(),'commtag')) ? $tagger : ''), - 'filer' => ((feature_enabled($conv->get_profile_owner(),'filing')) ? $filer : ''), - 'pinned' => ($pinned ? t('Pinned post') : ''), - 'pinnable' => (($this->is_toplevel() && local_channel() && $item['owner_xchan'] == $observer['xchan_hash'] && $allowed_type && $item['item_private'] == 0 && $item['item_delayed'] == 0) ? '1' : ''), - 'pinme' => ($pinned ? t('Unpin this post') : t('Pin this post')), - 'isdraft' => boolval($item['item_unpublished']), - 'draft_txt' => t('Saved draft'), - 'bookmark' => (($conv->get_profile_owner() == local_channel() && local_channel() && $has_bookmarks) ? t('Save Bookmarks') : ''), - 'addtocal' => (($has_event && ! $item['resource_id']) ? t('Add to Calendar') : ''), - 'drop' => $drop, - 'multidrop' => ((feature_enabled($conv->get_profile_owner(),'multi_delete')) ? $multidrop : ''), - 'dropdown_extras' => $dropdown_extras, - - // end toolbar buttons - - 'unseen_comments' => $unseen_comments, - 'comment_count' => $total_children, - 'comment_count_txt' => $comment_count_txt, - 'list_unseen_txt' => $list_unseen_txt, - 'markseen' => t('Mark all seen'), - 'responses' => $responses, - 'my_responses' => $my_responses, - 'like_count' => $like_count, - 'like_list' => $like_list, - 'like_list_part' => $like_list_part, - 'like_button_label' => $like_button_label, - 'like_modal_title' => t('Likes','noun'), - 'dislike_modal_title' => t('Dislikes','noun'), - 'dislike_count' => $dislike_count, - 'dislike_list' => $dislike_list, - 'dislike_list_part' => $dislike_list_part, - 'dislike_button_label' => $dislike_button_label, - 'modal_dismiss' => t('Close'), - 'showlike' => $showlike, - 'showdislike' => $showdislike, - 'comment' => ($item['item_delayed'] ? '' : $this->get_comment_box()), - 'previewing' => ($conv->is_preview() ? true : false ), - 'preview_lbl' => t('This is an unsaved preview'), - 'wait' => t('Please wait'), - 'submid' => str_replace(['+','='], ['',''], base64_encode($item['mid'])), - 'thread_level' => $thread_level, - 'indentpx' => intval(get_pconfig(local_channel(),'system','thread_indent_px',get_config('system','thread_indent_px',0))), - 'thread_max' => intval(get_config('system','thread_maxlevel',20)) + 1 - ); - - $arr = array('item' => $item, 'output' => $tmp_item); - call_hooks('display_item', $arr); - - $result = $arr['output']; - - $result['children'] = []; - - if (local_channel() && get_pconfig(local_channel(),'system','activitypub',get_config('system','activitypub', ACTIVITYPUB_ENABLED))) { - // place to store all the author addresses (links if not available) in the thread so we can auto-mention them in JS. - $result['authors'] = []; - // fix to add in sub-replies if replying to a comment on your own post from the top level. - if ($observer && ($profile_addr === $observer['xchan_hash'] || $profile_addr === $observer['xchan_addr'])) { - // ignore it - } - else { - $result['authors'][] = $profile_addr; - } - - // Add any mentions from the immediate parent, unless they are mentions of the current viewer or duplicates - if (isset($item['term']) && is_array($item['term'])) { - $additional_mentions = []; - foreach ($item['term'] as $t) { - if ($t['ttype'] == TERM_MENTION) { - $additional_mentions[] = ((($position = strpos($t['url'],'url=')) !== false) ? urldecode(substr($t['url'],$position + 4)) : $t['url']);; - } - } - if ($additional_mentions) { - $r = q("select hubloc_addr, hubloc_id_url, hubloc_hash from hubloc where hubloc_id_url in (" . protect_sprintf(stringify_array($additional_mentions, true)) . ") "); - if ($r) { - foreach ($r as $rv) { - $ment = (($r[0]['hubloc_addr']) ? $r[0]['hubloc_addr'] : $r[0]['hubloc_id_url']); - if ($ment) { - if ($observer && $observer['xchan_hash'] !== $rv['hubloc_hash'] && ! in_array($ment,$result['authors'])) { - $result['authors'][] = $ment; - } - } - } - } - } - } - } - - $nb_children = count($children); - - $total_children = $this->count_visible_descendants(); - - $visible_comments = get_config('system', 'expanded_comments', 3); - - if(($this->get_display_mode() === 'normal') && ($nb_children > 0)) { - if ($children) { - foreach($children as $child) { - $xz = $child->get_template_data($conv_responses, $thread_level + 1); - $result['children'][] = $xz; - } - } - // Collapse - if($total_children > $visible_comments && $thread_level == 1) { - $result['children'][0]['comment_firstcollapsed'] = true; - $result['children'][0]['num_comments'] = $comment_count_txt; - $result['children'][0]['hide_text'] = sprintf( t('%s show all'), ''); - } - } - - $result['private'] = $item['item_private']; - $result['toplevel'] = ($this->is_toplevel() ? 'toplevel_item' : ''); - - if($this->is_threaded()) { - $result['flatten'] = false; - $result['threaded'] = true; - } - else { - $result['flatten'] = true; - $result['threaded'] = false; - } - - return $result; - } - - public function get_id() { - return $this->get_data_value('id'); - } - - public function get_display_mode() { - return $this->display_mode; - } - - public function set_display_mode($mode) { - $this->display_mode = $mode; - } - - public function is_threaded() { - return $this->threaded; - } - - public function get_author() { - $xchan = $this->get_data_value('author'); - if($xchan['xchan_addr']) { - return $xchan['xchan_addr']; - } - return $xchan['xchan_url']; - } - - public function set_reload($val) { - $this->reload = $val; - } - - public function get_reload() { - return $this->reload; - } - - public function set_commentable($val) { - $this->commentable = $val; - foreach($this->get_children() as $child) - $child->set_commentable($val); - } - - public function is_commentable() { - return $this->commentable; - } - - /** - * Add a child item - */ - public function add_child($item) { - $item_id = $item->get_id(); - if(!$item_id) { - logger('[ERROR] Item::add_child : Item has no ID!!', LOGGER_DEBUG); - return false; - } - if($this->get_child($item->get_id())) { - logger('[WARN] Item::add_child : Item already exists ('. $item->get_id() .').', LOGGER_DEBUG); - return false; - } - - /* - * Only add what will be displayed - */ - - if(activity_match($item->get_data_value('verb'),ACTIVITY_LIKE) || activity_match($item->get_data_value('verb'),ACTIVITY_DISLIKE)) { - return false; - } - - $item->set_parent($this); - $this->children[] = $item; - return end($this->children); - } - - /** - * Get a child by its ID - */ - - public function get_child($id) { - foreach($this->get_children() as $child) { - if($child->get_id() == $id) - return $child; - } - return null; - } - - /** - * Get all our children - */ - - public function get_children() { - return $this->children; - } - - /** - * Set our parent - */ - protected function set_parent($item) { - $parent = $this->get_parent(); - if($parent) { - $parent->remove_child($this); - } - $this->parent = $item; - $this->set_conversation($item->get_conversation()); - } - - /** - * Remove our parent - */ - - protected function remove_parent() { - $this->parent = null; - $this->conversation = null; - } - - /** - * Remove a child - */ - - public function remove_child($item) { - $id = $item->get_id(); - foreach($this->get_children() as $key => $child) { - if($child->get_id() == $id) { - $child->remove_parent(); - unset($this->children[$key]); - // Reindex the array, in order to make sure there won't be any trouble on loops using count() - $this->children = array_values($this->children); - return true; - } - } - logger('[WARN] Item::remove_child : Item is not a child ('. $id .').', LOGGER_DEBUG); - return false; - } - - /** - * Get parent item - */ - protected function get_parent() { - return $this->parent; - } - - /** - * set conversation - */ - public function set_conversation($conv) { - $previous_mode = ($this->conversation ? $this->conversation->get_mode() : ''); - - $this->conversation = $conv; - - // Set it on our children too - foreach($this->get_children() as $child) - $child->set_conversation($conv); - } - - /** - * get conversation - */ - public function get_conversation() { - return $this->conversation; - } - - /** - * Get raw data - * - * We shouldn't need this - */ - public function get_data() { - return $this->data; - } - - /** - * Get a data value - * - * Returns: - * _ value on success - * _ false on failure - */ - public function get_data_value($name) { - if(!isset($this->data[$name])) { -// logger('[ERROR] Item::get_data_value : Item has no value name "'. $name .'".', LOGGER_DEBUG); - return false; - } - - return $this->data[$name]; - } - - /** - * Get template - */ - public function get_template() { - return $this->template; - } - - - public function set_template($t) { - $this->template = $t; - } - - /** - * Check if this is a toplevel post - */ - private function is_toplevel() { - return $this->toplevel; - } - - /** - * Count the total of our descendants - */ - private function count_descendants() { - $children = $this->get_children(); - $total = count($children); - if($total > 0) { - foreach($children as $child) { - $total += $child->count_descendants(); - } - } - return $total; - } - - public function count_visible_descendants() { - $total = 0; - $children = $this->get_children(); - if ($children) { - foreach ($children as $child) { - if (! visible_activity($child->data)) { - continue; - } - $total ++; - $total += $child->count_visible_descendants(); - } - } - return $total; - } - - - private function label_descendants($count = 0) { - if(! array_key_exists('sequence',$this->data)) { - if($count) { - $count ++; - } - $this->data['sequence'] = $count; - } - logger('labelled: ' . print_r($this->data,true), LOGGER_DATA); - $children = $this->get_children(); - $total = count($children); - if($total > 0) { - foreach($children as $child) { - if(! visible_activity($child->data)) { - continue; - } - if(! array_key_exists('sequence',$this->data)) { - $count ++; - $child->data['sequence'] = $count; - logger('labelled_child: ' . print_r($child->data,true), LOGGER_DATA); - } - $child->label_descendants($count); - } - } - } - - private function count_unseen_descendants() { - $children = $this->get_children(); - $total = count($children); - if($total > 0) { - $total = 0; - foreach($children as $child) { - if(! visible_activity($child->data)) { - continue; - } - if(intval($child->data['item_unseen'])) - $total ++; - } - } - return $total; - } - - - /** - * Get the template for the comment box - */ - private function get_comment_box_template() { - return $this->comment_box_template; - } - - /** - * Get the comment box - * - * Returns: - * _ The comment box string (empty if no comment box) - * _ false on failure - */ - private function get_comment_box($indent = 0) { - - if(!$this->is_toplevel() && !get_config('system','thread_allow',true)) { - return ''; - } - - $comment_box = ''; - $conv = $this->get_conversation(); - -// logger('Commentable conv: ' . $conv->is_commentable()); - - if(! $this->is_commentable()) - return; - - $template = get_markup_template($this->get_comment_box_template()); - - $observer = $conv->get_observer(); - - $arr = array('comment_buttons' => '','id' => $this->get_id()); - call_hooks('comment_buttons',$arr); - $comment_buttons = $arr['comment_buttons']; - - $feature_auto_save_draft = ((feature_enabled($conv->get_profile_owner(), 'auto_save_draft')) ? "true" : "false"); - $permanent_draft = ((intval($conv->get_profile_owner()) === intval(local_channel()) && Apps::system_app_installed($conv->get_profile_owner(),'Drafts')) ? ('Save draft') : EMPTY_STR); - - - - $comment_box = replace_macros($template,array( - '$return_path' => '', - '$threaded' => $this->is_threaded(), - '$jsreload' => $conv->reload, - '$type' => (($conv->get_mode() === 'channel') ? 'wall-comment' : 'net-comment'), - '$id' => $this->get_id(), - '$parent' => $this->get_id(), - '$comment_buttons' => $comment_buttons, - '$profile_uid' => $conv->get_profile_owner(), - '$mylink' => $observer['xchan_url'], - '$mytitle' => t('This is you'), - '$myphoto' => $observer['xchan_photo_s'], - '$comment' => t('Comment'), - '$submit' => t('Submit'), - '$edat' => EMPTY_STR, - '$edbold' => t('Bold'), - '$editalic' => t('Italic'), - '$eduline' => t('Underline'), - '$edquote' => t('Quote'), - '$edcode' => t('Code'), - '$edimg' => t('Image'), - '$edatt' => t('Attach/Upload file'), - '$edurl' => t('Insert Link'), - '$edvideo' => t('Video'), - '$preview' => t('Preview'), - '$reset' => t('Reset'), - '$indent' => $indent, - '$can_upload' => (perm_is_allowed($conv->get_profile_owner(),get_observer_hash(),'write_storage') && $conv->is_uploadable()), - '$feature_encrypt' => ((Apps::system_app_installed($conv->get_profile_owner(),'Secrets')) ? true : false), - '$feature_markup' => ((Apps::system_app_installed($conv->get_profile_owner(),'Markup')) ? true : false), - '$encrypt' => t('Encrypt text'), - '$cipher' => $conv->get_cipher(), - '$sourceapp' => App::$sourcename, - '$observer' => get_observer_hash(), - '$anoncomments' => ((($conv->get_mode() === 'channel' || $conv->get_mode() === 'display') && perm_is_allowed($conv->get_profile_owner(),'','post_comments')) ? true : false), - '$anonname' => [ 'anonname', t('Your full name (required)') ], - '$anonmail' => [ 'anonmail', t('Your email address (required)') ], - '$anonurl' => [ 'anonurl', t('Your website URL (optional)') ], - '$auto_save_draft' => $feature_auto_save_draft, - '$save' => $permanent_draft, - '$top' => $this->is_toplevel() - )); - - return $comment_box; - } - - private function get_redirect_url() { - return $this->redirect_url; - } - - /** - * Check if we are a wall to wall item and set the relevant properties - */ - protected function check_wall_to_wall() { - $conv = $this->get_conversation(); - $this->wall_to_wall = false; - $this->owner_url = ''; - $this->owner_photo = ''; - $this->owner_name = ''; - $this->owner_censored = false; - - if($conv->get_mode() === 'channel') - return; - - if($this->is_toplevel() && ($this->get_data_value('author_xchan') != $this->get_data_value('owner_xchan'))) { - $this->owner_url = chanlink_hash($this->data['owner']['xchan_hash']); - $this->owner_photo = $this->data['owner']['xchan_photo_m']; - $this->owner_name = $this->data['owner']['xchan_name']; - $this->wall_to_wall = true; - } - - // present friend-of-friend conversations from hyperdrive as relayed posts from the first friend - // we find among the respondents. - - if ($this->is_toplevel() && (! $this->data['owner']['abook_id'])) { - if ($this->data['children']) { - $friend = $this->find_a_friend($this->data['children']); - if ($friend) { - $this->owner_url = $friend['url']; - $this->owner_photo = $friend['photo']; - $this->owner_name = $friend['name']; - $this->owner_censored = $friend['censored']; - $this->wall_to_wall = true; - } - } - } - } - - private function find_a_friend($items) { - $ret = null; - if ($items) { - foreach ($items as $child) { - if ($child['author']['abook_id'] && (! intval($child['author']['abook_self']))) { - return [ - 'url' => chanlink_hash($child['author']['xchan_hash']), - 'photo' => $child['author']['xchan_photo_m'], - 'name' => $child['author']['xchan_name'], - 'censored' => (($child['author']['xchan_censored'] || $child['author']['abook_censor']) ? true : false) - ]; - if ($child['children']) { - $ret = $this->find_a_friend($child['children']); - if ($ret) { - break; - } - } - } - } - } - return $ret; - } - - - private function is_wall_to_wall() { - return $this->wall_to_wall; - } - - private function get_owner_url() { - return $this->owner_url; - } - - private function get_owner_photo() { - return $this->owner_photo; - } - - private function get_owner_name() { - return $this->owner_name; - } - - private function is_visiting() { - return $this->visiting; - } - + $locktype = intval($item['item_private']); + $shareable = ((($conv->get_profile_owner() == local_channel() && local_channel()) && (! intval($item['item_private']))) ? true : false); + + // allow an exemption for sharing stuff from your private feeds + if ($item['author']['xchan_network'] === 'rss') { + $shareable = true; + } + // @fixme + // Have recently added code to properly handle polls in group reshares by redirecting all of the poll responses to the group. + // Sharing a poll using a regular embedded share is harder because the poll will need to fork. This is due to comment permissions. + // The original poll author may not accept responses from strangers. Forking the poll will receive responses from the sharer's + // followers, but there's no elegant way to merge these two sets of results together. For now, we'll disable sharing polls. + if ($item['obj_type'] === 'Question') { + $shareable = false; + } + + + if ($item['item_restrict'] & 2) { + $privacy_warning = true; + $lock = t('This comment is part of a private conversation, yet was shared with the public. Discretion advised.'); + } + + $mode = $conv->get_mode(); + + $edlink = 'editpost'; + + if (local_channel() && $observer['xchan_hash'] === $item['author_xchan']) { + $edpost = array(z_root() . '/' . $edlink . '/' . $item['id'], t('Edit')); + } else { + $edpost = false; + } + + if (local_channel() && $observer['xchan_hash'] === $item['owner_xchan']) { + $myconv = true; + } else { + $myconv = false; + } + + + if ($item['verb'] === 'Announce') { + $edpost = false; + } + + + if ( + $observer && $observer['xchan_hash'] + && ( $observer['xchan_hash'] == $this->get_data_value('author_xchan') + || $observer['xchan_hash'] == $this->get_data_value('owner_xchan') + || $observer['xchan_hash'] == $this->get_data_value('source_xchan') + || $this->get_data_value('uid') == local_channel()) + ) { + $dropping = true; + } + + + if (array_key_exists('real_uid', $item)) { + $edpost = false; + $dropping = false; + } + + + if ($dropping) { + $drop = array( + 'dropping' => $dropping, + 'delete' => t('Delete'), + ); + } elseif (is_site_admin()) { + $drop = [ 'dropping' => true, 'delete' => t('Admin Delete') ]; + } + + if (isset($observer_is_pageowner) && $observer_is_pageowner) { + $multidrop = array( + 'select' => t('Select'), + ); + } + + $filer = ((($conv->get_profile_owner() == local_channel()) && (! array_key_exists('real_uid', $item))) ? t('Save to Folder') : false); + + $profile_avatar = $item['author']['xchan_photo_m']; + $profile_link = chanlink_hash($item['author_xchan']); + $profile_name = $item['author']['xchan_name']; + + $profile_addr = $item['author']['xchan_addr'] ? $item['author']['xchan_addr'] : $item['author']['xchan_url']; + + $location = format_location($item); + $isevent = false; + $attend = null; + $canvote = false; + + // process action responses - e.g. like/dislike/attend/agree/whatever + $response_verbs = [ 'like', 'dislike' ]; + + if ($item['obj_type'] === ACTIVITY_OBJ_EVENT) { + $response_verbs[] = 'attendyes'; + $response_verbs[] = 'attendno'; + $response_verbs[] = 'attendmaybe'; + if ($this->is_commentable() && $observer) { + $isevent = true; + $attend = array( t('I will attend'), t('I will not attend'), t('I might attend')); + $undo_attend = t('Undo attendance'); + } + } + + $responses = get_responses($conv_responses, $response_verbs, $this, $item); + + $my_responses = []; + foreach ($response_verbs as $v) { + $my_responses[$v] = ((isset($conv_responses[$v][$item['mid'] . '-m']) && $conv_responses[$v][$item['mid'] . '-m']) ? 1 : 0); + } + + $like_count = ((x($conv_responses['like'], $item['mid'])) ? $conv_responses['like'][$item['mid']] : ''); + $like_list = ((x($conv_responses['like'], $item['mid'])) ? $conv_responses['like'][$item['mid'] . '-l'] : ''); + if (($like_list) && (count($like_list) > MAX_LIKERS)) { + $like_list_part = array_slice($like_list, 0, MAX_LIKERS); + array_push($like_list_part, '' . t('View all') . ''); + } else { + $like_list_part = ''; + } + if (get_config('system', 'show_like_counts', true)) { + $like_button_label = tt('Like', 'Likes', $like_count, 'noun'); + } else { + $like_button_label = t('Likes', 'noun'); + } + + $dislike_count = ((x($conv_responses['dislike'], $item['mid'])) ? $conv_responses['dislike'][$item['mid']] : ''); + $dislike_list = ((x($conv_responses['dislike'], $item['mid'])) ? $conv_responses['dislike'][$item['mid'] . '-l'] : ''); + if (get_config('system', 'show_like_counts', true)) { + $dislike_button_label = tt('Dislike', 'Dislikes', $dislike_count, 'noun'); + } else { + $dislike_button_label = t('Dislikes', 'noun'); + } + + if (($dislike_list) && (count($dislike_list) > MAX_LIKERS)) { + $dislike_list_part = array_slice($dislike_list, 0, MAX_LIKERS); + array_push($dislike_list_part, '' . t('View all') . ''); + } else { + $dislike_list_part = ''; + } + + + $showlike = ((x($conv_responses['like'], $item['mid'])) ? format_like($conv_responses['like'][$item['mid']], $conv_responses['like'][$item['mid'] . '-l'], 'like', $item['mid']) : ''); + $showdislike = ((x($conv_responses['dislike'], $item['mid'])) + ? format_like($conv_responses['dislike'][$item['mid']], $conv_responses['dislike'][$item['mid'] . '-l'], 'dislike', $item['mid']) : ''); + + /* + * We should avoid doing this all the time, but it depends on the conversation mode + * And the conv mode may change when we change the conv, or it changes its mode + * Maybe we should establish a way to be notified about conversation changes + */ + + $this->check_wall_to_wall(); + + if ($this->is_toplevel()) { + if (local_channel() && ($conv->get_profile_owner() == local_channel() || intval($item['item_private']) === 0)) { + $star = [ + 'toggle' => t('Save'), + 'isstarred' => ((intval($item['item_starred'])) ? true : false), + ]; + } + } else { + $is_comment = true; + } + + + $verified = (intval($item['item_verified']) ? t('Message signature validated') : ''); + $forged = ((($item['sig']) && (! intval($item['item_verified']))) ? t('Message signature incorrect') : ''); + $unverified = '' ; // (($this->is_wall_to_wall() && (! intval($item['item_verified']))) ? t('Message cannot be verified') : ''); + + + if ($conv->get_profile_owner() == local_channel()) { + $tagger = array( + 'tagit' => t("Add Tag"), + 'classtagger' => "", + ); + } + + $has_bookmarks = false; + if (isset($item['term']) && is_array($item['term'])) { + foreach ($item['term'] as $t) { + if ($t['ttype'] == TERM_BOOKMARK) { + $has_bookmarks = true; + } + } + } + + $has_event = false; + if (($item['obj_type'] === ACTIVITY_OBJ_EVENT) && $conv->get_profile_owner() == local_channel()) { + $has_event = true; + } + + if ($this->is_commentable() && $observer) { + $like = array( t('I like this'), t('Undo like')); + $dislike = array( t('I don\'t like this'), t('Undo dislike') ); + } + + $share = $embed = EMPTY_STR; + + if ($shareable) { + $share = t('Repeat This'); + $embed = t('Share this'); + } + + $dreport = ''; + + $keep_reports = intval(get_config('system', 'expire_delivery_reports')); + if ($keep_reports === 0) { + $keep_reports = 10; + } + + if ((! get_config('system', 'disable_dreport')) && strcmp(datetime_convert('UTC', 'UTC', $item['created']), datetime_convert('UTC', 'UTC', "now - $keep_reports days")) > 0) { + $dreport = t('Delivery Report'); + $dreport_link = gen_link_id($item['mid']); + } + $is_new = false; + + if (strcmp(datetime_convert('UTC', 'UTC', $item['created']), datetime_convert('UTC', 'UTC', 'now - 12 hours')) > 0) { + $is_new = true; + } + + localize_item($item); + + $opts = []; + if ($this->is_wall_to_wall()) { + if ($this->owner_censored) { + $opts['censored'] = true; + } + } + + $body = prepare_body($item, true, $opts); + + // $viewthread (below) is only valid in list mode. If this is a channel page, build the thread viewing link + // since we can't depend on llink or plink pointing to the right local location. + + $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=' . urlencode(gen_link_id($item['mid'])); + } + + $comment_count_txt = sprintf(tt('%d comment', '%d comments', $total_children), $total_children); + $list_unseen_txt = (($unseen_comments) ? sprintf(t('%d unseen'), $unseen_comments) : ''); + + $children = $this->get_children(); + + + $has_tags = (($body['tags'] || $body['categories'] || $body['mentions'] || $body['attachments'] || $body['folders']) ? true : false); + + $dropdown_extras_arr = [ 'item' => $item , 'dropdown_extras' => '' ]; + call_hooks('dropdown_extras', $dropdown_extras_arr); + $dropdown_extras = $dropdown_extras_arr['dropdown_extras']; + + // Pinned item processing + $allowed_type = (in_array($item['item_type'], get_config('system', 'pin_types', [ ITEM_TYPE_POST ])) ? true : false); + $pinned_items = ($allowed_type ? get_pconfig($item['uid'], 'pinned', $item['item_type'], []) : []); + $pinned = ((! empty($pinned_items) && in_array($item['mid'], $pinned_items)) ? true : false); + + $tmp_item = array( + 'template' => $this->get_template(), + 'mode' => $mode, + 'item_type' => intval($item['item_type']), + 'comment_order' => $item['comment_order'], + 'parent' => $this->get_data_value('parent'), + 'collapsed' => ((intval($item['comment_order']) > 3) ? true : false), + 'type' => implode("", array_slice(explode("/", $item['verb']), -1)), + 'body' => $body['html'], + 'tags' => $body['tags'], + 'categories' => $body['categories'], + 'mentions' => $body['mentions'], + 'attachments' => $body['attachments'], + 'folders' => $body['folders'], + 'text' => strip_tags($body['html']), + 'id' => $this->get_id(), + 'mid' => $item['mid'], + 'isevent' => $isevent, + 'attend' => $attend, + 'undo_attend' => $undo_attend, + 'consensus' => '', + 'conlabels' => '', + 'canvote' => $canvote, + 'linktitle' => sprintf(t('View %s\'s profile - %s'), $profile_name, (($item['author']['xchan_addr']) ? $item['author']['xchan_addr'] : $item['author']['xchan_url'])), + 'olinktitle' => sprintf(t('View %s\'s profile - %s'), $this->get_owner_name(), (($item['owner']['xchan_addr']) ? $item['owner']['xchan_addr'] : $item['owner']['xchan_url'])), + 'llink' => $item['llink'], + 'viewthread' => $viewthread, + 'to' => t('to'), + 'via' => t('via'), + '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()), + 'dreport' => $dreport, + 'dreport_link' => ((isset($dreport_link) && $dreport_link) ? $dreport_link : EMPTY_STR), + 'myconv' => $myconv, + 'name' => $profile_name, + 'thumb' => $profile_avatar, + 'osparkle' => $osparkle, + 'sparkle' => $sparkle, + 'title' => $item['title'], + 'title_tosource' => get_pconfig($conv->get_profile_owner(), 'system', 'title_tosource'), + 'ago' => relative_date($item['created']), + 'app' => $item['app'], + 'str_app' => sprintf(t('from %s'), $item['app']), + 'isotime' => datetime_convert('UTC', date_default_timezone_get(), $item['created'], 'c'), + 'localtime' => datetime_convert('UTC', date_default_timezone_get(), $item['created'], 'r'), + 'editedtime' => (($item['edited'] != $item['created']) ? sprintf(t('last edited: %s'), datetime_convert('UTC', date_default_timezone_get(), $item['edited'], 'r')) : ''), + 'expiretime' => (($item['expires'] > NULL_DATE) ? sprintf(t('Expires: %s'), datetime_convert('UTC', date_default_timezone_get(), $item['expires'], 'r')) : ''), + 'lock' => $lock, + 'locktype' => $locktype, + 'delayed' => $item['item_delayed'], + 'privacy_warning' => $privacy_warning, + 'verified' => $verified, + 'unverified' => $unverified, + 'forged' => $forged, + 'location' => $location, + 'divider' => get_pconfig($conv->get_profile_owner(), 'system', 'item_divider'), + 'attend_label' => t('Attend'), + 'attend_title' => t('Attendance Options'), + 'vote_label' => t('Vote'), + 'vote_title' => t('Voting Options'), + 'comment_lbl' => (($this->is_commentable() && $observer) ? t('Reply') : ''), + 'is_comment' => $is_comment, + 'is_new' => $is_new, + 'mod_display' => ((argv(0) === 'display') ? true : false), // comments are not collapsed when using mod_display + 'owner_url' => $this->get_owner_url(), + 'owner_photo' => $this->get_owner_photo(), + 'owner_name' => $this->get_owner_name(), + 'photo' => $body['photo'], + 'event' => $body['event'], + 'has_tags' => $has_tags, + 'reactions' => $this->reactions, + + // Item toolbar buttons + + 'emojis' => '', // deprecated - use your operating system or a browser plugin + 'like' => $like, + 'dislike' => $dislike, + 'share' => $share, + 'embed' => $embed, + 'rawmid' => $item['mid'], + 'plink' => get_plink($item), + 'edpost' => $edpost, // ((feature_enabled($conv->get_profile_owner(),'edit_posts')) ? $edpost : ''), + 'star' => $star, + 'tagger' => ((feature_enabled($conv->get_profile_owner(), 'commtag')) ? $tagger : ''), + 'filer' => ((feature_enabled($conv->get_profile_owner(), 'filing')) ? $filer : ''), + 'pinned' => ($pinned ? t('Pinned post') : ''), + 'pinnable' => (($this->is_toplevel() && local_channel() && $item['owner_xchan'] == $observer['xchan_hash'] && $allowed_type && $item['item_private'] == 0 && $item['item_delayed'] == 0) ? '1' : ''), + 'pinme' => ($pinned ? t('Unpin this post') : t('Pin this post')), + 'isdraft' => boolval($item['item_unpublished']), + 'draft_txt' => t('Saved draft'), + 'bookmark' => (($conv->get_profile_owner() == local_channel() && local_channel() && $has_bookmarks) ? t('Save Bookmarks') : ''), + 'addtocal' => (($has_event && ! $item['resource_id']) ? t('Add to Calendar') : ''), + 'drop' => $drop, + 'multidrop' => ((feature_enabled($conv->get_profile_owner(), 'multi_delete')) ? $multidrop : ''), + 'dropdown_extras' => $dropdown_extras, + + // end toolbar buttons + + 'unseen_comments' => $unseen_comments, + 'comment_count' => $total_children, + 'comment_count_txt' => $comment_count_txt, + 'list_unseen_txt' => $list_unseen_txt, + 'markseen' => t('Mark all seen'), + 'responses' => $responses, + 'my_responses' => $my_responses, + 'like_count' => $like_count, + 'like_list' => $like_list, + 'like_list_part' => $like_list_part, + 'like_button_label' => $like_button_label, + 'like_modal_title' => t('Likes', 'noun'), + 'dislike_modal_title' => t('Dislikes', 'noun'), + 'dislike_count' => $dislike_count, + 'dislike_list' => $dislike_list, + 'dislike_list_part' => $dislike_list_part, + 'dislike_button_label' => $dislike_button_label, + 'modal_dismiss' => t('Close'), + 'showlike' => $showlike, + 'showdislike' => $showdislike, + 'comment' => ($item['item_delayed'] ? '' : $this->get_comment_box()), + 'previewing' => ($conv->is_preview() ? true : false ), + 'preview_lbl' => t('This is an unsaved preview'), + 'wait' => t('Please wait'), + 'submid' => str_replace(['+','='], ['',''], base64_encode($item['mid'])), + 'thread_level' => $thread_level, + 'indentpx' => intval(get_pconfig(local_channel(), 'system', 'thread_indent_px', get_config('system', 'thread_indent_px', 0))), + 'thread_max' => intval(get_config('system', 'thread_maxlevel', 20)) + 1 + ); + + $arr = array('item' => $item, 'output' => $tmp_item); + call_hooks('display_item', $arr); + + $result = $arr['output']; + + $result['children'] = []; + + if (local_channel() && get_pconfig(local_channel(), 'system', 'activitypub', get_config('system', 'activitypub', ACTIVITYPUB_ENABLED))) { + // place to store all the author addresses (links if not available) in the thread so we can auto-mention them in JS. + $result['authors'] = []; + // fix to add in sub-replies if replying to a comment on your own post from the top level. + if ($observer && ($profile_addr === $observer['xchan_hash'] || $profile_addr === $observer['xchan_addr'])) { + // ignore it + } else { + $result['authors'][] = $profile_addr; + } + + // Add any mentions from the immediate parent, unless they are mentions of the current viewer or duplicates + if (isset($item['term']) && is_array($item['term'])) { + $additional_mentions = []; + foreach ($item['term'] as $t) { + if ($t['ttype'] == TERM_MENTION) { + $additional_mentions[] = ((($position = strpos($t['url'], 'url=')) !== false) ? urldecode(substr($t['url'], $position + 4)) : $t['url']); + } + } + if ($additional_mentions) { + $r = q("select hubloc_addr, hubloc_id_url, hubloc_hash from hubloc where hubloc_id_url in (" . protect_sprintf(stringify_array($additional_mentions, true)) . ") "); + if ($r) { + foreach ($r as $rv) { + $ment = (($r[0]['hubloc_addr']) ? $r[0]['hubloc_addr'] : $r[0]['hubloc_id_url']); + if ($ment) { + if ($observer && $observer['xchan_hash'] !== $rv['hubloc_hash'] && ! in_array($ment, $result['authors'])) { + $result['authors'][] = $ment; + } + } + } + } + } + } + } + + $nb_children = count($children); + + $total_children = $this->count_visible_descendants(); + + $visible_comments = get_config('system', 'expanded_comments', 3); + + if (($this->get_display_mode() === 'normal') && ($nb_children > 0)) { + if ($children) { + foreach ($children as $child) { + $xz = $child->get_template_data($conv_responses, $thread_level + 1); + $result['children'][] = $xz; + } + } + // Collapse + if ($total_children > $visible_comments && $thread_level == 1) { + $result['children'][0]['comment_firstcollapsed'] = true; + $result['children'][0]['num_comments'] = $comment_count_txt; + $result['children'][0]['hide_text'] = sprintf(t('%s show all'), ''); + } + } + + $result['private'] = $item['item_private']; + $result['toplevel'] = ($this->is_toplevel() ? 'toplevel_item' : ''); + + if ($this->is_threaded()) { + $result['flatten'] = false; + $result['threaded'] = true; + } else { + $result['flatten'] = true; + $result['threaded'] = false; + } + + return $result; + } + + public function get_id() + { + return $this->get_data_value('id'); + } + + public function get_display_mode() + { + return $this->display_mode; + } + + public function set_display_mode($mode) + { + $this->display_mode = $mode; + } + + public function is_threaded() + { + return $this->threaded; + } + + public function get_author() + { + $xchan = $this->get_data_value('author'); + if ($xchan['xchan_addr']) { + return $xchan['xchan_addr']; + } + return $xchan['xchan_url']; + } + + public function set_reload($val) + { + $this->reload = $val; + } + + public function get_reload() + { + return $this->reload; + } + + public function set_commentable($val) + { + $this->commentable = $val; + foreach ($this->get_children() as $child) { + $child->set_commentable($val); + } + } + + public function is_commentable() + { + return $this->commentable; + } + + /** + * Add a child item + */ + public function add_child($item) + { + $item_id = $item->get_id(); + if (!$item_id) { + logger('[ERROR] Item::add_child : Item has no ID!!', LOGGER_DEBUG); + return false; + } + if ($this->get_child($item->get_id())) { + logger('[WARN] Item::add_child : Item already exists (' . $item->get_id() . ').', LOGGER_DEBUG); + return false; + } + + /* + * Only add what will be displayed + */ + + if (activity_match($item->get_data_value('verb'), ACTIVITY_LIKE) || activity_match($item->get_data_value('verb'), ACTIVITY_DISLIKE)) { + return false; + } + + $item->set_parent($this); + $this->children[] = $item; + return end($this->children); + } + + /** + * Get a child by its ID + */ + + public function get_child($id) + { + foreach ($this->get_children() as $child) { + if ($child->get_id() == $id) { + return $child; + } + } + return null; + } + + /** + * Get all our children + */ + + public function get_children() + { + return $this->children; + } + + /** + * Set our parent + */ + protected function set_parent($item) + { + $parent = $this->get_parent(); + if ($parent) { + $parent->remove_child($this); + } + $this->parent = $item; + $this->set_conversation($item->get_conversation()); + } + + /** + * Remove our parent + */ + + protected function remove_parent() + { + $this->parent = null; + $this->conversation = null; + } + + /** + * Remove a child + */ + + public function remove_child($item) + { + $id = $item->get_id(); + foreach ($this->get_children() as $key => $child) { + if ($child->get_id() == $id) { + $child->remove_parent(); + unset($this->children[$key]); + // Reindex the array, in order to make sure there won't be any trouble on loops using count() + $this->children = array_values($this->children); + return true; + } + } + logger('[WARN] Item::remove_child : Item is not a child (' . $id . ').', LOGGER_DEBUG); + return false; + } + + /** + * Get parent item + */ + protected function get_parent() + { + return $this->parent; + } + + /** + * set conversation + */ + public function set_conversation($conv) + { + $previous_mode = ($this->conversation ? $this->conversation->get_mode() : ''); + + $this->conversation = $conv; + + // Set it on our children too + foreach ($this->get_children() as $child) { + $child->set_conversation($conv); + } + } + + /** + * get conversation + */ + public function get_conversation() + { + return $this->conversation; + } + + /** + * Get raw data + * + * We shouldn't need this + */ + public function get_data() + { + return $this->data; + } + + /** + * Get a data value + * + * Returns: + * _ value on success + * _ false on failure + */ + public function get_data_value($name) + { + if (!isset($this->data[$name])) { +// logger('[ERROR] Item::get_data_value : Item has no value name "'. $name .'".', LOGGER_DEBUG); + return false; + } + + return $this->data[$name]; + } + + /** + * Get template + */ + public function get_template() + { + return $this->template; + } + + + public function set_template($t) + { + $this->template = $t; + } + + /** + * Check if this is a toplevel post + */ + private function is_toplevel() + { + return $this->toplevel; + } + + /** + * Count the total of our descendants + */ + private function count_descendants() + { + $children = $this->get_children(); + $total = count($children); + if ($total > 0) { + foreach ($children as $child) { + $total += $child->count_descendants(); + } + } + return $total; + } + + public function count_visible_descendants() + { + $total = 0; + $children = $this->get_children(); + if ($children) { + foreach ($children as $child) { + if (! visible_activity($child->data)) { + continue; + } + $total++; + $total += $child->count_visible_descendants(); + } + } + return $total; + } + + + private function label_descendants($count = 0) + { + if (! array_key_exists('sequence', $this->data)) { + if ($count) { + $count++; + } + $this->data['sequence'] = $count; + } + logger('labelled: ' . print_r($this->data, true), LOGGER_DATA); + $children = $this->get_children(); + $total = count($children); + if ($total > 0) { + foreach ($children as $child) { + if (! visible_activity($child->data)) { + continue; + } + if (! array_key_exists('sequence', $this->data)) { + $count++; + $child->data['sequence'] = $count; + logger('labelled_child: ' . print_r($child->data, true), LOGGER_DATA); + } + $child->label_descendants($count); + } + } + } + + private function count_unseen_descendants() + { + $children = $this->get_children(); + $total = count($children); + if ($total > 0) { + $total = 0; + foreach ($children as $child) { + if (! visible_activity($child->data)) { + continue; + } + if (intval($child->data['item_unseen'])) { + $total++; + } + } + } + return $total; + } + + + /** + * Get the template for the comment box + */ + private function get_comment_box_template() + { + return $this->comment_box_template; + } + + /** + * Get the comment box + * + * Returns: + * _ The comment box string (empty if no comment box) + * _ false on failure + */ + private function get_comment_box($indent = 0) + { + + if (!$this->is_toplevel() && !get_config('system', 'thread_allow', true)) { + return ''; + } + + $comment_box = ''; + $conv = $this->get_conversation(); + +// logger('Commentable conv: ' . $conv->is_commentable()); + + if (! $this->is_commentable()) { + return; + } + + $template = get_markup_template($this->get_comment_box_template()); + + $observer = $conv->get_observer(); + + $arr = array('comment_buttons' => '','id' => $this->get_id()); + call_hooks('comment_buttons', $arr); + $comment_buttons = $arr['comment_buttons']; + + $feature_auto_save_draft = ((feature_enabled($conv->get_profile_owner(), 'auto_save_draft')) ? "true" : "false"); + $permanent_draft = ((intval($conv->get_profile_owner()) === intval(local_channel()) && Apps::system_app_installed($conv->get_profile_owner(), 'Drafts')) ? ('Save draft') : EMPTY_STR); + + + + $comment_box = replace_macros($template, array( + '$return_path' => '', + '$threaded' => $this->is_threaded(), + '$jsreload' => $conv->reload, + '$type' => (($conv->get_mode() === 'channel') ? 'wall-comment' : 'net-comment'), + '$id' => $this->get_id(), + '$parent' => $this->get_id(), + '$comment_buttons' => $comment_buttons, + '$profile_uid' => $conv->get_profile_owner(), + '$mylink' => $observer['xchan_url'], + '$mytitle' => t('This is you'), + '$myphoto' => $observer['xchan_photo_s'], + '$comment' => t('Comment'), + '$submit' => t('Submit'), + '$edat' => EMPTY_STR, + '$edbold' => t('Bold'), + '$editalic' => t('Italic'), + '$eduline' => t('Underline'), + '$edquote' => t('Quote'), + '$edcode' => t('Code'), + '$edimg' => t('Image'), + '$edatt' => t('Attach/Upload file'), + '$edurl' => t('Insert Link'), + '$edvideo' => t('Video'), + '$preview' => t('Preview'), + '$reset' => t('Reset'), + '$indent' => $indent, + '$can_upload' => (perm_is_allowed($conv->get_profile_owner(), get_observer_hash(), 'write_storage') && $conv->is_uploadable()), + '$feature_encrypt' => ((Apps::system_app_installed($conv->get_profile_owner(), 'Secrets')) ? true : false), + '$feature_markup' => ((Apps::system_app_installed($conv->get_profile_owner(), 'Markup')) ? true : false), + '$encrypt' => t('Encrypt text'), + '$cipher' => $conv->get_cipher(), + '$sourceapp' => App::$sourcename, + '$observer' => get_observer_hash(), + '$anoncomments' => ((($conv->get_mode() === 'channel' || $conv->get_mode() === 'display') && perm_is_allowed($conv->get_profile_owner(), '', 'post_comments')) ? true : false), + '$anonname' => [ 'anonname', t('Your full name (required)') ], + '$anonmail' => [ 'anonmail', t('Your email address (required)') ], + '$anonurl' => [ 'anonurl', t('Your website URL (optional)') ], + '$auto_save_draft' => $feature_auto_save_draft, + '$save' => $permanent_draft, + '$top' => $this->is_toplevel() + )); + + return $comment_box; + } + + private function get_redirect_url() + { + return $this->redirect_url; + } + + /** + * Check if we are a wall to wall item and set the relevant properties + */ + protected function check_wall_to_wall() + { + $conv = $this->get_conversation(); + $this->wall_to_wall = false; + $this->owner_url = ''; + $this->owner_photo = ''; + $this->owner_name = ''; + $this->owner_censored = false; + + if ($conv->get_mode() === 'channel') { + return; + } + + if ($this->is_toplevel() && ($this->get_data_value('author_xchan') != $this->get_data_value('owner_xchan'))) { + $this->owner_url = chanlink_hash($this->data['owner']['xchan_hash']); + $this->owner_photo = $this->data['owner']['xchan_photo_m']; + $this->owner_name = $this->data['owner']['xchan_name']; + $this->wall_to_wall = true; + } + + // present friend-of-friend conversations from hyperdrive as relayed posts from the first friend + // we find among the respondents. + + if ($this->is_toplevel() && (! $this->data['owner']['abook_id'])) { + if ($this->data['children']) { + $friend = $this->find_a_friend($this->data['children']); + if ($friend) { + $this->owner_url = $friend['url']; + $this->owner_photo = $friend['photo']; + $this->owner_name = $friend['name']; + $this->owner_censored = $friend['censored']; + $this->wall_to_wall = true; + } + } + } + } + + private function find_a_friend($items) + { + $ret = null; + if ($items) { + foreach ($items as $child) { + if ($child['author']['abook_id'] && (! intval($child['author']['abook_self']))) { + return [ + 'url' => chanlink_hash($child['author']['xchan_hash']), + 'photo' => $child['author']['xchan_photo_m'], + 'name' => $child['author']['xchan_name'], + 'censored' => (($child['author']['xchan_censored'] || $child['author']['abook_censor']) ? true : false) + ]; + if ($child['children']) { + $ret = $this->find_a_friend($child['children']); + if ($ret) { + break; + } + } + } + } + } + return $ret; + } + + + private function is_wall_to_wall() + { + return $this->wall_to_wall; + } + + private function get_owner_url() + { + return $this->owner_url; + } + + private function get_owner_photo() + { + return $this->owner_photo; + } + + private function get_owner_name() + { + return $this->owner_name; + } + + private function is_visiting() + { + return $this->visiting; + } } - diff --git a/Zotlabs/Lib/ThreadListener.php b/Zotlabs/Lib/ThreadListener.php index 308e02255..1bf4cbe98 100644 --- a/Zotlabs/Lib/ThreadListener.php +++ b/Zotlabs/Lib/ThreadListener.php @@ -2,52 +2,62 @@ namespace Zotlabs\Lib; -class ThreadListener { +class ThreadListener +{ - static public function store($target_id,$portable_id,$ltype = 0) { - $x = self::fetch($target_id,$portable_id,$ltype = 0); - if(! $x) { - $r = q("insert into listeners ( target_id, portable_id, ltype ) values ( '%s', '%s' , %d ) ", - dbesc($target_id), - dbesc($portable_id), - intval($ltype) - ); - } - } + public static function store($target_id, $portable_id, $ltype = 0) + { + $x = self::fetch($target_id, $portable_id, $ltype = 0); + if (! $x) { + $r = q( + "insert into listeners ( target_id, portable_id, ltype ) values ( '%s', '%s' , %d ) ", + dbesc($target_id), + dbesc($portable_id), + intval($ltype) + ); + } + } - static public function fetch($target_id,$portable_id,$ltype = 0) { - $x = q("select * from listeners where target_id = '%s' and portable_id = '%s' and ltype = %d limit 1", - dbesc($target_id), - dbesc($portable_id), - intval($ltype) - ); - if($x) { - return $x[0]; - } - return false; - } + public static function fetch($target_id, $portable_id, $ltype = 0) + { + $x = q( + "select * from listeners where target_id = '%s' and portable_id = '%s' and ltype = %d limit 1", + dbesc($target_id), + dbesc($portable_id), + intval($ltype) + ); + if ($x) { + return $x[0]; + } + return false; + } - static public function fetch_by_target($target_id,$ltype = 0) { - $x = q("select * from listeners where target_id = '%s' and ltype = %d", - dbesc($target_id), - intval($ltype) - ); + public static function fetch_by_target($target_id, $ltype = 0) + { + $x = q( + "select * from listeners where target_id = '%s' and ltype = %d", + dbesc($target_id), + intval($ltype) + ); - return $x; - } + return $x; + } - static public function delete_by_target($target_id, $ltype = 0) { - return q("delete from listeners where target_id = '%s' and ltype = %d", - dbesc($target_id), - intval($ltype) - ); - } - - static public function delete_by_pid($portable_id, $ltype = 0) { - return q("delete from listeners where portable_id = '%s' and ltype = %d", - dbesc($portable_id), - intval($ltype) - ); - } + public static function delete_by_target($target_id, $ltype = 0) + { + return q( + "delete from listeners where target_id = '%s' and ltype = %d", + dbesc($target_id), + intval($ltype) + ); + } + public static function delete_by_pid($portable_id, $ltype = 0) + { + return q( + "delete from listeners where portable_id = '%s' and ltype = %d", + dbesc($portable_id), + intval($ltype) + ); + } } diff --git a/Zotlabs/Lib/ThreadStream.php b/Zotlabs/Lib/ThreadStream.php index 79eaa503b..bbbb882e6 100644 --- a/Zotlabs/Lib/ThreadStream.php +++ b/Zotlabs/Lib/ThreadStream.php @@ -1,7 +1,11 @@ -set_mode($mode); - $this->preview = $preview; - $this->uploadable = $uploadable; - $this->prepared_item = $prepared_item; - $c = ((local_channel()) ? get_pconfig(local_channel(),'system','default_cipher') : ''); - if($c) - $this->cipher = $c; - } + public function __construct($mode, $preview, $uploadable, $prepared_item = '') + { + $this->set_mode($mode); + $this->preview = $preview; + $this->uploadable = $uploadable; + $this->prepared_item = $prepared_item; + $c = ((local_channel()) ? get_pconfig(local_channel(), 'system', 'default_cipher') : ''); + if ($c) { + $this->cipher = $c; + } + } - /** - * Set the mode we'll be displayed on - */ - private function set_mode($mode) { - if($this->get_mode() == $mode) - return; + /** + * Set the mode we'll be displayed on + */ + private function set_mode($mode) + { + if ($this->get_mode() == $mode) { + return; + } - $this->observer = \App::get_observer(); - $ob_hash = (($this->observer) ? $this->observer['xchan_hash'] : ''); + $this->observer = App::get_observer(); + $ob_hash = (($this->observer) ? $this->observer['xchan_hash'] : ''); - switch($mode) { - case 'stream': - $this->profile_owner = local_channel(); - $this->writable = true; - break; - case 'pubstream': - $this->profile_owner = local_channel(); - $this->writable = ((local_channel()) ? true : false); - break; - case 'hq': - $this->profile_owner = local_channel(); - $this->writable = true; - break; - case 'channel': - $this->profile_owner = \App::$profile['profile_uid']; - $this->writable = perm_is_allowed($this->profile_owner,$ob_hash,'post_comments'); - break; - case 'cards': - $this->profile_owner = \App::$profile['profile_uid']; - $this->writable = perm_is_allowed($this->profile_owner,$ob_hash,'post_comments'); - $this->reload = $_SESSION['return_url']; - break; - case 'articles': - $this->profile_owner = \App::$profile['profile_uid']; - $this->writable = perm_is_allowed($this->profile_owner,$ob_hash,'post_comments'); - $this->reload = $_SESSION['return_url']; - break; - case 'display': - // in this mode we set profile_owner after initialisation (from conversation()) and then - // pull some trickery which allows us to re-invoke this function afterward - // it's an ugly hack so @FIXME - $this->writable = perm_is_allowed($this->profile_owner,$ob_hash,'post_comments'); - $this->uploadable = perm_is_allowed($this->profile_owner,$ob_hash,'write_storage'); - break; - case 'page': - $this->profile_owner = \App::$profile['uid']; - $this->writable = perm_is_allowed($this->profile_owner,$ob_hash,'post_comments'); - break; - default: - logger('[ERROR] Conversation::set_mode : Unhandled mode ('. $mode .').', LOGGER_DEBUG); - return false; - break; - } - $this->mode = $mode; - } + switch ($mode) { + case 'stream': + $this->profile_owner = local_channel(); + $this->writable = true; + break; + case 'pubstream': + $this->profile_owner = local_channel(); + $this->writable = ((local_channel()) ? true : false); + break; + case 'hq': + $this->profile_owner = local_channel(); + $this->writable = true; + break; + case 'channel': + $this->profile_owner = App::$profile['profile_uid']; + $this->writable = perm_is_allowed($this->profile_owner, $ob_hash, 'post_comments'); + break; + case 'cards': + $this->profile_owner = App::$profile['profile_uid']; + $this->writable = perm_is_allowed($this->profile_owner, $ob_hash, 'post_comments'); + $this->reload = $_SESSION['return_url']; + break; + case 'articles': + $this->profile_owner = App::$profile['profile_uid']; + $this->writable = perm_is_allowed($this->profile_owner, $ob_hash, 'post_comments'); + $this->reload = $_SESSION['return_url']; + break; + case 'display': + // in this mode we set profile_owner after initialisation (from conversation()) and then + // pull some trickery which allows us to re-invoke this function afterward + // it's an ugly hack so @FIXME + $this->writable = perm_is_allowed($this->profile_owner, $ob_hash, 'post_comments'); + $this->uploadable = perm_is_allowed($this->profile_owner, $ob_hash, 'write_storage'); + break; + case 'page': + $this->profile_owner = App::$profile['uid']; + $this->writable = perm_is_allowed($this->profile_owner, $ob_hash, 'post_comments'); + break; + default: + logger('[ERROR] Conversation::set_mode : Unhandled mode (' . $mode . ').', LOGGER_DEBUG); + return false; + break; + } + $this->mode = $mode; + } - /** - * Get mode - */ - public function get_mode() { - return $this->mode; - } + /** + * Get mode + */ + public function get_mode() + { + return $this->mode; + } - /** - * Check if page is writable - */ - public function is_writable() { - return $this->writable; - } + /** + * Check if page is writable + */ + public function is_writable() + { + return $this->writable; + } - public function is_commentable() { - return $this->commentable; - } + public function is_commentable() + { + return $this->commentable; + } - public function is_uploadable() { - return $this->uploadable; - } + public function is_uploadable() + { + return $this->uploadable; + } - /** - * Check if page is a preview - */ - public function is_preview() { - return $this->preview; - } + /** + * Check if page is a preview + */ + public function is_preview() + { + return $this->preview; + } - /** - * Get profile owner - */ - public function get_profile_owner() { - return $this->profile_owner; - } + /** + * Get profile owner + */ + public function get_profile_owner() + { + return $this->profile_owner; + } - public function set_profile_owner($uid) { - $this->profile_owner = $uid; - $mode = $this->get_mode(); - $this->mode = null; - $this->set_mode($mode); - } + public function set_profile_owner($uid) + { + $this->profile_owner = $uid; + $mode = $this->get_mode(); + $this->mode = null; + $this->set_mode($mode); + } - public function get_observer() { - return $this->observer; - } + public function get_observer() + { + return $this->observer; + } - public function get_cipher() { - return $this->cipher; - } + public function get_cipher() + { + return $this->cipher; + } - /** - * Add a thread to the conversation - * - * Returns: - * _ The inserted item on success - * _ false on failure - */ - public function add_thread($item) { - $item_id = $item->get_id(); - if(!$item_id) { - logger('Item has no ID!!', LOGGER_DEBUG, LOG_ERR); - return false; - } - if($this->get_thread($item->get_id())) { - logger('Thread already exists ('. $item->get_id() .').', LOGGER_DEBUG, LOG_WARNING); - return false; - } + /** + * Add a thread to the conversation + * + * Returns: + * _ The inserted item on success + * _ false on failure + */ + public function add_thread($item) + { + $item_id = $item->get_id(); + if (!$item_id) { + logger('Item has no ID!!', LOGGER_DEBUG, LOG_ERR); + return false; + } + if ($this->get_thread($item->get_id())) { + logger('Thread already exists (' . $item->get_id() . ').', LOGGER_DEBUG, LOG_WARNING); + return false; + } - /* - * Only add things that will be displayed - */ - - - if(($item->get_data_value('id') != $item->get_data_value('parent')) && (activity_match($item->get_data_value('verb'),ACTIVITY_LIKE) || activity_match($item->get_data_value('verb'),ACTIVITY_DISLIKE))) { - return false; - } - - $item->set_commentable(false); - $ob_hash = (($this->observer) ? $this->observer['xchan_hash'] : ''); - - if(! comments_are_now_closed($item->get_data())) { - if(($item->get_data_value('author_xchan') === $ob_hash) || ($item->get_data_value('owner_xchan') === $ob_hash)) - $item->set_commentable(true); - - if(intval($item->get_data_value('item_nocomment'))) { - $item->set_commentable(false); - } - elseif(! $item->is_commentable()) { - if((array_key_exists('owner',$item->data)) && intval($item->data['owner']['abook_self'])) - $item->set_commentable(perm_is_allowed($this->profile_owner,$ob_hash,'post_comments')); - else - $item->set_commentable(can_comment_on_post($ob_hash,$item->data)); - } - } - if($this->mode === 'pubstream' && (! local_channel())) { - $item->set_commentable(false); - } + /* + * Only add things that will be displayed + */ - $item->set_conversation($this); - $this->threads[] = $item; - return end($this->threads); - } + if (($item->get_data_value('id') != $item->get_data_value('parent')) && (activity_match($item->get_data_value('verb'), ACTIVITY_LIKE) || activity_match($item->get_data_value('verb'), ACTIVITY_DISLIKE))) { + return false; + } - /** - * Get data in a form usable by a conversation template - * - * We should find a way to avoid using those arguments (at least most of them) - * - * Returns: - * _ The data requested on success - * _ false on failure - */ - public function get_template_data($conv_responses) { - $result = []; + $item->set_commentable(false); + $ob_hash = (($this->observer) ? $this->observer['xchan_hash'] : ''); - foreach($this->threads as $item) { + if (! comments_are_now_closed($item->get_data())) { + if (($item->get_data_value('author_xchan') === $ob_hash) || ($item->get_data_value('owner_xchan') === $ob_hash)) { + $item->set_commentable(true); + } - if(($item->get_data_value('id') == $item->get_data_value('parent')) && $this->prepared_item) { - $item_data = $this->prepared_item; - } - else { - $item_data = $item->get_template_data($conv_responses); - } - if(!$item_data) { - logger('Failed to get item template data ('. $item->get_id() .').', LOGGER_DEBUG, LOG_ERR); - return false; - } - $result[] = $item_data; - } + if (intval($item->get_data_value('item_nocomment'))) { + $item->set_commentable(false); + } elseif (! $item->is_commentable()) { + if ((array_key_exists('owner', $item->data)) && intval($item->data['owner']['abook_self'])) { + $item->set_commentable(perm_is_allowed($this->profile_owner, $ob_hash, 'post_comments')); + } else { + $item->set_commentable(can_comment_on_post($ob_hash, $item->data)); + } + } + } + if ($this->mode === 'pubstream' && (! local_channel())) { + $item->set_commentable(false); + } - return $result; - } - /** - * Get a thread based on its item id - * - * Returns: - * _ The found item on success - * _ false on failure - */ - private function get_thread($id) { - foreach($this->threads as $item) { - if($item->get_id() == $id) - return $item; - } + $item->set_conversation($this); + $this->threads[] = $item; + return end($this->threads); + } - return false; - } + /** + * Get data in a form usable by a conversation template + * + * We should find a way to avoid using those arguments (at least most of them) + * + * Returns: + * _ The data requested on success + * _ false on failure + */ + public function get_template_data($conv_responses) + { + $result = []; + + foreach ($this->threads as $item) { + if (($item->get_data_value('id') == $item->get_data_value('parent')) && $this->prepared_item) { + $item_data = $this->prepared_item; + } else { + $item_data = $item->get_template_data($conv_responses); + } + if (!$item_data) { + logger('Failed to get item template data (' . $item->get_id() . ').', LOGGER_DEBUG, LOG_ERR); + return false; + } + $result[] = $item_data; + } + + return $result; + } + + /** + * Get a thread based on its item id + * + * Returns: + * _ The found item on success + * _ false on failure + */ + private function get_thread($id) + { + foreach ($this->threads as $item) { + if ($item->get_id() == $id) { + return $item; + } + } + + return false; + } } diff --git a/Zotlabs/Lib/Verify.php b/Zotlabs/Lib/Verify.php index d1f39d513..04b4114eb 100644 --- a/Zotlabs/Lib/Verify.php +++ b/Zotlabs/Lib/Verify.php @@ -2,62 +2,71 @@ namespace Zotlabs\Lib; +class Verify +{ -class Verify { + public static function create($type, $channel_id, $token, $meta) + { + return q( + "insert into verify ( vtype, channel, token, meta, created ) values ( '%s', %d, '%s', '%s', '%s' )", + dbesc($type), + intval($channel_id), + dbesc($token), + dbesc($meta), + dbesc(datetime_convert()) + ); + } - static function create($type,$channel_id,$token,$meta) { - return q("insert into verify ( vtype, channel, token, meta, created ) values ( '%s', %d, '%s', '%s', '%s' )", - dbesc($type), - intval($channel_id), - dbesc($token), - dbesc($meta), - dbesc(datetime_convert()) - ); - } + public static function match($type, $channel_id, $token, $meta) + { + $r = q( + "select id from verify where vtype = '%s' and channel = %d and token = '%s' and meta = '%s' limit 1", + dbesc($type), + intval($channel_id), + dbesc($token), + dbesc($meta) + ); + if ($r) { + q( + "delete from verify where id = %d", + intval($r[0]['id']) + ); + return true; + } + return false; + } - static function match($type,$channel_id,$token,$meta) { - $r = q("select id from verify where vtype = '%s' and channel = %d and token = '%s' and meta = '%s' limit 1", - dbesc($type), - intval($channel_id), - dbesc($token), - dbesc($meta) - ); - if($r) { - q("delete from verify where id = %d", - intval($r[0]['id']) - ); - return true; - } - return false; - } + public static function get_meta($type, $channel_id, $token) + { + $r = q( + "select id, meta from verify where vtype = '%s' and channel = %d and token = '%s' limit 1", + dbesc($type), + intval($channel_id), + dbesc($token) + ); + if ($r) { + q( + "delete from verify where id = %d", + intval($r[0]['id']) + ); + return $r[0]['meta']; + } + return false; + } - static function get_meta($type,$channel_id,$token) { - $r = q("select id, meta from verify where vtype = '%s' and channel = %d and token = '%s' limit 1", - dbesc($type), - intval($channel_id), - dbesc($token) - ); - if($r) { - q("delete from verify where id = %d", - intval($r[0]['id']) - ); - return $r[0]['meta']; - } - return false; - } - - /** - * @brief Purge entries of a verify-type older than interval. - * - * @param string $type Verify type - * @param string $interval SQL compatible time interval - */ - static function purge($type, $interval) { - q("delete from verify where vtype = '%s' and created < ( %s - INTERVAL %s )", - dbesc($type), - db_utcnow(), - db_quoteinterval($interval) - ); - } - -} \ No newline at end of file + /** + * @brief Purge entries of a verify-type older than interval. + * + * @param string $type Verify type + * @param string $interval SQL compatible time interval + */ + public static function purge($type, $interval) + { + q( + "delete from verify where vtype = '%s' and created < ( %s - INTERVAL %s )", + dbesc($type), + db_utcnow(), + db_quoteinterval($interval) + ); + } +} diff --git a/Zotlabs/Lib/Webfinger.php b/Zotlabs/Lib/Webfinger.php index 4f3fb34d6..e7aad7971 100644 --- a/Zotlabs/Lib/Webfinger.php +++ b/Zotlabs/Lib/Webfinger.php @@ -6,104 +6,104 @@ namespace Zotlabs\Lib; * @brief Fetch and return a webfinger for a resource * * @param string $resource - The resource - * @return boolean|string false or associative array from result JSON + * @return bool|string false or associative array from result JSON */ +class Webfinger +{ -class Webfinger { + private static $server = EMPTY_STR; + private static $resource = EMPTY_STR; - static private $server = EMPTY_STR; - static private $resource = EMPTY_STR; + public static function exec($resource) + { - static function exec($resource) { + if (!$resource) { + return false; + } - if (! $resource) { - return false; - } + self::parse_resource($resource); - self::parse_resource($resource); + if (!(self::$server && self::$resource)) { + return false; + } - if (! ( self::$server && self::$resource)) { - return false; - } + if (!check_siteallowed(self::$server)) { + logger('denied: ' . self::$server); + return false; + } - if (! check_siteallowed(self::$server)) { - logger('denied: ' . self::$server); - return false; - } + logger('fetching resource: ' . self::$resource . ' from ' . self::$server, LOGGER_DEBUG, LOG_INFO); - logger('fetching resource: ' . self::$resource . ' from ' . self::$server, LOGGER_DEBUG, LOG_INFO); + $url = 'https://' . self::$server . '/.well-known/webfinger?f=&resource=' . self::$resource; - $url = 'https://' . self::$server . '/.well-known/webfinger?f=&resource=' . self::$resource ; + $counter = 0; + $s = z_fetch_url($url, false, $counter, ['headers' => ['Accept: application/jrd+json, */*']]); - $counter = 0; - $s = z_fetch_url($url, false, $counter, [ 'headers' => [ 'Accept: application/jrd+json, */*' ] ]); + if ($s['success']) { + $j = json_decode($s['body'], true); + return ($j); + } - if ($s['success']) { - $j = json_decode($s['body'], true); - return($j); - } + return false; + } - return false; - } + public static function parse_resource($resource) + { - static function parse_resource($resource) { + self::$resource = urlencode($resource); - self::$resource = urlencode($resource); + if (strpos($resource, 'http') === 0) { + $m = parse_url($resource); + if ($m) { + if ($m['scheme'] !== 'https') { + return false; + } + self::$server = $m['host'] . (($m['port']) ? ':' . $m['port'] : ''); + } else { + return false; + } + } elseif (strpos($resource, 'tag:') === 0) { + $arr = explode(':', $resource); // split the tag + $h = explode(',', $arr[1]); // split the host,date + self::$server = $h[0]; + } else { + $x = explode('@', $resource); + if (!strlen($x[0])) { + // e.g. @dan@pixelfed.org + array_shift($x); + } + $username = $x[0]; + if (count($x) > 1) { + self::$server = $x[1]; + } else { + return false; + } + if (strpos($resource, 'acct:') !== 0) { + self::$resource = urlencode('acct:' . $resource); + } + } + } - if (strpos($resource,'http') === 0) { - $m = parse_url($resource); - if ($m) { - if ($m['scheme'] !== 'https') { - return false; - } - self::$server = $m['host'] . (($m['port']) ? ':' . $m['port'] : ''); - } - else { - return false; - } - } - elseif (strpos($resource,'tag:') === 0) { - $arr = explode(':',$resource); // split the tag - $h = explode(',',$arr[1]); // split the host,date - self::$server = $h[0]; - } - else { - $x = explode('@',$resource); - if (! strlen($x[0])) { - // e.g. @dan@pixelfed.org - array_shift($x); - } - $username = $x[0]; - if (count($x) > 1) { - self::$server = $x[1]; - } - else { - return false; - } - if (strpos($resource,'acct:') !== 0) { - self::$resource = urlencode('acct:' . $resource); - } - } - } + /** + * @brief fetch a webfinger resource and return a zot6 discovery url if present + * + */ - /** - * @brief fetch a webfinger resource and return a zot6 discovery url if present - * - */ + public static function zot_url($resource) + { - static function zot_url($resource) { + $arr = self::exec($resource); - $arr = self::exec($resource); - - if (is_array($arr) && array_key_exists('links',$arr)) { - foreach ($arr['links'] as $link) { + if (is_array($arr) && array_key_exists('links', $arr)) { + foreach ($arr['links'] as $link) { if (array_key_exists('rel',$link) && in_array($link['rel'], [ PROTOCOL_NOMAD, PROTOCOL_ZOT6 ])) { - if (array_key_exists('href',$link) && $link['href'] !== EMPTY_STR) { - return $link['href']; - } - } - } - } - return false; - } -} \ No newline at end of file + if (array_key_exists('href', $link) && $link['href'] !== EMPTY_STR) { + return $link['href']; + } + } + } + } + return false; + } +} + diff --git a/Zotlabs/Lib/XConfig.php b/Zotlabs/Lib/XConfig.php index 79e442d5a..a9d86fc88 100644 --- a/Zotlabs/Lib/XConfig.php +++ b/Zotlabs/Lib/XConfig.php @@ -24,153 +24,169 @@ use App; * $var = get_xconfig($observer, 'category', 'key'); * }@endcode */ -class XConfig { +class XConfig +{ - /** - * @brief Loads a full xchan's configuration into a cached storage. - * - * All configuration values of the given observer hash are stored in global - * cache which is available under the global variable App::$config[$xchan]. - * - * @param string $xchan - * The observer's hash - * @return void|false Returns false if xchan is not set - */ - static public function Load($xchan) { + /** + * @brief Loads a full xchan's configuration into a cached storage. + * + * All configuration values of the given observer hash are stored in global + * cache which is available under the global variable App::$config[$xchan]. + * + * @param string $xchan + * The observer's hash + * @return void|false Returns false if xchan is not set + */ + public static function Load($xchan) + { - if(! $xchan) - return false; + if (! $xchan) { + return false; + } - if(! array_key_exists($xchan, App::$config)) - App::$config[$xchan] = []; + if (! array_key_exists($xchan, App::$config)) { + App::$config[$xchan] = []; + } - $r = q("SELECT * FROM xconfig WHERE xchan = '%s'", - dbesc($xchan) - ); + $r = q( + "SELECT * FROM xconfig WHERE xchan = '%s'", + dbesc($xchan) + ); - if($r) { - foreach($r as $rr) { - $k = $rr['k']; - $c = $rr['cat']; - if(! array_key_exists($c, App::$config[$xchan])) { - App::$config[$xchan][$c] = []; - App::$config[$xchan][$c]['config_loaded'] = true; - } - App::$config[$xchan][$c][$k] = $rr['v']; - } - } - } + if ($r) { + foreach ($r as $rr) { + $k = $rr['k']; + $c = $rr['cat']; + if (! array_key_exists($c, App::$config[$xchan])) { + App::$config[$xchan][$c] = []; + App::$config[$xchan][$c]['config_loaded'] = true; + } + App::$config[$xchan][$c][$k] = $rr['v']; + } + } + } - /** - * @brief Get a particular observer's config variable given the category - * name ($family) and a key. - * - * Get a particular observer's config value from the given category ($family) - * and the $key from a cached storage in App::$config[$xchan]. - * - * Returns false if not set. - * - * @param string $xchan - * The observer's hash - * @param string $family - * The category of the configuration value - * @param string $key - * The configuration key to query - * @param boolean $default (optional) default false - * @return mixed Stored $value or false if it does not exist - */ - static public function Get($xchan, $family, $key, $default = false) { + /** + * @brief Get a particular observer's config variable given the category + * name ($family) and a key. + * + * Get a particular observer's config value from the given category ($family) + * and the $key from a cached storage in App::$config[$xchan]. + * + * Returns false if not set. + * + * @param string $xchan + * The observer's hash + * @param string $family + * The category of the configuration value + * @param string $key + * The configuration key to query + * @param bool $default (optional) default false + * @return mixed Stored $value or false if it does not exist + */ + public static function Get($xchan, $family, $key, $default = false) + { - if(! $xchan) - return $default; + if (! $xchan) { + return $default; + } - if(! array_key_exists($xchan, App::$config)) - load_xconfig($xchan); + 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 $default; + if ((! array_key_exists($family, App::$config[$xchan])) || (! array_key_exists($key, App::$config[$xchan][$family]))) { + return $default; + } - return unserialise(App::$config[$xchan][$family][$key]); - } + return unserialise(App::$config[$xchan][$family][$key]); + } - /** - * @brief Sets a configuration value for an observer. - * - * Stores a config value ($value) in the category ($family) under the key ($key) - * for the observer's $xchan hash. - * - * @param string $xchan - * The observer's hash - * @param string $family - * The category of the configuration value - * @param string $key - * The configuration key to set - * @param string $value - * The value to store - * @return mixed Stored $value or false - */ - static public function Set($xchan, $family, $key, $value) { + /** + * @brief Sets a configuration value for an observer. + * + * Stores a config value ($value) in the category ($family) under the key ($key) + * for the observer's $xchan hash. + * + * @param string $xchan + * The observer's hash + * @param string $family + * The category of the configuration value + * @param string $key + * The configuration key to set + * @param string $value + * The value to store + * @return mixed Stored $value or false + */ + public static function Set($xchan, $family, $key, $value) + { - // manage array value - $dbvalue = ((is_array($value)) ? serialise($value) : $value); - $dbvalue = ((is_bool($dbvalue)) ? intval($dbvalue) : $dbvalue); + // manage array value + $dbvalue = ((is_array($value)) ? serialise($value) : $value); + $dbvalue = ((is_bool($dbvalue)) ? intval($dbvalue) : $dbvalue); - if(self::Get($xchan, $family, $key) === false) { - if(! array_key_exists($xchan, App::$config)) - App::$config[$xchan] = []; - if(! array_key_exists($family, App::$config[$xchan])) - App::$config[$xchan][$family] = []; + if (self::Get($xchan, $family, $key) === false) { + if (! array_key_exists($xchan, App::$config)) { + App::$config[$xchan] = []; + } + if (! array_key_exists($family, App::$config[$xchan])) { + App::$config[$xchan][$family] = []; + } - $ret = q("INSERT INTO xconfig ( xchan, cat, k, v ) VALUES ( '%s', '%s', '%s', '%s' )", - dbesc($xchan), - dbesc($family), - dbesc($key), - dbesc($dbvalue) - ); - } - else { - $ret = q("UPDATE xconfig SET v = '%s' WHERE xchan = '%s' and cat = '%s' AND k = '%s'", - dbesc($dbvalue), - dbesc($xchan), - dbesc($family), - dbesc($key) - ); - } + $ret = q( + "INSERT INTO xconfig ( xchan, cat, k, v ) VALUES ( '%s', '%s', '%s', '%s' )", + dbesc($xchan), + dbesc($family), + dbesc($key), + dbesc($dbvalue) + ); + } else { + $ret = q( + "UPDATE xconfig SET v = '%s' WHERE xchan = '%s' and cat = '%s' AND k = '%s'", + dbesc($dbvalue), + dbesc($xchan), + dbesc($family), + dbesc($key) + ); + } - App::$config[$xchan][$family][$key] = $value; + App::$config[$xchan][$family][$key] = $value; - if($ret) - return $value; + if ($ret) { + return $value; + } - return $ret; - } + return $ret; + } - /** - * @brief Deletes the given key from the observer's config. - * - * Removes the configured value from the stored cache in App::$config[$xchan] - * and removes it from the database. - * - * @param string $xchan - * The observer's hash - * @param string $family - * The category of the configuration value - * @param string $key - * The configuration key to delete - * @return mixed - */ - static public function Delete($xchan, $family, $key) { + /** + * @brief Deletes the given key from the observer's config. + * + * Removes the configured value from the stored cache in App::$config[$xchan] + * and removes it from the database. + * + * @param string $xchan + * The observer's hash + * @param string $family + * The category of the configuration value + * @param string $key + * The configuration key to delete + * @return mixed + */ + public static function Delete($xchan, $family, $key) + { - if(isset(App::$config[$xchan]) && isset(App::$config[$xchan][$family]) && isset(App::$config[$xchan][$family][$key])) - unset(App::$config[$xchan][$family][$key]); + if (isset(App::$config[$xchan]) && isset(App::$config[$xchan][$family]) && isset(App::$config[$xchan][$family][$key])) { + unset(App::$config[$xchan][$family][$key]); + } - $ret = q("DELETE FROM xconfig WHERE xchan = '%s' AND cat = '%s' AND k = '%s'", - dbesc($xchan), - dbesc($family), - dbesc($key) - ); - - return $ret; - } + $ret = q( + "DELETE FROM xconfig WHERE xchan = '%s' AND cat = '%s' AND k = '%s'", + dbesc($xchan), + dbesc($family), + dbesc($key) + ); + return $ret; + } } diff --git a/Zotlabs/Lib/ZotURL.php b/Zotlabs/Lib/ZotURL.php index da0dc61e2..23441d715 100644 --- a/Zotlabs/Lib/ZotURL.php +++ b/Zotlabs/Lib/ZotURL.php @@ -4,123 +4,124 @@ namespace Zotlabs\Lib; use Zotlabs\Web\HTTPSig; +class ZotURL +{ -class ZotURL { + public static function fetch($url, $channel, $hub = null) + { - static public function fetch($url,$channel,$hub = null) { + $ret = [ 'success' => false ]; - $ret = [ 'success' => false ]; - - if(strpos($url,'x-zot:') !== 0) { - return $ret; - } + if (strpos($url, 'x-zot:') !== 0) { + return $ret; + } - if(! $url) { - return $ret; - } + if (! $url) { + return $ret; + } - $portable_url = substr($url,6); - $u = explode('/',$portable_url); - $portable_id = $u[0]; + $portable_url = substr($url, 6); + $u = explode('/', $portable_url); + $portable_id = $u[0]; - $hosts = self::lookup($portable_id,$hub); + $hosts = self::lookup($portable_id, $hub); - if(! $hosts) { - return $ret; - } + if (! $hosts) { + return $ret; + } - foreach($hosts as $h) { - $newurl = $h . '/id/' . $portable_url; + foreach ($hosts as $h) { + $newurl = $h . '/id/' . $portable_url; - $m = parse_url($newurl); + $m = parse_url($newurl); - $data = json_encode([ 'zot_token' => random_string() ]); + $data = json_encode([ 'zot_token' => random_string() ]); - if($channel && $m) { - - $headers = [ + if ($channel && $m) { + $headers = [ 'Accept' => 'application/x-nomad+json, application/x-zot+json', 'Content-Type' => 'application/x-nomad+json', - 'X-Zot-Token' => random_string(), - 'Digest' => HTTPSig::generate_digest_header($data), - 'Host' => $m['host'], - '(request-target)' => 'post ' . get_request_string($newurl) - ]; - $h = HTTPSig::create_sig($headers,$channel['channel_prvkey'],channel_url($channel),false); - } - else { - $h = [ 'Accept: application/x-nomad+json' ]; - } - - $result = []; + 'X-Zot-Token' => random_string(), + 'Digest' => HTTPSig::generate_digest_header($data), + 'Host' => $m['host'], + '(request-target)' => 'post ' . get_request_string($newurl) + ]; + $h = HTTPSig::create_sig($headers, $channel['channel_prvkey'], channel_url($channel), false); + } else { + $h = [ 'Accept: application/x-nomad+json, application/x-zot+json' ]; + } - $redirects = 0; - $x = z_post_url($newurl,$data,$redirects, [ 'headers' => $h ] ); - if($x['success']) { - return $x; - } - } + $result = []; - return $ret; + $redirects = 0; + $x = z_post_url($newurl, $data, $redirects, [ 'headers' => $h ]); + if ($x['success']) { + return $x; + } + } - } + return $ret; + } - static public function is_zoturl($s) { + public static function is_zoturl($s) + { - if(strpos($url,'x-zot:') === 0) { - return true; - } - return false; - } + if (strpos($url, 'x-zot:') === 0) { + return true; + } + return false; + } - static public function lookup($portable_id,$hub) { + public static function lookup($portable_id, $hub) + { - $r = q("select * from hubloc left join site on hubloc_url = site_url where hubloc_hash = '%s' and site_dead = 0 order by hubloc_primary desc", - dbesc($portable_id) - ); + $r = q( + "select * from hubloc left join site on hubloc_url = site_url where hubloc_hash = '%s' and site_dead = 0 order by hubloc_primary desc", + dbesc($portable_id) + ); - if(! $r) { + if (! $r) { + // extend to network lookup - // extend to network lookup + $path = '/q/' . $portable_id; - $path = '/q/' . $portable_id; + // first check sending hub since they have recently communicated with this object - // first check sending hub since they have recently communicated with this object + $redirects = 0; - $redirects = 0; + if ($hub) { + $x = z_fetch_url($hub['hubloc_url'] . $path, false, $redirects); + $u = self::parse_response($x); + if ($u) { + return $u; + } + } - if($hub) { - $x = z_fetch_url($hub['hubloc_url'] . $path, false, $redirects); - $u = self::parse_response($x); - if($u) { - return $u; - } - } + // If this fails, fallback on directory servers - // If this fails, fallback on directory servers - - return false; - } - return ids_to_array($r,'hubloc_url'); - } + return false; + } + return ids_to_array($r, 'hubloc_url'); + } - static public function parse_response($arr) { - if(! $arr['success']) { - return false; - } - $a = json_decode($arr['body'],true); - if($a['success'] && array_key_exists('results', $a) && is_array($a['results']) && count($a['results'])) { - foreach($a['results'] as $b) { - $m = discover_by_webbie($b); - if($m) { - return([ $b ]); - } - } - } - return false; - } + public static function parse_response($arr) + { + if (! $arr['success']) { + return false; + } + $a = json_decode($arr['body'], true); + if ($a['success'] && array_key_exists('results', $a) && is_array($a['results']) && count($a['results'])) { + foreach ($a['results'] as $b) { + $m = discover_by_webbie($b); + if ($m) { + return([ $b ]); + } + } + } + return false; + } +} -} \ No newline at end of file diff --git a/Zotlabs/Lib/Zotfinger.php b/Zotlabs/Lib/Zotfinger.php index 90a5aa20c..60a545315 100644 --- a/Zotlabs/Lib/Zotfinger.php +++ b/Zotlabs/Lib/Zotfinger.php @@ -4,60 +4,59 @@ namespace Zotlabs\Lib; use Zotlabs\Web\HTTPSig; -class Zotfinger { +class Zotfinger +{ - static function exec($resource,$channel = null,$verify = true) { + public static function exec($resource, $channel = null, $verify = true) + { - if (! $resource) { - return false; - } + if (!$resource) { + return false; + } - $m = parse_url($resource); - - if ($m['host'] !== punify($m['host'])) { - $url = str_replace($m['host'],punify($m['host']),$url); - $m['host'] = punify($m['host']); - } + $m = parse_url($resource); - $data = json_encode([ 'zot_token' => random_string() ]); + if ($m['host'] !== punify($m['host'])) { + $url = str_replace($m['host'], punify($m['host']), $url); + $m['host'] = punify($m['host']); + } - if ($channel && $m) { + $data = json_encode(['zot_token' => random_string()]); - $headers = [ - 'Accept' => 'application/x-nomad+json, application/x-zot+json', - 'Content-Type' => 'application/x-nomad+json', - 'X-Zot-Token' => random_string(), - 'Digest' => HTTPSig::generate_digest_header($data), - 'Host' => $m['host'], - '(request-target)' => 'post ' . get_request_string($resource) - ]; - $h = HTTPSig::create_sig($headers,$channel['channel_prvkey'],channel_url($channel),false); - } - else { - $h = [ 'Accept: application/x-nomad+json, application/x-zot+json' ]; - } - - $result = []; + if ($channel && $m) { + $headers = [ + 'Accept' => 'application/x-nomad+json, application/x-zot+json', + 'Content-Type' => 'application/x-nomad+json', + 'X-Zot-Token' => random_string(), + 'Digest' => HTTPSig::generate_digest_header($data), + 'Host' => $m['host'], + '(request-target)' => 'post ' . get_request_string($resource) + ]; + $h = HTTPSig::create_sig($headers, $channel['channel_prvkey'], channel_url($channel), false); + } else { + $h = ['Accept: application/x-nomad+json, application/x-zot+json']; + } - $redirects = 0; - $x = z_post_url($resource,$data,$redirects, [ 'headers' => $h ] ); + $result = []; - if ($x['success']) { + $redirects = 0; + $x = z_post_url($resource, $data, $redirects, ['headers' => $h]); - if ($verify) { - $result['signature'] = HTTPSig::verify($x, EMPTY_STR, 'zot6'); - } - - $result['data'] = json_decode($x['body'],true); + if ($x['success']) { + if ($verify) { + $result['signature'] = HTTPSig::verify($x, EMPTY_STR, 'zot6'); + } - if ($result['data'] && is_array($result['data']) && array_key_exists('encrypted',$result['data']) && $result['data']['encrypted']) { - $result['data'] = json_decode(Crypto::unencapsulate($result['data'],get_config('system','prvkey')),true); - } + $result['data'] = json_decode($x['body'], true); - return $result; - } + if ($result['data'] && is_array($result['data']) && array_key_exists('encrypted', $result['data']) && $result['data']['encrypted']) { + $result['data'] = json_decode(Crypto::unencapsulate($result['data'], get_config('system', 'prvkey')), true); + } - return false; - } + return $result; + } + + return false; + } +} -} \ No newline at end of file diff --git a/Zotlabs/Module/Acl.php b/Zotlabs/Module/Acl.php index da6c50e75..e0f764ae9 100644 --- a/Zotlabs/Module/Acl.php +++ b/Zotlabs/Module/Acl.php @@ -23,101 +23,98 @@ require_once('include/acl_selectors.php'); * keys however this functionality has grown in an ad-hoc manner and has gotten * quite messy over time. */ - -class Acl extends Controller { +class Acl extends Controller +{ - function init() { + public function init() + { - // logger('mod_acl: ' . print_r($_REQUEST,true),LOGGER_DATA); + // logger('mod_acl: ' . print_r($_REQUEST,true),LOGGER_DATA); - $start = (x($_REQUEST,'start') ? $_REQUEST['start'] : 0); - $count = (x($_REQUEST,'count') ? $_REQUEST['count'] : 500); - $search = (x($_REQUEST,'search') ? $_REQUEST['search'] : ''); - $type = (x($_REQUEST,'type') ? $_REQUEST['type'] : ''); - $noforums = (x($_REQUEST,'n') ? $_REQUEST['n'] : false); + $start = (x($_REQUEST, 'start') ? $_REQUEST['start'] : 0); + $count = (x($_REQUEST, 'count') ? $_REQUEST['count'] : 500); + $search = (x($_REQUEST, 'search') ? $_REQUEST['search'] : ''); + $type = (x($_REQUEST, 'type') ? $_REQUEST['type'] : ''); + $noforums = (x($_REQUEST, 'n') ? $_REQUEST['n'] : false); - // $type = - // '' => standard ACL request - // 'g' => Groups only ACL request - // 'f' => forums only ACL request - // 'c' => Connections only ACL request or editor (textarea) mention request - // $_REQUEST['search'] contains ACL search text. + // $type = + // '' => standard ACL request + // 'g' => Groups only ACL request + // 'f' => forums only ACL request + // 'c' => Connections only ACL request or editor (textarea) mention request + // $_REQUEST['search'] contains ACL search text. - // $type = - // 'm' => autocomplete private mail recipient (checks post_mail permission) - // 'a' => autocomplete connections (mod_connections, mod_poke, mod_sources, mod_photos) - // 'x' => nav search bar autocomplete (match any xchan) - // 'z' => autocomplete any xchan, but also include abook_alias, requires non-zero local_channel() - // and also contains xid without urlencode, used specifically by activity_filter widget - // $_REQUEST['query'] contains autocomplete search text. - - - // The different autocomplete libraries use different names for the search text - // parameter. Internally we'll use $search to represent the search text no matter - // what request variable it was attached to. - - if (array_key_exists('query',$_REQUEST)) { - $search = $_REQUEST['query']; - } - - if ( (! local_channel()) && (! in_array($type, [ 'x', 'c', 'f' ]))) { - killme(); - } - - $permitted = []; - - if (in_array($type, [ 'm', 'a', 'f' ])) { - - // These queries require permission checking. We'll create a simple array of xchan_hash for those with - // the requisite permissions which we can check against. - - $x = q("select xchan from abconfig where chan = %d and cat = 'system' and k = 'their_perms' and v like '%s'", - intval(local_channel()), - dbesc(($type === 'm') ? '%post_mail%' : '%tag_deliver%') - ); - - $permitted = ids_to_array($x,'xchan'); - } + // $type = + // 'm' => autocomplete private mail recipient (checks post_mail permission) + // 'a' => autocomplete connections (mod_connections, mod_poke, mod_sources, mod_photos) + // 'x' => nav search bar autocomplete (match any xchan) + // 'z' => autocomplete any xchan, but also include abook_alias, requires non-zero local_channel() + // and also contains xid without urlencode, used specifically by activity_filter widget + // $_REQUEST['query'] contains autocomplete search text. - if ($search) { - $sql_extra = " AND pgrp.gname LIKE " . protect_sprintf( "'%" . dbesc($search) . "%'" ) . " "; - // sql_extra2 is typically used when we don't have a local_channel - so we are not search abook_alias - $sql_extra2 = " AND ( xchan_name LIKE " . protect_sprintf( "'%" . dbesc($search) . "%'" ) . " OR xchan_addr LIKE " . protect_sprintf( "'%" . dbesc(punify($search)) . ((strpos($search,'@') === false) ? "%@%'" : "%'")) . ") "; + // The different autocomplete libraries use different names for the search text + // parameter. Internally we'll use $search to represent the search text no matter + // what request variable it was attached to. + + if (array_key_exists('query', $_REQUEST)) { + $search = $_REQUEST['query']; + } + + if ((!local_channel()) && (!in_array($type, ['x', 'c', 'f']))) { + killme(); + } + + $permitted = []; + + if (in_array($type, ['m', 'a', 'f'])) { + // These queries require permission checking. We'll create a simple array of xchan_hash for those with + // the requisite permissions which we can check against. + + $x = q( + "select xchan from abconfig where chan = %d and cat = 'system' and k = 'their_perms' and v like '%s'", + intval(local_channel()), + dbesc(($type === 'm') ? '%post_mail%' : '%tag_deliver%') + ); + + $permitted = ids_to_array($x, 'xchan'); + } - - // This horrible mess is needed because position also returns 0 if nothing is found. - // Would be MUCH easier if it instead returned a very large value - // Otherwise we could just - // order by LEAST(POSITION($search IN xchan_name),POSITION($search IN xchan_addr)). - - $order_extra2 = "CASE WHEN xchan_name LIKE " - . protect_sprintf( "'%" . dbesc($search) . "%'" ) - . " then POSITION('" . protect_sprintf(dbesc($search)) - . "' IN xchan_name) else position('" . protect_sprintf(dbesc(punify($search))) . "' IN xchan_addr) end, "; - - $sql_extra3 = "AND ( xchan_addr like " . protect_sprintf( "'%" . dbesc(punify($search)) . "%'" ) . " OR xchan_name like " . protect_sprintf( "'%" . dbesc($search) . "%'" ) . " OR abook_alias like " . protect_sprintf( "'%" . dbesc($search) . "%'" ) . " ) "; - - $sql_extra4 = "AND ( xchan_name LIKE " . protect_sprintf( "'%" . dbesc($search) . "%'" ) . " OR xchan_addr LIKE " . protect_sprintf( "'%" . dbesc(punify($search)) . ((strpos($search,'@') === false) ? "%@%'" : "%'")) . " OR abook_alias LIKE " . protect_sprintf( "'%" . dbesc($search) . "%'") . ") "; + if ($search) { + $sql_extra = " AND pgrp.gname LIKE " . protect_sprintf("'%" . dbesc($search) . "%'") . " "; + // sql_extra2 is typically used when we don't have a local_channel - so we are not search abook_alias + $sql_extra2 = " AND ( xchan_name LIKE " . protect_sprintf("'%" . dbesc($search) . "%'") . " OR xchan_addr LIKE " . protect_sprintf("'%" . dbesc(punify($search)) . ((strpos($search, '@') === false) ? "%@%'" : "%'")) . ") "; - } - else { - $sql_extra = $sql_extra2 = $sql_extra3 = $sql_extra4 = ""; - } - - - $groups = []; - $contacts = []; - - if ($type == '' || $type == 'g') { + // This horrible mess is needed because position also returns 0 if nothing is found. + // Would be MUCH easier if it instead returned a very large value + // Otherwise we could just + // order by LEAST(POSITION($search IN xchan_name),POSITION($search IN xchan_addr)). - // Normal privacy groups + $order_extra2 = "CASE WHEN xchan_name LIKE " + . protect_sprintf("'%" . dbesc($search) . "%'") + . " then POSITION('" . protect_sprintf(dbesc($search)) + . "' IN xchan_name) else position('" . protect_sprintf(dbesc(punify($search))) . "' IN xchan_addr) end, "; - $r = q("SELECT pgrp.id, pgrp.hash, pgrp.gname + $sql_extra3 = "AND ( xchan_addr like " . protect_sprintf("'%" . dbesc(punify($search)) . "%'") . " OR xchan_name like " . protect_sprintf("'%" . dbesc($search) . "%'") . " OR abook_alias like " . protect_sprintf("'%" . dbesc($search) . "%'") . " ) "; + + $sql_extra4 = "AND ( xchan_name LIKE " . protect_sprintf("'%" . dbesc($search) . "%'") . " OR xchan_addr LIKE " . protect_sprintf("'%" . dbesc(punify($search)) . ((strpos($search, '@') === false) ? "%@%'" : "%'")) . " OR abook_alias LIKE " . protect_sprintf("'%" . dbesc($search) . "%'") . ") "; + } else { + $sql_extra = $sql_extra2 = $sql_extra3 = $sql_extra4 = ""; + } + + + $groups = []; + $contacts = []; + + if ($type == '' || $type == 'g') { + // Normal privacy groups + + $r = q( + "SELECT pgrp.id, pgrp.hash, pgrp.gname FROM pgrp, pgrp_member WHERE pgrp.deleted = 0 AND pgrp.uid = %d AND pgrp_member.gid = pgrp.id @@ -125,238 +122,226 @@ class Acl extends Controller { GROUP BY pgrp.id ORDER BY pgrp.gname LIMIT %d OFFSET %d", - intval(local_channel()), - intval($count), - intval($start) - ); + intval(local_channel()), + intval($count), + intval($start) + ); - if ($r) { - foreach ($r as $g) { - // logger('acl: group: ' . $g['gname'] . ' members: ' . AccessList::members_xchan(local_channel(),$g['id'])); - $groups[] = [ - "type" => "g", - "photo" => "images/twopeople.png", - "name" => $g['gname'], - "id" => $g['id'], - "xid" => $g['hash'], - "uids" => AccessList::members_xchan(local_channel(),$g['id']), - "link" => '' - ]; - } - } - } - - if ($type == '' || $type == 'c' || $type === 'f') { + if ($r) { + foreach ($r as $g) { + // logger('acl: group: ' . $g['gname'] . ' members: ' . AccessList::members_xchan(local_channel(),$g['id'])); + $groups[] = [ + "type" => "g", + "photo" => "images/twopeople.png", + "name" => $g['gname'], + "id" => $g['id'], + "xid" => $g['hash'], + "uids" => AccessList::members_xchan(local_channel(), $g['id']), + "link" => '' + ]; + } + } + } - // Getting info from the abook is better for local users because it contains info about permissions - if (local_channel()) { + if ($type == '' || $type == 'c' || $type === 'f') { + // Getting info from the abook is better for local users because it contains info about permissions + if (local_channel()) { + // add connections - // add connections - - $r = q("SELECT abook_id as id, xchan_hash as hash, xchan_name as name, xchan_photo_s as micro, xchan_url as url, xchan_addr as nick, xchan_type, abook_flags, abook_self + $r = q( + "SELECT abook_id as id, xchan_hash as hash, xchan_name as name, xchan_photo_s as micro, xchan_url as url, xchan_addr as nick, xchan_type, abook_flags, abook_self FROM abook left join xchan on abook_xchan = xchan_hash - WHERE abook_channel = %d AND abook_blocked = 0 and abook_pending = 0 and xchan_deleted = 0 $sql_extra4 order by xchan_name asc limit $count" , - intval(local_channel()) - ); - - } - else { // Visitors - $r = q("SELECT xchan_hash as id, xchan_hash as hash, xchan_name as name, xchan_photo_s as micro, xchan_url as url, xchan_addr as nick, 0 as abook_flags, 0 as abook_self + WHERE abook_channel = %d AND abook_blocked = 0 and abook_pending = 0 and xchan_deleted = 0 $sql_extra4 order by xchan_name asc limit $count", + intval(local_channel()) + ); + } else { // Visitors + $r = q( + "SELECT xchan_hash as id, xchan_hash as hash, xchan_name as name, xchan_photo_s as micro, xchan_url as url, xchan_addr as nick, 0 as abook_flags, 0 as abook_self FROM xchan left join xlink on xlink_link = xchan_hash - WHERE xlink_xchan = '%s' AND xchan_deleted = 0 $sql_extra2 order by $order_extra2 xchan_name asc limit $count" , - dbesc(get_observer_hash()) - ); - - } - if ((count($r) < 100) && $type == 'c') { - $r2 = q("SELECT xchan_hash as id, xchan_hash as hash, xchan_name as name, xchan_photo_s as micro, xchan_url as url, xchan_addr as nick, 0 as abook_flags, 0 as abook_self - FROM xchan WHERE xchan_deleted = 0 and xchan_network != 'unknown' $sql_extra2 order by $order_extra2 xchan_name asc limit $count" - ); - if ($r2) { - $r = array_merge($r,$r2); - $r = unique_multidim_array($r,'hash'); - } - } - - } - elseif ($type == 'm') { - - $r = []; - $z = q("SELECT xchan_hash as hash, xchan_name as name, xchan_addr as nick, xchan_photo_s as micro, xchan_url as url + WHERE xlink_xchan = '%s' AND xchan_deleted = 0 $sql_extra2 order by $order_extra2 xchan_name asc limit $count", + dbesc(get_observer_hash()) + ); + } + if ((count($r) < 100) && $type == 'c') { + $r2 = q("SELECT xchan_hash as id, xchan_hash as hash, xchan_name as name, xchan_photo_s as micro, xchan_url as url, xchan_addr as nick, 0 as abook_flags, 0 as abook_self + FROM xchan WHERE xchan_deleted = 0 and xchan_network != 'unknown' $sql_extra2 order by $order_extra2 xchan_name asc limit $count"); + if ($r2) { + $r = array_merge($r, $r2); + $r = unique_multidim_array($r, 'hash'); + } + } + } elseif ($type == 'm') { + $r = []; + $z = q( + "SELECT xchan_hash as hash, xchan_name as name, xchan_addr as nick, xchan_photo_s as micro, xchan_url as url FROM abook left join xchan on abook_xchan = xchan_hash WHERE abook_channel = %d and xchan_deleted = 0 $sql_extra3 ORDER BY xchan_name ASC ", - intval(local_channel()) - ); - if ($z) { - foreach ($z as $zz) { - if (in_array($zz['hash'],$permitted)) { - $r[] = $zz; - } - } - } - - } - elseif ($type == 'a') { - - $r = q("SELECT abook_id as id, xchan_name as name, xchan_hash as hash, xchan_addr as nick, xchan_photo_s as micro, xchan_network as network, xchan_url as url, xchan_addr as attag FROM abook left join xchan on abook_xchan = xchan_hash + intval(local_channel()) + ); + if ($z) { + foreach ($z as $zz) { + if (in_array($zz['hash'], $permitted)) { + $r[] = $zz; + } + } + } + } elseif ($type == 'a') { + $r = q( + "SELECT abook_id as id, xchan_name as name, xchan_hash as hash, xchan_addr as nick, xchan_photo_s as micro, xchan_network as network, xchan_url as url, xchan_addr as attag FROM abook left join xchan on abook_xchan = xchan_hash WHERE abook_channel = %d and xchan_deleted = 0 $sql_extra3 ORDER BY xchan_name ASC ", - intval(local_channel()) - ); - - } - elseif ($type == 'z') { - $r = q("SELECT xchan_name as name, xchan_hash as hash, xchan_addr as nick, xchan_photo_s as micro, xchan_network as network, xchan_url as url, xchan_addr as attag FROM xchan left join abook on xchan_hash = abook_xchan + intval(local_channel()) + ); + } elseif ($type == 'z') { + $r = q( + "SELECT xchan_name as name, xchan_hash as hash, xchan_addr as nick, xchan_photo_s as micro, xchan_network as network, xchan_url as url, xchan_addr as attag FROM xchan left join abook on xchan_hash = abook_xchan WHERE ( abook_channel = %d OR abook_channel IS NULL ) and xchan_deleted = 0 $sql_extra3 ORDER BY xchan_name ASC ", - intval(local_channel()) - ); - } + intval(local_channel()) + ); + } elseif ($type == 'x') { + $contacts = []; + $r = $this->navbar_complete(); + if ($r) { + foreach ($r as $g) { + $contacts[] = [ + "photo" => $g['photo'], + "name" => $g['name'], + "nick" => $g['address'], + 'link' => (($g['address']) ? $g['address'] : $g['url']), + 'xchan' => $g['hash'] + ]; + } + } - elseif ($type == 'x') { - $contacts = []; - $r = $this->navbar_complete(); - if ($r) { - foreach ($r as $g) { - $contacts[] = [ - "photo" => $g['photo'], - "name" => $g['name'], - "nick" => $g['address'], - 'link' => (($g['address']) ? $g['address'] : $g['url']), - 'xchan' => $g['hash'] - ]; - } - } - - $o = [ - 'start' => $start, - 'count' => $count, - 'items' => $contacts, - ]; - json_return_and_die($o); - } - else { - $r = []; - } - - if ($r) { - foreach ($r as $g) { - - if (isset($g['network']) && in_array($g['network'],['rss','anon','unknown']) && ($type != 'a')) { - continue; - } + $o = [ + 'start' => $start, + 'count' => $count, + 'items' => $contacts, + ]; + json_return_and_die($o); + } else { + $r = []; + } - // 'z' (activity_filter autocomplete) requires an un-encoded hash to prevent double encoding - - if ($type !== 'z') { - $g['hash'] = urlencode($g['hash']); - } - - if (! $g['nick']) { - $g['nick'] = $g['url']; - } + if ($r) { + foreach ($r as $g) { + if (isset($g['network']) && in_array($g['network'], ['rss', 'anon', 'unknown']) && ($type != 'a')) { + continue; + } - if (in_array($g['hash'],$permitted) && $type === 'f' && (! $noforums)) { - $contacts[] = [ - "type" => "c", - "photo" => "images/twopeople.png", - "name" => $g['name'], - "id" => urlencode($g['id']), - "xid" => $g['hash'], - "link" => (($g['nick']) ? $g['nick'] : $g['url']), - "nick" => substr($g['nick'],0,strpos($g['nick'],'@')), - "self" => (intval($g['abook_self']) ? 'abook-self' : ''), - "taggable" => 'taggable', - "label" => t('network') - ]; - } - if ($type !== 'f') { - $contacts[] = [ - "type" => "c", - "photo" => $g['micro'], - "name" => $g['name'], - "id" => urlencode($g['id']), - "xid" => $g['hash'], - "link" => (($g['nick']) ? $g['nick'] : $g['url']), - "nick" => ((strpos($g['nick'],'@')) ? substr($g['nick'],0,strpos($g['nick'],'@')) : $g['nick']), - "self" => (intval($g['abook_self']) ? 'abook-self' : ''), - "taggable" => '', - "label" => '', - ]; - } - } - } - - $items = array_merge($groups, $contacts); - - $o = [ - 'start' => $start, - 'count' => $count, - 'items' => $items, - ]; + // 'z' (activity_filter autocomplete) requires an un-encoded hash to prevent double encoding - json_return_and_die($o); - } + if ($type !== 'z') { + $g['hash'] = urlencode($g['hash']); + } + + if (!$g['nick']) { + $g['nick'] = $g['url']; + } + + if (in_array($g['hash'], $permitted) && $type === 'f' && (!$noforums)) { + $contacts[] = [ + "type" => "c", + "photo" => "images/twopeople.png", + "name" => $g['name'], + "id" => urlencode($g['id']), + "xid" => $g['hash'], + "link" => (($g['nick']) ? $g['nick'] : $g['url']), + "nick" => substr($g['nick'], 0, strpos($g['nick'], '@')), + "self" => (intval($g['abook_self']) ? 'abook-self' : ''), + "taggable" => 'taggable', + "label" => t('network') + ]; + } + if ($type !== 'f') { + $contacts[] = [ + "type" => "c", + "photo" => $g['micro'], + "name" => $g['name'], + "id" => urlencode($g['id']), + "xid" => $g['hash'], + "link" => (($g['nick']) ? $g['nick'] : $g['url']), + "nick" => ((strpos($g['nick'], '@')) ? substr($g['nick'], 0, strpos($g['nick'], '@')) : $g['nick']), + "self" => (intval($g['abook_self']) ? 'abook-self' : ''), + "taggable" => '', + "label" => '', + ]; + } + } + } + + $items = array_merge($groups, $contacts); + + $o = [ + 'start' => $start, + 'count' => $count, + 'items' => $items, + ]; + + json_return_and_die($o); + } - function navbar_complete() { - - // logger('navbar_complete'); - - if (observer_prohibited()) { - return; - } - - $dirmode = intval(get_config('system','directory_mode')); - $search = ((x($_REQUEST,'search')) ? htmlentities($_REQUEST['search'],ENT_COMPAT,'UTF-8',false) : ''); - if (! $search || mb_strlen($search) < 2) { - return []; - } - - $star = false; - $address = false; - - if (substr($search,0,1) === '@') { - $search = substr($search,1); - } - - if (substr($search,0,1) === '*') { - $star = true; - $search = substr($search,1); - } + public function navbar_complete() + { - if (strpos($search,'@') !== false) { - $address = true; - } + // logger('navbar_complete'); - - $url = z_root() . '/dirsearch'; + if (observer_prohibited()) { + return; + } - - $results = []; + $dirmode = intval(get_config('system', 'directory_mode')); + $search = ((x($_REQUEST, 'search')) ? htmlentities($_REQUEST['search'], ENT_COMPAT, 'UTF-8', false) : ''); + if (!$search || mb_strlen($search) < 2) { + return []; + } - $count = (x($_REQUEST,'count') ? $_REQUEST['count'] : 100); + $star = false; + $address = false; - if ($url) { - $query = $url . '?f='; - $query .= '&name=' . urlencode($search) . "&limit=$count" . (($address) ? '&address=' . urlencode(punify($search)) : ''); - - $x = z_fetch_url($query); - if ($x['success']) { - $t = 0; - $j = json_decode($x['body'],true); - if ($j && $j['results']) { - $results = $j['results']; - } - } - } - return $results; - } + if (substr($search, 0, 1) === '@') { + $search = substr($search, 1); + } + + if (substr($search, 0, 1) === '*') { + $star = true; + $search = substr($search, 1); + } + + if (strpos($search, '@') !== false) { + $address = true; + } + + + $url = z_root() . '/dirsearch'; + + + $results = []; + + $count = (x($_REQUEST, 'count') ? $_REQUEST['count'] : 100); + + if ($url) { + $query = $url . '?f='; + $query .= '&name=' . urlencode($search) . "&limit=$count" . (($address) ? '&address=' . urlencode(punify($search)) : ''); + + $x = z_fetch_url($query); + if ($x['success']) { + $t = 0; + $j = json_decode($x['body'], true); + if ($j && $j['results']) { + $results = $j['results']; + } + } + } + return $results; + } } diff --git a/Zotlabs/Module/Activity.php b/Zotlabs/Module/Activity.php index 5849ae45d..5d9c154f0 100644 --- a/Zotlabs/Module/Activity.php +++ b/Zotlabs/Module/Activity.php @@ -1,4 +1,5 @@ [ + ACTIVITYSTREAMS_JSONLD_REV, + 'https://w3id.org/security/v1', + ZlibActivity::ap_schema() + ]], $i); - if(! $i) - http_status_exit(404, 'Not found'); - - $x = array_merge(['@context' => [ - ACTIVITYSTREAMS_JSONLD_REV, - 'https://w3id.org/security/v1', - ZlibActivity::ap_schema() - ]], $i); - - $headers = []; - $headers['Content-Type'] = 'application/x-nomad+json' ; - $x['signature'] = LDSignatures::sign($x,$chan); - $ret = json_encode($x, JSON_UNESCAPED_SLASHES); - $headers['Digest'] = HTTPSig::generate_digest_header($ret); - $headers['(request-target)'] = strtolower($_SERVER['REQUEST_METHOD']) . ' ' . $_SERVER['REQUEST_URI']; - $h = HTTPSig::create_sig($headers,$chan['channel_prvkey'],channel_url($chan)); - HTTPSig::set_headers($h); - echo $ret; - killme(); - - } + $headers = []; + $headers['Content-Type'] = 'application/x-nomad+json'; + $x['signature'] = LDSignatures::sign($x, $chan); + $ret = json_encode($x, JSON_UNESCAPED_SLASHES); + $headers['Digest'] = HTTPSig::generate_digest_header($ret); + $headers['(request-target)'] = strtolower($_SERVER['REQUEST_METHOD']) . ' ' . $_SERVER['REQUEST_URI']; + $h = HTTPSig::create_sig($headers, $chan['channel_prvkey'], channel_url($chan)); + HTTPSig::set_headers($h); + echo $ret; + killme(); + } + goaway(z_root() . '/item/' . argv(1)); + } +} - goaway(z_root() . '/item/' . argv(1)); - } - -} \ No newline at end of file diff --git a/Zotlabs/Module/Admin.php b/Zotlabs/Module/Admin.php index 8c9a40a1f..dcba31e2d 100644 --- a/Zotlabs/Module/Admin.php +++ b/Zotlabs/Module/Admin.php @@ -1,4 +1,5 @@ sm = new SubModule(); - } + public function __construct() + { + $this->sm = new SubModule(); + } - function init() { + public function init() + { - logger('admin_init', LOGGER_DEBUG); + logger('admin_init', LOGGER_DEBUG); - if (! is_site_admin()) { - logger('admin denied.'); - return; - } - - if (argc() > 1) { - $this->sm->call('init'); - } - } + if (!is_site_admin()) { + logger('admin denied.'); + return; + } + + if (argc() > 1) { + $this->sm->call('init'); + } + } - function post() { + public function post() + { - logger('admin_post', LOGGER_DEBUG); + logger('admin_post', LOGGER_DEBUG); - if (! is_site_admin()) { - logger('admin denied.'); - return; - } - - if (argc() > 1) { - $this->sm->call('post'); - } + if (!is_site_admin()) { + logger('admin denied.'); + return; + } - // goaway(z_root() . '/admin' ); - } + if (argc() > 1) { + $this->sm->call('post'); + } - /** - * @return string - */ + // goaway(z_root() . '/admin' ); + } - function get() { + /** + * @return string + */ - logger('admin_content', LOGGER_DEBUG); + public function get() + { - if (! is_site_admin()) { - logger('admin denied.'); - return login(false); - } + logger('admin_content', LOGGER_DEBUG); - /* - * Page content - */ + if (!is_site_admin()) { + logger('admin denied.'); + return login(false); + } - nav_set_selected('Admin'); + /* + * Page content + */ - $o = ''; + nav_set_selected('Admin'); - if (argc() > 1) { - $o = $this->sm->call('get'); - if ($o === false) { - notice( t('Item not found.') ); - } - } - else { - $o = $this->admin_page_summary(); - } + $o = ''; - if (is_ajax()) { - echo $o; - killme(); - } - else { - return $o; - } - } + if (argc() > 1) { + $o = $this->sm->call('get'); + if ($o === false) { + notice(t('Item not found.')); + } + } else { + $o = $this->admin_page_summary(); + } + + if (is_ajax()) { + echo $o; + killme(); + } else { + return $o; + } + } - /** - * @brief Returns content for Admin Summary Page. - * - * @return string HTML from parsed admin_summary.tpl - */ + /** + * @brief Returns content for Admin Summary Page. + * + * @return string HTML from parsed admin_summary.tpl + */ - function admin_page_summary() { + public function admin_page_summary() + { - // list total user accounts, expirations etc. - $accounts = []; - $r = q("SELECT COUNT(CASE WHEN account_id > 0 THEN 1 ELSE NULL END) AS total, COUNT(CASE WHEN account_expires > %s THEN 1 ELSE NULL END) AS expiring, COUNT(CASE WHEN account_expires < %s AND account_expires > '%s' THEN 1 ELSE NULL END) AS expired, COUNT(CASE WHEN (account_flags & %d)>0 THEN 1 ELSE NULL END) AS blocked FROM account", - db_utcnow(), - db_utcnow(), - dbesc(NULL_DATE), - intval(ACCOUNT_BLOCKED) - ); - if ($r) { - $accounts['total'] = [ 'label' => t('Accounts'), 'val' => $r[0]['total'] ]; - $accounts['blocked'] = [ 'label' => t('Blocked accounts'), 'val' => $r[0]['blocked'] ]; - $accounts['expired'] = [ 'label' => t('Expired accounts'), 'val' => $r[0]['expired'] ]; - $accounts['expiring'] = [ 'label' => t('Expiring accounts'), 'val' => $r[0]['expiring'] ]; - } + // list total user accounts, expirations etc. + $accounts = []; + $r = q( + "SELECT COUNT(CASE WHEN account_id > 0 THEN 1 ELSE NULL END) AS total, COUNT(CASE WHEN account_expires > %s THEN 1 ELSE NULL END) AS expiring, COUNT(CASE WHEN account_expires < %s AND account_expires > '%s' THEN 1 ELSE NULL END) AS expired, COUNT(CASE WHEN (account_flags & %d)>0 THEN 1 ELSE NULL END) AS blocked FROM account", + db_utcnow(), + db_utcnow(), + dbesc(NULL_DATE), + intval(ACCOUNT_BLOCKED) + ); + if ($r) { + $accounts['total'] = ['label' => t('Accounts'), 'val' => $r[0]['total']]; + $accounts['blocked'] = ['label' => t('Blocked accounts'), 'val' => $r[0]['blocked']]; + $accounts['expired'] = ['label' => t('Expired accounts'), 'val' => $r[0]['expired']]; + $accounts['expiring'] = ['label' => t('Expiring accounts'), 'val' => $r[0]['expiring']]; + } - // pending registrations + // pending registrations - $pdg = q("SELECT account.*, register.hash from account left join register on account_id = register.uid where (account_flags & %d ) > 0 ", - intval(ACCOUNT_PENDING) - ); + $pdg = q( + "SELECT account.*, register.hash from account left join register on account_id = register.uid where (account_flags & %d ) > 0 ", + intval(ACCOUNT_PENDING) + ); - $pending = (($pdg) ? count($pdg) : 0); + $pending = (($pdg) ? count($pdg) : 0); - // available channels, primary and clones - $channels = []; - $r = q("SELECT COUNT(*) AS total, COUNT(CASE WHEN channel_primary = 1 THEN 1 ELSE NULL END) AS main, COUNT(CASE WHEN channel_primary = 0 THEN 1 ELSE NULL END) AS clones FROM channel WHERE channel_removed = 0 and channel_system = 0"); - if ($r) { - $channels['total'] = [ 'label' => t('Channels'), 'val' => $r[0]['total'] ]; - $channels['main'] = [ 'label' => t('Primary'), 'val' => $r[0]['main'] ]; - $channels['clones'] = [ 'label' => t('Clones'), 'val' => $r[0]['clones'] ]; - } + // available channels, primary and clones + $channels = []; + $r = q("SELECT COUNT(*) AS total, COUNT(CASE WHEN channel_primary = 1 THEN 1 ELSE NULL END) AS main, COUNT(CASE WHEN channel_primary = 0 THEN 1 ELSE NULL END) AS clones FROM channel WHERE channel_removed = 0 and channel_system = 0"); + if ($r) { + $channels['total'] = ['label' => t('Channels'), 'val' => $r[0]['total']]; + $channels['main'] = ['label' => t('Primary'), 'val' => $r[0]['main']]; + $channels['clones'] = ['label' => t('Clones'), 'val' => $r[0]['clones']]; + } - // We can do better, but this is a quick queue status - $r = q("SELECT COUNT(outq_delivered) AS total FROM outq WHERE outq_delivered = 0"); - $queue = (($r) ? $r[0]['total'] : 0); - $queues = [ 'label' => t('Message queues'), 'queue' => $queue ]; + // We can do better, but this is a quick queue status + $r = q("SELECT COUNT(outq_delivered) AS total FROM outq WHERE outq_delivered = 0"); + $queue = (($r) ? $r[0]['total'] : 0); + $queues = ['label' => t('Message queues'), 'queue' => $queue]; - $plugins = []; + $plugins = []; - if (is_array(App::$plugins) && App::$plugins) { - foreach (App::$plugins as $p) { - if ($p) { - $plugins[] = $p; - } - } - sort($plugins); - } - else { - $plugins = 0; - } + if (is_array(App::$plugins) && App::$plugins) { + foreach (App::$plugins as $p) { + if ($p) { + $plugins[] = $p; + } + } + sort($plugins); + } else { + $plugins = 0; + } - // Could be extended to provide also other alerts to the admin + // Could be extended to provide also other alerts to the admin - $alertmsg = ''; + $alertmsg = ''; - $upgrade = EMPTY_STR; + $upgrade = EMPTY_STR; - if((! defined('PLATFORM_ARCHITECTURE')) || (PLATFORM_ARCHITECTURE === 'zap')) { - $vrelease = get_repository_version('release'); - $vdev = get_repository_version('dev'); - $upgrade = ((version_compare(STD_VERSION,$vrelease) < 0) ? t('Your software should be updated') : ''); - } - - $t = get_markup_template('admin_summary.tpl'); - return replace_macros($t, [ - '$title' => t('Administration'), - '$page' => t('Summary'), - '$adminalertmsg' => $alertmsg, - '$queues' => $queues, - '$accounts' => [ t('Registered accounts'), $accounts ], - '$pending' => [ t('Pending registrations'), $pending ], - '$channels' => [ t('Registered channels'), $channels ], - '$plugins' => (($plugins) ? [ t('Active addons'), $plugins ] : EMPTY_STR), - '$version' => [ t('Version'), STD_VERSION ], - '$vmaster' => [ t('Repository version (release)'), $vrelease ], - '$vdev' => [ t('Repository version (dev)'), $vdev ], - '$upgrade' => $upgrade, - '$build' => Config::Get('system', 'db_version') - ]); - } + if ((!defined('PLATFORM_ARCHITECTURE')) || (PLATFORM_ARCHITECTURE === 'zap')) { + $vrelease = get_repository_version('release'); + $vdev = get_repository_version('dev'); + $upgrade = ((version_compare(STD_VERSION, $vrelease) < 0) ? t('Your software should be updated') : ''); + } + $t = get_markup_template('admin_summary.tpl'); + return replace_macros($t, [ + '$title' => t('Administration'), + '$page' => t('Summary'), + '$adminalertmsg' => $alertmsg, + '$queues' => $queues, + '$accounts' => [t('Registered accounts'), $accounts], + '$pending' => [t('Pending registrations'), $pending], + '$channels' => [t('Registered channels'), $channels], + '$plugins' => (($plugins) ? [t('Active addons'), $plugins] : EMPTY_STR), + '$version' => [t('Version'), STD_VERSION], + '$vmaster' => [t('Repository version (release)'), $vrelease], + '$vdev' => [t('Repository version (dev)'), $vdev], + '$upgrade' => $upgrade, + '$build' => Config::Get('system', 'db_version') + ]); + } } diff --git a/Zotlabs/Module/Admin/Account_edit.php b/Zotlabs/Module/Admin/Account_edit.php index d9156c744..00262f8fa 100644 --- a/Zotlabs/Module/Admin/Account_edit.php +++ b/Zotlabs/Module/Admin/Account_edit.php @@ -2,80 +2,82 @@ namespace Zotlabs\Module\Admin; +class Account_edit +{ + public function post() + { -class Account_edit { + $account_id = $_REQUEST['aid']; - function post() { + if (!$account_id) { + return; + } - $account_id = $_REQUEST['aid']; - - if(! $account_id) - return; - - $pass1 = trim($_REQUEST['pass1']); - $pass2 = trim($_REQUEST['pass2']); - if($pass1 && $pass2 && ($pass1 === $pass2)) { - $salt = random_string(32); - $password_encoded = hash('whirlpool', $salt . $pass1); - $r = q("update account set account_salt = '%s', account_password = '%s', + $pass1 = trim($_REQUEST['pass1']); + $pass2 = trim($_REQUEST['pass2']); + if ($pass1 && $pass2 && ($pass1 === $pass2)) { + $salt = random_string(32); + $password_encoded = hash('whirlpool', $salt . $pass1); + $r = q( + "update account set account_salt = '%s', account_password = '%s', account_password_changed = '%s' where account_id = %d", - dbesc($salt), - dbesc($password_encoded), - dbesc(datetime_convert()), - intval($account_id) - ); - if($r) - info( sprintf( t('Password changed for account %d.'), $account_id). EOL); + dbesc($salt), + dbesc($password_encoded), + dbesc(datetime_convert()), + intval($account_id) + ); + if ($r) { + info(sprintf(t('Password changed for account %d.'), $account_id) . EOL); + } + } - } + $service_class = trim($_REQUEST['service_class']); + $account_language = trim($_REQUEST['account_language']); - $service_class = trim($_REQUEST['service_class']); - $account_language = trim($_REQUEST['account_language']); - - $r = q("update account set account_service_class = '%s', account_language = '%s' + $r = q( + "update account set account_service_class = '%s', account_language = '%s' where account_id = %d", - dbesc($service_class), - dbesc($account_language), - intval($account_id) - ); + dbesc($service_class), + dbesc($account_language), + intval($account_id) + ); - if($r) - info( t('Account settings updated.') . EOL); + if ($r) { + info(t('Account settings updated.') . EOL); + } - goaway(z_root() . '/admin/accounts'); - } + goaway(z_root() . '/admin/accounts'); + } - function get() { - if(argc() > 2) - $account_id = argv(2); + public function get() + { + if (argc() > 2) { + $account_id = argv(2); + } - $x = q("select * from account where account_id = %d limit 1", - intval($account_id) - ); + $x = q( + "select * from account where account_id = %d limit 1", + intval($account_id) + ); - if(! $x) { - notice ( t('Account not found.') . EOL); - return ''; - } + if (!$x) { + notice(t('Account not found.') . EOL); + return ''; + } - $a = replace_macros(get_markup_template('admin_account_edit.tpl'), [ - '$account' => $x[0], - '$title' => t('Account Edit'), - '$pass1' => [ 'pass1', t('New Password'), ' ','' ], - '$pass2' => [ 'pass2', t('New Password again'), ' ','' ], - '$account_language' => [ 'account_language' , t('Account language (for emails)'), $x[0]['account_language'], '', language_list() ], - '$service_class' => [ 'service_class', t('Service class'), $x[0]['account_service_class'], '' ], - '$submit' => t('Submit'), - ] - ); + $a = replace_macros(get_markup_template('admin_account_edit.tpl'), [ + '$account' => $x[0], + '$title' => t('Account Edit'), + '$pass1' => ['pass1', t('New Password'), ' ', ''], + '$pass2' => ['pass2', t('New Password again'), ' ', ''], + '$account_language' => ['account_language', t('Account language (for emails)'), $x[0]['account_language'], '', language_list()], + '$service_class' => ['service_class', t('Service class'), $x[0]['account_service_class'], ''], + '$submit' => t('Submit'), + ]); - return $a; - - - } - - -} \ No newline at end of file + return $a; + } +} diff --git a/Zotlabs/Module/Admin/Accounts.php b/Zotlabs/Module/Admin/Accounts.php index 1b9b0d768..b59e33bee 100644 --- a/Zotlabs/Module/Admin/Accounts.php +++ b/Zotlabs/Module/Admin/Accounts.php @@ -4,199 +4,208 @@ namespace Zotlabs\Module\Admin; use App; -class Accounts { - - /** - * @brief Handle POST actions on accounts admin page. - * - * This function is called when on the admin user/account page the form was - * submitted to handle multiple operations at once. If one of the icons next - * to an entry are pressed the function admin_page_accounts() will handle this. - * - */ +class Accounts +{ - function post() { + /** + * @brief Handle POST actions on accounts admin page. + * + * This function is called when on the admin user/account page the form was + * submitted to handle multiple operations at once. If one of the icons next + * to an entry are pressed the function admin_page_accounts() will handle this. + * + */ - $pending = ( x($_POST, 'pending') ? $_POST['pending'] : [] ); - $users = ( x($_POST, 'user') ? $_POST['user'] : [] ); - $blocked = ( x($_POST, 'blocked') ? $_POST['blocked'] : [] ); - - check_form_security_token_redirectOnErr('/admin/accounts', 'admin_accounts'); - + public function post() + { - // account block/unblock button was submitted - if (x($_POST, 'page_accounts_block')) { - for ($i = 0; $i < count($users); $i++) { - // if account is blocked remove blocked bit-flag, otherwise add blocked bit-flag - $op = ($blocked[$i]) ? '& ~' : '| '; - q("UPDATE account SET account_flags = (account_flags $op %d) WHERE account_id = %d", - intval(ACCOUNT_BLOCKED), - intval($users[$i]) - ); - } - notice( sprintf( tt("%s account blocked/unblocked", "%s accounts blocked/unblocked", count($users)), count($users)) ); - } + $pending = (x($_POST, 'pending') ? $_POST['pending'] : []); + $users = (x($_POST, 'user') ? $_POST['user'] : []); + $blocked = (x($_POST, 'blocked') ? $_POST['blocked'] : []); - // account delete button was submitted - if (x($_POST, 'page_accounts_delete')) { - foreach ($users as $uid){ - account_remove($uid, true, false); - } - notice( sprintf( tt("%s account deleted", "%s accounts deleted", count($users)), count($users)) ); - } - - // registration approved button was submitted - if (x($_POST, 'page_accounts_approve')) { - foreach ($pending as $hash) { - account_allow($hash); - } - } - - // registration deny button was submitted - if (x($_POST, 'page_accounts_deny')) { - foreach ($pending as $hash) { - account_deny($hash); - } - } - - goaway(z_root() . '/admin/accounts' ); - } + check_form_security_token_redirectOnErr('/admin/accounts', 'admin_accounts'); - /** - * @brief Generate accounts admin page and handle single item operations. - * - * This function generates the accounts/account admin page and handles the actions - * if an icon next to an entry was clicked. If several items were selected and - * the form was submitted it is handled by the function admin_page_accounts_post(). - * - * @return string - */ - function get(){ - if (argc() > 2) { - $uid = argv(3); - $account = q("SELECT * FROM account WHERE account_id = %d", - intval($uid) - ); - - if (! $account) { - notice( t('Account not found') . EOL); - goaway(z_root() . '/admin/accounts' ); - } - - check_form_security_token_redirectOnErr('/admin/accounts', 'admin_accounts', 't'); - - switch (argv(2)) { - case 'delete': - // delete user - account_remove($uid,true,false); - - notice( sprintf(t("Account '%s' deleted"), $account[0]['account_email']) . EOL); - break; - case 'block': - q("UPDATE account SET account_flags = ( account_flags | %d ) WHERE account_id = %d", - intval(ACCOUNT_BLOCKED), - intval($uid) - ); - - notice( sprintf( t("Account '%s' blocked") , $account[0]['account_email']) . EOL); - break; - case 'unblock': - q("UPDATE account SET account_flags = ( account_flags & ~ %d ) WHERE account_id = %d", - intval(ACCOUNT_BLOCKED), - intval($uid) - ); - - notice( sprintf( t("Account '%s' unblocked"), $account[0]['account_email']) . EOL); - break; - } - - goaway(z_root() . '/admin/accounts' ); - } - - /* get pending */ - $pending = q("SELECT account.*, register.hash from account left join register on account_id = register.uid where (account_flags & %d ) != 0 ", - intval(ACCOUNT_PENDING) - ); - - /* get accounts */ - - $total = q("SELECT count(*) as total FROM account"); - if (count($total)) { - App::set_pager_total($total[0]['total']); - App::set_pager_itemspage(100); - } - - $serviceclass = (($_REQUEST['class']) ? " and account_service_class = '" . dbesc($_REQUEST['class']) . "' " : ''); + // account block/unblock button was submitted + if (x($_POST, 'page_accounts_block')) { + for ($i = 0; $i < count($users); $i++) { + // if account is blocked remove blocked bit-flag, otherwise add blocked bit-flag + $op = ($blocked[$i]) ? '& ~' : '| '; + q( + "UPDATE account SET account_flags = (account_flags $op %d) WHERE account_id = %d", + intval(ACCOUNT_BLOCKED), + intval($users[$i]) + ); + } + notice(sprintf(tt("%s account blocked/unblocked", "%s accounts blocked/unblocked", count($users)), count($users))); + } - $key = (($_REQUEST['key']) ? dbesc($_REQUEST['key']) : 'account_id'); - $dir = 'asc'; - if (array_key_exists('dir',$_REQUEST)) { - $dir = ((intval($_REQUEST['dir'])) ? 'asc' : 'desc'); - } - - $base = z_root() . '/admin/accounts?f='; - $odir = (($dir === 'asc') ? '0' : '1'); + // account delete button was submitted + if (x($_POST, 'page_accounts_delete')) { + foreach ($users as $uid) { + account_remove($uid, true, false); + } + notice(sprintf(tt("%s account deleted", "%s accounts deleted", count($users)), count($users))); + } - $users = q("SELECT account_id , account_email, account_lastlog, account_created, account_expires, account_service_class, ( account_flags & %d ) > 0 as blocked, + // registration approved button was submitted + if (x($_POST, 'page_accounts_approve')) { + foreach ($pending as $hash) { + account_allow($hash); + } + } + + // registration deny button was submitted + if (x($_POST, 'page_accounts_deny')) { + foreach ($pending as $hash) { + account_deny($hash); + } + } + + goaway(z_root() . '/admin/accounts'); + } + + /** + * @brief Generate accounts admin page and handle single item operations. + * + * This function generates the accounts/account admin page and handles the actions + * if an icon next to an entry was clicked. If several items were selected and + * the form was submitted it is handled by the function admin_page_accounts_post(). + * + * @return string + */ + + public function get() + { + if (argc() > 2) { + $uid = argv(3); + $account = q( + "SELECT * FROM account WHERE account_id = %d", + intval($uid) + ); + + if (!$account) { + notice(t('Account not found') . EOL); + goaway(z_root() . '/admin/accounts'); + } + + check_form_security_token_redirectOnErr('/admin/accounts', 'admin_accounts', 't'); + + switch (argv(2)) { + case 'delete': + // delete user + account_remove($uid, true, false); + + notice(sprintf(t("Account '%s' deleted"), $account[0]['account_email']) . EOL); + break; + case 'block': + q( + "UPDATE account SET account_flags = ( account_flags | %d ) WHERE account_id = %d", + intval(ACCOUNT_BLOCKED), + intval($uid) + ); + + notice(sprintf(t("Account '%s' blocked"), $account[0]['account_email']) . EOL); + break; + case 'unblock': + q( + "UPDATE account SET account_flags = ( account_flags & ~ %d ) WHERE account_id = %d", + intval(ACCOUNT_BLOCKED), + intval($uid) + ); + + notice(sprintf(t("Account '%s' unblocked"), $account[0]['account_email']) . EOL); + break; + } + + goaway(z_root() . '/admin/accounts'); + } + + /* get pending */ + $pending = q( + "SELECT account.*, register.hash from account left join register on account_id = register.uid where (account_flags & %d ) != 0 ", + intval(ACCOUNT_PENDING) + ); + + /* get accounts */ + + $total = q("SELECT count(*) as total FROM account"); + if (count($total)) { + App::set_pager_total($total[0]['total']); + App::set_pager_itemspage(100); + } + + $serviceclass = (($_REQUEST['class']) ? " and account_service_class = '" . dbesc($_REQUEST['class']) . "' " : ''); + + $key = (($_REQUEST['key']) ? dbesc($_REQUEST['key']) : 'account_id'); + $dir = 'asc'; + if (array_key_exists('dir', $_REQUEST)) { + $dir = ((intval($_REQUEST['dir'])) ? 'asc' : 'desc'); + } + + $base = z_root() . '/admin/accounts?f='; + $odir = (($dir === 'asc') ? '0' : '1'); + + $users = q( + "SELECT account_id , account_email, account_lastlog, account_created, account_expires, account_service_class, ( account_flags & %d ) > 0 as blocked, (SELECT %s FROM channel as ch WHERE ch.channel_account_id = ac.account_id and ch.channel_removed = 0 ) as channels FROM account as ac where true $serviceclass and account_flags != %d order by $key $dir limit %d offset %d ", - intval(ACCOUNT_BLOCKED), - db_concat('ch.channel_address', ' '), - intval(ACCOUNT_BLOCKED | ACCOUNT_PENDING), - intval(App::$pager['itemspage']), - intval(App::$pager['start']) - ); + intval(ACCOUNT_BLOCKED), + db_concat('ch.channel_address', ' '), + intval(ACCOUNT_BLOCKED | ACCOUNT_PENDING), + intval(App::$pager['itemspage']), + intval(App::$pager['start']) + ); - if ($users) { - for($x = 0; $x < count($users); $x ++) { - $channel_arr = explode(' ',$users[$x]['channels']); - if ($channel_arr) { - $linked = []; - foreach ( $channel_arr as $c) { - $linked[] = '' . $c . ''; - } - $users[$x]['channels'] = implode(' ',$linked); - } - } - } + if ($users) { + for ($x = 0; $x < count($users); $x++) { + $channel_arr = explode(' ', $users[$x]['channels']); + if ($channel_arr) { + $linked = []; + foreach ($channel_arr as $c) { + $linked[] = '' . $c . ''; + } + $users[$x]['channels'] = implode(' ', $linked); + } + } + } - $t = - $o = replace_macros(get_markup_template('admin_accounts.tpl'), [ - '$title' => t('Administration'), - '$page' => t('Accounts'), - '$submit' => t('Submit'), - '$select_all' => t('select all'), - '$h_pending' => t('Registrations waiting for confirm'), - '$th_pending' => array( t('Request date'), t('Email') ), - '$no_pending' => t('No registrations.'), - '$approve' => t('Approve'), - '$deny' => t('Deny'), - '$delete' => t('Delete'), - '$block' => t('Block'), - '$unblock' => t('Unblock'), - '$odir' => $odir, - '$base' => $base, - '$h_users' => t('Accounts'), - '$th_users' => [ - [ t('ID'), 'account_id' ], - [ t('Email'), 'account_email' ], - [ t('All Channels'), 'channels' ], - [ t('Register date'), 'account_created' ], - [ t('Last login'), 'account_lastlog' ], - [ t('Expires'), 'account_expires' ], - [ t('Service Class'), 'account_service_class'] - ], - '$confirm_delete_multi' => t('Selected accounts will be deleted!\n\nEverything these accounts had posted on this site will be permanently deleted!\n\nAre you sure?'), - '$confirm_delete' => t('The account {0} will be deleted!\n\nEverything this account has posted on this site will be permanently deleted!\n\nAre you sure?'), - '$form_security_token' => get_form_security_token("admin_accounts"), - '$baseurl' => z_root(), - '$pending' => $pending, - '$users' => $users, - ]); - - $o .= paginate($a); - - return $o; - } + $t = + $o = replace_macros(get_markup_template('admin_accounts.tpl'), [ + '$title' => t('Administration'), + '$page' => t('Accounts'), + '$submit' => t('Submit'), + '$select_all' => t('select all'), + '$h_pending' => t('Registrations waiting for confirm'), + '$th_pending' => array(t('Request date'), t('Email')), + '$no_pending' => t('No registrations.'), + '$approve' => t('Approve'), + '$deny' => t('Deny'), + '$delete' => t('Delete'), + '$block' => t('Block'), + '$unblock' => t('Unblock'), + '$odir' => $odir, + '$base' => $base, + '$h_users' => t('Accounts'), + '$th_users' => [ + [t('ID'), 'account_id'], + [t('Email'), 'account_email'], + [t('All Channels'), 'channels'], + [t('Register date'), 'account_created'], + [t('Last login'), 'account_lastlog'], + [t('Expires'), 'account_expires'], + [t('Service Class'), 'account_service_class'] + ], + '$confirm_delete_multi' => t('Selected accounts will be deleted!\n\nEverything these accounts had posted on this site will be permanently deleted!\n\nAre you sure?'), + '$confirm_delete' => t('The account {0} will be deleted!\n\nEverything this account has posted on this site will be permanently deleted!\n\nAre you sure?'), + '$form_security_token' => get_form_security_token("admin_accounts"), + '$baseurl' => z_root(), + '$pending' => $pending, + '$users' => $users, + ]); + + $o .= paginate($a); + + return $o; + } } diff --git a/Zotlabs/Module/Admin/Addons.php b/Zotlabs/Module/Admin/Addons.php index 8f49d9633..62f57c364 100644 --- a/Zotlabs/Module/Admin/Addons.php +++ b/Zotlabs/Module/Admin/Addons.php @@ -2,479 +2,486 @@ namespace Zotlabs\Module\Admin; -use \Zotlabs\Storage\GitRepo; -use \Michelf\MarkdownExtra; +use App; +use PHPGit\Exception\GitException; +use Zotlabs\Storage\GitRepo; +use Michelf\MarkdownExtra; -class Addons { +class Addons +{ - /** - * @brief - * - */ - function post() { + /** + * @brief + * + */ + public function post() + { - if(argc() > 2 && is_file("addon/" . argv(2) . "/" . argv(2) . ".php")) { - @include_once("addon/" . argv(2) . "/" . argv(2) . ".php"); - if(function_exists(argv(2).'_plugin_admin_post')) { - $func = argv(2) . '_plugin_admin_post'; - $func($a); - } + if (argc() > 2 && is_file("addon/" . argv(2) . "/" . argv(2) . ".php")) { + @include_once("addon/" . argv(2) . "/" . argv(2) . ".php"); + if (function_exists(argv(2) . '_plugin_admin_post')) { + $func = argv(2) . '_plugin_admin_post'; + $func($a); + } - goaway(z_root() . '/admin/addons/' . argv(2) ); - } - elseif(argc() > 2) { - switch(argv(2)) { - case 'updaterepo': - if (array_key_exists('repoName', $_REQUEST)) { - $repoName = $_REQUEST['repoName']; - } - else { - json_return_and_die(array('message' => 'No repo name provided.', 'success' => false)); - } - $extendDir = 'cache/git/sys/extend'; - $addonDir = $extendDir . '/addon'; - if (!file_exists($extendDir)) { - if (!mkdir($extendDir, 0770, true)) { - logger('Error creating extend folder: ' . $extendDir); - json_return_and_die(array('message' => 'Error creating extend folder: ' . $extendDir, 'success' => false)); - } - else { - if (!symlink(realpath('extend/addon'), $addonDir)) { - logger('Error creating symlink to addon folder: ' . $addonDir); - json_return_and_die(array('message' => 'Error creating symlink to addon folder: ' . $addonDir, 'success' => false)); - } - } - } - $repoDir = 'cache/git/sys/extend/addon/' . $repoName; - if (!is_dir($repoDir)) { - logger('Repo directory does not exist: ' . $repoDir); - json_return_and_die(array('message' => 'Invalid addon repo.', 'success' => false)); - } - if (!is_writable($repoDir)) { - logger('Repo directory not writable to web server: ' . $repoDir); - json_return_and_die(array('message' => 'Repo directory not writable to web server.', 'success' => false)); - } - $git = new GitRepo('sys', null, false, $repoName, $repoDir); - try { - if ($git->pull()) { - $files = array_diff(scandir($repoDir), array('.', '..')); - foreach ($files as $file) { - if (is_dir($repoDir . '/' . $file) && $file !== '.git') { - $source = '../extend/addon/' . $repoName . '/' . $file; - $target = realpath('addon/') . '/' . $file; - unlink($target); - if (!symlink($source, $target)) { - logger('Error linking addons to /addon'); - json_return_and_die(array('message' => 'Error linking addons to /addon', 'success' => false)); - } - } - } - json_return_and_die(array('message' => 'Repo updated.', 'success' => true)); - } else { - json_return_and_die(array('message' => 'Error updating addon repo.', 'success' => false)); - } - } catch (\PHPGit\Exception\GitException $e) { - json_return_and_die(array('message' => 'Error updating addon repo.', 'success' => false)); - } - case 'removerepo': - if (array_key_exists('repoName', $_REQUEST)) { - $repoName = $_REQUEST['repoName']; - } else { - json_return_and_die(array('message' => 'No repo name provided.', 'success' => false)); - } - $extendDir = 'cache/git/sys/extend'; - $addonDir = $extendDir . '/addon'; - if (!file_exists($extendDir)) { - if (!mkdir($extendDir, 0770, true)) { - logger('Error creating extend folder: ' . $extendDir); - json_return_and_die(array('message' => 'Error creating extend folder: ' . $extendDir, 'success' => false)); - } else { - if (!symlink(realpath('extend/addon'), $addonDir)) { - logger('Error creating symlink to addon folder: ' . $addonDir); - json_return_and_die(array('message' => 'Error creating symlink to addon folder: ' . $addonDir, 'success' => false)); - } - } - } - $repoDir = 'cache/git/sys/extend/addon/' . $repoName; - if (!is_dir($repoDir)) { - logger('Repo directory does not exist: ' . $repoDir); - json_return_and_die(array('message' => 'Invalid addon repo.', 'success' => false)); - } - if (!is_writable($repoDir)) { - logger('Repo directory not writable to web server: ' . $repoDir); - json_return_and_die(array('message' => 'Repo directory not writable to web server.', 'success' => false)); - } - /// @TODO remove directory and unlink /addon/files - if (rrmdir($repoDir)) { - json_return_and_die(array('message' => 'Repo deleted.', 'success' => true)); - } else { - json_return_and_die(array('message' => 'Error deleting addon repo.', 'success' => false)); - } - case 'installrepo': - if (array_key_exists('repoURL', $_REQUEST)) { - $repoURL = $_REQUEST['repoURL']; - $extendDir = 'cache/git/sys/extend'; - $addonDir = $extendDir . '/addon'; - if (!file_exists($extendDir)) { - if (!mkdir($extendDir, 0770, true)) { - logger('Error creating extend folder: ' . $extendDir); - json_return_and_die(array('message' => 'Error creating extend folder: ' . $extendDir, 'success' => false)); - } else { - if (!symlink(realpath('extend/addon'), $addonDir)) { - logger('Error creating symlink to addon folder: ' . $addonDir); - json_return_and_die(array('message' => 'Error creating symlink to addon folder: ' . $addonDir, 'success' => false)); - } - } - } - if (!is_writable($extendDir)) { - logger('Directory not writable to web server: ' . $extendDir); - json_return_and_die(array('message' => 'Directory not writable to web server.', 'success' => false)); - } - $repoName = null; - if (array_key_exists('repoName', $_REQUEST) && $_REQUEST['repoName'] !== '') { - $repoName = $_REQUEST['repoName']; - } else { - $repoName = GitRepo::getRepoNameFromURL($repoURL); - } - if (!$repoName) { - logger('Invalid git repo'); - json_return_and_die(array('message' => 'Invalid git repo', 'success' => false)); - } - $repoDir = $addonDir . '/' . $repoName; - $tempRepoBaseDir = 'cache/git/sys/temp/'; - $tempAddonDir = $tempRepoBaseDir . $repoName; + goaway(z_root() . '/admin/addons/' . argv(2)); + } elseif (argc() > 2) { + switch (argv(2)) { + case 'updaterepo': + if (array_key_exists('repoName', $_REQUEST)) { + $repoName = $_REQUEST['repoName']; + } else { + json_return_and_die(array('message' => 'No repo name provided.', 'success' => false)); + } + $extendDir = 'cache/git/sys/extend'; + $addonDir = $extendDir . '/addon'; + if (!file_exists($extendDir)) { + if (!mkdir($extendDir, 0770, true)) { + logger('Error creating extend folder: ' . $extendDir); + json_return_and_die(array('message' => 'Error creating extend folder: ' . $extendDir, 'success' => false)); + } else { + if (!symlink(realpath('extend/addon'), $addonDir)) { + logger('Error creating symlink to addon folder: ' . $addonDir); + json_return_and_die(array('message' => 'Error creating symlink to addon folder: ' . $addonDir, 'success' => false)); + } + } + } + $repoDir = 'cache/git/sys/extend/addon/' . $repoName; + if (!is_dir($repoDir)) { + logger('Repo directory does not exist: ' . $repoDir); + json_return_and_die(array('message' => 'Invalid addon repo.', 'success' => false)); + } + if (!is_writable($repoDir)) { + logger('Repo directory not writable to web server: ' . $repoDir); + json_return_and_die(array('message' => 'Repo directory not writable to web server.', 'success' => false)); + } + $git = new GitRepo('sys', null, false, $repoName, $repoDir); + try { + if ($git->pull()) { + $files = array_diff(scandir($repoDir), array('.', '..')); + foreach ($files as $file) { + if (is_dir($repoDir . '/' . $file) && $file !== '.git') { + $source = '../extend/addon/' . $repoName . '/' . $file; + $target = realpath('addon/') . '/' . $file; + unlink($target); + if (!symlink($source, $target)) { + logger('Error linking addons to /addon'); + json_return_and_die(array('message' => 'Error linking addons to /addon', 'success' => false)); + } + } + } + json_return_and_die(array('message' => 'Repo updated.', 'success' => true)); + } else { + json_return_and_die(array('message' => 'Error updating addon repo.', 'success' => false)); + } + } catch (GitException $e) { + json_return_and_die(array('message' => 'Error updating addon repo.', 'success' => false)); + } + case 'removerepo': + if (array_key_exists('repoName', $_REQUEST)) { + $repoName = $_REQUEST['repoName']; + } else { + json_return_and_die(array('message' => 'No repo name provided.', 'success' => false)); + } + $extendDir = 'cache/git/sys/extend'; + $addonDir = $extendDir . '/addon'; + if (!file_exists($extendDir)) { + if (!mkdir($extendDir, 0770, true)) { + logger('Error creating extend folder: ' . $extendDir); + json_return_and_die(array('message' => 'Error creating extend folder: ' . $extendDir, 'success' => false)); + } else { + if (!symlink(realpath('extend/addon'), $addonDir)) { + logger('Error creating symlink to addon folder: ' . $addonDir); + json_return_and_die(array('message' => 'Error creating symlink to addon folder: ' . $addonDir, 'success' => false)); + } + } + } + $repoDir = 'cache/git/sys/extend/addon/' . $repoName; + if (!is_dir($repoDir)) { + logger('Repo directory does not exist: ' . $repoDir); + json_return_and_die(array('message' => 'Invalid addon repo.', 'success' => false)); + } + if (!is_writable($repoDir)) { + logger('Repo directory not writable to web server: ' . $repoDir); + json_return_and_die(array('message' => 'Repo directory not writable to web server.', 'success' => false)); + } + /// @TODO remove directory and unlink /addon/files + if (rrmdir($repoDir)) { + json_return_and_die(array('message' => 'Repo deleted.', 'success' => true)); + } else { + json_return_and_die(array('message' => 'Error deleting addon repo.', 'success' => false)); + } + case 'installrepo': + if (array_key_exists('repoURL', $_REQUEST)) { + $repoURL = $_REQUEST['repoURL']; + $extendDir = 'cache/git/sys/extend'; + $addonDir = $extendDir . '/addon'; + if (!file_exists($extendDir)) { + if (!mkdir($extendDir, 0770, true)) { + logger('Error creating extend folder: ' . $extendDir); + json_return_and_die(array('message' => 'Error creating extend folder: ' . $extendDir, 'success' => false)); + } else { + if (!symlink(realpath('extend/addon'), $addonDir)) { + logger('Error creating symlink to addon folder: ' . $addonDir); + json_return_and_die(array('message' => 'Error creating symlink to addon folder: ' . $addonDir, 'success' => false)); + } + } + } + if (!is_writable($extendDir)) { + logger('Directory not writable to web server: ' . $extendDir); + json_return_and_die(array('message' => 'Directory not writable to web server.', 'success' => false)); + } + $repoName = null; + if (array_key_exists('repoName', $_REQUEST) && $_REQUEST['repoName'] !== '') { + $repoName = $_REQUEST['repoName']; + } else { + $repoName = GitRepo::getRepoNameFromURL($repoURL); + } + if (!$repoName) { + logger('Invalid git repo'); + json_return_and_die(array('message' => 'Invalid git repo', 'success' => false)); + } + $repoDir = $addonDir . '/' . $repoName; + $tempRepoBaseDir = 'cache/git/sys/temp/'; + $tempAddonDir = $tempRepoBaseDir . $repoName; - if (!is_writable($addonDir) || !is_writable($tempAddonDir)) { - logger('Temp repo directory or /extend/addon not writable to web server: ' . $tempAddonDir); - json_return_and_die(array('message' => 'Temp repo directory not writable to web server.', 'success' => false)); - } - rename($tempAddonDir, $repoDir); + if (!is_writable($addonDir) || !is_writable($tempAddonDir)) { + logger('Temp repo directory or /extend/addon not writable to web server: ' . $tempAddonDir); + json_return_and_die(array('message' => 'Temp repo directory not writable to web server.', 'success' => false)); + } + rename($tempAddonDir, $repoDir); - if (!is_writable(realpath('addon/'))) { - logger('/addon directory not writable to web server: ' . $tempAddonDir); - json_return_and_die(array('message' => '/addon directory not writable to web server.', 'success' => false)); - } - $files = array_diff(scandir($repoDir), array('.', '..')); - foreach ($files as $file) { - if (is_dir($repoDir . '/' . $file) && $file !== '.git') { - $source = '../extend/addon/' . $repoName . '/' . $file; - $target = realpath('addon/') . '/' . $file; - unlink($target); - if (!symlink($source, $target)) { - logger('Error linking addons to /addon'); - json_return_and_die(array('message' => 'Error linking addons to /addon', 'success' => false)); - } - } - } - $git = new GitRepo('sys', $repoURL, false, $repoName, $repoDir); - $repo = $git->probeRepo(); - json_return_and_die(array('repo' => $repo, 'message' => '', 'success' => true)); - } - case 'addrepo': - if (array_key_exists('repoURL', $_REQUEST)) { - $repoURL = $_REQUEST['repoURL']; - $extendDir = 'cache/git/sys/extend'; - $addonDir = $extendDir . '/addon'; - $tempAddonDir = realpath('cache') . '/git/sys/temp'; - if (!file_exists($extendDir)) { - if (!mkdir($extendDir, 0770, true)) { - logger('Error creating extend folder: ' . $extendDir); - json_return_and_die(array('message' => 'Error creating extend folder: ' . $extendDir, 'success' => false)); - } else { - if (!symlink(realpath('extend/addon'), $addonDir)) { - logger('Error creating symlink to addon folder: ' . $addonDir); - json_return_and_die(array('message' => 'Error creating symlink to addon folder: ' . $addonDir, 'success' => false)); - } - } - } - if (!is_dir($tempAddonDir)) { - if (!mkdir($tempAddonDir, 0770, true)) { - logger('Error creating temp plugin repo folder: ' . $tempAddonDir); - json_return_and_die(array('message' => 'Error creating temp plugin repo folder: ' . $tempAddonDir, 'success' => false)); - } - } - $repoName = null; - if (array_key_exists('repoName', $_REQUEST) && $_REQUEST['repoName'] !== '') { - $repoName = $_REQUEST['repoName']; - } else { - $repoName = GitRepo::getRepoNameFromURL($repoURL); - } - if (!$repoName) { - logger('Invalid git repo'); - json_return_and_die(array('message' => 'Invalid git repo: ' . $repoName, 'success' => false)); - } - $repoDir = $tempAddonDir . '/' . $repoName; - if (!is_writable($tempAddonDir)) { - logger('Temporary directory for new addon repo is not writable to web server: ' . $tempAddonDir); - json_return_and_die(array('message' => 'Temporary directory for new addon repo is not writable to web server.', 'success' => false)); - } - // clone the repo if new automatically - $git = new GitRepo('sys', $repoURL, true, $repoName, $repoDir); + if (!is_writable(realpath('addon/'))) { + logger('/addon directory not writable to web server: ' . $tempAddonDir); + json_return_and_die(array('message' => '/addon directory not writable to web server.', 'success' => false)); + } + $files = array_diff(scandir($repoDir), array('.', '..')); + foreach ($files as $file) { + if (is_dir($repoDir . '/' . $file) && $file !== '.git') { + $source = '../extend/addon/' . $repoName . '/' . $file; + $target = realpath('addon/') . '/' . $file; + unlink($target); + if (!symlink($source, $target)) { + logger('Error linking addons to /addon'); + json_return_and_die(array('message' => 'Error linking addons to /addon', 'success' => false)); + } + } + } + $git = new GitRepo('sys', $repoURL, false, $repoName, $repoDir); + $repo = $git->probeRepo(); + json_return_and_die(array('repo' => $repo, 'message' => '', 'success' => true)); + } + case 'addrepo': + if (array_key_exists('repoURL', $_REQUEST)) { + $repoURL = $_REQUEST['repoURL']; + $extendDir = 'cache/git/sys/extend'; + $addonDir = $extendDir . '/addon'; + $tempAddonDir = realpath('cache') . '/git/sys/temp'; + if (!file_exists($extendDir)) { + if (!mkdir($extendDir, 0770, true)) { + logger('Error creating extend folder: ' . $extendDir); + json_return_and_die(array('message' => 'Error creating extend folder: ' . $extendDir, 'success' => false)); + } else { + if (!symlink(realpath('extend/addon'), $addonDir)) { + logger('Error creating symlink to addon folder: ' . $addonDir); + json_return_and_die(array('message' => 'Error creating symlink to addon folder: ' . $addonDir, 'success' => false)); + } + } + } + if (!is_dir($tempAddonDir)) { + if (!mkdir($tempAddonDir, 0770, true)) { + logger('Error creating temp plugin repo folder: ' . $tempAddonDir); + json_return_and_die(array('message' => 'Error creating temp plugin repo folder: ' . $tempAddonDir, 'success' => false)); + } + } + $repoName = null; + if (array_key_exists('repoName', $_REQUEST) && $_REQUEST['repoName'] !== '') { + $repoName = $_REQUEST['repoName']; + } else { + $repoName = GitRepo::getRepoNameFromURL($repoURL); + } + if (!$repoName) { + logger('Invalid git repo'); + json_return_and_die(array('message' => 'Invalid git repo: ' . $repoName, 'success' => false)); + } + $repoDir = $tempAddonDir . '/' . $repoName; + if (!is_writable($tempAddonDir)) { + logger('Temporary directory for new addon repo is not writable to web server: ' . $tempAddonDir); + json_return_and_die(array('message' => 'Temporary directory for new addon repo is not writable to web server.', 'success' => false)); + } + // clone the repo if new automatically + $git = new GitRepo('sys', $repoURL, true, $repoName, $repoDir); - $remotes = $git->git->remote(); - $fetchURL = $remotes['origin']['fetch']; - if ($fetchURL !== $git->url) { - if (rrmdir($repoDir)) { - $git = new GitRepo('sys', $repoURL, true, $repoName, $repoDir); - } else { - json_return_and_die(array('message' => 'Error deleting existing addon repo.', 'success' => false)); - } - } - $repo = $git->probeRepo(); - $repo['readme'] = $repo['manifest'] = null; - foreach ($git->git->tree('master') as $object) { - if ($object['type'] == 'blob' && (strtolower($object['file']) === 'readme.md' || strtolower($object['file']) === 'readme')) { - $repo['readme'] = MarkdownExtra::defaultTransform($git->git->cat->blob($object['hash'])); - } else if ($object['type'] == 'blob' && strtolower($object['file']) === 'manifest.json') { - $repo['manifest'] = $git->git->cat->blob($object['hash']); - } - } - json_return_and_die(array('repo' => $repo, 'message' => '', 'success' => true)); - } else { - json_return_and_die(array('message' => 'No repo URL provided', 'success' => false)); - } - break; - default: - break; - } - } - } + $remotes = $git->git->remote(); + $fetchURL = $remotes['origin']['fetch']; + if ($fetchURL !== $git->url) { + if (rrmdir($repoDir)) { + $git = new GitRepo('sys', $repoURL, true, $repoName, $repoDir); + } else { + json_return_and_die(array('message' => 'Error deleting existing addon repo.', 'success' => false)); + } + } + $repo = $git->probeRepo(); + $repo['readme'] = $repo['manifest'] = null; + foreach ($git->git->tree('master') as $object) { + if ($object['type'] == 'blob' && (strtolower($object['file']) === 'readme.md' || strtolower($object['file']) === 'readme')) { + $repo['readme'] = MarkdownExtra::defaultTransform($git->git->cat->blob($object['hash'])); + } elseif ($object['type'] == 'blob' && strtolower($object['file']) === 'manifest.json') { + $repo['manifest'] = $git->git->cat->blob($object['hash']); + } + } + json_return_and_die(array('repo' => $repo, 'message' => '', 'success' => true)); + } else { + json_return_and_die(array('message' => 'No repo URL provided', 'success' => false)); + } + break; + default: + break; + } + } + } - /** - * @brief Addons admin page. - * - * @return string with parsed HTML - */ - function get() { + /** + * @brief Addons admin page. + * + * @return string with parsed HTML + */ + public function get() + { - /* - * Single plugin - */ + /* + * Single plugin + */ - if (\App::$argc == 3){ - $plugin = \App::$argv[2]; - if (!is_file("addon/$plugin/$plugin.php")){ - notice( t("Item not found.") ); - return ''; - } + if (App::$argc == 3) { + $plugin = App::$argv[2]; + if (!is_file("addon/$plugin/$plugin.php")) { + notice(t("Item not found.")); + return ''; + } - $enabled = in_array($plugin,\App::$plugins); - $info = get_plugin_info($plugin); - $x = check_plugin_versions($info); + $enabled = in_array($plugin, App::$plugins); + $info = get_plugin_info($plugin); + $x = check_plugin_versions($info); - // disable plugins which are installed but incompatible versions + // disable plugins which are installed but incompatible versions - if($enabled && ! $x) { - $enabled = false; - $idz = array_search($plugin, \App::$plugins); - if ($idz !== false) { - unset(\App::$plugins[$idz]); - uninstall_plugin($plugin); - set_config("system","addon", implode(", ",\App::$plugins)); - } - } - $info['disabled'] = 1-intval($x); + if ($enabled && !$x) { + $enabled = false; + $idz = array_search($plugin, App::$plugins); + if ($idz !== false) { + unset(App::$plugins[$idz]); + uninstall_plugin($plugin); + set_config("system", "addon", implode(", ", App::$plugins)); + } + } + $info['disabled'] = 1 - intval($x); - if (x($_GET,"a") && $_GET['a']=="t"){ - check_form_security_token_redirectOnErr('/admin/addons', 'admin_addons', 't'); - $pinstalled = false; - // Toggle plugin status - $idx = array_search($plugin, \App::$plugins); - if ($idx !== false){ - unset(\App::$plugins[$idx]); - uninstall_plugin($plugin); - $pinstalled = false; - info( sprintf( t("Plugin %s disabled."), $plugin ) ); - } else { - \App::$plugins[] = $plugin; - install_plugin($plugin); - $pinstalled = true; - info( sprintf( t("Plugin %s enabled."), $plugin ) ); - } - set_config("system","addon", implode(", ",\App::$plugins)); + if (x($_GET, "a") && $_GET['a'] == "t") { + check_form_security_token_redirectOnErr('/admin/addons', 'admin_addons', 't'); + $pinstalled = false; + // Toggle plugin status + $idx = array_search($plugin, App::$plugins); + if ($idx !== false) { + unset(App::$plugins[$idx]); + uninstall_plugin($plugin); + $pinstalled = false; + info(sprintf(t("Plugin %s disabled."), $plugin)); + } else { + App::$plugins[] = $plugin; + install_plugin($plugin); + $pinstalled = true; + info(sprintf(t("Plugin %s enabled."), $plugin)); + } + set_config("system", "addon", implode(", ", App::$plugins)); - if($pinstalled) { - @require_once("addon/$plugin/$plugin.php"); - if(function_exists($plugin.'_plugin_admin')) - goaway(z_root() . '/admin/addons/' . $plugin); - } - goaway(z_root() . '/admin/addons' ); - } + if ($pinstalled) { + @require_once("addon/$plugin/$plugin.php"); + if (function_exists($plugin . '_plugin_admin')) { + goaway(z_root() . '/admin/addons/' . $plugin); + } + } + goaway(z_root() . '/admin/addons'); + } - // display plugin details + // display plugin details - if (in_array($plugin, \App::$plugins)){ - $status = 'on'; - $action = t('Disable'); - } else { - $status = 'off'; - $action = t('Enable'); - } + if (in_array($plugin, App::$plugins)) { + $status = 'on'; + $action = t('Disable'); + } else { + $status = 'off'; + $action = t('Enable'); + } - $readme = null; - if (is_file("addon/$plugin/README.md")){ - $readme = file_get_contents("addon/$plugin/README.md"); - $readme = MarkdownExtra::defaultTransform($readme); - } else if (is_file("addon/$plugin/README")){ - $readme = "
        ". file_get_contents("addon/$plugin/README") ."
        "; - } + $readme = null; + if (is_file("addon/$plugin/README.md")) { + $readme = file_get_contents("addon/$plugin/README.md"); + $readme = MarkdownExtra::defaultTransform($readme); + } elseif (is_file("addon/$plugin/README")) { + $readme = "
        " . file_get_contents("addon/$plugin/README") . "
        "; + } - $admin_form = ''; + $admin_form = ''; - $r = q("select * from addon where plugin_admin = 1 and aname = '%s' limit 1", - dbesc($plugin) - ); + $r = q( + "select * from addon where plugin_admin = 1 and aname = '%s' limit 1", + dbesc($plugin) + ); - if($r) { - @require_once("addon/$plugin/$plugin.php"); - if(function_exists($plugin.'_plugin_admin')) { - $func = $plugin.'_plugin_admin'; - $func($a, $admin_form); - } - } + if ($r) { + @require_once("addon/$plugin/$plugin.php"); + if (function_exists($plugin . '_plugin_admin')) { + $func = $plugin . '_plugin_admin'; + $func($a, $admin_form); + } + } - $t = get_markup_template('admin_plugins_details.tpl'); - return replace_macros($t, array( - '$title' => t('Administration'), - '$page' => t('Addons'), - '$toggle' => t('Toggle'), - '$settings' => t('Settings'), - '$baseurl' => z_root(), + $t = get_markup_template('admin_plugins_details.tpl'); + return replace_macros($t, array( + '$title' => t('Administration'), + '$page' => t('Addons'), + '$toggle' => t('Toggle'), + '$settings' => t('Settings'), + '$baseurl' => z_root(), - '$plugin' => $plugin, - '$status' => $status, - '$action' => $action, - '$info' => $info, - '$str_author' => t('Author: '), - '$str_maintainer' => t('Maintainer: '), - '$str_minversion' => t('Minimum project version: '), - '$str_maxversion' => t('Maximum project version: '), - '$str_minphpversion' => t('Minimum PHP version: '), - '$str_serverroles' => t('Compatible Server Roles: '), - '$str_requires' => t('Requires: '), - '$disabled' => t('Disabled - version incompatibility'), + '$plugin' => $plugin, + '$status' => $status, + '$action' => $action, + '$info' => $info, + '$str_author' => t('Author: '), + '$str_maintainer' => t('Maintainer: '), + '$str_minversion' => t('Minimum project version: '), + '$str_maxversion' => t('Maximum project version: '), + '$str_minphpversion' => t('Minimum PHP version: '), + '$str_serverroles' => t('Compatible Server Roles: '), + '$str_requires' => t('Requires: '), + '$disabled' => t('Disabled - version incompatibility'), - '$admin_form' => $admin_form, - '$function' => 'addons', - '$screenshot' => '', - '$readme' => $readme, + '$admin_form' => $admin_form, + '$function' => 'addons', + '$screenshot' => '', + '$readme' => $readme, - '$form_security_token' => get_form_security_token('admin_addons'), - )); - } + '$form_security_token' => get_form_security_token('admin_addons'), + )); + } - /* - * List plugins - */ - $plugins = []; - $files = glob('addon/*/'); - if($files) { - foreach($files as $file) { - if ($file === 'addon/vendor/') { - continue; - } - if (is_dir($file)){ - list($tmp, $id) = array_map('trim', explode('/', $file)); - $info = get_plugin_info($id); - $enabled = in_array($id,\App::$plugins); - $x = check_plugin_versions($info); + /* + * List plugins + */ + $plugins = []; + $files = glob('addon/*/'); + if ($files) { + foreach ($files as $file) { + if ($file === 'addon/vendor/') { + continue; + } + if (is_dir($file)) { + list($tmp, $id) = array_map('trim', explode('/', $file)); + $info = get_plugin_info($id); + $enabled = in_array($id, App::$plugins); + $x = check_plugin_versions($info); - // disable plugins which are installed but incompatible versions + // disable plugins which are installed but incompatible versions - if($enabled && ! $x) { - $enabled = false; - $idz = array_search($id, \App::$plugins); - if ($idz !== false) { - unset(\App::$plugins[$idz]); - uninstall_plugin($id); - set_config("system","addon", implode(", ",\App::$plugins)); - } - } - $info['disabled'] = 1-intval($x); + if ($enabled && !$x) { + $enabled = false; + $idz = array_search($id, App::$plugins); + if ($idz !== false) { + unset(App::$plugins[$idz]); + uninstall_plugin($id); + set_config("system", "addon", implode(", ", App::$plugins)); + } + } + $info['disabled'] = 1 - intval($x); - $plugins[] = array( $id, (($enabled)?"on":"off") , $info); - } - } - } + $plugins[] = array($id, (($enabled) ? "on" : "off"), $info); + } + } + } - usort($plugins,'self::plugin_sort'); + usort($plugins, 'self::plugin_sort'); - $allowManageRepos = false; - if(is_writable('extend/addon') && is_writable('cache')) { - $allowManageRepos = true; - } + $allowManageRepos = false; + if (is_writable('extend/addon') && is_writable('cache')) { + $allowManageRepos = true; + } - $admin_plugins_add_repo_form= replace_macros( - get_markup_template('admin_plugins_addrepo.tpl'), array( - '$post' => 'admin/addons/addrepo', - '$desc' => t('Enter the public git repository URL of the addon repo.'), - '$repoURL' => array('repoURL', t('Addon repo git URL'), '', ''), - '$repoName' => array('repoName', t('Custom repo name'), '', '', t('(optional)')), - '$submit' => t('Download Addon Repo') - ) - ); - $newRepoModalID = random_string(3); - $newRepoModal = replace_macros( - get_markup_template('generic_modal.tpl'), array( - '$id' => $newRepoModalID, - '$title' => t('Install new repo'), - '$ok' => t('Install'), - '$cancel' => t('Cancel') - ) - ); + $admin_plugins_add_repo_form = replace_macros( + get_markup_template('admin_plugins_addrepo.tpl'), + array( + '$post' => 'admin/addons/addrepo', + '$desc' => t('Enter the public git repository URL of the addon repo.'), + '$repoURL' => array('repoURL', t('Addon repo git URL'), '', ''), + '$repoName' => array('repoName', t('Custom repo name'), '', '', t('(optional)')), + '$submit' => t('Download Addon Repo') + ) + ); + $newRepoModalID = random_string(3); + $newRepoModal = replace_macros( + get_markup_template('generic_modal.tpl'), + array( + '$id' => $newRepoModalID, + '$title' => t('Install new repo'), + '$ok' => t('Install'), + '$cancel' => t('Cancel') + ) + ); - $reponames = $this->listAddonRepos(); - $addonrepos = []; - foreach($reponames as $repo) { - $addonrepos[] = array('name' => $repo, 'description' => ''); - /// @TODO Parse repo info to provide more information about repos - } + $reponames = $this->listAddonRepos(); + $addonrepos = []; + foreach ($reponames as $repo) { + $addonrepos[] = array('name' => $repo, 'description' => ''); + /// @TODO Parse repo info to provide more information about repos + } - $t = get_markup_template('admin_plugins.tpl'); - return replace_macros($t, array( - '$title' => t('Administration'), - '$page' => t('Addons'), - '$submit' => t('Submit'), - '$baseurl' => z_root(), - '$function' => 'addons', - '$plugins' => $plugins, - '$disabled' => t('Disabled - version incompatibility'), - '$form_security_token' => get_form_security_token('admin_addons'), - '$allowManageRepos' => $allowManageRepos, - '$managerepos' => t('Manage Repos'), - '$installedtitle' => t('Installed Addon Repositories'), - '$addnewrepotitle' => t('Install a New Addon Repository'), - '$expandform' => false, - '$form' => $admin_plugins_add_repo_form, - '$newRepoModal' => $newRepoModal, - '$newRepoModalID' => $newRepoModalID, - '$addonrepos' => $addonrepos, - '$repoUpdateButton' => t('Update'), - '$repoBranchButton' => t('Switch branch'), - '$repoRemoveButton' => t('Remove') - )); - } + $t = get_markup_template('admin_plugins.tpl'); + return replace_macros($t, array( + '$title' => t('Administration'), + '$page' => t('Addons'), + '$submit' => t('Submit'), + '$baseurl' => z_root(), + '$function' => 'addons', + '$plugins' => $plugins, + '$disabled' => t('Disabled - version incompatibility'), + '$form_security_token' => get_form_security_token('admin_addons'), + '$allowManageRepos' => $allowManageRepos, + '$managerepos' => t('Manage Repos'), + '$installedtitle' => t('Installed Addon Repositories'), + '$addnewrepotitle' => t('Install a New Addon Repository'), + '$expandform' => false, + '$form' => $admin_plugins_add_repo_form, + '$newRepoModal' => $newRepoModal, + '$newRepoModalID' => $newRepoModalID, + '$addonrepos' => $addonrepos, + '$repoUpdateButton' => t('Update'), + '$repoBranchButton' => t('Switch branch'), + '$repoRemoveButton' => t('Remove') + )); + } - function listAddonRepos() { - $addonrepos = []; - $addonDir = 'extend/addon/'; - if(is_dir($addonDir)) { - if ($handle = opendir($addonDir)) { - while (false !== ($entry = readdir($handle))) { - if ($entry != "." && $entry != "..") { - $addonrepos[] = $entry; - } - } - closedir($handle); - } - } - return $addonrepos; - } + public function listAddonRepos() + { + $addonrepos = []; + $addonDir = 'extend/addon/'; + if (is_dir($addonDir)) { + if ($handle = opendir($addonDir)) { + while (false !== ($entry = readdir($handle))) { + if ($entry != "." && $entry != "..") { + $addonrepos[] = $entry; + } + } + closedir($handle); + } + } + return $addonrepos; + } - static public function plugin_sort($a,$b) { - return(strcmp(strtolower($a[2]['name']),strtolower($b[2]['name']))); - } - -} \ No newline at end of file + public static function plugin_sort($a, $b) + { + return (strcmp(strtolower($a[2]['name']), strtolower($b[2]['name']))); + } +} diff --git a/Zotlabs/Module/Admin/Channels.php b/Zotlabs/Module/Admin/Channels.php index 1456a45af..b34bcd182 100644 --- a/Zotlabs/Module/Admin/Channels.php +++ b/Zotlabs/Module/Admin/Channels.php @@ -9,175 +9,191 @@ use Zotlabs\Daemon\Run; * @brief Admin Module for Channels. * */ - -class Channels { +class Channels +{ - /** - * @brief Handle POST actions on channels admin page. - * - */ - function post() { + /** + * @brief Handle POST actions on channels admin page. + * + */ + public function post() + { - $channels = ( x($_POST, 'channel') ? $_POST['channel'] : Array() ); + $channels = (x($_POST, 'channel') ? $_POST['channel'] : array()); - check_form_security_token_redirectOnErr('/admin/channels', 'admin_channels'); + check_form_security_token_redirectOnErr('/admin/channels', 'admin_channels'); - $xor = db_getfunc('^'); + $xor = db_getfunc('^'); - if(x($_POST, 'page_channels_block')) { - foreach($channels as $uid) { - q("UPDATE channel SET channel_pageflags = ( channel_pageflags $xor %d ) where channel_id = %d", - intval(PAGE_CENSORED), - intval( $uid ) - ); - Run::Summon( [ 'Directory', $uid, 'nopush' ] ); - } - notice( sprintf( tt("%s channel censored/uncensored", "%s channels censored/uncensored", count($channels)), count($channels)) ); - } - if(x($_POST, 'page_channels_code')) { - foreach($channels as $uid) { - q("UPDATE channel SET channel_pageflags = ( channel_pageflags $xor %d ) where channel_id = %d", - intval(PAGE_ALLOWCODE), - intval( $uid ) - ); - } - notice( sprintf( tt("%s channel code allowed/disallowed", "%s channels code allowed/disallowed", count($channels)), count($channels)) ); - } - if(x($_POST, 'page_channels_delete')) { - foreach($channels as $uid) { - channel_remove($uid, true); - } - notice( sprintf( tt("%s channel deleted", "%s channels deleted", count($channels)), count($channels)) ); - } + if (x($_POST, 'page_channels_block')) { + foreach ($channels as $uid) { + q( + "UPDATE channel SET channel_pageflags = ( channel_pageflags $xor %d ) where channel_id = %d", + intval(PAGE_CENSORED), + intval($uid) + ); + Run::Summon(['Directory', $uid, 'nopush']); + } + notice(sprintf(tt("%s channel censored/uncensored", "%s channels censored/uncensored", count($channels)), count($channels))); + } + if (x($_POST, 'page_channels_code')) { + foreach ($channels as $uid) { + q( + "UPDATE channel SET channel_pageflags = ( channel_pageflags $xor %d ) where channel_id = %d", + intval(PAGE_ALLOWCODE), + intval($uid) + ); + } + notice(sprintf(tt("%s channel code allowed/disallowed", "%s channels code allowed/disallowed", count($channels)), count($channels))); + } + if (x($_POST, 'page_channels_delete')) { + foreach ($channels as $uid) { + channel_remove($uid, true); + } + notice(sprintf(tt("%s channel deleted", "%s channels deleted", count($channels)), count($channels))); + } - goaway(z_root() . '/admin/channels' ); - } + goaway(z_root() . '/admin/channels'); + } - /** - * @brief Generate channels admin page and handle single item operations. - * - * @return string with parsed HTML - */ - function get() { - if(argc() > 2) { - $uid = argv(3); - $channel = q("SELECT * FROM channel WHERE channel_id = %d", - intval($uid) - ); + /** + * @brief Generate channels admin page and handle single item operations. + * + * @return string with parsed HTML + */ + public function get() + { + if (argc() > 2) { + $uid = argv(3); + $channel = q( + "SELECT * FROM channel WHERE channel_id = %d", + intval($uid) + ); - if(! $channel) { - notice( t('Channel not found') . EOL); - goaway(z_root() . '/admin/channels' ); - } + if (!$channel) { + notice(t('Channel not found') . EOL); + goaway(z_root() . '/admin/channels'); + } - switch(argv(2)) { - case "delete":{ - check_form_security_token_redirectOnErr('/admin/channels', 'admin_channels', 't'); - // delete channel - channel_remove($uid,true); + switch (argv(2)) { + case "delete": + { + check_form_security_token_redirectOnErr('/admin/channels', 'admin_channels', 't'); + // delete channel + channel_remove($uid, true); - notice( sprintf(t("Channel '%s' deleted"), $channel[0]['channel_name']) . EOL); - }; break; + notice(sprintf(t("Channel '%s' deleted"), $channel[0]['channel_name']) . EOL); + } + break; - case "block":{ - check_form_security_token_redirectOnErr('/admin/channels', 'admin_channels', 't'); - $pflags = $channel[0]['channel_pageflags'] ^ PAGE_CENSORED; - q("UPDATE channel SET channel_pageflags = %d where channel_id = %d", - intval($pflags), - intval( $uid ) - ); - Run::Summon( [ 'Directory', $uid, 'nopush' ]); + case "block": + { + check_form_security_token_redirectOnErr('/admin/channels', 'admin_channels', 't'); + $pflags = $channel[0]['channel_pageflags'] ^ PAGE_CENSORED; + q( + "UPDATE channel SET channel_pageflags = %d where channel_id = %d", + intval($pflags), + intval($uid) + ); + Run::Summon(['Directory', $uid, 'nopush']); - notice( sprintf( (($pflags & PAGE_CENSORED) ? t("Channel '%s' censored"): t("Channel '%s' uncensored")) , $channel[0]['channel_name'] . ' (' . $channel[0]['channel_address'] . ')' ) . EOL); - }; break; + notice(sprintf((($pflags & PAGE_CENSORED) ? t("Channel '%s' censored") : t("Channel '%s' uncensored")), $channel[0]['channel_name'] . ' (' . $channel[0]['channel_address'] . ')') . EOL); + } + break; - case "code":{ - check_form_security_token_redirectOnErr('/admin/channels', 'admin_channels', 't'); - $pflags = $channel[0]['channel_pageflags'] ^ PAGE_ALLOWCODE; - q("UPDATE channel SET channel_pageflags = %d where channel_id = %d", - intval($pflags), - intval( $uid ) - ); + case "code": + { + check_form_security_token_redirectOnErr('/admin/channels', 'admin_channels', 't'); + $pflags = $channel[0]['channel_pageflags'] ^ PAGE_ALLOWCODE; + q( + "UPDATE channel SET channel_pageflags = %d where channel_id = %d", + intval($pflags), + intval($uid) + ); - notice( sprintf( (($pflags & PAGE_ALLOWCODE) ? t("Channel '%s' code allowed"): t("Channel '%s' code disallowed")) , $channel[0]['channel_name'] . ' (' . $channel[0]['channel_address'] . ')' ) . EOL); - }; break; + notice(sprintf((($pflags & PAGE_ALLOWCODE) ? t("Channel '%s' code allowed") : t("Channel '%s' code disallowed")), $channel[0]['channel_name'] . ' (' . $channel[0]['channel_address'] . ')') . EOL); + } + break; - default: - break; - } - goaway(z_root() . '/admin/channels' ); - } + default: + break; + } + goaway(z_root() . '/admin/channels'); + } - $key = (($_REQUEST['key']) ? dbesc($_REQUEST['key']) : 'channel_id'); - $dir = 'asc'; - if(array_key_exists('dir',$_REQUEST)) - $dir = ((intval($_REQUEST['dir'])) ? 'asc' : 'desc'); + $key = (($_REQUEST['key']) ? dbesc($_REQUEST['key']) : 'channel_id'); + $dir = 'asc'; + if (array_key_exists('dir', $_REQUEST)) { + $dir = ((intval($_REQUEST['dir'])) ? 'asc' : 'desc'); + } - $base = z_root() . '/admin/channels?f='; - $odir = (($dir === 'asc') ? '0' : '1'); + $base = z_root() . '/admin/channels?f='; + $odir = (($dir === 'asc') ? '0' : '1'); - /* get channels */ + /* get channels */ - $total = q("SELECT count(*) as total FROM channel where channel_removed = 0 and channel_system = 0"); - if($total) { - App::set_pager_total($total[0]['total']); - App::set_pager_itemspage(100); - } + $total = q("SELECT count(*) as total FROM channel where channel_removed = 0 and channel_system = 0"); + if ($total) { + App::set_pager_total($total[0]['total']); + App::set_pager_itemspage(100); + } - $channels = q("SELECT * from channel where channel_removed = 0 and channel_system = 0 order by $key $dir limit %d offset %d ", - intval(App::$pager['itemspage']), - intval(App::$pager['start']) - ); + $channels = q( + "SELECT * from channel where channel_removed = 0 and channel_system = 0 order by $key $dir limit %d offset %d ", + intval(App::$pager['itemspage']), + intval(App::$pager['start']) + ); - if($channels) { - for($x = 0; $x < count($channels); $x ++) { - if($channels[$x]['channel_pageflags'] & PAGE_CENSORED) - $channels[$x]['blocked'] = true; - else - $channels[$x]['blocked'] = false; + if ($channels) { + for ($x = 0; $x < count($channels); $x++) { + if ($channels[$x]['channel_pageflags'] & PAGE_CENSORED) { + $channels[$x]['blocked'] = true; + } else { + $channels[$x]['blocked'] = false; + } - if($channels[$x]['channel_pageflags'] & PAGE_ALLOWCODE) - $channels[$x]['allowcode'] = true; - else - $channels[$x]['allowcode'] = false; - - $channels[$x]['channel_link'] = z_root() . '/channel/' . $channels[$x]['channel_address']; - } - } + if ($channels[$x]['channel_pageflags'] & PAGE_ALLOWCODE) { + $channels[$x]['allowcode'] = true; + } else { + $channels[$x]['allowcode'] = false; + } - call_hooks('admin_channels',$channels); + $channels[$x]['channel_link'] = z_root() . '/channel/' . $channels[$x]['channel_address']; + } + } - $o = replace_macros(get_markup_template('admin_channels.tpl'), [ - // strings // - '$title' => t('Administration'), - '$page' => t('Channels'), - '$submit' => t('Submit'), - '$select_all' => t('select all'), - '$delete' => t('Delete'), - '$block' => t('Censor'), - '$unblock' => t('Uncensor'), - '$code' => t('Allow Code'), - '$uncode' => t('Disallow Code'), - '$h_channels' => t('Channel'), - '$base' => $base, - '$odir' => $odir, - '$th_channels' => array( - [ t('UID'), 'channel_id' ], - [ t('Name'), 'channel_name' ], - [ t('Address'), 'channel_address' ]), + call_hooks('admin_channels', $channels); - '$confirm_delete_multi' => t('Selected channels will be deleted!\n\nEverything that was posted in these channels on this site will be permanently deleted!\n\nAre you sure?'), - '$confirm_delete' => t('The channel {0} will be deleted!\n\nEverything that was posted in this channel on this site will be permanently deleted!\n\nAre you sure?'), + $o = replace_macros(get_markup_template('admin_channels.tpl'), [ + // strings // + '$title' => t('Administration'), + '$page' => t('Channels'), + '$submit' => t('Submit'), + '$select_all' => t('select all'), + '$delete' => t('Delete'), + '$block' => t('Censor'), + '$unblock' => t('Uncensor'), + '$code' => t('Allow Code'), + '$uncode' => t('Disallow Code'), + '$h_channels' => t('Channel'), + '$base' => $base, + '$odir' => $odir, + '$th_channels' => array( + [t('UID'), 'channel_id'], + [t('Name'), 'channel_name'], + [t('Address'), 'channel_address']), - '$form_security_token' => get_form_security_token('admin_channels'), + '$confirm_delete_multi' => t('Selected channels will be deleted!\n\nEverything that was posted in these channels on this site will be permanently deleted!\n\nAre you sure?'), + '$confirm_delete' => t('The channel {0} will be deleted!\n\nEverything that was posted in this channel on this site will be permanently deleted!\n\nAre you sure?'), - // values // - '$baseurl' => z_root(), - '$channels' => $channels, - ]); - $o .= paginate($a); + '$form_security_token' => get_form_security_token('admin_channels'), - return $o; - } + // values // + '$baseurl' => z_root(), + '$channels' => $channels, + ]); + $o .= paginate($a); -} \ No newline at end of file + return $o; + } +} diff --git a/Zotlabs/Module/Admin/Cover_photo.php b/Zotlabs/Module/Admin/Cover_photo.php index 3c50a2b42..d411e92bf 100644 --- a/Zotlabs/Module/Admin/Cover_photo.php +++ b/Zotlabs/Module/Admin/Cover_photo.php @@ -1,4 +1,5 @@ 0 order by imgscale asc LIMIT 1", + dbesc($image_id), + intval($channel['channel_id']) + ); + + if ($r) { + $max_thumb = intval(get_config('system', 'max_thumbnail', 1600)); + $iscaled = false; + if (intval($r[0]['height']) > $max_thumb || intval($r[0]['width']) > $max_thumb) { + $imagick_path = get_config('system', 'imagick_convert_path'); + if ($imagick_path && @file_exists($imagick_path) && intval($r[0]['os_storage'])) { + $fname = dbunescbin($r[0]['content']); + $tmp_name = $fname . '-001'; + $newsize = photo_calculate_scale(array_merge(getimagesize($fname), ['max' => $max_thumb])); + $cmd = $imagick_path . ' ' . escapeshellarg(PROJECT_BASE . '/' . $fname) . ' -resize ' . $newsize . ' ' . escapeshellarg(PROJECT_BASE . '/' . $tmp_name); + // logger('imagick thumbnail command: ' . $cmd); + for ($x = 0; $x < 4; $x++) { + exec($cmd); + if (file_exists($tmp_name)) { + break; + } + } + if (file_exists($tmp_name)) { + $base_image = $r[0]; + $gis = getimagesize($tmp_name); + logger('gis: ' . print_r($gis, true)); + $base_image['width'] = $gis[0]; + $base_image['height'] = $gis[1]; + $base_image['content'] = @file_get_contents($tmp_name); + $iscaled = true; + @unlink($tmp_name); + } + } + } + if (!$iscaled) { + $base_image = $r[0]; + $base_image['content'] = (($base_image['os_storage']) ? @file_get_contents(dbunescbin($base_image['content'])) : dbunescbin($base_image['content'])); + } + + $im = photo_factory($base_image['content'], $base_image['mimetype']); + if ($im->is_valid()) { + // We are scaling and cropping the relative pixel locations to the original photo instead of the + // scaled photo we operated on. + + // First load the scaled photo to check its size. (Should probably pass this in the post form and save + // a query.) + + $g = q( + "select width, height from photo where resource_id = '%s' and uid = %d and imgscale = 3", + dbesc($image_id), + intval($channel['channel_id']) + ); - $srcX = intval($_POST['xstart']); - $srcY = intval($_POST['ystart']); - $srcW = intval($_POST['xfinal']) - $srcX; - $srcH = intval($_POST['yfinal']) - $srcY; - - $r = q("select gender from profile where uid = %d and is_default = 1 limit 1", - intval($channel['channel_id']) - ); - if ($r) { - $profile = array_shift($r); - } - - $r = q("SELECT * FROM photo WHERE resource_id = '%s' AND uid = %d AND imgscale > 0 order by imgscale asc LIMIT 1", - dbesc($image_id), - intval($channel['channel_id']) - ); - - if ($r) { + $scaled_width = $g[0]['width']; + $scaled_height = $g[0]['height']; - $max_thumb = intval(get_config('system','max_thumbnail',1600)); - $iscaled = false; - if (intval($r[0]['height']) > $max_thumb || intval($r[0]['width']) > $max_thumb) { - $imagick_path = get_config('system','imagick_convert_path'); - if ($imagick_path && @file_exists($imagick_path) && intval($r[0]['os_storage'])) { + if ((!$scaled_width) || (!$scaled_height)) { + logger('potential divide by zero scaling cover photo'); + return; + } - $fname = dbunescbin($r[0]['content']); - $tmp_name = $fname . '-001'; - $newsize = photo_calculate_scale(array_merge(getimagesize($fname),['max' => $max_thumb])); - $cmd = $imagick_path . ' ' . escapeshellarg(PROJECT_BASE . '/' . $fname) . ' -resize ' . $newsize . ' ' . escapeshellarg(PROJECT_BASE . '/' . $tmp_name); - // logger('imagick thumbnail command: ' . $cmd); - for ($x = 0; $x < 4; $x ++) { - exec($cmd); - if (file_exists($tmp_name)) { - break; - } - } - if (file_exists($tmp_name)) { - $base_image = $r[0]; - $gis = getimagesize($tmp_name); -logger('gis: ' . print_r($gis,true)); - $base_image['width'] = $gis[0]; - $base_image['height'] = $gis[1]; - $base_image['content'] = @file_get_contents($tmp_name); - $iscaled = true; - @unlink($tmp_name); - } - } - } - if (! $iscaled) { - $base_image = $r[0]; - $base_image['content'] = (($base_image['os_storage']) ? @file_get_contents(dbunescbin($base_image['content'])) : dbunescbin($base_image['content'])); - } + // unset all other cover photos - $im = photo_factory($base_image['content'], $base_image['mimetype']); - if ($im->is_valid()) { - - // We are scaling and cropping the relative pixel locations to the original photo instead of the - // scaled photo we operated on. - - // First load the scaled photo to check its size. (Should probably pass this in the post form and save - // a query.) - - $g = q("select width, height from photo where resource_id = '%s' and uid = %d and imgscale = 3", - dbesc($image_id), - intval($channel['channel_id']) - ); - - - $scaled_width = $g[0]['width']; - $scaled_height = $g[0]['height']; - - if ((! $scaled_width) || (! $scaled_height)) { - logger('potential divide by zero scaling cover photo'); - return; - } - - // unset all other cover photos - - q("update photo set photo_usage = %d where photo_usage = %d and uid = %d", - intval(PHOTO_NORMAL), - intval(PHOTO_COVER), - intval($channel['channel_id']) - ); - - $orig_srcx = ( $base_image['width'] / $scaled_width ) * $srcX; - $orig_srcy = ( $base_image['height'] / $scaled_height ) * $srcY; - $orig_srcw = ( $srcW / $scaled_width ) * $base_image['width']; - $orig_srch = ( $srcH / $scaled_height ) * $base_image['height']; - - $im->cropImageRect(1200,435,$orig_srcx, $orig_srcy, $orig_srcw, $orig_srch); - - $aid = get_account_id(); - - $p = [ - 'aid' => 0, - 'uid' => $channel['channel_id'], - 'resource_id' => $base_image['resource_id'], - 'filename' => $base_image['filename'], - 'album' => t('Cover Photos'), - 'os_path' => $base_image['os_path'], - 'display_path' => $base_image['display_path'], - 'created' => $base_image['created'], - 'edited' => $base_image['edited'] - ]; - - $p['imgscale'] = 7; - $p['photo_usage'] = PHOTO_COVER; - - $r1 = $im->storeThumbnail($p, PHOTO_RES_COVER_1200); + q( + "update photo set photo_usage = %d where photo_usage = %d and uid = %d", + intval(PHOTO_NORMAL), + intval(PHOTO_COVER), + intval($channel['channel_id']) + ); - $im->doScaleImage(850,310); - $p['imgscale'] = 8; + $orig_srcx = ($base_image['width'] / $scaled_width) * $srcX; + $orig_srcy = ($base_image['height'] / $scaled_height) * $srcY; + $orig_srcw = ($srcW / $scaled_width) * $base_image['width']; + $orig_srch = ($srcH / $scaled_height) * $base_image['height']; - $r2 = $im->storeThumbnail($p, PHOTO_RES_COVER_850); - - $im->doScaleImage(425,160); - $p['imgscale'] = 9; - - $r3 = $im->storeThumbnail($p, PHOTO_RES_COVER_425); - - if ($r1 === false || $r2 === false || $r3 === false) { - // if one failed, delete them all so we can start over. - notice( t('Image resize failed.') . EOL ); - $x = q("delete from photo where resource_id = '%s' and uid = %d and imgscale >= 7 ", - dbesc($base_image['resource_id']), - intval($channel['channel_id']) - ); - return; - } - - } - else - notice( t('Unable to process image') . EOL); - } - - goaway(z_root() . '/admin'); - - } - - - $hash = photo_new_resource(); - $smallest = 0; - - $matches = []; - $partial = false; + $im->cropImageRect(1200, 435, $orig_srcx, $orig_srcy, $orig_srcw, $orig_srch); - if (array_key_exists('HTTP_CONTENT_RANGE',$_SERVER)) { - $pm = preg_match('/bytes (\d*)\-(\d*)\/(\d*)/',$_SERVER['HTTP_CONTENT_RANGE'],$matches); - if ($pm) { - logger('Content-Range: ' . print_r($matches,true)); - $partial = true; - } - } + $aid = get_account_id(); - if ($partial) { - $x = save_chunk($channel,$matches[1],$matches[2],$matches[3]); + $p = [ + 'aid' => 0, + 'uid' => $channel['channel_id'], + 'resource_id' => $base_image['resource_id'], + 'filename' => $base_image['filename'], + 'album' => t('Cover Photos'), + 'os_path' => $base_image['os_path'], + 'display_path' => $base_image['display_path'], + 'created' => $base_image['created'], + 'edited' => $base_image['edited'] + ]; - if ($x['partial']) { - header('Range: bytes=0-' . (($x['length']) ? $x['length'] - 1 : 0)); - json_return_and_die($x); - } - else { - header('Range: bytes=0-' . (($x['size']) ? $x['size'] - 1 : 0)); + $p['imgscale'] = 7; + $p['photo_usage'] = PHOTO_COVER; - $_FILES['userfile'] = [ - 'name' => $x['name'], - 'type' => $x['type'], - 'tmp_name' => $x['tmp_name'], - 'error' => $x['error'], - 'size' => $x['size'] - ]; - } - } - else { - if (! array_key_exists('userfile',$_FILES)) { - $_FILES['userfile'] = [ - 'name' => $_FILES['files']['name'], - 'type' => $_FILES['files']['type'], - 'tmp_name' => $_FILES['files']['tmp_name'], - 'error' => $_FILES['files']['error'], - 'size' => $_FILES['files']['size'] - ]; - } - } + $r1 = $im->storeThumbnail($p, PHOTO_RES_COVER_1200); - $res = attach_store($channel, $channel['channel_hash'], '', array('album' => t('Cover Photos'), 'hash' => $hash)); - - logger('attach_store: ' . print_r($res,true),LOGGER_DEBUG); + $im->doScaleImage(850, 310); + $p['imgscale'] = 8; - json_return_and_die([ 'message' => $hash ]); - - } - - - /** - * @brief Generate content of profile-photo view - * - * @return string - * - */ - - - function get() { - - if (! is_site_admin()) { - notice( t('Permission denied.') . EOL ); - return; - } - - $channel = get_sys_channel(); - - $newuser = false; - - if (argc() == 3 && argv(1) === 'new') - $newuser = true; + $r2 = $im->storeThumbnail($p, PHOTO_RES_COVER_850); + + $im->doScaleImage(425, 160); + $p['imgscale'] = 9; + + $r3 = $im->storeThumbnail($p, PHOTO_RES_COVER_425); + + if ($r1 === false || $r2 === false || $r3 === false) { + // if one failed, delete them all so we can start over. + notice(t('Image resize failed.') . EOL); + $x = q( + "delete from photo where resource_id = '%s' and uid = %d and imgscale >= 7 ", + dbesc($base_image['resource_id']), + intval($channel['channel_id']) + ); + return; + } + } else { + notice(t('Unable to process image') . EOL); + } + } + + goaway(z_root() . '/admin'); + } - if (argv(2) === 'reset') { - q("update photo set photo_usage = %d where photo_usage = %d and uid = %d", - intval(PHOTO_NORMAL), - intval(PHOTO_COVER), - intval($channel['channel_id']) - ); - } + $hash = photo_new_resource(); + $smallest = 0; - if (argv(2) === 'use') { - if (argc() < 4) { - notice( t('Permission denied.') . EOL ); - return; - }; - - // check_form_security_token_redirectOnErr('/cover_photo', 'cover_photo'); - - $resource_id = argv(3); - - $r = q("SELECT id, album, imgscale FROM photo WHERE uid = %d AND resource_id = '%s' and imgscale > 0 ORDER BY imgscale ASC", - intval($channel['channel_id']), - dbesc($resource_id) - ); - if (! $r) { - notice( t('Photo not available.') . EOL ); - return; - } - $havescale = false; - foreach ($r as $rr) { - if ($rr['imgscale'] == 7) { - $havescale = true; - } - } - - $r = q("SELECT content, mimetype, resource_id, os_storage FROM photo WHERE id = %d and uid = %d limit 1", - intval($r[0]['id']), - intval($channel['channel_id']) - - ); - if (! $r) { - notice( t('Photo not available.') . EOL ); - return; - } - - if (intval($r[0]['os_storage'])) { - $data = @file_get_contents(dbunescbin($r[0]['content'])); - } - else { - $data = dbunescbin($r[0]['content']); - } - - $ph = photo_factory($data, $r[0]['mimetype']); - $smallest = 0; - if ($ph->is_valid()) { - // go ahead as if we have just uploaded a new photo to crop - $i = q("select resource_id, imgscale from photo where resource_id = '%s' and uid = %d and imgscale = 0", - dbesc($r[0]['resource_id']), - intval($channel['channel_id']) - ); - - if ($i) { - $hash = $i[0]['resource_id']; - foreach ($i as $ii) { - $smallest = intval($ii['imgscale']); - } - } - } - - $this->cover_photo_crop_ui_head($ph, $hash, $smallest); - } - - - if(! array_key_exists('imagecrop',App::$data)) { - - $o .= replace_macros(get_markup_template('admin_cover_photo.tpl'), [ - '$user' => $channel['channel_address'], - '$channel_id' => $channel['channel_id'], - '$info' => t('Your cover photo may be visible to anybody on the internet'), - '$existing' => get_cover_photo($channel['channel_id'],'array',PHOTO_RES_COVER_850), - '$lbl_upfile' => t('Upload File:'), - '$lbl_profiles' => t('Select a profile:'), - '$title' => t('Change Cover Photo'), - '$submit' => t('Upload'), - '$profiles' => $profiles, - '$embedPhotos' => t('Use a photo from your albums'), - '$embedPhotosModalTitle' => t('Use a photo from your albums'), - '$embedPhotosModalCancel' => t('Cancel'), - '$embedPhotosModalOK' => t('OK'), - '$modalchooseimages' => t('Choose images to embed'), - '$modalchoosealbum' => t('Choose an album'), - '$modaldiffalbum' => t('Choose a different album'), - '$modalerrorlist' => t('Error getting album list'), - '$modalerrorlink' => t('Error getting photo link'), - '$modalerroralbum' => t('Error getting album'), - '$form_security_token' => get_form_security_token("cover_photo"), - '$select' => t('Select previously uploaded photo'), + $matches = []; + $partial = false; - ]); - - call_hooks('cover_photo_content_end', $o); - - return $o; - } - else { - $filename = App::$data['imagecrop'] . '-3'; - $resolution = 3; + if (array_key_exists('HTTP_CONTENT_RANGE', $_SERVER)) { + $pm = preg_match('/bytes (\d*)\-(\d*)\/(\d*)/', $_SERVER['HTTP_CONTENT_RANGE'], $matches); + if ($pm) { + logger('Content-Range: ' . print_r($matches, true)); + $partial = true; + } + } - $o .= replace_macros(get_markup_template('admin_cropcover.tpl'), [ - '$filename' => $filename, - '$profile' => intval($_REQUEST['profile']), - '$resource' => \App::$data['imagecrop'] . '-3', - '$image_url' => z_root() . '/photo/' . $filename, - '$title' => t('Crop Image'), - '$desc' => t('Please adjust the image cropping for optimum viewing.'), - '$form_security_token' => get_form_security_token("cover_photo"), - '$done' => t('Done Editing') - ]); - return $o; - } - } - - /* @brief Generate the UI for photo-cropping - * - * @param $a Current application - * @param $ph Photo-Factory - * @return void - * - */ - - function cover_photo_crop_ui_head($ph, $hash, $smallest){ - - $max_length = get_config('system','max_image_length', MAX_IMAGE_LENGTH); + if ($partial) { + $x = save_chunk($channel, $matches[1], $matches[2], $matches[3]); - if ($max_length > 0) { - $ph->scaleImage($max_length); - } - - $width = $ph->getWidth(); - $height = $ph->getHeight(); - - if ($width < 300 || $height < 300) { - $ph->scaleImageUp(240); - $width = $ph->getWidth(); - $height = $ph->getHeight(); - } - - - App::$data['imagecrop'] = $hash; - App::$data['imagecrop_resolution'] = $smallest; - App::$page['htmlhead'] .= replace_macros(get_markup_template('crophead.tpl'), []); - return; - } - - + if ($x['partial']) { + header('Range: bytes=0-' . (($x['length']) ? $x['length'] - 1 : 0)); + json_return_and_die($x); + } else { + header('Range: bytes=0-' . (($x['size']) ? $x['size'] - 1 : 0)); + + $_FILES['userfile'] = [ + 'name' => $x['name'], + 'type' => $x['type'], + 'tmp_name' => $x['tmp_name'], + 'error' => $x['error'], + 'size' => $x['size'] + ]; + } + } else { + if (!array_key_exists('userfile', $_FILES)) { + $_FILES['userfile'] = [ + 'name' => $_FILES['files']['name'], + 'type' => $_FILES['files']['type'], + 'tmp_name' => $_FILES['files']['tmp_name'], + 'error' => $_FILES['files']['error'], + 'size' => $_FILES['files']['size'] + ]; + } + } + + $res = attach_store($channel, $channel['channel_hash'], '', array('album' => t('Cover Photos'), 'hash' => $hash)); + + logger('attach_store: ' . print_r($res, true), LOGGER_DEBUG); + + json_return_and_die(['message' => $hash]); + } + + + /** + * @brief Generate content of profile-photo view + * + * @return string + * + */ + + + public function get() + { + + if (!is_site_admin()) { + notice(t('Permission denied.') . EOL); + return; + } + + $channel = get_sys_channel(); + + $newuser = false; + + if (argc() == 3 && argv(1) === 'new') { + $newuser = true; + } + + + if (argv(2) === 'reset') { + q( + "update photo set photo_usage = %d where photo_usage = %d and uid = %d", + intval(PHOTO_NORMAL), + intval(PHOTO_COVER), + intval($channel['channel_id']) + ); + } + + if (argv(2) === 'use') { + if (argc() < 4) { + notice(t('Permission denied.') . EOL); + return; + } + + // check_form_security_token_redirectOnErr('/cover_photo', 'cover_photo'); + + $resource_id = argv(3); + + $r = q( + "SELECT id, album, imgscale FROM photo WHERE uid = %d AND resource_id = '%s' and imgscale > 0 ORDER BY imgscale ASC", + intval($channel['channel_id']), + dbesc($resource_id) + ); + if (!$r) { + notice(t('Photo not available.') . EOL); + return; + } + $havescale = false; + foreach ($r as $rr) { + if ($rr['imgscale'] == 7) { + $havescale = true; + } + } + + $r = q( + "SELECT content, mimetype, resource_id, os_storage FROM photo WHERE id = %d and uid = %d limit 1", + intval($r[0]['id']), + intval($channel['channel_id']) + ); + if (!$r) { + notice(t('Photo not available.') . EOL); + return; + } + + if (intval($r[0]['os_storage'])) { + $data = @file_get_contents(dbunescbin($r[0]['content'])); + } else { + $data = dbunescbin($r[0]['content']); + } + + $ph = photo_factory($data, $r[0]['mimetype']); + $smallest = 0; + if ($ph->is_valid()) { + // go ahead as if we have just uploaded a new photo to crop + $i = q( + "select resource_id, imgscale from photo where resource_id = '%s' and uid = %d and imgscale = 0", + dbesc($r[0]['resource_id']), + intval($channel['channel_id']) + ); + + if ($i) { + $hash = $i[0]['resource_id']; + foreach ($i as $ii) { + $smallest = intval($ii['imgscale']); + } + } + } + + $this->cover_photo_crop_ui_head($ph, $hash, $smallest); + } + + + if (!array_key_exists('imagecrop', App::$data)) { + $o .= replace_macros(get_markup_template('admin_cover_photo.tpl'), [ + '$user' => $channel['channel_address'], + '$channel_id' => $channel['channel_id'], + '$info' => t('Your cover photo may be visible to anybody on the internet'), + '$existing' => get_cover_photo($channel['channel_id'], 'array', PHOTO_RES_COVER_850), + '$lbl_upfile' => t('Upload File:'), + '$lbl_profiles' => t('Select a profile:'), + '$title' => t('Change Cover Photo'), + '$submit' => t('Upload'), + '$profiles' => $profiles, + '$embedPhotos' => t('Use a photo from your albums'), + '$embedPhotosModalTitle' => t('Use a photo from your albums'), + '$embedPhotosModalCancel' => t('Cancel'), + '$embedPhotosModalOK' => t('OK'), + '$modalchooseimages' => t('Choose images to embed'), + '$modalchoosealbum' => t('Choose an album'), + '$modaldiffalbum' => t('Choose a different album'), + '$modalerrorlist' => t('Error getting album list'), + '$modalerrorlink' => t('Error getting photo link'), + '$modalerroralbum' => t('Error getting album'), + '$form_security_token' => get_form_security_token("cover_photo"), + '$select' => t('Select previously uploaded photo'), + + ]); + + call_hooks('cover_photo_content_end', $o); + + return $o; + } else { + $filename = App::$data['imagecrop'] . '-3'; + $resolution = 3; + + $o .= replace_macros(get_markup_template('admin_cropcover.tpl'), [ + '$filename' => $filename, + '$profile' => intval($_REQUEST['profile']), + '$resource' => App::$data['imagecrop'] . '-3', + '$image_url' => z_root() . '/photo/' . $filename, + '$title' => t('Crop Image'), + '$desc' => t('Please adjust the image cropping for optimum viewing.'), + '$form_security_token' => get_form_security_token("cover_photo"), + '$done' => t('Done Editing') + ]); + return $o; + } + } + + /* @brief Generate the UI for photo-cropping + * + * @param $a Current application + * @param $ph Photo-Factory + * @return void + * + */ + + public function cover_photo_crop_ui_head($ph, $hash, $smallest) + { + + $max_length = get_config('system', 'max_image_length', MAX_IMAGE_LENGTH); + + if ($max_length > 0) { + $ph->scaleImage($max_length); + } + + $width = $ph->getWidth(); + $height = $ph->getHeight(); + + if ($width < 300 || $height < 300) { + $ph->scaleImageUp(240); + $width = $ph->getWidth(); + $height = $ph->getHeight(); + } + + + App::$data['imagecrop'] = $hash; + App::$data['imagecrop_resolution'] = $smallest; + App::$page['htmlhead'] .= replace_macros(get_markup_template('crophead.tpl'), []); + return; + } } diff --git a/Zotlabs/Module/Admin/Dbsync.php b/Zotlabs/Module/Admin/Dbsync.php index 75be1b90f..fae8c09cb 100644 --- a/Zotlabs/Module/Admin/Dbsync.php +++ b/Zotlabs/Module/Admin/Dbsync.php @@ -2,112 +2,107 @@ namespace Zotlabs\Module\Admin; +class Dbsync +{ -class Dbsync { - + public function get() + { + $o = ''; - function get() { - $o = ''; - - if(argc() > 3 && intval(argv(3)) && argv(2) === 'mark') { - // remove the old style config if it exists - del_config('database', 'update_r' . intval(argv(3))); - set_config('database', '_' . intval(argv(3)), 'success'); - if(intval(get_config('system','db_version')) < intval(argv(3))) - set_config('system','db_version',intval(argv(3))); - info( t('Update has been marked successful') . EOL); - goaway(z_root() . '/admin/dbsync'); - } + if (argc() > 3 && intval(argv(3)) && argv(2) === 'mark') { + // remove the old style config if it exists + del_config('database', 'update_r' . intval(argv(3))); + set_config('database', '_' . intval(argv(3)), 'success'); + if (intval(get_config('system', 'db_version')) < intval(argv(3))) { + set_config('system', 'db_version', intval(argv(3))); + } + info(t('Update has been marked successful') . EOL); + goaway(z_root() . '/admin/dbsync'); + } - if(argc() > 3 && intval(argv(3)) && argv(2) === 'verify') { + if (argc() > 3 && intval(argv(3)) && argv(2) === 'verify') { + $s = '_' . intval(argv(3)); + $cls = '\\Zotlabs\Update\\' . $s; + if (class_exists($cls)) { + $c = new $cls(); + if (method_exists($c, 'verify')) { + $retval = $c->verify(); + if ($retval === UPDATE_FAILED) { + $o .= sprintf(t('Verification of update %s failed. Check system logs.'), $s); + } elseif ($retval === UPDATE_SUCCESS) { + $o .= sprintf(t('Update %s was successfully applied.'), $s); + set_config('database', $s, 'success'); + } else { + $o .= sprintf(t('Verifying update %s did not return a status. Unknown if it succeeded.'), $s); + } + } else { + $o .= sprintf(t('Update %s does not contain a verification function.'), $s); + } + } else { + $o .= sprintf(t('Update function %s could not be found.'), $s); + } - $s = '_' . intval(argv(3)); - $cls = '\\Zotlabs\Update\\' . $s ; - if(class_exists($cls)) { - $c = new $cls(); - if(method_exists($c,'verify')) { - $retval = $c->verify(); - if($retval === UPDATE_FAILED) { - $o .= sprintf( t('Verification of update %s failed. Check system logs.'), $s); - } - elseif($retval === UPDATE_SUCCESS) { - $o .= sprintf( t('Update %s was successfully applied.'), $s); - set_config('database',$s, 'success'); - } - else - $o .= sprintf( t('Verifying update %s did not return a status. Unknown if it succeeded.'), $s); - } - else { - $o .= sprintf( t('Update %s does not contain a verification function.'), $s ); - } - } - else - $o .= sprintf( t('Update function %s could not be found.'), $s); - - return $o; + return $o; + // remove the old style config if it exists + del_config('database', 'update_r' . intval(argv(3))); + set_config('database', '_' . intval(argv(3)), 'success'); + if (intval(get_config('system', 'db_version')) < intval(argv(3))) { + set_config('system', 'db_version', intval(argv(3))); + } + info(t('Update has been marked successful') . EOL); + goaway(z_root() . '/admin/dbsync'); + } + if (argc() > 2 && intval(argv(2))) { + $x = intval(argv(2)); + $s = '_' . $x; + $cls = '\\Zotlabs\Update\\' . $s; + if (class_exists($cls)) { + $c = new $cls(); + $retval = $c->run(); + if ($retval === UPDATE_FAILED) { + $o .= sprintf(t('Executing update procedure %s failed. Check system logs.'), $s); + } elseif ($retval === UPDATE_SUCCESS) { + $o .= sprintf(t('Update %s was successfully applied.'), $s); + set_config('database', $s, 'success'); + } else { + $o .= sprintf(t('Update %s did not return a status. It cannot be determined if it was successful.'), $s); + } + } else { + $o .= sprintf(t('Update function %s could not be found.'), $s); + } + return $o; + } - // remove the old style config if it exists - del_config('database', 'update_r' . intval(argv(3))); - set_config('database', '_' . intval(argv(3)), 'success'); - if(intval(get_config('system','db_version')) < intval(argv(3))) - set_config('system','db_version',intval(argv(3))); - info( t('Update has been marked successful') . EOL); - goaway(z_root() . '/admin/dbsync'); - } + $failed = []; + $r = q("select * from config where cat = 'database' "); + if (count($r)) { + foreach ($r as $rr) { + $upd = intval(substr($rr['k'], -4)); + if ($rr['v'] === 'success') { + continue; + } + $failed[] = $upd; + } + } + if (count($failed)) { + $o = replace_macros(get_markup_template('failed_updates.tpl'), array( + '$base' => z_root(), + '$banner' => t('Failed Updates'), + '$desc' => '', + '$mark' => t('Mark success (if update was manually applied)'), + '$verify' => t('Attempt to verify this update if a verification procedure exists'), + '$apply' => t('Attempt to execute this update step automatically'), + '$failed' => $failed + )); + } else { + return '

        ' . t('No failed updates.') . '

        '; + } + return $o; + } +} - if(argc() > 2 && intval(argv(2))) { - $x = intval(argv(2)); - $s = '_' . $x; - $cls = '\\Zotlabs\Update\\' . $s ; - if(class_exists($cls)) { - $c = new $cls(); - $retval = $c->run(); - if($retval === UPDATE_FAILED) { - $o .= sprintf( t('Executing update procedure %s failed. Check system logs.'), $s); - } - elseif($retval === UPDATE_SUCCESS) { - $o .= sprintf( t('Update %s was successfully applied.'), $s); - set_config('database',$s, 'success'); - } - else - $o .= sprintf( t('Update %s did not return a status. It cannot be determined if it was successful.'), $s); - } - else - $o .= sprintf( t('Update function %s could not be found.'), $s); - - return $o; - } - - $failed = []; - $r = q("select * from config where cat = 'database' "); - if(count($r)) { - foreach($r as $rr) { - $upd = intval(substr($rr['k'],-4)); - if($rr['v'] === 'success') - continue; - $failed[] = $upd; - } - } - if(count($failed)) { - $o = replace_macros(get_markup_template('failed_updates.tpl'),array( - '$base' => z_root(), - '$banner' => t('Failed Updates'), - '$desc' => '', - '$mark' => t('Mark success (if update was manually applied)'), - '$verify' => t('Attempt to verify this update if a verification procedure exists'), - '$apply' => t('Attempt to execute this update step automatically'), - '$failed' => $failed - )); - } - else { - return '

        ' . t('No failed updates.') . '

        '; - } - - return $o; - } -} \ No newline at end of file diff --git a/Zotlabs/Module/Admin/Logs.php b/Zotlabs/Module/Admin/Logs.php index c83fc6a9a..e41b80675 100644 --- a/Zotlabs/Module/Admin/Logs.php +++ b/Zotlabs/Module/Admin/Logs.php @@ -2,100 +2,97 @@ namespace Zotlabs\Module\Admin; - -class Logs { +class Logs +{ - - /** - * @brief POST handler for logs admin page. - * - */ + /** + * @brief POST handler for logs admin page. + * + */ - function post() { - if (x($_POST, 'page_logs')) { - check_form_security_token_redirectOnErr('/admin/logs', 'admin_logs'); - - $logfile = ((x($_POST,'logfile')) ? notags(trim($_POST['logfile'])) : ''); - $debugging = ((x($_POST,'debugging')) ? true : false); - $loglevel = ((x($_POST,'loglevel')) ? intval(trim($_POST['loglevel'])) : 0); - - set_config('system','logfile', $logfile); - set_config('system','debugging', $debugging); - set_config('system','loglevel', $loglevel); - } - - info( t('Log settings updated.') ); - goaway(z_root() . '/admin/logs' ); - } - - /** - * @brief Logs admin page. - * - * @return string - */ + public function post() + { + if (x($_POST, 'page_logs')) { + check_form_security_token_redirectOnErr('/admin/logs', 'admin_logs'); - function get() { - - $log_choices = Array( - LOGGER_NORMAL => 'Normal', - LOGGER_TRACE => 'Trace', - LOGGER_DEBUG => 'Debug', - LOGGER_DATA => 'Data', - LOGGER_ALL => 'All' - ); - - $t = get_markup_template('admin_logs.tpl'); - - $f = get_config('system', 'logfile'); - - $data = ''; - - if(!file_exists($f)) { - $data = t("Error trying to open $f log file.\r\n
        Check to see if file $f exist and is + $logfile = ((x($_POST, 'logfile')) ? notags(trim($_POST['logfile'])) : ''); + $debugging = ((x($_POST, 'debugging')) ? true : false); + $loglevel = ((x($_POST, 'loglevel')) ? intval(trim($_POST['loglevel'])) : 0); + + set_config('system', 'logfile', $logfile); + set_config('system', 'debugging', $debugging); + set_config('system', 'loglevel', $loglevel); + } + + info(t('Log settings updated.')); + goaway(z_root() . '/admin/logs'); + } + + /** + * @brief Logs admin page. + * + * @return string + */ + + public function get() + { + + $log_choices = array( + LOGGER_NORMAL => 'Normal', + LOGGER_TRACE => 'Trace', + LOGGER_DEBUG => 'Debug', + LOGGER_DATA => 'Data', + LOGGER_ALL => 'All' + ); + + $t = get_markup_template('admin_logs.tpl'); + + $f = get_config('system', 'logfile'); + + $data = ''; + + if (!file_exists($f)) { + $data = t("Error trying to open $f log file.\r\n
        Check to see if file $f exist and is readable."); - } - else { - $fp = fopen($f, 'r'); - if(!$fp) { - $data = t("Couldn't open $f log file.\r\n
        Check to see if file $f is readable."); - } - else { - $fstat = fstat($fp); - $size = $fstat['size']; - if($size != 0) - { - if($size > 5000000 || $size < 0) - $size = 5000000; - $seek = fseek($fp,0-$size,SEEK_END); - if($seek === 0) { - $data = escape_tags(fread($fp,$size)); - while(! feof($fp)) - $data .= escape_tags(fread($fp,4096)); - } - } - fclose($fp); - } - } - - return replace_macros($t, array( - '$title' => t('Administration'), - '$page' => t('Logs'), - '$submit' => t('Submit'), - '$clear' => t('Clear'), - '$data' => $data, - '$baseurl' => z_root(), - '$logname' => get_config('system','logfile'), - - // name, label, value, help string, extra data... - '$debugging' => array('debugging', t("Debugging"),get_config('system','debugging'), ""), - '$logfile' => array('logfile', t("Log file"), get_config('system','logfile'), t("Must be writable by web server. Relative to your top-level webserver directory.")), - '$loglevel' => array('loglevel', t("Log level"), get_config('system','loglevel'), "", $log_choices), - - '$form_security_token' => get_form_security_token('admin_logs'), - )); - } - + } else { + $fp = fopen($f, 'r'); + if (!$fp) { + $data = t("Couldn't open $f log file.\r\n
        Check to see if file $f is readable."); + } else { + $fstat = fstat($fp); + $size = $fstat['size']; + if ($size != 0) { + if ($size > 5000000 || $size < 0) { + $size = 5000000; + } + $seek = fseek($fp, 0 - $size, SEEK_END); + if ($seek === 0) { + $data = escape_tags(fread($fp, $size)); + while (!feof($fp)) { + $data .= escape_tags(fread($fp, 4096)); + } + } + } + fclose($fp); + } + } + return replace_macros($t, array( + '$title' => t('Administration'), + '$page' => t('Logs'), + '$submit' => t('Submit'), + '$clear' => t('Clear'), + '$data' => $data, + '$baseurl' => z_root(), + '$logname' => get_config('system', 'logfile'), -} \ No newline at end of file + // name, label, value, help string, extra data... + '$debugging' => array('debugging', t("Debugging"), get_config('system', 'debugging'), ""), + '$logfile' => array('logfile', t("Log file"), get_config('system', 'logfile'), t("Must be writable by web server. Relative to your top-level webserver directory.")), + '$loglevel' => array('loglevel', t("Log level"), get_config('system', 'loglevel'), "", $log_choices), + + '$form_security_token' => get_form_security_token('admin_logs'), + )); + } +} diff --git a/Zotlabs/Module/Admin/Profile_photo.php b/Zotlabs/Module/Admin/Profile_photo.php index c2e3d28a6..bfe966ddb 100644 --- a/Zotlabs/Module/Admin/Profile_photo.php +++ b/Zotlabs/Module/Admin/Profile_photo.php @@ -1,4 +1,5 @@ is_valid()) { - - $im->cropImage(300,$srcX,$srcY,$srcW,$srcH); - - $aid = 0; - - $p = [ - 'aid' => $aid, - 'uid' => $channel['channel_id'], - 'resource_id' => $base_image['resource_id'], - 'filename' => $base_image['filename'], - 'album' => t('Profile Photos'), - 'os_path' => $base_image['os_path'], - 'display_path' => $base_image['display_path'], - 'created' => $base_image['created'], - 'edited' => $base_image['edited'] - ]; - - $p['imgscale'] = PHOTO_RES_PROFILE_300; - $p['photo_usage'] = (($is_default_profile) ? PHOTO_PROFILE : PHOTO_NORMAL); - - $r1 = $im->storeThumbnail($p, PHOTO_RES_PROFILE_300); - - $im->scaleImage(80); - $p['imgscale'] = PHOTO_RES_PROFILE_80; - - $r2 = $im->storeThumbnail($p, PHOTO_RES_PROFILE_80); - - $im->scaleImage(48); - $p['imgscale'] = PHOTO_RES_PROFILE_48; - - $r3 = $im->storeThumbnail($p, PHOTO_RES_PROFILE_48); - - if ($r1 === false || $r2 === false || $r3 === false) { - // if one failed, delete them all so we can start over. - notice( t('Image resize failed.') . EOL ); - $x = q("delete from photo where resource_id = '%s' and uid = %d and imgscale in ( %d, %d, %d ) ", - dbesc($base_image['resource_id']), - $channel['channel_id'], - intval(PHOTO_RES_PROFILE_300), - intval(PHOTO_RES_PROFILE_80), - intval(PHOTO_RES_PROFILE_48) - ); - return; - } +class Profile_photo +{ - $r = q("UPDATE photo SET photo_usage = %d WHERE photo_usage = %d + /* @brief Initalize the profile-photo edit view + * + * @return void + * + */ + + public function init() + { + + if (!is_site_admin()) { + return; + } + + $channel = get_sys_channel(); + Libprofile::load($channel['channel_address']); + } + + /* @brief Evaluate posted values + * + * @param $a Current application + * @return void + * + */ + + public function post() + { + + if (!is_site_admin()) { + return; + } + + $channel = get_sys_channel(); + + check_form_security_token_redirectOnErr('/profile_photo', 'profile_photo'); + + if ((array_key_exists('cropfinal', $_POST)) && (intval($_POST['cropfinal']) == 1)) { + // logger('crop: ' . print_r($_POST,true)); + + // phase 2 - we have finished cropping + + if (argc() != 3) { + notice(t('Image uploaded but image cropping failed.') . EOL); + return; + } + + $image_id = argv(2); + + if (substr($image_id, -2, 1) == '-') { + $scale = substr($image_id, -1, 1); + $image_id = substr($image_id, 0, -2); + } + + // unless proven otherwise + $is_default_profile = 1; + + if ($_REQUEST['profile']) { + $r = q( + "select id, profile_guid, is_default, gender from profile where id = %d and uid = %d limit 1", + intval($_REQUEST['profile']), + intval($channel['channel_id']) + ); + if ($r) { + $profile = array_shift($r); + if (!intval($profile['is_default'])) { + $is_default_profile = 0; + } + } + } + + $srcX = intval($_POST['xstart']); + $srcY = intval($_POST['ystart']); + $srcW = intval($_POST['xfinal']) - $srcX; + $srcH = intval($_POST['yfinal']) - $srcY; + + $r = q( + "SELECT * FROM photo WHERE resource_id = '%s' AND uid = %d AND imgscale = %d LIMIT 1", + dbesc($image_id), + dbesc($channel['channel_id']), + intval($scale) + ); + if ($r) { + $base_image = array_shift($r); + $base_image['content'] = (($base_image['os_storage']) ? @file_get_contents(dbunescbin($base_image['content'])) : dbunescbin($base_image['content'])); + + $im = photo_factory($base_image['content'], $base_image['mimetype']); + if ($im->is_valid()) { + $im->cropImage(300, $srcX, $srcY, $srcW, $srcH); + + $aid = 0; + + $p = [ + 'aid' => $aid, + 'uid' => $channel['channel_id'], + 'resource_id' => $base_image['resource_id'], + 'filename' => $base_image['filename'], + 'album' => t('Profile Photos'), + 'os_path' => $base_image['os_path'], + 'display_path' => $base_image['display_path'], + 'created' => $base_image['created'], + 'edited' => $base_image['edited'] + ]; + + $p['imgscale'] = PHOTO_RES_PROFILE_300; + $p['photo_usage'] = (($is_default_profile) ? PHOTO_PROFILE : PHOTO_NORMAL); + + $r1 = $im->storeThumbnail($p, PHOTO_RES_PROFILE_300); + + $im->scaleImage(80); + $p['imgscale'] = PHOTO_RES_PROFILE_80; + + $r2 = $im->storeThumbnail($p, PHOTO_RES_PROFILE_80); + + $im->scaleImage(48); + $p['imgscale'] = PHOTO_RES_PROFILE_48; + + $r3 = $im->storeThumbnail($p, PHOTO_RES_PROFILE_48); + + if ($r1 === false || $r2 === false || $r3 === false) { + // if one failed, delete them all so we can start over. + notice(t('Image resize failed.') . EOL); + $x = q( + "delete from photo where resource_id = '%s' and uid = %d and imgscale in ( %d, %d, %d ) ", + dbesc($base_image['resource_id']), + $channel['channel_id'], + intval(PHOTO_RES_PROFILE_300), + intval(PHOTO_RES_PROFILE_80), + intval(PHOTO_RES_PROFILE_48) + ); + return; + } + + + $r = q( + "UPDATE photo SET photo_usage = %d WHERE photo_usage = %d AND resource_id != '%s' AND uid = %d", - intval(PHOTO_NORMAL), - intval(PHOTO_PROFILE), - dbesc($base_image['resource_id']), - intval($channel['channel_id']) - ); - - // We'll set the updated profile-photo timestamp even if it isn't the default profile, - // so that browsers will do a cache update unconditionally - // Also set links back to site-specific profile photo url in case it was - // changed to a generic URL by a clone operation. Otherwise the new photo may - // not get pushed to other sites correctly. - - $r = q("UPDATE xchan set xchan_photo_mimetype = '%s', xchan_photo_date = '%s', xchan_photo_l = '%s', xchan_photo_m = '%s', xchan_photo_s = '%s' + intval(PHOTO_NORMAL), + intval(PHOTO_PROFILE), + dbesc($base_image['resource_id']), + intval($channel['channel_id']) + ); + + // We'll set the updated profile-photo timestamp even if it isn't the default profile, + // so that browsers will do a cache update unconditionally + // Also set links back to site-specific profile photo url in case it was + // changed to a generic URL by a clone operation. Otherwise the new photo may + // not get pushed to other sites correctly. + + $r = q( + "UPDATE xchan set xchan_photo_mimetype = '%s', xchan_photo_date = '%s', xchan_photo_l = '%s', xchan_photo_m = '%s', xchan_photo_s = '%s' where xchan_hash = '%s'", - dbesc($im->getType()), - dbesc(datetime_convert()), - dbesc(z_root() . '/photo/profile/l/' . $channel['channel_id']), - dbesc(z_root() . '/photo/profile/m/' . $channel['channel_id']), - dbesc(z_root() . '/photo/profile/s/' . $channel['channel_id']), - dbesc($channel['xchan_hash']) - ); + dbesc($im->getType()), + dbesc(datetime_convert()), + dbesc(z_root() . '/photo/profile/l/' . $channel['channel_id']), + dbesc(z_root() . '/photo/profile/m/' . $channel['channel_id']), + dbesc(z_root() . '/photo/profile/s/' . $channel['channel_id']), + dbesc($channel['xchan_hash']) + ); - // Similarly, tell the nav bar to bypass the cache and update the avatar image. - $_SESSION['reload_avatar'] = true; + // Similarly, tell the nav bar to bypass the cache and update the avatar image. + $_SESSION['reload_avatar'] = true; - Config::Set('system','site_icon_url',z_root() . '/photo/profile/m/' . $channel['channel_id']); + Config::Set('system', 'site_icon_url', z_root() . '/photo/profile/m/' . $channel['channel_id']); - info( t('Shift-reload the page or clear browser cache if the new photo does not display immediately.') . EOL); - - } - else { - notice( t('Unable to process image') . EOL); - } - } - - goaway(z_root() . '/admin'); - } - - // A new photo was uploaded. Store it and save some important details - // in App::$data for use in the cropping function - - - $hash = photo_new_resource(); - $importing = false; - $smallest = 0; - + info(t('Shift-reload the page or clear browser cache if the new photo does not display immediately.') . EOL); + } else { + notice(t('Unable to process image') . EOL); + } + } - if ($_REQUEST['importfile']) { - $hash = $_REQUEST['importfile']; - $importing = true; - } - else { + goaway(z_root() . '/admin'); + } - $matches = []; - $partial = false; + // A new photo was uploaded. Store it and save some important details + // in App::$data for use in the cropping function - if (array_key_exists('HTTP_CONTENT_RANGE',$_SERVER)) { - $pm = preg_match('/bytes (\d*)\-(\d*)\/(\d*)/',$_SERVER['HTTP_CONTENT_RANGE'],$matches); - if ($pm) { - logger('Content-Range: ' . print_r($matches,true), LOGGER_DEBUG); - $partial = true; - } - } - if ($partial) { - $x = save_chunk($channel,$matches[1],$matches[2],$matches[3]); + $hash = photo_new_resource(); + $importing = false; + $smallest = 0; - if ($x['partial']) { - header('Range: bytes=0-' . (($x['length']) ? $x['length'] - 1 : 0)); - json_return_and_die($x); - } - else { - header('Range: bytes=0-' . (($x['size']) ? $x['size'] - 1 : 0)); - $_FILES['userfile'] = [ - 'name' => $x['name'], - 'type' => $x['type'], - 'tmp_name' => $x['tmp_name'], - 'error' => $x['error'], - 'size' => $x['size'] - ]; - } - } - else { - if (! array_key_exists('userfile',$_FILES)) { - $_FILES['userfile'] = [ - 'name' => $_FILES['files']['name'], - 'type' => $_FILES['files']['type'], - 'tmp_name' => $_FILES['files']['tmp_name'], - 'error' => $_FILES['files']['error'], - 'size' => $_FILES['files']['size'] - ]; - } - } + if ($_REQUEST['importfile']) { + $hash = $_REQUEST['importfile']; + $importing = true; + } else { + $matches = []; + $partial = false; - $res = attach_store($channel, $channel['channel_hash'], '', array('album' => t('Profile Photos'), 'hash' => $hash)); - - logger('attach_store: ' . print_r($res,true), LOGGER_DEBUG); + if (array_key_exists('HTTP_CONTENT_RANGE', $_SERVER)) { + $pm = preg_match('/bytes (\d*)\-(\d*)\/(\d*)/', $_SERVER['HTTP_CONTENT_RANGE'], $matches); + if ($pm) { + logger('Content-Range: ' . print_r($matches, true), LOGGER_DEBUG); + $partial = true; + } + } - json_return_and_die([ 'message' => $hash ]); - } - - if (($res && intval($res['data']['is_photo'])) || $importing) { - $i = q("select * from photo where resource_id = '%s' and uid = %d order by imgscale", - dbesc($hash), - intval($channel['channel_hash']) - ); - - if (! $i) { - notice( t('Image upload failed.') . EOL ); - return; - } - $os_storage = false; - - foreach ($i as $ii) { - if (intval($ii['imgscale']) < PHOTO_RES_640) { - $smallest = intval($ii['imgscale']); - $os_storage = intval($ii['os_storage']); - $imagedata = $ii['content']; - $filetype = $ii['mimetype']; - } - } - } - - $imagedata = (($os_storage) ? @file_get_contents(dbunescbin($imagedata)) : dbunescbin($imagedata)); - $ph = photo_factory($imagedata, $filetype); - - if (! $ph->is_valid()) { - notice( t('Unable to process image.') . EOL ); - return; - } - - return $this->profile_photo_crop_ui_head($ph, $hash, $smallest); + if ($partial) { + $x = save_chunk($channel, $matches[1], $matches[2], $matches[3]); - // This will "fall through" to the get() method, and since - // App::$data['imagecrop'] is set, it will proceed to cropping - // rather than present the upload form - } - - - /* @brief Generate content of profile-photo view - * - * @return void - * - */ - - - function get() { - - if (! is_site_admin()) { - notice( t('Permission denied.') . EOL ); - return; - } - - $channel = get_sys_channel(); - $pf = 0; - $newuser = false; - - if (argc() == 3 && argv(2) === 'new') { - $newuser = true; - } + if ($x['partial']) { + header('Range: bytes=0-' . (($x['length']) ? $x['length'] - 1 : 0)); + json_return_and_die($x); + } else { + header('Range: bytes=0-' . (($x['size']) ? $x['size'] - 1 : 0)); - if (argv(2) === 'reset') { - Config::Delete('system','site_icon_url'); - } + $_FILES['userfile'] = [ + 'name' => $x['name'], + 'type' => $x['type'], + 'tmp_name' => $x['tmp_name'], + 'error' => $x['error'], + 'size' => $x['size'] + ]; + } + } else { + if (!array_key_exists('userfile', $_FILES)) { + $_FILES['userfile'] = [ + 'name' => $_FILES['files']['name'], + 'type' => $_FILES['files']['type'], + 'tmp_name' => $_FILES['files']['tmp_name'], + 'error' => $_FILES['files']['error'], + 'size' => $_FILES['files']['size'] + ]; + } + } - if (argv(2) === 'use') { - if (argc() < 4) { - notice( t('Permission denied.') . EOL ); - return; - }; - - $resource_id = argv(3); - - $pf = (($_REQUEST['pf']) ? intval($_REQUEST['pf']) : 0); + $res = attach_store($channel, $channel['channel_hash'], '', array('album' => t('Profile Photos'), 'hash' => $hash)); - $c = q("select id, is_default from profile where uid = %d", - intval($channel['channel_id']) - ); + logger('attach_store: ' . print_r($res, true), LOGGER_DEBUG); - $multi_profiles = true; + json_return_and_die(['message' => $hash]); + } - if (($c) && (count($c) === 1) && (intval($c[0]['is_default']))) { - $_REQUEST['profile'] = $c[0]['id']; - $multi_profiles = false; - } - else { - $_REQUEST['profile'] = $pf; - } + if (($res && intval($res['data']['is_photo'])) || $importing) { + $i = q( + "select * from photo where resource_id = '%s' and uid = %d order by imgscale", + dbesc($hash), + intval($channel['channel_hash']) + ); - $r = q("SELECT id, album, imgscale FROM photo WHERE uid = %d AND resource_id = '%s' ORDER BY imgscale ASC", - intval($channel['channel_id']), - dbesc($resource_id) - ); - if (! $r) { - notice( t('Photo not available.') . EOL ); - return; - } - $havescale = false; - foreach ($r as $rr) { - if ($rr['imgscale'] == PHOTO_RES_PROFILE_80) { - $havescale = true; - } - } - - // set an already loaded and cropped photo as profile photo - - if ($havescale) { - // unset any existing profile photos - $r = q("UPDATE photo SET photo_usage = %d WHERE photo_usage = %d AND uid = %d", - intval(PHOTO_NORMAL), - intval(PHOTO_PROFILE), - intval($channel['channel_id']) - ); - - $r = q("UPDATE photo SET photo_usage = %d WHERE uid = %d AND resource_id = '%s'", - intval(PHOTO_PROFILE), - intval($channel['channel_id']), - dbesc($resource_id) - ); - - $r = q("UPDATE xchan set xchan_photo_date = '%s' where xchan_hash = '%s'", - dbesc(datetime_convert()), - dbesc($channel['xchan_hash']) - ); - - goaway(z_root() . '/admin'); - } - - $r = q("SELECT content, mimetype, resource_id, os_storage FROM photo WHERE id = %d and uid = %d limit 1", - intval($r[0]['id']), - intval($channel['channel_id']) - - ); - if (! $r) { - notice( t('Photo not available.') . EOL ); - return; - } - - if (intval($r[0]['os_storage'])) { - $data = @file_get_contents(dbunescbin($r[0]['content'])); - } - else { - $data = dbunescbin($r[0]['content']); - } - - $ph = photo_factory($data, $r[0]['mimetype']); - $smallest = 0; - if ($ph->is_valid()) { - // go ahead as if we have just uploaded a new photo to crop - $i = q("select resource_id, imgscale from photo where resource_id = '%s' and uid = %d order by imgscale", - dbesc($r[0]['resource_id']), - intval($channel['channel_id']) - ); - - if ($i) { - $hash = $i[0]['resource_id']; - foreach ($i as $ii) { - if (intval($ii['imgscale']) < PHOTO_RES_640) { - $smallest = intval($ii['imgscale']); - } - } - } - } - - if ($multi_profiles) { - App::$data['importfile'] = $resource_id; - } - else { - $this->profile_photo_crop_ui_head($ph, $hash, $smallest); - } + if (!$i) { + notice(t('Image upload failed.') . EOL); + return; + } + $os_storage = false; - // falls through with App::$data['imagecrop'] set so we go straight to the cropping section + foreach ($i as $ii) { + if (intval($ii['imgscale']) < PHOTO_RES_640) { + $smallest = intval($ii['imgscale']); + $os_storage = intval($ii['os_storage']); + $imagedata = $ii['content']; + $filetype = $ii['mimetype']; + } + } + } - } - - // present an upload form + $imagedata = (($os_storage) ? @file_get_contents(dbunescbin($imagedata)) : dbunescbin($imagedata)); + $ph = photo_factory($imagedata, $filetype); - $profiles = q("select id, profile_name as name, is_default from profile where uid = %d order by id asc", - intval($channel['channel_id']) - ); + if (!$ph->is_valid()) { + notice(t('Unable to process image.') . EOL); + return; + } - if ($profiles) { - for ($x = 0; $x < count($profiles); $x ++) { - $profiles[$x]['selected'] = false; - if ($pf && $profiles[$x]['id'] == $pf) { - $profiles[$x]['selected'] = true; - } - if ((! $pf) && $profiles[$x]['is_default']) { - $profiles[$x]['selected'] = true; - } - } - } + return $this->profile_photo_crop_ui_head($ph, $hash, $smallest); - $importing = ((array_key_exists('importfile',App::$data)) ? true : false); - - if (! array_key_exists('imagecrop', App::$data)) { - - $tpl = get_markup_template('admin_profile_photo.tpl'); - - $o .= replace_macros($tpl, [ - '$user' => $channel['channel_address'], - '$channel_id' => $channel['channel_id'], - '$info' => ((count($profiles) > 1) ? t('Your default profile photo is visible to anybody on the internet. Profile photos for alternate profiles will inherit the permissions of the profile') : t('Your site photo is visible to anybody on the internet and may be distributed to other websites.')), - '$importfile' => (($importing) ? App::$data['importfile'] : ''), - '$lbl_upfile' => t('Upload File:'), - '$lbl_profiles' => t('Select a profile:'), - '$title' => (($importing) ? t('Use Photo for Site Logo') : t('Change Site Logo')), - '$submit' => (($importing) ? t('Use') : t('Upload')), - '$profiles' => $profiles, - '$single' => ((count($profiles) == 1) ? true : false), - '$profile0' => $profiles[0], - '$embedPhotos' => t('Use a photo from your albums'), - '$embedPhotosModalTitle' => t('Use a photo from your albums'), - '$embedPhotosModalCancel' => t('Cancel'), - '$embedPhotosModalOK' => t('OK'), - '$modalchooseimages' => t('Choose images to embed'), - '$modalchoosealbum' => t('Choose an album'), - '$modaldiffalbum' => t('Choose a different album'), - '$modalerrorlist' => t('Error getting album list'), - '$modalerrorlink' => t('Error getting photo link'), - '$modalerroralbum' => t('Error getting album'), - '$form_security_token' => get_form_security_token("profile_photo"), - '$select' => t('Select previously uploaded photo'), - ]); - - call_hooks('profile_photo_content_end', $o); - return $o; - } - else { + // This will "fall through" to the get() method, and since + // App::$data['imagecrop'] is set, it will proceed to cropping + // rather than present the upload form + } - // present a cropping form - $filename = App::$data['imagecrop'] . '-' . App::$data['imagecrop_resolution']; - $resolution = App::$data['imagecrop_resolution']; - $o .= replace_macros(get_markup_template('admin_cropbody.tpl'), [ - '$filename' => $filename, - '$profile' => intval($_REQUEST['profile']), - '$resource' => App::$data['imagecrop'] . '-' . App::$data['imagecrop_resolution'], - '$image_url' => z_root() . '/photo/' . $filename, - '$title' => t('Crop Image'), - '$desc' => t('Please adjust the image cropping for optimum viewing.'), - '$form_security_token' => get_form_security_token("profile_photo"), - '$done' => t('Done Editing') - ]); - return $o; - } - } - - /* @brief Generate the UI for photo-cropping - * - * @param $ph Photo-Factory - * @return void - * - */ - - function profile_photo_crop_ui_head($ph, $hash, $smallest) { - $max_length = get_config('system','max_image_length', MAX_IMAGE_LENGTH); + /* @brief Generate content of profile-photo view + * + * @return void + * + */ - if ($max_length > 0) { - $ph->scaleImage($max_length); - } - - App::$data['width'] = $ph->getWidth(); - App::$data['height'] = $ph->getHeight(); - - if (App::$data['width'] < 500 || App::$data['height'] < 500) { - $ph->scaleImageUp(400); - App::$data['width'] = $ph->getWidth(); - App::$data['height'] = $ph->getHeight(); - } - - App::$data['imagecrop'] = $hash; - App::$data['imagecrop_resolution'] = $smallest; - App::$page['htmlhead'] .= replace_macros(get_markup_template('crophead.tpl'), []); - return; - } + + public function get() + { + + if (!is_site_admin()) { + notice(t('Permission denied.') . EOL); + return; + } + + $channel = get_sys_channel(); + $pf = 0; + $newuser = false; + + if (argc() == 3 && argv(2) === 'new') { + $newuser = true; + } + + if (argv(2) === 'reset') { + Config::Delete('system', 'site_icon_url'); + } + + if (argv(2) === 'use') { + if (argc() < 4) { + notice(t('Permission denied.') . EOL); + return; + } + + $resource_id = argv(3); + + $pf = (($_REQUEST['pf']) ? intval($_REQUEST['pf']) : 0); + + $c = q( + "select id, is_default from profile where uid = %d", + intval($channel['channel_id']) + ); + + $multi_profiles = true; + + if (($c) && (count($c) === 1) && (intval($c[0]['is_default']))) { + $_REQUEST['profile'] = $c[0]['id']; + $multi_profiles = false; + } else { + $_REQUEST['profile'] = $pf; + } + + $r = q( + "SELECT id, album, imgscale FROM photo WHERE uid = %d AND resource_id = '%s' ORDER BY imgscale ASC", + intval($channel['channel_id']), + dbesc($resource_id) + ); + if (!$r) { + notice(t('Photo not available.') . EOL); + return; + } + $havescale = false; + foreach ($r as $rr) { + if ($rr['imgscale'] == PHOTO_RES_PROFILE_80) { + $havescale = true; + } + } + + // set an already loaded and cropped photo as profile photo + + if ($havescale) { + // unset any existing profile photos + $r = q( + "UPDATE photo SET photo_usage = %d WHERE photo_usage = %d AND uid = %d", + intval(PHOTO_NORMAL), + intval(PHOTO_PROFILE), + intval($channel['channel_id']) + ); + + $r = q( + "UPDATE photo SET photo_usage = %d WHERE uid = %d AND resource_id = '%s'", + intval(PHOTO_PROFILE), + intval($channel['channel_id']), + dbesc($resource_id) + ); + + $r = q( + "UPDATE xchan set xchan_photo_date = '%s' where xchan_hash = '%s'", + dbesc(datetime_convert()), + dbesc($channel['xchan_hash']) + ); + + goaway(z_root() . '/admin'); + } + + $r = q( + "SELECT content, mimetype, resource_id, os_storage FROM photo WHERE id = %d and uid = %d limit 1", + intval($r[0]['id']), + intval($channel['channel_id']) + ); + if (!$r) { + notice(t('Photo not available.') . EOL); + return; + } + + if (intval($r[0]['os_storage'])) { + $data = @file_get_contents(dbunescbin($r[0]['content'])); + } else { + $data = dbunescbin($r[0]['content']); + } + + $ph = photo_factory($data, $r[0]['mimetype']); + $smallest = 0; + if ($ph->is_valid()) { + // go ahead as if we have just uploaded a new photo to crop + $i = q( + "select resource_id, imgscale from photo where resource_id = '%s' and uid = %d order by imgscale", + dbesc($r[0]['resource_id']), + intval($channel['channel_id']) + ); + + if ($i) { + $hash = $i[0]['resource_id']; + foreach ($i as $ii) { + if (intval($ii['imgscale']) < PHOTO_RES_640) { + $smallest = intval($ii['imgscale']); + } + } + } + } + + if ($multi_profiles) { + App::$data['importfile'] = $resource_id; + } else { + $this->profile_photo_crop_ui_head($ph, $hash, $smallest); + } + + // falls through with App::$data['imagecrop'] set so we go straight to the cropping section + } + + // present an upload form + + $profiles = q( + "select id, profile_name as name, is_default from profile where uid = %d order by id asc", + intval($channel['channel_id']) + ); + + if ($profiles) { + for ($x = 0; $x < count($profiles); $x++) { + $profiles[$x]['selected'] = false; + if ($pf && $profiles[$x]['id'] == $pf) { + $profiles[$x]['selected'] = true; + } + if ((!$pf) && $profiles[$x]['is_default']) { + $profiles[$x]['selected'] = true; + } + } + } + + $importing = ((array_key_exists('importfile', App::$data)) ? true : false); + + if (!array_key_exists('imagecrop', App::$data)) { + $tpl = get_markup_template('admin_profile_photo.tpl'); + + $o .= replace_macros($tpl, [ + '$user' => $channel['channel_address'], + '$channel_id' => $channel['channel_id'], + '$info' => ((count($profiles) > 1) ? t('Your default profile photo is visible to anybody on the internet. Profile photos for alternate profiles will inherit the permissions of the profile') : t('Your site photo is visible to anybody on the internet and may be distributed to other websites.')), + '$importfile' => (($importing) ? App::$data['importfile'] : ''), + '$lbl_upfile' => t('Upload File:'), + '$lbl_profiles' => t('Select a profile:'), + '$title' => (($importing) ? t('Use Photo for Site Logo') : t('Change Site Logo')), + '$submit' => (($importing) ? t('Use') : t('Upload')), + '$profiles' => $profiles, + '$single' => ((count($profiles) == 1) ? true : false), + '$profile0' => $profiles[0], + '$embedPhotos' => t('Use a photo from your albums'), + '$embedPhotosModalTitle' => t('Use a photo from your albums'), + '$embedPhotosModalCancel' => t('Cancel'), + '$embedPhotosModalOK' => t('OK'), + '$modalchooseimages' => t('Choose images to embed'), + '$modalchoosealbum' => t('Choose an album'), + '$modaldiffalbum' => t('Choose a different album'), + '$modalerrorlist' => t('Error getting album list'), + '$modalerrorlink' => t('Error getting photo link'), + '$modalerroralbum' => t('Error getting album'), + '$form_security_token' => get_form_security_token("profile_photo"), + '$select' => t('Select previously uploaded photo'), + ]); + + call_hooks('profile_photo_content_end', $o); + return $o; + } else { + // present a cropping form + + $filename = App::$data['imagecrop'] . '-' . App::$data['imagecrop_resolution']; + $resolution = App::$data['imagecrop_resolution']; + $o .= replace_macros(get_markup_template('admin_cropbody.tpl'), [ + '$filename' => $filename, + '$profile' => intval($_REQUEST['profile']), + '$resource' => App::$data['imagecrop'] . '-' . App::$data['imagecrop_resolution'], + '$image_url' => z_root() . '/photo/' . $filename, + '$title' => t('Crop Image'), + '$desc' => t('Please adjust the image cropping for optimum viewing.'), + '$form_security_token' => get_form_security_token("profile_photo"), + '$done' => t('Done Editing') + ]); + return $o; + } + } + + /* @brief Generate the UI for photo-cropping + * + * @param $ph Photo-Factory + * @return void + * + */ + + public function profile_photo_crop_ui_head($ph, $hash, $smallest) + { + $max_length = get_config('system', 'max_image_length', MAX_IMAGE_LENGTH); + + if ($max_length > 0) { + $ph->scaleImage($max_length); + } + + App::$data['width'] = $ph->getWidth(); + App::$data['height'] = $ph->getHeight(); + + if (App::$data['width'] < 500 || App::$data['height'] < 500) { + $ph->scaleImageUp(400); + App::$data['width'] = $ph->getWidth(); + App::$data['height'] = $ph->getHeight(); + } + + App::$data['imagecrop'] = $hash; + App::$data['imagecrop_resolution'] = $smallest; + App::$page['htmlhead'] .= replace_macros(get_markup_template('crophead.tpl'), []); + return; + } } diff --git a/Zotlabs/Module/Admin/Profs.php b/Zotlabs/Module/Admin/Profs.php index 9e5949a9e..4a09d372c 100644 --- a/Zotlabs/Module/Admin/Profs.php +++ b/Zotlabs/Module/Admin/Profs.php @@ -2,189 +2,195 @@ namespace Zotlabs\Module\Admin; +class Profs +{ -class Profs { + public function post() + { - function post() { - - if(array_key_exists('basic',$_REQUEST)) { - $arr = explode(',',$_REQUEST['basic']); - array_walk($arr,'array_trim'); - $narr = []; - if(count($arr)) { - foreach($arr as $a) { - if(strlen($a)) { - $narr[] = $a; - } - } - } - if(! $narr) - del_config('system','profile_fields_basic'); - else - set_config('system','profile_fields_basic',$narr); + if (array_key_exists('basic', $_REQUEST)) { + $arr = explode(',', $_REQUEST['basic']); + array_walk($arr, 'array_trim'); + $narr = []; + if (count($arr)) { + foreach ($arr as $a) { + if (strlen($a)) { + $narr[] = $a; + } + } + } + if (!$narr) { + del_config('system', 'profile_fields_basic'); + } else { + set_config('system', 'profile_fields_basic', $narr); + } - if(array_key_exists('advanced',$_REQUEST)) { - $arr = explode(',',$_REQUEST['advanced']); - array_walk($arr,'array_trim'); - $narr = []; - if(count($arr)) { - foreach($arr as $a) { - if(strlen($a)) { - $narr[] = $a; - } - } - } - if(! $narr) - del_config('system','profile_fields_advanced'); - else - set_config('system','profile_fields_advanced',$narr); - - } - goaway(z_root() . '/admin/profs'); - } - - - if(array_key_exists('field_name',$_REQUEST)) { - if($_REQUEST['id']) { - $r = q("update profdef set field_name = '%s', field_type = '%s', field_desc = '%s' field_help = '%s', field_inputs = '%s' where id = %d", - dbesc($_REQUEST['field_name']), - dbesc($_REQUEST['field_type']), - dbesc($_REQUEST['field_desc']), - dbesc($_REQUEST['field_help']), - dbesc($_REQUEST['field_inputs']), - intval($_REQUEST['id']) - ); - } - else { - $r = q("insert into profdef ( field_name, field_type, field_desc, field_help, field_inputs ) values ( '%s' , '%s', '%s', '%s', '%s' )", - dbesc($_REQUEST['field_name']), - dbesc($_REQUEST['field_type']), - dbesc($_REQUEST['field_desc']), - dbesc($_REQUEST['field_help']), - dbesc($_REQUEST['field_inputs']) - ); - } - } - - - // add to chosen array basic or advanced - - goaway(z_root() . '/admin/profs'); - } - - function get() { - - if((argc() > 3) && argv(2) == 'drop' && intval(argv(3))) { - $r = q("delete from profdef where id = %d", - intval(argv(3)) - ); - // remove from allowed fields - - goaway(z_root() . '/admin/profs'); - } - - if((argc() > 2) && argv(2) === 'new') { - return replace_macros(get_markup_template('profdef_edit.tpl'),array( - '$header' => t('New Profile Field'), - '$field_name' => array('field_name',t('Field nickname'),$_REQUEST['field_name'],t('System name of field')), - '$field_type' => array('field_type',t('Input type'),(($_REQUEST['field_type']) ? $_REQUEST['field_type'] : 'text'),''), - '$field_desc' => array('field_desc',t('Field Name'),$_REQUEST['field_desc'],t('Label on profile pages')), - '$field_help' => array('field_help',t('Help text'),$_REQUEST['field_help'],t('Additional info (optional)')), - '$submit' => t('Save') - )); - } - - if((argc() > 2) && intval(argv(2))) { - $r = q("select * from profdef where id = %d limit 1", - intval(argv(2)) - ); - if(! $r) { - notice( t('Field definition not found') . EOL); - goaway(z_root() . '/admin/profs'); - } - - return replace_macros(get_markup_template('profdef_edit.tpl'),array( - '$id' => intval($r[0]['id']), - '$header' => t('Edit Profile Field'), - '$field_name' => array('field_name',t('Field nickname'),$r[0]['field_name'],t('System name of field')), - '$field_type' => array('field_type',t('Input type'),$r[0]['field_type'],''), - '$field_desc' => array('field_desc',t('Field Name'),$r[0]['field_desc'],t('Label on profile pages')), - '$field_help' => array('field_help',t('Help text'),$r[0]['field_help'],t('Additional info (optional)')), - '$submit' => t('Save') - )); - } - - $basic = ''; - $barr = []; - $fields = get_profile_fields_basic(); - - if(! $fields) - $fields = get_profile_fields_basic(1); - if($fields) { - foreach($fields as $k => $v) { - if($basic) - $basic .= ', '; - $basic .= trim($k); - $barr[] = trim($k); - } - } - - $advanced = ''; - $fields = get_profile_fields_advanced(); - if(! $fields) - $fields = get_profile_fields_advanced(1); - if($fields) { - foreach($fields as $k => $v) { - if(in_array(trim($k),$barr)) - continue; - if($advanced) - $advanced .= ', '; - $advanced .= trim($k); - } - } - - $all = ''; - $fields = get_profile_fields_advanced(1); - if($fields) { - foreach($fields as $k => $v) { - if($all) - $all .= ', '; - $all .= trim($k); - } - } - - $r = q("select * from profdef where true"); - if($r) { - foreach($r as $rr) { - if($all) - $all .= ', '; - $all .= $rr['field_name']; - } - } - - - $o = replace_macros(get_markup_template('admin_profiles.tpl'),array( - '$title' => t('Profile Fields'), - '$basic' => array('basic',t('Basic Profile Fields'),$basic,''), - '$advanced' => array('advanced',t('Advanced Profile Fields'),$advanced,t('(In addition to basic fields)')), - '$all' => $all, - '$all_desc' => t('All available fields'), - '$cust_field_desc' => t('Custom Fields'), - '$cust_fields' => $r, - '$edit' => t('Edit'), - '$drop' => t('Delete'), - '$new' => t('Create Custom Field'), - '$submit' => t('Submit') - )); - - return $o; - - - } + if (array_key_exists('advanced', $_REQUEST)) { + $arr = explode(',', $_REQUEST['advanced']); + array_walk($arr, 'array_trim'); + $narr = []; + if (count($arr)) { + foreach ($arr as $a) { + if (strlen($a)) { + $narr[] = $a; + } + } + } + if (!$narr) { + del_config('system', 'profile_fields_advanced'); + } else { + set_config('system', 'profile_fields_advanced', $narr); + } + } + goaway(z_root() . '/admin/profs'); + } + if (array_key_exists('field_name', $_REQUEST)) { + if ($_REQUEST['id']) { + $r = q( + "update profdef set field_name = '%s', field_type = '%s', field_desc = '%s' field_help = '%s', field_inputs = '%s' where id = %d", + dbesc($_REQUEST['field_name']), + dbesc($_REQUEST['field_type']), + dbesc($_REQUEST['field_desc']), + dbesc($_REQUEST['field_help']), + dbesc($_REQUEST['field_inputs']), + intval($_REQUEST['id']) + ); + } else { + $r = q( + "insert into profdef ( field_name, field_type, field_desc, field_help, field_inputs ) values ( '%s' , '%s', '%s', '%s', '%s' )", + dbesc($_REQUEST['field_name']), + dbesc($_REQUEST['field_type']), + dbesc($_REQUEST['field_desc']), + dbesc($_REQUEST['field_help']), + dbesc($_REQUEST['field_inputs']) + ); + } + } + // add to chosen array basic or advanced -} \ No newline at end of file + goaway(z_root() . '/admin/profs'); + } + + public function get() + { + + if ((argc() > 3) && argv(2) == 'drop' && intval(argv(3))) { + $r = q( + "delete from profdef where id = %d", + intval(argv(3)) + ); + // remove from allowed fields + + goaway(z_root() . '/admin/profs'); + } + + if ((argc() > 2) && argv(2) === 'new') { + return replace_macros(get_markup_template('profdef_edit.tpl'), array( + '$header' => t('New Profile Field'), + '$field_name' => array('field_name', t('Field nickname'), $_REQUEST['field_name'], t('System name of field')), + '$field_type' => array('field_type', t('Input type'), (($_REQUEST['field_type']) ? $_REQUEST['field_type'] : 'text'), ''), + '$field_desc' => array('field_desc', t('Field Name'), $_REQUEST['field_desc'], t('Label on profile pages')), + '$field_help' => array('field_help', t('Help text'), $_REQUEST['field_help'], t('Additional info (optional)')), + '$submit' => t('Save') + )); + } + + if ((argc() > 2) && intval(argv(2))) { + $r = q( + "select * from profdef where id = %d limit 1", + intval(argv(2)) + ); + if (!$r) { + notice(t('Field definition not found') . EOL); + goaway(z_root() . '/admin/profs'); + } + + return replace_macros(get_markup_template('profdef_edit.tpl'), array( + '$id' => intval($r[0]['id']), + '$header' => t('Edit Profile Field'), + '$field_name' => array('field_name', t('Field nickname'), $r[0]['field_name'], t('System name of field')), + '$field_type' => array('field_type', t('Input type'), $r[0]['field_type'], ''), + '$field_desc' => array('field_desc', t('Field Name'), $r[0]['field_desc'], t('Label on profile pages')), + '$field_help' => array('field_help', t('Help text'), $r[0]['field_help'], t('Additional info (optional)')), + '$submit' => t('Save') + )); + } + + $basic = ''; + $barr = []; + $fields = get_profile_fields_basic(); + + if (!$fields) { + $fields = get_profile_fields_basic(1); + } + if ($fields) { + foreach ($fields as $k => $v) { + if ($basic) { + $basic .= ', '; + } + $basic .= trim($k); + $barr[] = trim($k); + } + } + + $advanced = ''; + $fields = get_profile_fields_advanced(); + if (!$fields) { + $fields = get_profile_fields_advanced(1); + } + if ($fields) { + foreach ($fields as $k => $v) { + if (in_array(trim($k), $barr)) { + continue; + } + if ($advanced) { + $advanced .= ', '; + } + $advanced .= trim($k); + } + } + + $all = ''; + $fields = get_profile_fields_advanced(1); + if ($fields) { + foreach ($fields as $k => $v) { + if ($all) { + $all .= ', '; + } + $all .= trim($k); + } + } + + $r = q("select * from profdef where true"); + if ($r) { + foreach ($r as $rr) { + if ($all) { + $all .= ', '; + } + $all .= $rr['field_name']; + } + } + + + $o = replace_macros(get_markup_template('admin_profiles.tpl'), array( + '$title' => t('Profile Fields'), + '$basic' => array('basic', t('Basic Profile Fields'), $basic, ''), + '$advanced' => array('advanced', t('Advanced Profile Fields'), $advanced, t('(In addition to basic fields)')), + '$all' => $all, + '$all_desc' => t('All available fields'), + '$cust_field_desc' => t('Custom Fields'), + '$cust_fields' => $r, + '$edit' => t('Edit'), + '$drop' => t('Delete'), + '$new' => t('Create Custom Field'), + '$submit' => t('Submit') + )); + + return $o; + } +} diff --git a/Zotlabs/Module/Admin/Queue.php b/Zotlabs/Module/Admin/Queue.php index defcffc14..136dd46f5 100644 --- a/Zotlabs/Module/Admin/Queue.php +++ b/Zotlabs/Module/Admin/Queue.php @@ -4,48 +4,46 @@ namespace Zotlabs\Module\Admin; use Zotlabs\Lib\Queue as ZQueue; -class Queue { - - function get() { +class Queue +{ - $o = ''; - - $expert = ((array_key_exists('expert',$_REQUEST)) ? intval($_REQUEST['expert']) : 0); - - if($_REQUEST['drophub']) { - hubloc_mark_as_down($_REQUEST['drophub']); - ZQueue::remove_by_posturl($_REQUEST['drophub']); - } - - if($_REQUEST['emptyhub']) { - ZQueue::remove_by_posturl($_REQUEST['emptyhub']); - } - - $r = q("select count(outq_posturl) as total, max(outq_priority) as priority, outq_posturl from outq + public function get() + { + + $o = ''; + + $expert = ((array_key_exists('expert', $_REQUEST)) ? intval($_REQUEST['expert']) : 0); + + if ($_REQUEST['drophub']) { + hubloc_mark_as_down($_REQUEST['drophub']); + ZQueue::remove_by_posturl($_REQUEST['drophub']); + } + + if ($_REQUEST['emptyhub']) { + ZQueue::remove_by_posturl($_REQUEST['emptyhub']); + } + + $r = q("select count(outq_posturl) as total, max(outq_priority) as priority, outq_posturl from outq where outq_delivered = 0 group by outq_posturl order by total desc"); - - for($x = 0; $x < count($r); $x ++) { - $r[$x]['eurl'] = urlencode($r[$x]['outq_posturl']); - $r[$x]['connected'] = datetime_convert('UTC',date_default_timezone_get(),$r[$x]['connected'],'Y-m-d'); - } - - $o = replace_macros(get_markup_template('admin_queue.tpl'), array( - '$banner' => t('Queue Statistics'), - '$numentries' => t('Total Entries'), - '$priority' => t('Priority'), - '$desturl' => t('Destination URL'), - '$nukehub' => t('Mark hub permanently offline'), - '$empty' => t('Empty queue for this hub'), - '$lastconn' => t('Last known contact'), - '$hasentries' => ((count($r)) ? true : false), - '$entries' => $r, - '$expert' => $expert - )); - - return $o; - } - + for ($x = 0; $x < count($r); $x++) { + $r[$x]['eurl'] = urlencode($r[$x]['outq_posturl']); + $r[$x]['connected'] = datetime_convert('UTC', date_default_timezone_get(), $r[$x]['connected'], 'Y-m-d'); + } + $o = replace_macros(get_markup_template('admin_queue.tpl'), array( + '$banner' => t('Queue Statistics'), + '$numentries' => t('Total Entries'), + '$priority' => t('Priority'), + '$desturl' => t('Destination URL'), + '$nukehub' => t('Mark hub permanently offline'), + '$empty' => t('Empty queue for this hub'), + '$lastconn' => t('Last known contact'), + '$hasentries' => ((count($r)) ? true : false), + '$entries' => $r, + '$expert' => $expert + )); -} \ No newline at end of file + return $o; + } +} diff --git a/Zotlabs/Module/Admin/Security.php b/Zotlabs/Module/Admin/Security.php index 7b900c2cf..70c5673fe 100644 --- a/Zotlabs/Module/Admin/Security.php +++ b/Zotlabs/Module/Admin/Security.php @@ -2,186 +2,187 @@ namespace Zotlabs\Module\Admin; +class Security +{ -class Security { + public function post() + { + check_form_security_token_redirectOnErr('/admin/security', 'admin_security'); - function post() { - check_form_security_token_redirectOnErr('/admin/security', 'admin_security'); - - $allowed_email = ((x($_POST,'allowed_email')) ? notags(trim($_POST['allowed_email'])) : ''); - set_config('system','allowed_email', $allowed_email); + $allowed_email = ((x($_POST, 'allowed_email')) ? notags(trim($_POST['allowed_email'])) : ''); + set_config('system', 'allowed_email', $allowed_email); - $not_allowed_email = ((x($_POST,'not_allowed_email')) ? notags(trim($_POST['not_allowed_email'])) : ''); - set_config('system','not_allowed_email', $not_allowed_email); + $not_allowed_email = ((x($_POST, 'not_allowed_email')) ? notags(trim($_POST['not_allowed_email'])) : ''); + set_config('system', 'not_allowed_email', $not_allowed_email); - $anonymous_comments = ((x($_POST,'anonymous_comments')) ? intval($_POST['anonymous_comments']) : 0); - set_config('system','anonymous_comments', $anonymous_comments); - - $block_public = ((x($_POST,'block_public')) ? True : False); - set_config('system','block_public',$block_public); + $anonymous_comments = ((x($_POST, 'anonymous_comments')) ? intval($_POST['anonymous_comments']) : 0); + set_config('system', 'anonymous_comments', $anonymous_comments); - $block_public_search = ((x($_POST,'block_public_search')) ? 1 : 0); - set_config('system','block_public_search',$block_public_search); + $block_public = ((x($_POST, 'block_public')) ? true : false); + set_config('system', 'block_public', $block_public); - $block_public_dir = ((x($_POST,'block_public_directory')) ? True : False); - set_config('system', 'block_public_directory', $block_public_dir); + $block_public_search = ((x($_POST, 'block_public_search')) ? 1 : 0); + set_config('system', 'block_public_search', $block_public_search); - $localdir_hide = ((x($_POST,'localdir_hide')) ? 1 : 0); - set_config('system','localdir_hide',$localdir_hide); + $block_public_dir = ((x($_POST, 'block_public_directory')) ? true : false); + set_config('system', 'block_public_directory', $block_public_dir); - $cloud_noroot = ((x($_POST,'cloud_noroot')) ? 1 : 0); - set_config('system','cloud_disable_siteroot',1 - $cloud_noroot); + $localdir_hide = ((x($_POST, 'localdir_hide')) ? 1 : 0); + set_config('system', 'localdir_hide', $localdir_hide); - $cloud_disksize = ((x($_POST,'cloud_disksize')) ? 1 : 0); - set_config('system','cloud_report_disksize',$cloud_disksize); + $cloud_noroot = ((x($_POST, 'cloud_noroot')) ? 1 : 0); + set_config('system', 'cloud_disable_siteroot', 1 - $cloud_noroot); - $thumbnail_security = ((x($_POST,'thumbnail_security')) ? intval($_POST['thumbnail_security']) : 0); - set_config('system', 'thumbnail_security' , $thumbnail_security); + $cloud_disksize = ((x($_POST, 'cloud_disksize')) ? 1 : 0); + set_config('system', 'cloud_report_disksize', $cloud_disksize); - $inline_pdf = ((x($_POST,'inline_pdf')) ? intval($_POST['inline_pdf']) : 0); - set_config('system', 'inline_pdf' , $inline_pdf); + $thumbnail_security = ((x($_POST, 'thumbnail_security')) ? intval($_POST['thumbnail_security']) : 0); + set_config('system', 'thumbnail_security', $thumbnail_security); - $ws = $this->trim_array_elems(explode("\n",$_POST['allowed_sites'])); - set_config('system','allowed_sites',$ws); - - $bs = $this->trim_array_elems(explode("\n",$_POST['denied_sites'])); - set_config('system','denied_sites',$bs); - - $wc = $this->trim_array_elems(explode("\n",$_POST['allowed_channels'])); - set_config('system','allowed_channels',$wc); - - $bc = $this->trim_array_elems(explode("\n",$_POST['denied_channels'])); - set_config('system','denied_channels',$bc); + $inline_pdf = ((x($_POST, 'inline_pdf')) ? intval($_POST['inline_pdf']) : 0); + set_config('system', 'inline_pdf', $inline_pdf); - $ws = $this->trim_array_elems(explode("\n",$_POST['pubstream_allowed_sites'])); - set_config('system','pubstream_allowed_sites',$ws); - - $bs = $this->trim_array_elems(explode("\n",$_POST['pubstream_denied_sites'])); - set_config('system','pubstream_denied_sites',$bs); - - $wc = $this->trim_array_elems(explode("\n",$_POST['pubstream_allowed_channels'])); - set_config('system','pubstream_allowed_channels',$wc); - - $bc = $this->trim_array_elems(explode("\n",$_POST['pubstream_denied_channels'])); - set_config('system','pubstream_denied_channels',$bc); + $ws = $this->trim_array_elems(explode("\n", $_POST['allowed_sites'])); + set_config('system', 'allowed_sites', $ws); - $embed_sslonly = ((x($_POST,'embed_sslonly')) ? True : False); - set_config('system','embed_sslonly',$embed_sslonly); - - $we = $this->trim_array_elems(explode("\n",$_POST['embed_allow'])); - set_config('system','embed_allow',$we); - - $be = $this->trim_array_elems(explode("\n",$_POST['embed_deny'])); - set_config('system','embed_deny',$be); - - $ts = ((x($_POST,'transport_security')) ? True : False); - set_config('system','transport_security_header',$ts); + $bs = $this->trim_array_elems(explode("\n", $_POST['denied_sites'])); + set_config('system', 'denied_sites', $bs); - $cs = ((x($_POST,'content_security')) ? True : False); - set_config('system','content_security_policy',$cs); + $wc = $this->trim_array_elems(explode("\n", $_POST['allowed_channels'])); + set_config('system', 'allowed_channels', $wc); - goaway(z_root() . '/admin/security'); - } - - + $bc = $this->trim_array_elems(explode("\n", $_POST['denied_channels'])); + set_config('system', 'denied_channels', $bc); - function get() { - - $allowedsites = get_config('system','allowed_sites'); - $allowedsites_str = ((is_array($allowedsites)) ? implode("\n",$allowedsites) : ''); - - $deniedsites = get_config('system','denied_sites'); - $deniedsites_str = ((is_array($deniedsites)) ? implode("\n",$deniedsites) : ''); - - - $allowedchannels = get_config('system','allowed_channels'); - $allowedchannels_str = ((is_array($allowedchannels)) ? implode("\n",$allowedchannels) : ''); - - $deniedchannels = get_config('system','denied_channels'); - $deniedchannels_str = ((is_array($deniedchannels)) ? implode("\n",$deniedchannels) : ''); + $ws = $this->trim_array_elems(explode("\n", $_POST['pubstream_allowed_sites'])); + set_config('system', 'pubstream_allowed_sites', $ws); - $psallowedsites = get_config('system','pubstream_allowed_sites'); - $psallowedsites_str = ((is_array($psallowedsites)) ? implode("\n",$psallowedsites) : ''); - - $psdeniedsites = get_config('system','pubstream_denied_sites'); - $psdeniedsites_str = ((is_array($psdeniedsites)) ? implode("\n",$psdeniedsites) : ''); - - - $psallowedchannels = get_config('system','pubstream_allowed_channels'); - $psallowedchannels_str = ((is_array($psallowedchannels)) ? implode("\n",$psallowedchannels) : ''); - - $psdeniedchannels = get_config('system','pubstream_denied_channels'); - $psdeniedchannels_str = ((is_array($psdeniedchannels)) ? implode("\n",$psdeniedchannels) : ''); + $bs = $this->trim_array_elems(explode("\n", $_POST['pubstream_denied_sites'])); + set_config('system', 'pubstream_denied_sites', $bs); - $allowedembeds = get_config('system','embed_allow'); - $allowedembeds_str = ((is_array($allowedembeds)) ? implode("\n",$allowedembeds) : ''); - - $deniedembeds = get_config('system','embed_deny'); - $deniedembeds_str = ((is_array($deniedembeds)) ? implode("\n",$deniedembeds) : ''); - - $embed_coop = intval(get_config('system','embed_coop')); - - if((! $allowedembeds) && (! $deniedembeds)) { - $embedhelp1 = t("By default, unfiltered HTML is allowed in embedded media. This is inherently insecure."); - } + $wc = $this->trim_array_elems(explode("\n", $_POST['pubstream_allowed_channels'])); + set_config('system', 'pubstream_allowed_channels', $wc); - $embedhelp2 = t("The recommended setting is to only allow unfiltered HTML from the following sites:"); - $embedhelp3 = t("https://youtube.com/
        https://www.youtube.com/
        https://youtu.be/
        https://vimeo.com/
        https://soundcloud.com/
        "); - $embedhelp4 = t("All other embedded content will be filtered, unless embedded content from that site is explicitly blocked."); - - $t = get_markup_template('admin_security.tpl'); - return replace_macros($t, array( - '$title' => t('Administration'), - '$page' => t('Security'), - '$form_security_token' => get_form_security_token('admin_security'), - '$block_public' => array('block_public', t("Block public"), get_config('system','block_public'), t("Check to block public access to all otherwise public personal pages on this site unless you are currently authenticated.")), - '$block_public_search' => array('block_public_search', t("Block public search"), get_config('system','block_public_search', 1), t("Prevent access to search content unless you are currently authenticated.")), - '$block_public_dir' => [ 'block_public_directory', t('Block directory from visitors'), get_config('system','block_public_directory',true), t('Only allow authenticated access to directory.') ], - '$localdir_hide' => [ 'localdir_hide', t('Hide local directory'), intval(get_config('system','localdir_hide')), t('Only use the global directory') ], - '$cloud_noroot' => [ 'cloud_noroot', t('Provide a cloud root directory'), 1 - intval(get_config('system','cloud_disable_siteroot',true)), t('The cloud root directory lists all channel names which provide public files. Otherwise only the names of connections are shown.') ], - '$cloud_disksize' => [ 'cloud_disksize', t('Show total disk space available to cloud uploads'), intval(get_config('system','cloud_report_disksize')), '' ], - '$thumbnail_security' => [ 'thumbnail_security', t("Allow SVG thumbnails in file browser"), get_config('system','thumbnail_security',0), t("WARNING: SVG images may contain malicious code.") ], + $bc = $this->trim_array_elems(explode("\n", $_POST['pubstream_denied_channels'])); + set_config('system', 'pubstream_denied_channels', $bc); - '$inline_pdf' => [ 'inline_pdf', t("Allow embedded (inline) PDF files"), get_config('system','inline_pdf',0), '' ], - '$anonymous_comments' => [ 'anonymous_comments', t('Permit anonymous comments'), intval(get_config('system','anonymous_comments')), t('Moderation will be performed by channels that select this comment option.') ], - '$transport_security' => array('transport_security', t('Set "Transport Security" HTTP header'),intval(get_config('system','transport_security_header')),''), - '$content_security' => array('content_security', t('Set "Content Security Policy" HTTP header'),intval(get_config('system','content_security_policy')),''), - '$allowed_email' => array('allowed_email', t("Allowed email domains"), get_config('system','allowed_email'), t("Comma separated list of domains which are allowed in email addresses for registrations to this site. Wildcards are accepted. Empty to allow any domains")), - '$not_allowed_email' => array('not_allowed_email', t("Not allowed email domains"), get_config('system','not_allowed_email'), t("Comma separated list of domains which are not allowed in email addresses for registrations to this site. Wildcards are accepted. Empty to allow any domains, unless allowed domains have been defined.")), - '$allowed_sites' => array('allowed_sites', t('Allow communications only from these sites'), $allowedsites_str, t('One site per line. Leave empty to allow communication from anywhere by default')), - '$denied_sites' => array('denied_sites', t('Block communications from these sites'), $deniedsites_str, ''), - '$allowed_channels' => array('allowed_channels', t('Allow communications only from these channels'), $allowedchannels_str, t('One channel (hash) per line. Leave empty to allow communication from any channel by default')), - '$denied_channels' => array('denied_channels', t('Block communications from these channels'), $deniedchannels_str, ''), + $embed_sslonly = ((x($_POST, 'embed_sslonly')) ? true : false); + set_config('system', 'embed_sslonly', $embed_sslonly); - '$psallowed_sites' => array('pubstream_allowed_sites', t('Allow public stream communications only from these sites'), $psallowedsites_str, t('One site per line. Leave empty to allow communication from anywhere by default')), - '$psdenied_sites' => array('pubstream_denied_sites', t('Block public stream communications from these sites'), $psdeniedsites_str, ''), - '$psallowed_channels' => array('pubstream_allowed_channels', t('Allow public stream communications only from these channels'), $psallowedchannels_str, t('One channel (hash) per line. Leave empty to allow communication from any channel by default')), - '$psdenied_channels' => array('pubstream_denied_channels', t('Block public stream communications from these channels'), $psdeniedchannels_str, ''), + $we = $this->trim_array_elems(explode("\n", $_POST['embed_allow'])); + set_config('system', 'embed_allow', $we); + + $be = $this->trim_array_elems(explode("\n", $_POST['embed_deny'])); + set_config('system', 'embed_deny', $be); + + $ts = ((x($_POST, 'transport_security')) ? true : false); + set_config('system', 'transport_security_header', $ts); + + $cs = ((x($_POST, 'content_security')) ? true : false); + set_config('system', 'content_security_policy', $cs); + + goaway(z_root() . '/admin/security'); + } - '$embed_sslonly' => array('embed_sslonly',t('Only allow embeds from secure (SSL) websites and links.'), intval(get_config('system','embed_sslonly')),''), - '$embed_allow' => array('embed_allow', t('Allow unfiltered embedded HTML content only from these domains'), $allowedembeds_str, t('One site per line. By default embedded content is filtered.')), - '$embed_deny' => array('embed_deny', t('Block embedded HTML from these domains'), $deniedembeds_str, ''), - -// '$embed_coop' => array('embed_coop', t('Cooperative embed security'), $embed_coop, t('Enable to share embed security with other compatible sites/hubs')), + public function get() + { - '$submit' => t('Submit') - )); - } + $allowedsites = get_config('system', 'allowed_sites'); + $allowedsites_str = ((is_array($allowedsites)) ? implode("\n", $allowedsites) : ''); + + $deniedsites = get_config('system', 'denied_sites'); + $deniedsites_str = ((is_array($deniedsites)) ? implode("\n", $deniedsites) : ''); - function trim_array_elems($arr) { - $narr = []; - - if($arr && is_array($arr)) { - for($x = 0; $x < count($arr); $x ++) { - $y = trim($arr[$x]); - if($y) - $narr[] = $y; - } - } - return $narr; - } - - -} \ No newline at end of file + $allowedchannels = get_config('system', 'allowed_channels'); + $allowedchannels_str = ((is_array($allowedchannels)) ? implode("\n", $allowedchannels) : ''); + + $deniedchannels = get_config('system', 'denied_channels'); + $deniedchannels_str = ((is_array($deniedchannels)) ? implode("\n", $deniedchannels) : ''); + + $psallowedsites = get_config('system', 'pubstream_allowed_sites'); + $psallowedsites_str = ((is_array($psallowedsites)) ? implode("\n", $psallowedsites) : ''); + + $psdeniedsites = get_config('system', 'pubstream_denied_sites'); + $psdeniedsites_str = ((is_array($psdeniedsites)) ? implode("\n", $psdeniedsites) : ''); + + + $psallowedchannels = get_config('system', 'pubstream_allowed_channels'); + $psallowedchannels_str = ((is_array($psallowedchannels)) ? implode("\n", $psallowedchannels) : ''); + + $psdeniedchannels = get_config('system', 'pubstream_denied_channels'); + $psdeniedchannels_str = ((is_array($psdeniedchannels)) ? implode("\n", $psdeniedchannels) : ''); + + $allowedembeds = get_config('system', 'embed_allow'); + $allowedembeds_str = ((is_array($allowedembeds)) ? implode("\n", $allowedembeds) : ''); + + $deniedembeds = get_config('system', 'embed_deny'); + $deniedembeds_str = ((is_array($deniedembeds)) ? implode("\n", $deniedembeds) : ''); + + $embed_coop = intval(get_config('system', 'embed_coop')); + + if ((!$allowedembeds) && (!$deniedembeds)) { + $embedhelp1 = t("By default, unfiltered HTML is allowed in embedded media. This is inherently insecure."); + } + + $embedhelp2 = t("The recommended setting is to only allow unfiltered HTML from the following sites:"); + $embedhelp3 = t("https://youtube.com/
        https://www.youtube.com/
        https://youtu.be/
        https://vimeo.com/
        https://soundcloud.com/
        "); + $embedhelp4 = t("All other embedded content will be filtered, unless embedded content from that site is explicitly blocked."); + + $t = get_markup_template('admin_security.tpl'); + return replace_macros($t, array( + '$title' => t('Administration'), + '$page' => t('Security'), + '$form_security_token' => get_form_security_token('admin_security'), + '$block_public' => array('block_public', t("Block public"), get_config('system', 'block_public'), t("Check to block public access to all otherwise public personal pages on this site unless you are currently authenticated.")), + '$block_public_search' => array('block_public_search', t("Block public search"), get_config('system', 'block_public_search', 1), t("Prevent access to search content unless you are currently authenticated.")), + '$block_public_dir' => ['block_public_directory', t('Block directory from visitors'), get_config('system', 'block_public_directory', true), t('Only allow authenticated access to directory.')], + '$localdir_hide' => ['localdir_hide', t('Hide local directory'), intval(get_config('system', 'localdir_hide')), t('Only use the global directory')], + '$cloud_noroot' => ['cloud_noroot', t('Provide a cloud root directory'), 1 - intval(get_config('system', 'cloud_disable_siteroot', true)), t('The cloud root directory lists all channel names which provide public files. Otherwise only the names of connections are shown.')], + '$cloud_disksize' => ['cloud_disksize', t('Show total disk space available to cloud uploads'), intval(get_config('system', 'cloud_report_disksize')), ''], + '$thumbnail_security' => ['thumbnail_security', t("Allow SVG thumbnails in file browser"), get_config('system', 'thumbnail_security', 0), t("WARNING: SVG images may contain malicious code.")], + + '$inline_pdf' => ['inline_pdf', t("Allow embedded (inline) PDF files"), get_config('system', 'inline_pdf', 0), ''], + '$anonymous_comments' => ['anonymous_comments', t('Permit anonymous comments'), intval(get_config('system', 'anonymous_comments')), t('Moderation will be performed by channels that select this comment option.')], + '$transport_security' => array('transport_security', t('Set "Transport Security" HTTP header'), intval(get_config('system', 'transport_security_header')), ''), + '$content_security' => array('content_security', t('Set "Content Security Policy" HTTP header'), intval(get_config('system', 'content_security_policy')), ''), + '$allowed_email' => array('allowed_email', t("Allowed email domains"), get_config('system', 'allowed_email'), t("Comma separated list of domains which are allowed in email addresses for registrations to this site. Wildcards are accepted. Empty to allow any domains")), + '$not_allowed_email' => array('not_allowed_email', t("Not allowed email domains"), get_config('system', 'not_allowed_email'), t("Comma separated list of domains which are not allowed in email addresses for registrations to this site. Wildcards are accepted. Empty to allow any domains, unless allowed domains have been defined.")), + '$allowed_sites' => array('allowed_sites', t('Allow communications only from these sites'), $allowedsites_str, t('One site per line. Leave empty to allow communication from anywhere by default')), + '$denied_sites' => array('denied_sites', t('Block communications from these sites'), $deniedsites_str, ''), + '$allowed_channels' => array('allowed_channels', t('Allow communications only from these channels'), $allowedchannels_str, t('One channel (hash) per line. Leave empty to allow communication from any channel by default')), + '$denied_channels' => array('denied_channels', t('Block communications from these channels'), $deniedchannels_str, ''), + + '$psallowed_sites' => array('pubstream_allowed_sites', t('Allow public stream communications only from these sites'), $psallowedsites_str, t('One site per line. Leave empty to allow communication from anywhere by default')), + '$psdenied_sites' => array('pubstream_denied_sites', t('Block public stream communications from these sites'), $psdeniedsites_str, ''), + '$psallowed_channels' => array('pubstream_allowed_channels', t('Allow public stream communications only from these channels'), $psallowedchannels_str, t('One channel (hash) per line. Leave empty to allow communication from any channel by default')), + '$psdenied_channels' => array('pubstream_denied_channels', t('Block public stream communications from these channels'), $psdeniedchannels_str, ''), + + + '$embed_sslonly' => array('embed_sslonly', t('Only allow embeds from secure (SSL) websites and links.'), intval(get_config('system', 'embed_sslonly')), ''), + '$embed_allow' => array('embed_allow', t('Allow unfiltered embedded HTML content only from these domains'), $allowedembeds_str, t('One site per line. By default embedded content is filtered.')), + '$embed_deny' => array('embed_deny', t('Block embedded HTML from these domains'), $deniedembeds_str, ''), + +// '$embed_coop' => array('embed_coop', t('Cooperative embed security'), $embed_coop, t('Enable to share embed security with other compatible sites/hubs')), + + '$submit' => t('Submit') + )); + } + + + public function trim_array_elems($arr) + { + $narr = []; + + if ($arr && is_array($arr)) { + for ($x = 0; $x < count($arr); $x++) { + $y = trim($arr[$x]); + if ($y) { + $narr[] = $y; + } + } + } + return $narr; + } +} diff --git a/Zotlabs/Module/Admin/Site.php b/Zotlabs/Module/Admin/Site.php index 37956c31a..f31f3a58f 100644 --- a/Zotlabs/Module/Admin/Site.php +++ b/Zotlabs/Module/Admin/Site.php @@ -6,364 +6,374 @@ use App; use Zotlabs\Lib\System; use Zotlabs\Access\PermissionRoles; -class Site { +class Site +{ - /** - * @brief POST handler for Admin Site Page. - * - */ - function post() { + /** + * @brief POST handler for Admin Site Page. + * + */ + public function post() + { - if(! is_site_admin()) { - return; - } - - if (! x($_POST, 'page_site')) { - return; - } + if (!is_site_admin()) { + return; + } - $sys = get_sys_channel(); + if (!x($_POST, 'page_site')) { + return; + } - check_form_security_token_redirectOnErr('/admin/site', 'admin_site'); + $sys = get_sys_channel(); - $sitename = ((x($_POST,'sitename')) ? notags(trim($_POST['sitename'])) : App::get_hostname()); + check_form_security_token_redirectOnErr('/admin/site', 'admin_site'); - $admininfo = ((x($_POST,'admininfo')) ? trim($_POST['admininfo']) : false); - $siteinfo = ((x($_POST,'siteinfo')) ? trim($_POST['siteinfo']) : ''); - $language = ((x($_POST,'language')) ? notags(trim($_POST['language'])) : 'en'); - $theme = ((x($_POST,'theme')) ? notags(trim($_POST['theme'])) : ''); -// $theme_mobile = ((x($_POST,'theme_mobile')) ? notags(trim($_POST['theme_mobile'])) : ''); -// $site_channel = ((x($_POST,'site_channel')) ? notags(trim($_POST['site_channel'])) : ''); - $maximagesize = ((x($_POST,'maximagesize')) ? intval(trim($_POST['maximagesize'])) : 0); + $sitename = ((x($_POST, 'sitename')) ? notags(trim($_POST['sitename'])) : App::get_hostname()); - $register_policy = ((x($_POST,'register_policy')) ? intval(trim($_POST['register_policy'])) : 0); - $minimum_age = ((x($_POST,'minimum_age')) ? intval(trim($_POST['minimum_age'])) : 13); - $access_policy = ((x($_POST,'access_policy')) ? intval(trim($_POST['access_policy'])) : 0); - $invite_only = ((x($_POST,'invite_only')) ? True : False); - $abandon_days = ((x($_POST,'abandon_days')) ? intval(trim($_POST['abandon_days'])) : 0); + $admininfo = ((x($_POST, 'admininfo')) ? trim($_POST['admininfo']) : false); + $siteinfo = ((x($_POST, 'siteinfo')) ? trim($_POST['siteinfo']) : ''); + $language = ((x($_POST, 'language')) ? notags(trim($_POST['language'])) : 'en'); + $theme = ((x($_POST, 'theme')) ? notags(trim($_POST['theme'])) : ''); +// $theme_mobile = ((x($_POST,'theme_mobile')) ? notags(trim($_POST['theme_mobile'])) : ''); +// $site_channel = ((x($_POST,'site_channel')) ? notags(trim($_POST['site_channel'])) : ''); + $maximagesize = ((x($_POST, 'maximagesize')) ? intval(trim($_POST['maximagesize'])) : 0); - $register_text = ((x($_POST,'register_text')) ? notags(trim($_POST['register_text'])) : ''); - $site_sellpage = ((x($_POST,'site_sellpage')) ? notags(trim($_POST['site_sellpage'])) : ''); - $site_location = ((x($_POST,'site_location')) ? notags(trim($_POST['site_location'])) : ''); - $frontpage = ((x($_POST,'frontpage')) ? notags(trim($_POST['frontpage'])) : ''); - $firstpage = ((x($_POST,'firstpage')) ? notags(trim($_POST['firstpage'])) : 'profiles'); - $first_page = ((x($_POST,'first_page')) ? notags(trim($_POST['first_page'])) : 'profiles'); - // check value after trim - if(! $first_page) { - $first_page = 'profiles'; - } - $mirror_frontpage = ((x($_POST,'mirror_frontpage')) ? intval(trim($_POST['mirror_frontpage'])) : 0); - $directory_server = ((x($_POST,'directory_server')) ? trim($_POST['directory_server']) : ''); - $force_publish = ((x($_POST,'publish_all')) ? True : False); - $open_pubstream = ((x($_POST,'open_pubstream')) ? True : False); - $public_stream_mode = ((x($_POST,'public_stream_mode')) ? intval($_POST['public_stream_mode']) : PUBLIC_STREAM_NONE); - $animations = ((x($_POST,'animations')) ? True : False); - $login_on_homepage = ((x($_POST,'login_on_homepage')) ? True : False); - $enable_context_help = ((x($_POST,'enable_context_help')) ? True : False); - $global_directory = ((x($_POST,'directory_submit_url')) ? notags(trim($_POST['directory_submit_url'])) : ''); - $no_community_page = !((x($_POST,'no_community_page')) ? True : False); - $default_expire_days = ((array_key_exists('default_expire_days',$_POST)) ? intval($_POST['default_expire_days']) : 0); - $active_expire_days = ((array_key_exists('active_expire_days',$_POST)) ? intval($_POST['active_expire_days']) : 7); - $max_imported_follow = ((x($_POST,'max_imported_follow')) ? intval(trim($_POST['max_imported_follow'])) : MAX_IMPORTED_FOLLOW); + $register_policy = ((x($_POST, 'register_policy')) ? intval(trim($_POST['register_policy'])) : 0); + $minimum_age = ((x($_POST, 'minimum_age')) ? intval(trim($_POST['minimum_age'])) : 13); + $access_policy = ((x($_POST, 'access_policy')) ? intval(trim($_POST['access_policy'])) : 0); + $invite_only = ((x($_POST, 'invite_only')) ? true : false); + $abandon_days = ((x($_POST, 'abandon_days')) ? intval(trim($_POST['abandon_days'])) : 0); - $reply_address = ((array_key_exists('reply_address',$_POST) && trim($_POST['reply_address'])) ? trim($_POST['reply_address']) : 'noreply@' . \App::get_hostname()); - $from_email = ((array_key_exists('from_email',$_POST) && trim($_POST['from_email'])) ? trim($_POST['from_email']) : 'Administrator@' . \App::get_hostname()); - $from_email_name = ((array_key_exists('from_email_name',$_POST) && trim($_POST['from_email_name'])) ? trim($_POST['from_email_name']) : \Zotlabs\Lib\System::get_site_name()); + $register_text = ((x($_POST, 'register_text')) ? notags(trim($_POST['register_text'])) : ''); + $site_sellpage = ((x($_POST, 'site_sellpage')) ? notags(trim($_POST['site_sellpage'])) : ''); + $site_location = ((x($_POST, 'site_location')) ? notags(trim($_POST['site_location'])) : ''); + $frontpage = ((x($_POST, 'frontpage')) ? notags(trim($_POST['frontpage'])) : ''); + $firstpage = ((x($_POST, 'firstpage')) ? notags(trim($_POST['firstpage'])) : 'profiles'); + $first_page = ((x($_POST, 'first_page')) ? notags(trim($_POST['first_page'])) : 'profiles'); + // check value after trim + if (!$first_page) { + $first_page = 'profiles'; + } + $mirror_frontpage = ((x($_POST, 'mirror_frontpage')) ? intval(trim($_POST['mirror_frontpage'])) : 0); + $directory_server = ((x($_POST, 'directory_server')) ? trim($_POST['directory_server']) : ''); + $force_publish = ((x($_POST, 'publish_all')) ? true : false); + $open_pubstream = ((x($_POST, 'open_pubstream')) ? true : false); + $public_stream_mode = ((x($_POST, 'public_stream_mode')) ? intval($_POST['public_stream_mode']) : PUBLIC_STREAM_NONE); + $animations = ((x($_POST, 'animations')) ? true : false); + $login_on_homepage = ((x($_POST, 'login_on_homepage')) ? true : false); + $enable_context_help = ((x($_POST, 'enable_context_help')) ? true : false); + $global_directory = ((x($_POST, 'directory_submit_url')) ? notags(trim($_POST['directory_submit_url'])) : ''); + $no_community_page = !((x($_POST, 'no_community_page')) ? true : false); + $default_expire_days = ((array_key_exists('default_expire_days', $_POST)) ? intval($_POST['default_expire_days']) : 0); + $active_expire_days = ((array_key_exists('active_expire_days', $_POST)) ? intval($_POST['active_expire_days']) : 7); + $max_imported_follow = ((x($_POST, 'max_imported_follow')) ? intval(trim($_POST['max_imported_follow'])) : MAX_IMPORTED_FOLLOW); - $verifyssl = ((x($_POST,'verifyssl')) ? True : False); - $proxyuser = ((x($_POST,'proxyuser')) ? notags(trim($_POST['proxyuser'])) : ''); - $proxy = ((x($_POST,'proxy')) ? notags(trim($_POST['proxy'])) : ''); - $timeout = ((x($_POST,'timeout')) ? intval(trim($_POST['timeout'])) : 60); - $post_timeout = ((x($_POST,'post_timeout')) ? intval(trim($_POST['post_timeout'])) : 90); - $show_like_counts = ((x($_POST,'show_like_counts')) ? intval(trim($_POST['show_like_counts'])) : 0); - $cache_images = ((x($_POST,'cache_images')) ? intval(trim($_POST['cache_images'])) : 0); - $delivery_interval = ((x($_POST,'delivery_interval'))? intval(trim($_POST['delivery_interval'])) : 0); - $delivery_batch_count = ((x($_POST,'delivery_batch_count') && $_POST['delivery_batch_count'] > 0)? intval(trim($_POST['delivery_batch_count'])) : 3); - $poll_interval = ((x($_POST,'poll_interval')) ? intval(trim($_POST['poll_interval'])) : 0); - $maxloadavg = ((x($_POST,'maxloadavg')) ? intval(trim($_POST['maxloadavg'])) : 50); -// $feed_contacts = ((x($_POST,'feed_contacts')) ? intval($_POST['feed_contacts']) : 0); - $ap_contacts = ((x($_POST,'ap_contacts')) ? intval($_POST['ap_contacts']) : 0); - $verify_email = ((x($_POST,'verify_email')) ? 1 : 0); - $imagick_path = ((x($_POST,'imagick_path')) ? trim($_POST['imagick_path']) : ''); - $force_queue = ((intval($_POST['force_queue']) > 0) ? intval($_POST['force_queue']) : 3000); - $pub_incl = escape_tags(trim($_POST['pub_incl'])); - $pub_excl = escape_tags(trim($_POST['pub_excl'])); + $reply_address = ((array_key_exists('reply_address', $_POST) && trim($_POST['reply_address'])) ? trim($_POST['reply_address']) : 'noreply@' . App::get_hostname()); + $from_email = ((array_key_exists('from_email', $_POST) && trim($_POST['from_email'])) ? trim($_POST['from_email']) : 'Administrator@' . App::get_hostname()); + $from_email_name = ((array_key_exists('from_email_name', $_POST) && trim($_POST['from_email_name'])) ? trim($_POST['from_email_name']) : System::get_site_name()); - $permissions_role = escape_tags(trim($_POST['permissions_role'])); + $verifyssl = ((x($_POST, 'verifyssl')) ? true : false); + $proxyuser = ((x($_POST, 'proxyuser')) ? notags(trim($_POST['proxyuser'])) : ''); + $proxy = ((x($_POST, 'proxy')) ? notags(trim($_POST['proxy'])) : ''); + $timeout = ((x($_POST, 'timeout')) ? intval(trim($_POST['timeout'])) : 60); + $post_timeout = ((x($_POST, 'post_timeout')) ? intval(trim($_POST['post_timeout'])) : 90); + $show_like_counts = ((x($_POST, 'show_like_counts')) ? intval(trim($_POST['show_like_counts'])) : 0); + $cache_images = ((x($_POST, 'cache_images')) ? intval(trim($_POST['cache_images'])) : 0); + $delivery_interval = ((x($_POST, 'delivery_interval')) ? intval(trim($_POST['delivery_interval'])) : 0); + $delivery_batch_count = ((x($_POST, 'delivery_batch_count') && $_POST['delivery_batch_count'] > 0) ? intval(trim($_POST['delivery_batch_count'])) : 3); + $poll_interval = ((x($_POST, 'poll_interval')) ? intval(trim($_POST['poll_interval'])) : 0); + $maxloadavg = ((x($_POST, 'maxloadavg')) ? intval(trim($_POST['maxloadavg'])) : 50); +// $feed_contacts = ((x($_POST,'feed_contacts')) ? intval($_POST['feed_contacts']) : 0); + $ap_contacts = ((x($_POST, 'ap_contacts')) ? intval($_POST['ap_contacts']) : 0); + $verify_email = ((x($_POST, 'verify_email')) ? 1 : 0); + $imagick_path = ((x($_POST, 'imagick_path')) ? trim($_POST['imagick_path']) : ''); + $force_queue = ((intval($_POST['force_queue']) > 0) ? intval($_POST['force_queue']) : 3000); + $pub_incl = escape_tags(trim($_POST['pub_incl'])); + $pub_excl = escape_tags(trim($_POST['pub_excl'])); -// set_config('system', 'feed_contacts', $feed_contacts); - set_config('system', 'activitypub', $ap_contacts); - set_config('system', 'delivery_interval', $delivery_interval); - set_config('system', 'delivery_batch_count', $delivery_batch_count); - set_config('system', 'poll_interval', $poll_interval); - set_config('system', 'maxloadavg', $maxloadavg); - set_config('system', 'frontpage', $frontpage); - set_config('system', 'cache_images', $cache_images); - set_config('system', 'sellpage', $site_sellpage); - set_config('system', 'workflow_channel_next', $first_page); - set_config('system', 'site_location', $site_location); - set_config('system', 'mirror_frontpage', $mirror_frontpage); - set_config('system', 'sitename', $sitename); - set_config('system', 'login_on_homepage', $login_on_homepage); - set_config('system', 'enable_context_help', $enable_context_help); - set_config('system', 'verify_email', $verify_email); - set_config('system', 'default_expire_days', $default_expire_days); - set_config('system', 'active_expire_days', $active_expire_days); - set_config('system', 'reply_address', $reply_address); - set_config('system', 'from_email', $from_email); - set_config('system', 'from_email_name' , $from_email_name); - set_config('system', 'imagick_convert_path' , $imagick_path); - set_config('system', 'default_permissions_role', $permissions_role); - set_config('system', 'show_like_counts', $show_like_counts); - set_config('system', 'pubstream_incl',$pub_incl); - set_config('system', 'pubstream_excl',$pub_excl); - set_config('system', 'max_imported_follow', $max_imported_follow); - set_config('system', 'animated_avatars', $animations); - + $permissions_role = escape_tags(trim($_POST['permissions_role'])); - if ($directory_server) { - set_config('system','directory_server',$directory_server); - } - - if ($admininfo == '') { - del_config('system', 'admininfo'); - } - else { - require_once('include/text.php'); - linkify_tags($admininfo, local_channel()); - set_config('system', 'admininfo', $admininfo); - } - set_config('system','siteinfo',$siteinfo); - - // sync sitename and siteinfo updates to the system channel - - q("update profile set about = '%s' where uid = %d and is_default = 1", - dbesc($siteinfo), - intval($sys['channel_id']) - ); - q("update profile set fullname = '%s' where uid = %d and is_default = 1", - dbesc($sitename), - intval($sys['channel_id']) - ); - q("update channel set channel_name = '%s' where channel_id = %d", - dbesc($sitename), - intval($sys['channel_id']) - ); - q("update xchan set xchan_name = '%s' , xchan_name_updated = '%s' where xchan_hash = '%s'", - dbesc($sitename), - dbesc(datetime_convert()), - dbesc($sys['channel_hash']) - ); - - set_config('system', 'language', $language); - set_config('system', 'theme', $theme); - // set_config('system','site_channel', $site_channel); - set_config('system','maximagesize', $maximagesize); - - set_config('system','register_policy', $register_policy); - set_config('system','minimum_age', $minimum_age); - set_config('system','invitation_only', $invite_only); - set_config('system','access_policy', $access_policy); - set_config('system','account_abandon_days', $abandon_days); - set_config('system','register_text', $register_text); - set_config('system','publish_all', $force_publish); - set_config('system','public_stream_mode', $public_stream_mode); - set_config('system','open_pubstream', $open_pubstream); - set_config('system','force_queue_threshold', $force_queue); - if ($global_directory == '') { - del_config('system', 'directory_submit_url'); - } else { - set_config('system', 'directory_submit_url', $global_directory); - } - - set_config('system','no_community_page', $no_community_page); - set_config('system','no_utf', $no_utf); - set_config('system','verifyssl', $verifyssl); - set_config('system','proxyuser', $proxyuser); - set_config('system','proxy', $proxy); - set_config('system','curl_timeout', $timeout); - set_config('system','curl_post_timeout', $post_timeout); - - info( t('Site settings updated.') . EOL); - goaway(z_root() . '/admin/site' ); - } - - /** - * @brief Admin page site. - * - * @return string with HTML - */ - - function get() { - - /* Installed langs */ - $lang_choices = []; - $langs = glob('view/*/strings.php'); - - if (is_array($langs) && count($langs)) { - if (! in_array('view/en/strings.php',$langs)) - $langs[] = 'view/en/'; - asort($langs); - foreach ($langs as $l) { - $t = explode("/",$l); - $lang_choices[$t[1]] = $t[1]; - } - } - - /* Installed themes */ - $theme_choices_mobile["---"] = t("Default"); - $theme_choices = []; - $files = glob('view/theme/*'); - if ($files) { - foreach ($files as $file) { - $vars = ''; - $f = basename($file); - - $info = get_theme_info($f); - $compatible = check_plugin_versions($info); - if (! $compatible) { - $theme_choices[$f] = $theme_choices_mobile[$f] = sprintf(t('%s - (Incompatible)'), $f); - continue; - } - - if (file_exists($file . '/library')) - continue; - if (file_exists($file . '/mobile')) - $vars = t('mobile'); - if (file_exists($file . '/experimental')) - $vars .= t('experimental'); - if (file_exists($file . '/unsupported')) - $vars .= t('unsupported'); - if ($vars) { - $theme_choices[$f] = $f . ' (' . $vars . ')'; - $theme_choices_mobile[$f] = $f . ' (' . $vars . ')'; - } - else { - $theme_choices[$f] = $f; - $theme_choices_mobile[$f] = $f; - } - } - } - - $dir_choices = null; - $dirmode = get_config('system','directory_mode'); - $realm = get_directory_realm(); - - // directory server should not be set or settable unless we are a directory client - // avoid older redmatrix servers which don't have modern encryption - - if ($dirmode == DIRECTORY_MODE_NORMAL) { - $x = q("select site_url from site where site_flags in (%d,%d) and site_realm = '%s' and site_dead = 0", - intval(DIRECTORY_MODE_SECONDARY), - intval(DIRECTORY_MODE_PRIMARY), - dbesc($realm) - ); - if ($x) { - $dir_choices = []; - foreach ($x as $xx) { - $dir_choices[$xx['site_url']] = $xx['site_url']; - } - } - } +// set_config('system', 'feed_contacts', $feed_contacts); + set_config('system', 'activitypub', $ap_contacts); + set_config('system', 'delivery_interval', $delivery_interval); + set_config('system', 'delivery_batch_count', $delivery_batch_count); + set_config('system', 'poll_interval', $poll_interval); + set_config('system', 'maxloadavg', $maxloadavg); + set_config('system', 'frontpage', $frontpage); + set_config('system', 'cache_images', $cache_images); + set_config('system', 'sellpage', $site_sellpage); + set_config('system', 'workflow_channel_next', $first_page); + set_config('system', 'site_location', $site_location); + set_config('system', 'mirror_frontpage', $mirror_frontpage); + set_config('system', 'sitename', $sitename); + set_config('system', 'login_on_homepage', $login_on_homepage); + set_config('system', 'enable_context_help', $enable_context_help); + set_config('system', 'verify_email', $verify_email); + set_config('system', 'default_expire_days', $default_expire_days); + set_config('system', 'active_expire_days', $active_expire_days); + set_config('system', 'reply_address', $reply_address); + set_config('system', 'from_email', $from_email); + set_config('system', 'from_email_name', $from_email_name); + set_config('system', 'imagick_convert_path', $imagick_path); + set_config('system', 'default_permissions_role', $permissions_role); + set_config('system', 'show_like_counts', $show_like_counts); + set_config('system', 'pubstream_incl', $pub_incl); + set_config('system', 'pubstream_excl', $pub_excl); + set_config('system', 'max_imported_follow', $max_imported_follow); + set_config('system', 'animated_avatars', $animations); - /* Admin Info */ - - $admininfo = get_config('system', 'admininfo'); + if ($directory_server) { + set_config('system', 'directory_server', $directory_server); + } - /* Register policy */ - $register_choices = [ - REGISTER_CLOSED => t("No"), - REGISTER_APPROVE => t("Yes - with approval"), - REGISTER_OPEN => t("Yes") - ]; + if ($admininfo == '') { + del_config('system', 'admininfo'); + } else { + require_once('include/text.php'); + linkify_tags($admininfo, local_channel()); + set_config('system', 'admininfo', $admininfo); + } + set_config('system', 'siteinfo', $siteinfo); - /* Acess policy */ - $access_choices = [ - ACCESS_PRIVATE => t("My site is not a public server"), - ACCESS_FREE => t("My site provides free public access"), - ACCESS_PAID => t("My site provides paid public access"), - ACCESS_TIERED => t("My site provides free public access and premium paid plans") - ]; + // sync sitename and siteinfo updates to the system channel - $perm_roles = PermissionRoles::roles(); - $default_role = get_config('system','default_permissions_role','social'); + q( + "update profile set about = '%s' where uid = %d and is_default = 1", + dbesc($siteinfo), + intval($sys['channel_id']) + ); + q( + "update profile set fullname = '%s' where uid = %d and is_default = 1", + dbesc($sitename), + intval($sys['channel_id']) + ); + q( + "update channel set channel_name = '%s' where channel_id = %d", + dbesc($sitename), + intval($sys['channel_id']) + ); + q( + "update xchan set xchan_name = '%s' , xchan_name_updated = '%s' where xchan_hash = '%s'", + dbesc($sitename), + dbesc(datetime_convert()), + dbesc($sys['channel_hash']) + ); - $role = [ 'permissions_role' , t('Default permission role for new accounts'), $default_role, t('This role will be used for the first channel created after registration.'),$perm_roles ]; + set_config('system', 'language', $language); + set_config('system', 'theme', $theme); + // set_config('system','site_channel', $site_channel); + set_config('system', 'maximagesize', $maximagesize); + + set_config('system', 'register_policy', $register_policy); + set_config('system', 'minimum_age', $minimum_age); + set_config('system', 'invitation_only', $invite_only); + set_config('system', 'access_policy', $access_policy); + set_config('system', 'account_abandon_days', $abandon_days); + set_config('system', 'register_text', $register_text); + set_config('system', 'publish_all', $force_publish); + set_config('system', 'public_stream_mode', $public_stream_mode); + set_config('system', 'open_pubstream', $open_pubstream); + set_config('system', 'force_queue_threshold', $force_queue); + if ($global_directory == '') { + del_config('system', 'directory_submit_url'); + } else { + set_config('system', 'directory_submit_url', $global_directory); + } + + set_config('system', 'no_community_page', $no_community_page); + set_config('system', 'no_utf', $no_utf); + set_config('system', 'verifyssl', $verifyssl); + set_config('system', 'proxyuser', $proxyuser); + set_config('system', 'proxy', $proxy); + set_config('system', 'curl_timeout', $timeout); + set_config('system', 'curl_post_timeout', $post_timeout); + + info(t('Site settings updated.') . EOL); + goaway(z_root() . '/admin/site'); + } + + /** + * @brief Admin page site. + * + * @return string with HTML + */ + + public function get() + { + + /* Installed langs */ + $lang_choices = []; + $langs = glob('view/*/strings.php'); + + if (is_array($langs) && count($langs)) { + if (!in_array('view/en/strings.php', $langs)) { + $langs[] = 'view/en/'; + } + asort($langs); + foreach ($langs as $l) { + $t = explode("/", $l); + $lang_choices[$t[1]] = $t[1]; + } + } + + /* Installed themes */ + $theme_choices_mobile["---"] = t("Default"); + $theme_choices = []; + $files = glob('view/theme/*'); + if ($files) { + foreach ($files as $file) { + $vars = ''; + $f = basename($file); + + $info = get_theme_info($f); + $compatible = check_plugin_versions($info); + if (!$compatible) { + $theme_choices[$f] = $theme_choices_mobile[$f] = sprintf(t('%s - (Incompatible)'), $f); + continue; + } + + if (file_exists($file . '/library')) { + continue; + } + if (file_exists($file . '/mobile')) { + $vars = t('mobile'); + } + if (file_exists($file . '/experimental')) { + $vars .= t('experimental'); + } + if (file_exists($file . '/unsupported')) { + $vars .= t('unsupported'); + } + if ($vars) { + $theme_choices[$f] = $f . ' (' . $vars . ')'; + $theme_choices_mobile[$f] = $f . ' (' . $vars . ')'; + } else { + $theme_choices[$f] = $f; + $theme_choices_mobile[$f] = $f; + } + } + } + + $dir_choices = null; + $dirmode = get_config('system', 'directory_mode'); + $realm = get_directory_realm(); + + // directory server should not be set or settable unless we are a directory client + // avoid older redmatrix servers which don't have modern encryption + + if ($dirmode == DIRECTORY_MODE_NORMAL) { + $x = q( + "select site_url from site where site_flags in (%d,%d) and site_realm = '%s' and site_dead = 0", + intval(DIRECTORY_MODE_SECONDARY), + intval(DIRECTORY_MODE_PRIMARY), + dbesc($realm) + ); + if ($x) { + $dir_choices = []; + foreach ($x as $xx) { + $dir_choices[$xx['site_url']] = $xx['site_url']; + } + } + } - $homelogin = get_config('system','login_on_homepage'); - $enable_context_help = get_config('system','enable_context_help'); + /* Admin Info */ - return replace_macros(get_markup_template('admin_site.tpl'), [ - '$title' => t('Administration'), - '$page' => t('Site'), - '$submit' => t('Submit'), - '$h_basic' => t('Site Configuration'), - '$registration' => t('Registration'), - '$upload' => t('File upload'), - '$corporate' => t('Policies'), - '$advanced' => t('Advanced'), - '$baseurl' => z_root(), - '$sitename' => [ 'sitename', t("Site name"), htmlspecialchars(get_config('system','sitename', App::get_hostname()), ENT_QUOTES, 'UTF-8'),'' ], - '$admininfo' => [ 'admininfo', t("Administrator Information"), $admininfo, t("Contact information for site administrators. Displayed on siteinfo page. BBCode may be used here.") ], - '$siteinfo' => [ 'siteinfo', t('Site Information'), get_config('system','siteinfo'), t("Publicly visible description of this site. Displayed on siteinfo page. BBCode may be used here.") ], - '$language' => [ 'language', t("System language"), get_config('system','language','en'), "", $lang_choices ], - '$theme' => [ 'theme', t("System theme"), get_config('system','theme'), t("Default system theme - may be over-ridden by user profiles - change theme settings"), $theme_choices ], -// '$theme_mobile' => [ 'theme_mobile', t("Mobile system theme"), get_config('system','mobile_theme'), t("Theme for mobile devices"), $theme_choices_mobile ], -// '$site_channel' => [ 'site_channel', t("Channel to use for this website's static pages"), get_config('system','site_channel'), t("Site Channel") ], - '$ap_contacts' => [ 'ap_contacts', t('ActivityPub protocol'),get_config('system','activitypub', ACTIVITYPUB_ENABLED),t('Provides access to software supporting the ActivityPub protocol.') ], - '$maximagesize' => [ 'maximagesize', t("Maximum image size"), intval(get_config('system','maximagesize')), t("Maximum size in bytes of uploaded images. Default is 0, which means no limits.") ], - '$cache_images' => [ 'cache_images', t('Cache all public images'), intval(get_config('system','cache_images',1)), t('If disabled, proxy non-SSL images, but do not store locally') ], - '$register_policy' => [ 'register_policy', t("Does this site allow new member registration?"), get_config('system','register_policy'), "", $register_choices ], - '$invite_only' => [ 'invite_only', t("Invitation only"), get_config('system','invitation_only'), t("Only allow new member registrations with an invitation code. New member registration must be allowed for this to work.") ], - '$invite_working' => defined('INVITE_WORKING'), - '$minimum_age' => [ 'minimum_age', t("Minimum age"), (x(get_config('system','minimum_age'))?get_config('system','minimum_age'):13), t("Minimum age (in years) for who may register on this site.") ], - '$access_policy' => [ 'access_policy', t("Which best describes the types of account offered by this hub?"), get_config('system','access_policy'), t("If a public server policy is selected, this information may be displayed on the public server site list."), $access_choices ], - '$register_text' => [ 'register_text', t("Register text"), htmlspecialchars(get_config('system','register_text'), ENT_QUOTES, 'UTF-8'), t("Will be displayed prominently on the registration page.") ], - '$role' => $role, - '$frontpage' => [ 'frontpage', t("Site homepage to show visitors (default: login box)"), get_config('system','frontpage'), t("example: 'public' to show public stream, 'page/sys/home' to show a system webpage called 'home' or 'include:home.html' to include a file.") ], - '$mirror_frontpage' => [ 'mirror_frontpage', t("Preserve site homepage URL"), get_config('system','mirror_frontpage'), t('Present the site homepage in a frame at the original location instead of redirecting') ], - '$abandon_days' => [ 'abandon_days', t('Accounts abandoned after x days'), get_config('system','account_abandon_days'), t('Will not waste system resources polling external sites for abandonded accounts. Enter 0 for no time limit.') ], - '$block_public_dir' => [ 'block_public_directory', t('Block directory from visitors'), get_config('system','block_public_directory',true), t('Only allow authenticated access to directory.') ], - '$verify_email' => [ 'verify_email', t("Verify Email Addresses"), get_config('system','verify_email'), t("Check to verify email addresses used in account registration (recommended).") ], - '$force_publish' => [ 'publish_all', t("Force publish in directory"), get_config('system','publish_all'), t("Check to force all profiles on this site to be listed in the site directory.") ], + $admininfo = get_config('system', 'admininfo'); - '$public_stream_mode' => [ 'public_stream_mode', t('Public stream'), intval(get_config('system','public_stream_mode',0)), t('Provide a Public stream on your site. This content is unmoderated.'), [ - 0 => t('the Public stream is disabled'), - 1 => t('the Public stream contains public conversations from this site only'), - 2 => t('the Public stream contains public conversations from anywhere on the internet'), - ]], - - '$open_pubstream' => [ 'open_pubstream', t('Allow anybody on the internet to access the Public stream'), get_config('system','open_pubstream',0), t('Default is to only allow viewing by site members. Warning: this content is unmoderated.') ], - '$show_like_counts' => [ 'show_like_counts', t('Show numbers of likes and dislikes in conversations'), get_config('system','show_like_counts',1), t('If disabled, the presence of likes and dislikes will be shown, but without totals.') ], - '$animations' => [ 'animations', t('Permit animated profile photos'), get_config('system','animated_avatars',true), t('Changing this may take several days to work through the system') ], - '$incl' => [ 'pub_incl',t('Only import Public stream posts with this text'), get_config('system','pubstream_incl'),t('words one per line or #tags or /patterns/ or lang=xx, leave blank to import all posts') ], - '$excl' => [ 'pub_excl',t('Do not import Public stream posts with this text'), get_config('system','pubstream_excl'),t('words one per line or #tags or /patterns/ or lang=xx, leave blank to import all posts') ], - '$max_imported_follow' => [ 'max_imported_follow', t('Maximum number of imported friends of friends'), get_config('system','max_imported_follow', MAX_IMPORTED_FOLLOW), t('Warning: higher numbers will improve the quality of friend suggestions and directory results but can exponentially increase resource usage') ], - '$login_on_homepage' => [ 'login_on_homepage', t("Login on Homepage"),((intval($homelogin) || $homelogin === false) ? 1 : '') , t("Present a login box to visitors on the home page if no other content has been configured.") ], - '$enable_context_help' => [ 'enable_context_help', t("Enable context help"),((intval($enable_context_help) === 1 || $enable_context_help === false) ? 1 : 0) , t("Display contextual help for the current page when the help button is pressed.") ], - '$reply_address' => [ 'reply_address', t('Reply-to email address for system generated email.'), get_config('system','reply_address','noreply@' . \App::get_hostname()),'' ], - '$from_email' => [ 'from_email', t('Sender (From) email address for system generated email.'), get_config('system','from_email','Administrator@' . \App::get_hostname()),'' ], - '$from_email_name' => [ 'from_email_name', t('Display name of email sender for system generated email.'), get_config('system','from_email_name',\Zotlabs\Lib\System::get_site_name()),'' ], - '$directory_server' => (($dir_choices) ? [ 'directory_server', t("Directory Server URL"), get_config('system','directory_server'), t("Default directory server"), $dir_choices ] : null), - '$proxyuser' => [ 'proxyuser', t("Proxy user"), get_config('system','proxyuser'), "" ], - '$proxy' => [ 'proxy', t("Proxy URL"), get_config('system','proxy'), "" ], - '$timeout' => [ 'timeout', t("Network fetch timeout"), (x(get_config('system','curl_timeout'))?get_config('system','curl_timeout'):60), t("Value is in seconds. Set to 0 for unlimited (not recommended).") ], - '$post_timeout' => [ 'post_timeout', t("Network post timeout"), (x(get_config('system','curl_post_timeout'))?get_config('system','curl_post_timeout'):90), t("Value is in seconds. Set to 0 for unlimited (not recommended).") ], - '$delivery_interval' => [ 'delivery_interval', t("Delivery interval"), (x(get_config('system','delivery_interval'))?get_config('system','delivery_interval'):2), t("Delay background delivery processes by this many seconds to reduce system load. Recommend: 4-5 for shared hosts, 2-3 for virtual private servers. 0-1 for large dedicated servers.") ], - '$delivery_batch_count' => [ 'delivery_batch_count', t('Deliveries per process'),(x(get_config('system','delivery_batch_count'))?get_config('system','delivery_batch_count'):3), t("Number of deliveries to attempt in a single operating system process. Adjust if necessary to tune system performance. Recommend: 1-5.") ], - '$force_queue' => [ 'force_queue', t("Queue Threshold"), get_config('system','force_queue_threshold',3000), t("Always defer immediate delivery if queue contains more than this number of entries.") ], - '$poll_interval' => [ 'poll_interval', t("Poll interval"), (x(get_config('system','poll_interval'))?get_config('system','poll_interval'):2), t("Delay background polling processes by this many seconds to reduce system load. If 0, use delivery interval.") ], - '$imagick_path' => [ 'imagick_path', t("Path to ImageMagick convert program"), get_config('system','imagick_convert_path'), t("If set, use this program to generate photo thumbnails for huge images ( > 4000 pixels in either dimension), otherwise memory exhaustion may occur. Example: /usr/bin/convert") ], - '$maxloadavg' => [ 'maxloadavg', t("Maximum Load Average"), ((intval(get_config('system','maxloadavg')) > 0)?get_config('system','maxloadavg'):50), t("Maximum system load before delivery and poll processes are deferred - default 50.") ], - '$default_expire_days' => [ 'default_expire_days', t('Expiration period in days for imported streams and cached images'), intval(get_config('system','default_expire_days',60)), t('0 for no expiration of imported content') ], - '$active_expire_days' => [ 'active_expire_days', t('Do not expire any posts which have comments less than this many days ago'), intval(get_config('system','active_expire_days',7)), '' ], - '$sellpage' => [ 'site_sellpage', t('Public servers: Optional landing (marketing) webpage for new registrants'), get_config('system','sellpage',''), sprintf( t('Create this page first. Default is %s/register'),z_root()) ], - '$first_page' => [ 'first_page', t('Page to display after creating a new channel'), get_config('system','workflow_channel_next','profiles'), t('Default: profiles') ], - '$location' => [ 'site_location', t('Site location'), get_config('system','site_location',''), t('Region or country - shared with other sites') ], - '$form_security_token' => get_form_security_token("admin_site"), - ]); - } + /* Register policy */ + $register_choices = [ + REGISTER_CLOSED => t("No"), + REGISTER_APPROVE => t("Yes - with approval"), + REGISTER_OPEN => t("Yes") + ]; + /* Acess policy */ + $access_choices = [ + ACCESS_PRIVATE => t("My site is not a public server"), + ACCESS_FREE => t("My site provides free public access"), + ACCESS_PAID => t("My site provides paid public access"), + ACCESS_TIERED => t("My site provides free public access and premium paid plans") + ]; + + $perm_roles = PermissionRoles::roles(); + $default_role = get_config('system', 'default_permissions_role', 'social'); + + $role = ['permissions_role', t('Default permission role for new accounts'), $default_role, t('This role will be used for the first channel created after registration.'), $perm_roles]; + + + $homelogin = get_config('system', 'login_on_homepage'); + $enable_context_help = get_config('system', 'enable_context_help'); + + return replace_macros(get_markup_template('admin_site.tpl'), [ + '$title' => t('Administration'), + '$page' => t('Site'), + '$submit' => t('Submit'), + '$h_basic' => t('Site Configuration'), + '$registration' => t('Registration'), + '$upload' => t('File upload'), + '$corporate' => t('Policies'), + '$advanced' => t('Advanced'), + '$baseurl' => z_root(), + '$sitename' => ['sitename', t("Site name"), htmlspecialchars(get_config('system', 'sitename', App::get_hostname()), ENT_QUOTES, 'UTF-8'), ''], + '$admininfo' => ['admininfo', t("Administrator Information"), $admininfo, t("Contact information for site administrators. Displayed on siteinfo page. BBCode may be used here.")], + '$siteinfo' => ['siteinfo', t('Site Information'), get_config('system', 'siteinfo'), t("Publicly visible description of this site. Displayed on siteinfo page. BBCode may be used here.")], + '$language' => ['language', t("System language"), get_config('system', 'language', 'en'), "", $lang_choices], + '$theme' => ['theme', t("System theme"), get_config('system', 'theme'), t("Default system theme - may be over-ridden by user profiles - change theme settings"), $theme_choices], +// '$theme_mobile' => [ 'theme_mobile', t("Mobile system theme"), get_config('system','mobile_theme'), t("Theme for mobile devices"), $theme_choices_mobile ], +// '$site_channel' => [ 'site_channel', t("Channel to use for this website's static pages"), get_config('system','site_channel'), t("Site Channel") ], + '$ap_contacts' => ['ap_contacts', t('ActivityPub protocol'), get_config('system', 'activitypub', ACTIVITYPUB_ENABLED), t('Provides access to software supporting the ActivityPub protocol.')], + '$maximagesize' => ['maximagesize', t("Maximum image size"), intval(get_config('system', 'maximagesize')), t("Maximum size in bytes of uploaded images. Default is 0, which means no limits.")], + '$cache_images' => ['cache_images', t('Cache all public images'), intval(get_config('system', 'cache_images', 1)), t('If disabled, proxy non-SSL images, but do not store locally')], + '$register_policy' => ['register_policy', t("Does this site allow new member registration?"), get_config('system', 'register_policy'), "", $register_choices], + '$invite_only' => ['invite_only', t("Invitation only"), get_config('system', 'invitation_only'), t("Only allow new member registrations with an invitation code. New member registration must be allowed for this to work.")], + '$invite_working' => defined('INVITE_WORKING'), + '$minimum_age' => ['minimum_age', t("Minimum age"), (x(get_config('system', 'minimum_age')) ? get_config('system', 'minimum_age') : 13), t("Minimum age (in years) for who may register on this site.")], + '$access_policy' => ['access_policy', t("Which best describes the types of account offered by this hub?"), get_config('system', 'access_policy'), t("If a public server policy is selected, this information may be displayed on the public server site list."), $access_choices], + '$register_text' => ['register_text', t("Register text"), htmlspecialchars(get_config('system', 'register_text'), ENT_QUOTES, 'UTF-8'), t("Will be displayed prominently on the registration page.")], + '$role' => $role, + '$frontpage' => ['frontpage', t("Site homepage to show visitors (default: login box)"), get_config('system', 'frontpage'), t("example: 'public' to show public stream, 'page/sys/home' to show a system webpage called 'home' or 'include:home.html' to include a file.")], + '$mirror_frontpage' => ['mirror_frontpage', t("Preserve site homepage URL"), get_config('system', 'mirror_frontpage'), t('Present the site homepage in a frame at the original location instead of redirecting')], + '$abandon_days' => ['abandon_days', t('Accounts abandoned after x days'), get_config('system', 'account_abandon_days'), t('Will not waste system resources polling external sites for abandonded accounts. Enter 0 for no time limit.')], + '$block_public_dir' => ['block_public_directory', t('Block directory from visitors'), get_config('system', 'block_public_directory', true), t('Only allow authenticated access to directory.')], + '$verify_email' => ['verify_email', t("Verify Email Addresses"), get_config('system', 'verify_email'), t("Check to verify email addresses used in account registration (recommended).")], + '$force_publish' => ['publish_all', t("Force publish in directory"), get_config('system', 'publish_all'), t("Check to force all profiles on this site to be listed in the site directory.")], + + '$public_stream_mode' => ['public_stream_mode', t('Public stream'), intval(get_config('system', 'public_stream_mode', 0)), t('Provide a Public stream on your site. This content is unmoderated.'), [ + 0 => t('the Public stream is disabled'), + 1 => t('the Public stream contains public conversations from this site only'), + 2 => t('the Public stream contains public conversations from anywhere on the internet'), + ]], + + '$open_pubstream' => ['open_pubstream', t('Allow anybody on the internet to access the Public stream'), get_config('system', 'open_pubstream', 0), t('Default is to only allow viewing by site members. Warning: this content is unmoderated.')], + '$show_like_counts' => ['show_like_counts', t('Show numbers of likes and dislikes in conversations'), get_config('system', 'show_like_counts', 1), t('If disabled, the presence of likes and dislikes will be shown, but without totals.')], + '$animations' => ['animations', t('Permit animated profile photos'), get_config('system', 'animated_avatars', true), t('Changing this may take several days to work through the system')], + '$incl' => ['pub_incl', t('Only import Public stream posts with this text'), get_config('system', 'pubstream_incl'), t('words one per line or #tags or /patterns/ or lang=xx, leave blank to import all posts')], + '$excl' => ['pub_excl', t('Do not import Public stream posts with this text'), get_config('system', 'pubstream_excl'), t('words one per line or #tags or /patterns/ or lang=xx, leave blank to import all posts')], + '$max_imported_follow' => ['max_imported_follow', t('Maximum number of imported friends of friends'), get_config('system', 'max_imported_follow', MAX_IMPORTED_FOLLOW), t('Warning: higher numbers will improve the quality of friend suggestions and directory results but can exponentially increase resource usage')], + '$login_on_homepage' => ['login_on_homepage', t("Login on Homepage"), ((intval($homelogin) || $homelogin === false) ? 1 : ''), t("Present a login box to visitors on the home page if no other content has been configured.")], + '$enable_context_help' => ['enable_context_help', t("Enable context help"), ((intval($enable_context_help) === 1 || $enable_context_help === false) ? 1 : 0), t("Display contextual help for the current page when the help button is pressed.")], + '$reply_address' => ['reply_address', t('Reply-to email address for system generated email.'), get_config('system', 'reply_address', 'noreply@' . App::get_hostname()), ''], + '$from_email' => ['from_email', t('Sender (From) email address for system generated email.'), get_config('system', 'from_email', 'Administrator@' . App::get_hostname()), ''], + '$from_email_name' => ['from_email_name', t('Display name of email sender for system generated email.'), get_config('system', 'from_email_name', System::get_site_name()), ''], + '$directory_server' => (($dir_choices) ? ['directory_server', t("Directory Server URL"), get_config('system', 'directory_server'), t("Default directory server"), $dir_choices] : null), + '$proxyuser' => ['proxyuser', t("Proxy user"), get_config('system', 'proxyuser'), ""], + '$proxy' => ['proxy', t("Proxy URL"), get_config('system', 'proxy'), ""], + '$timeout' => ['timeout', t("Network fetch timeout"), (x(get_config('system', 'curl_timeout')) ? get_config('system', 'curl_timeout') : 60), t("Value is in seconds. Set to 0 for unlimited (not recommended).")], + '$post_timeout' => ['post_timeout', t("Network post timeout"), (x(get_config('system', 'curl_post_timeout')) ? get_config('system', 'curl_post_timeout') : 90), t("Value is in seconds. Set to 0 for unlimited (not recommended).")], + '$delivery_interval' => ['delivery_interval', t("Delivery interval"), (x(get_config('system', 'delivery_interval')) ? get_config('system', 'delivery_interval') : 2), t("Delay background delivery processes by this many seconds to reduce system load. Recommend: 4-5 for shared hosts, 2-3 for virtual private servers. 0-1 for large dedicated servers.")], + '$delivery_batch_count' => ['delivery_batch_count', t('Deliveries per process'), (x(get_config('system', 'delivery_batch_count')) ? get_config('system', 'delivery_batch_count') : 3), t("Number of deliveries to attempt in a single operating system process. Adjust if necessary to tune system performance. Recommend: 1-5.")], + '$force_queue' => ['force_queue', t("Queue Threshold"), get_config('system', 'force_queue_threshold', 3000), t("Always defer immediate delivery if queue contains more than this number of entries.")], + '$poll_interval' => ['poll_interval', t("Poll interval"), (x(get_config('system', 'poll_interval')) ? get_config('system', 'poll_interval') : 2), t("Delay background polling processes by this many seconds to reduce system load. If 0, use delivery interval.")], + '$imagick_path' => ['imagick_path', t("Path to ImageMagick convert program"), get_config('system', 'imagick_convert_path'), t("If set, use this program to generate photo thumbnails for huge images ( > 4000 pixels in either dimension), otherwise memory exhaustion may occur. Example: /usr/bin/convert")], + '$maxloadavg' => ['maxloadavg', t("Maximum Load Average"), ((intval(get_config('system', 'maxloadavg')) > 0) ? get_config('system', 'maxloadavg') : 50), t("Maximum system load before delivery and poll processes are deferred - default 50.")], + '$default_expire_days' => ['default_expire_days', t('Expiration period in days for imported streams and cached images'), intval(get_config('system', 'default_expire_days', 60)), t('0 for no expiration of imported content')], + '$active_expire_days' => ['active_expire_days', t('Do not expire any posts which have comments less than this many days ago'), intval(get_config('system', 'active_expire_days', 7)), ''], + '$sellpage' => ['site_sellpage', t('Public servers: Optional landing (marketing) webpage for new registrants'), get_config('system', 'sellpage', ''), sprintf(t('Create this page first. Default is %s/register'), z_root())], + '$first_page' => ['first_page', t('Page to display after creating a new channel'), get_config('system', 'workflow_channel_next', 'profiles'), t('Default: profiles')], + '$location' => ['site_location', t('Site location'), get_config('system', 'site_location', ''), t('Region or country - shared with other sites')], + '$form_security_token' => get_form_security_token("admin_site"), + ]); + } } diff --git a/Zotlabs/Module/Admin/Themes.php b/Zotlabs/Module/Admin/Themes.php index d88648c8c..20bbd508b 100644 --- a/Zotlabs/Module/Admin/Themes.php +++ b/Zotlabs/Module/Admin/Themes.php @@ -2,227 +2,240 @@ namespace Zotlabs\Module\Admin; -use \Michelf\MarkdownExtra; +use App; +use Michelf\MarkdownExtra; /** * @brief Admin area theme settings. */ -class Themes { +class Themes +{ - /** - * @brief - * - */ - function post() { + /** + * @brief + * + */ + public function post() + { - $theme = argv(2); - if (is_file("view/theme/$theme/php/config.php")){ - require_once("view/theme/$theme/php/config.php"); - /// @FIXME add parent theme if derived - if (function_exists('theme_admin_post')){ - theme_admin_post($a); - } - } - info(t('Theme settings updated.')); - if(is_ajax()) - return; + $theme = argv(2); + if (is_file("view/theme/$theme/php/config.php")) { + require_once("view/theme/$theme/php/config.php"); + /// @FIXME add parent theme if derived + if (function_exists('theme_admin_post')) { + theme_admin_post($a); + } + } + info(t('Theme settings updated.')); + if (is_ajax()) { + return; + } - goaway(z_root() . '/admin/themes/' . $theme ); - } + goaway(z_root() . '/admin/themes/' . $theme); + } - /** - * @brief Themes admin page. - * - * @return string with parsed HTML - */ - function get(){ - $allowed_themes_str = get_config('system', 'allowed_themes'); - $allowed_themes_raw = explode(',', $allowed_themes_str); - $allowed_themes = []; - if(count($allowed_themes_raw)) - foreach($allowed_themes_raw as $x) - if(strlen(trim($x))) - $allowed_themes[] = trim($x); + /** + * @brief Themes admin page. + * + * @return string with parsed HTML + */ + public function get() + { + $allowed_themes_str = get_config('system', 'allowed_themes'); + $allowed_themes_raw = explode(',', $allowed_themes_str); + $allowed_themes = []; + if (count($allowed_themes_raw)) { + foreach ($allowed_themes_raw as $x) { + if (strlen(trim($x))) { + $allowed_themes[] = trim($x); + } + } + } - $themes = []; - $files = glob('view/theme/*'); - if($files) { - foreach($files as $file) { - $f = basename($file); - $is_experimental = intval(file_exists($file . '/.experimental')); - $is_supported = 1-(intval(file_exists($file . '/.unsupported'))); // Is not used yet - $is_allowed = intval(in_array($f,$allowed_themes)); - $themes[] = array('name' => $f, 'experimental' => $is_experimental, 'supported' => $is_supported, 'allowed' => $is_allowed); - } - } + $themes = []; + $files = glob('view/theme/*'); + if ($files) { + foreach ($files as $file) { + $f = basename($file); + $is_experimental = intval(file_exists($file . '/.experimental')); + $is_supported = 1 - (intval(file_exists($file . '/.unsupported'))); // Is not used yet + $is_allowed = intval(in_array($f, $allowed_themes)); + $themes[] = array('name' => $f, 'experimental' => $is_experimental, 'supported' => $is_supported, 'allowed' => $is_allowed); + } + } - if(! count($themes)) { - notice( t('No themes found.')); - return ''; - } + if (!count($themes)) { + notice(t('No themes found.')); + return ''; + } - /* - * Single theme - */ + /* + * Single theme + */ - if (\App::$argc == 3){ - $theme = \App::$argv[2]; - if(! is_dir("view/theme/$theme")){ - notice( t("Item not found.") ); - return ''; - } + if (App::$argc == 3) { + $theme = App::$argv[2]; + if (!is_dir("view/theme/$theme")) { + notice(t("Item not found.")); + return ''; + } - if (x($_GET,"a") && $_GET['a']=="t"){ - check_form_security_token_redirectOnErr('/admin/themes', 'admin_themes', 't'); + if (x($_GET, "a") && $_GET['a'] == "t") { + check_form_security_token_redirectOnErr('/admin/themes', 'admin_themes', 't'); - // Toggle theme status + // Toggle theme status - $this->toggle_theme($themes, $theme, $result); - $s = $this->rebuild_theme_table($themes); - if($result) - info( sprintf('Theme %s enabled.', $theme)); - else - info( sprintf('Theme %s disabled.', $theme)); + $this->toggle_theme($themes, $theme, $result); + $s = $this->rebuild_theme_table($themes); + if ($result) { + info(sprintf('Theme %s enabled.', $theme)); + } else { + info(sprintf('Theme %s disabled.', $theme)); + } - set_config('system', 'allowed_themes', $s); - goaway(z_root() . '/admin/themes' ); - } + set_config('system', 'allowed_themes', $s); + goaway(z_root() . '/admin/themes'); + } - // display theme details + // display theme details - if ($this->theme_status($themes,$theme)) { - $status="on"; $action= t("Disable"); - } else { - $status="off"; $action= t("Enable"); - } + if ($this->theme_status($themes, $theme)) { + $status = "on"; + $action = t("Disable"); + } else { + $status = "off"; + $action = t("Enable"); + } - $readme=Null; - if (is_file("view/theme/$theme/README.md")){ - $readme = file_get_contents("view/theme/$theme/README.md"); - $readme = MarkdownExtra::defaultTransform($readme); - } else if (is_file("view/theme/$theme/README")){ - $readme = '
        '. file_get_contents("view/theme/$theme/README") .'
        '; - } + $readme = null; + if (is_file("view/theme/$theme/README.md")) { + $readme = file_get_contents("view/theme/$theme/README.md"); + $readme = MarkdownExtra::defaultTransform($readme); + } elseif (is_file("view/theme/$theme/README")) { + $readme = '
        ' . file_get_contents("view/theme/$theme/README") . '
        '; + } - $admin_form = ''; - if (is_file("view/theme/$theme/php/config.php")){ - require_once("view/theme/$theme/php/config.php"); - if(function_exists("theme_admin")){ - $admin_form = theme_admin($a); - } - } + $admin_form = ''; + if (is_file("view/theme/$theme/php/config.php")) { + require_once("view/theme/$theme/php/config.php"); + if (function_exists("theme_admin")) { + $admin_form = theme_admin($a); + } + } - $screenshot = array( get_theme_screenshot($theme), t('Screenshot')); - if(! stristr($screenshot[0],$theme)) - $screenshot = null; + $screenshot = array(get_theme_screenshot($theme), t('Screenshot')); + if (!stristr($screenshot[0], $theme)) { + $screenshot = null; + } - $t = get_markup_template('admin_plugins_details.tpl'); - return replace_macros($t, array( - '$title' => t('Administration'), - '$page' => t('Themes'), - '$toggle' => t('Toggle'), - '$settings' => t('Settings'), - '$baseurl' => z_root(), + $t = get_markup_template('admin_plugins_details.tpl'); + return replace_macros($t, array( + '$title' => t('Administration'), + '$page' => t('Themes'), + '$toggle' => t('Toggle'), + '$settings' => t('Settings'), + '$baseurl' => z_root(), - '$plugin' => $theme, - '$status' => $status, - '$action' => $action, - '$info' => get_theme_info($theme), - '$function' => 'themes', - '$admin_form' => $admin_form, - '$str_author' => t('Author: '), - '$str_maintainer' => t('Maintainer: '), - '$screenshot' => $screenshot, - '$readme' => $readme, + '$plugin' => $theme, + '$status' => $status, + '$action' => $action, + '$info' => get_theme_info($theme), + '$function' => 'themes', + '$admin_form' => $admin_form, + '$str_author' => t('Author: '), + '$str_maintainer' => t('Maintainer: '), + '$screenshot' => $screenshot, + '$readme' => $readme, - '$form_security_token' => get_form_security_token('admin_themes'), - )); - } + '$form_security_token' => get_form_security_token('admin_themes'), + )); + } - /* - * List themes - */ + /* + * List themes + */ - $xthemes = []; - if($themes) { - foreach($themes as $th) { - $xthemes[] = array($th['name'],(($th['allowed']) ? "on" : "off"), get_theme_info($th['name'])); - } - } + $xthemes = []; + if ($themes) { + foreach ($themes as $th) { + $xthemes[] = array($th['name'], (($th['allowed']) ? "on" : "off"), get_theme_info($th['name'])); + } + } - $t = get_markup_template('admin_plugins.tpl'); - return replace_macros($t, array( - '$title' => t('Administration'), - '$page' => t('Themes'), - '$submit' => t('Submit'), - '$baseurl' => z_root(), - '$function' => 'themes', - '$plugins' => $xthemes, - '$experimental' => t('[Experimental]'), - '$unsupported' => t('[Unsupported]'), - '$form_security_token' => get_form_security_token('admin_themes'), - )); - } + $t = get_markup_template('admin_plugins.tpl'); + return replace_macros($t, array( + '$title' => t('Administration'), + '$page' => t('Themes'), + '$submit' => t('Submit'), + '$baseurl' => z_root(), + '$function' => 'themes', + '$plugins' => $xthemes, + '$experimental' => t('[Experimental]'), + '$unsupported' => t('[Unsupported]'), + '$form_security_token' => get_form_security_token('admin_themes'), + )); + } - /** - * @brief Toggle a theme. - * - * @param array &$themes - * @param[in] string $th - * @param[out] int &$result - */ - function toggle_theme(&$themes, $th, &$result) { - for($x = 0; $x < count($themes); $x ++) { - if($themes[$x]['name'] === $th) { - if($themes[$x]['allowed']) { - $themes[$x]['allowed'] = 0; - $result = 0; - } - else { - $themes[$x]['allowed'] = 1; - $result = 1; - } - } - } - } + /** + * @brief Toggle a theme. + * + * @param array &$themes + * @param[in] string $th + * @param[out] int &$result + */ + public function toggle_theme(&$themes, $th, &$result) + { + for ($x = 0; $x < count($themes); $x++) { + if ($themes[$x]['name'] === $th) { + if ($themes[$x]['allowed']) { + $themes[$x]['allowed'] = 0; + $result = 0; + } else { + $themes[$x]['allowed'] = 1; + $result = 1; + } + } + } + } - /** - * @param array $themes - * @param string $th - * @return int - */ - function theme_status($themes, $th) { - for($x = 0; $x < count($themes); $x ++) { - if($themes[$x]['name'] === $th) { - if($themes[$x]['allowed']) { - return 1; - } - else { - return 0; - } - } - } - return 0; - } - - /** - * @param array $themes - * @return string - */ - function rebuild_theme_table($themes) { - $o = ''; - if(count($themes)) { - foreach($themes as $th) { - if($th['allowed']) { - if(strlen($o)) - $o .= ','; - $o .= $th['name']; - } - } - } - return $o; - } + /** + * @param array $themes + * @param string $th + * @return int + */ + public function theme_status($themes, $th) + { + for ($x = 0; $x < count($themes); $x++) { + if ($themes[$x]['name'] === $th) { + if ($themes[$x]['allowed']) { + return 1; + } else { + return 0; + } + } + } + return 0; + } + /** + * @param array $themes + * @return string + */ + public function rebuild_theme_table($themes) + { + $o = ''; + if (count($themes)) { + foreach ($themes as $th) { + if ($th['allowed']) { + if (strlen($o)) { + $o .= ','; + } + $o .= $th['name']; + } + } + } + return $o; + } } diff --git a/Zotlabs/Module/Affinity.php b/Zotlabs/Module/Affinity.php index de5cd79b5..1df26928b 100644 --- a/Zotlabs/Module/Affinity.php +++ b/Zotlabs/Module/Affinity.php @@ -4,88 +4,89 @@ namespace Zotlabs\Module; use Zotlabs\Lib\Apps; use Zotlabs\Lib\Libsync; +use Zotlabs\Web\Controller; -class Affinity extends \Zotlabs\Web\Controller { +class Affinity extends Controller +{ - function post() { + public function post() + { - if(! ( local_channel() && Apps::system_app_installed(local_channel(),'Friend Zoom'))) { + if (!(local_channel() && Apps::system_app_installed(local_channel(), 'Friend Zoom'))) { return; } - if($_POST['affinity-submit']) { - $cmax = intval($_POST['affinity_cmax']); - if($cmax < 0 || $cmax > 99) - $cmax = 99; - $cmin = intval($_POST['affinity_cmin']); - if($cmin < 0 || $cmin > 99) - $cmin = 0; - set_pconfig(local_channel(),'affinity','cmin',0); - set_pconfig(local_channel(),'affinity','cmax',$cmax); + if ($_POST['affinity-submit']) { + $cmax = intval($_POST['affinity_cmax']); + if ($cmax < 0 || $cmax > 99) { + $cmax = 99; + } + $cmin = intval($_POST['affinity_cmin']); + if ($cmin < 0 || $cmin > 99) { + $cmin = 0; + } + set_pconfig(local_channel(), 'affinity', 'cmin', 0); + set_pconfig(local_channel(), 'affinity', 'cmax', $cmax); - info( t('Friend Zoom settings updated.') . EOL); + info(t('Friend Zoom settings updated.') . EOL); + } - } - - Libsync::build_sync_packet(); - - } + Libsync::build_sync_packet(); + } - function get() { + public function get() + { $desc = t('This app (when installed) presents a slider control in your connection editor and also on your stream page. The slider represents your degree of friendship with each connection. It allows you to zoom in or out and display conversations from only your closest friends or everybody in your stream.'); $text = ''; - if(! ( local_channel() && Apps::system_app_installed(local_channel(),'Friend Zoom'))) { + if (!(local_channel() && Apps::system_app_installed(local_channel(), 'Friend Zoom'))) { return $text; } - $text .= EOL . t('The number below represents the default maximum slider position for your stream page as a percentage.') . EOL . EOL; + $text .= EOL . t('The number below represents the default maximum slider position for your stream page as a percentage.') . EOL . EOL; - $setting_fields = $text; + $setting_fields = $text; - $cmax = intval(get_pconfig(local_channel(),'affinity','cmax')); - $cmax = (($cmax) ? $cmax : 99); -// $setting_fields .= replace_macros(get_markup_template('field_input.tpl'), array( -// '$field' => array('affinity_cmax', t('Default maximum affinity level'), $cmax, t('0-99 default 99')) -// )); + $cmax = intval(get_pconfig(local_channel(), 'affinity', 'cmax')); + $cmax = (($cmax) ? $cmax : 99); +// $setting_fields .= replace_macros(get_markup_template('field_input.tpl'), array( +// '$field' => array('affinity_cmax', t('Default maximum affinity level'), $cmax, t('0-99 default 99')) +// )); - if(Apps::system_app_installed(local_channel(),'Friend Zoom')) { + if (Apps::system_app_installed(local_channel(), 'Friend Zoom')) { + $labels = array( + 0 => t('Me'), + 20 => t('Family'), + 40 => t('Friends'), + 60 => t('Peers'), + 80 => t('Connections'), + 99 => t('All') + ); + call_hooks('affinity_labels', $labels); - $labels = array( - 0 => t('Me'), - 20 => t('Family'), - 40 => t('Friends'), - 60 => t('Peers'), - 80 => t('Connections'), - 99 => t('All') - ); - call_hooks('affinity_labels',$labels); - - $tpl = get_markup_template('affinity.tpl'); - $x = replace_macros($tpl, [ - '$cmin' => 0, - '$cmax' => $cmax, - '$lbl' => t('Default friend zoom in/out'), - '$refresh' => t('Refresh'), - '$labels' => $labels, - ]); - - - $arr = array('html' => $x); - call_hooks('affinity_slider',$arr); - $setting_fields .= $arr['html']; - } - - $s .= replace_macros(get_markup_template('generic_app_settings.tpl'), array( - '$addon' => array('affinity', '' . t('Friend Zoom Settings'), '', t('Submit')), - '$content' => $setting_fields - )); - - return $s; - } + $tpl = get_markup_template('affinity.tpl'); + $x = replace_macros($tpl, [ + '$cmin' => 0, + '$cmax' => $cmax, + '$lbl' => t('Default friend zoom in/out'), + '$refresh' => t('Refresh'), + '$labels' => $labels, + ]); -} \ No newline at end of file + $arr = array('html' => $x); + call_hooks('affinity_slider', $arr); + $setting_fields .= $arr['html']; + } + + $s .= replace_macros(get_markup_template('generic_app_settings.tpl'), array( + '$addon' => array('affinity', '' . t('Friend Zoom Settings'), '', t('Submit')), + '$content' => $setting_fields + )); + + return $s; + } +} diff --git a/Zotlabs/Module/Album.php b/Zotlabs/Module/Album.php index 2102fd876..dda4d9bce 100644 --- a/Zotlabs/Module/Album.php +++ b/Zotlabs/Module/Album.php @@ -1,4 +1,5 @@ 1) { - $channel = channelx_by_nick(argv(1)); - } - if (! $channel) { - http_status_exit(404,'Not found.'); - } + $channel = null; - $sql_extra = permissions_sql($channel['channel_id'],$observer_xchan); + if (argc() > 1) { + $channel = channelx_by_nick(argv(1)); + } + if (!$channel) { + http_status_exit(404, 'Not found.'); + } - if (argc() > 2) { - $folder = argv(2); - $r = q("select * from attach where is_dir = 1 and hash = '%s' and uid = %d $sql_extra limit 1", - dbesc($folder), - intval($channel['channel_id']) - ); - $allowed = (($r) ? attach_can_view($channel['channel_id'],$observer_xchan,$r[0]['hash'],$bear) : false); - } - else { - $folder = EMPTY_STR; - $allowed = perm_is_allowed($channel['channel_id'],$observer_xchan,'view_storage'); - } + $sql_extra = permissions_sql($channel['channel_id'], $observer_xchan); - if (! $allowed) { - http_status_exit(403,'Permission denied.'); - } + if (argc() > 2) { + $folder = argv(2); + $r = q( + "select * from attach where is_dir = 1 and hash = '%s' and uid = %d $sql_extra limit 1", + dbesc($folder), + intval($channel['channel_id']) + ); + $allowed = (($r) ? attach_can_view($channel['channel_id'], $observer_xchan, $r[0]['hash'], $bear) : false); + } else { + $folder = EMPTY_STR; + $allowed = perm_is_allowed($channel['channel_id'], $observer_xchan, 'view_storage'); + } - $x = q("select * from attach where folder = '%s' and uid = %d $sql_extra", - dbesc($folder), - intval($channel['channel_id']) - ); + if (!$allowed) { + http_status_exit(403, 'Permission denied.'); + } - $contents = []; + $x = q( + "select * from attach where folder = '%s' and uid = %d $sql_extra", + dbesc($folder), + intval($channel['channel_id']) + ); - if ($x) { - foreach ($x as $xv) { - if (intval($xv['is_dir'])) { - continue; - } - if (! attach_can_view($channel['channel_id'],$observer_xchan,$xv['hash'],$bear)) { - continue; - } - if (intval($xv['is_photo'])) { - $contents[] = z_root() . '/photo/' . $xv['hash']; - } - } - } - - $obj = Activity::encode_simple_collection($contents, App::$query_string, 'OrderedCollection', count($contents)); - as_return_and_die($obj,$channel); + $contents = []; - } + if ($x) { + foreach ($x as $xv) { + if (intval($xv['is_dir'])) { + continue; + } + if (!attach_can_view($channel['channel_id'], $observer_xchan, $xv['hash'], $bear)) { + continue; + } + if (intval($xv['is_photo'])) { + $contents[] = z_root() . '/photo/' . $xv['hash']; + } + } + } - } -} \ No newline at end of file + $obj = Activity::encode_simple_collection($contents, App::$query_string, 'OrderedCollection', count($contents)); + as_return_and_die($obj, $channel); + } + } +} diff --git a/Zotlabs/Module/Ap_probe.php b/Zotlabs/Module/Ap_probe.php index d5599df27..478a1eb62 100644 --- a/Zotlabs/Module/Ap_probe.php +++ b/Zotlabs/Module/Ap_probe.php @@ -1,4 +1,5 @@ t('ActivityPub Probe Diagnostic'), + '$resource' => ['resource', t('Object URL'), $_REQUEST['resource'], EMPTY_STR], + '$authf' => ['authf', t('Authenticated fetch'), $_REQUEST['authf'], EMPTY_STR, [t('No'), t('Yes')]], + '$submit' => t('Submit') + ]); - $o = replace_macros(get_markup_template('ap_probe.tpl'), [ - '$page_title' => t('ActivityPub Probe Diagnostic'), - '$resource' => [ 'resource', t('Object URL') , $_REQUEST['resource'], EMPTY_STR ], - '$authf' => [ 'authf', t('Authenticated fetch'), $_REQUEST['authf'], EMPTY_STR, [ t('No'), t('Yes') ] ], - '$submit' => t('Submit') - ]); + if (x($_REQUEST, 'resource')) { + $resource = $_REQUEST['resource']; + if ($_REQUEST['authf']) { + $channel = App::get_channel(); + if (!$channel) { + $channel = get_sys_channel(); + } + } - if (x($_REQUEST,'resource')) { - $resource = $_REQUEST['resource']; - if ($_REQUEST['authf']) { - $channel = App::get_channel(); - if (! $channel) { - $channel = get_sys_channel(); - } - } + $x = Activity::fetch($resource, $channel, null, true); - $x = Activity::fetch($resource,$channel,null,true); + if ($x) { + $o .= '
        ' . str_replace('\\n', "\n", htmlspecialchars(json_encode($x, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT))) . '
        '; + } + } - if ($x) { - $o .= '
        ' . str_replace('\\n',"\n",htmlspecialchars(json_encode($x,JSON_UNESCAPED_SLASHES|JSON_PRETTY_PRINT))) . '
        '; - } - - } - - return $o; - } - + return $o; + } } diff --git a/Zotlabs/Module/Api.php b/Zotlabs/Module/Api.php index e47dff5f4..a64a7fad6 100644 --- a/Zotlabs/Module/Api.php +++ b/Zotlabs/Module/Api.php @@ -1,4 +1,5 @@ "; var_dump($e); - killme(); - } - - - if(x($_POST,'oauth_yes')){ - - $app = $this->oauth_get_client($request); - if (is_null($app)) - return "Invalid request. Unknown token."; + public function get() + { - $consumer = new OAuth1Consumer($app['client_id'], $app['pw'], $app['redirect_uri']); - - $verifier = md5($app['secret'] . local_channel()); - set_config('oauth', $verifier, local_channel()); - - - if($consumer->callback_url != null) { - $params = $request->get_parameters(); - $glue = '?'; - if(strstr($consumer->callback_url,$glue)) - $glue = '?'; - goaway($consumer->callback_url . $glue . "oauth_token=" . OAuth1Util::urlencode_rfc3986($params['oauth_token']) . "&oauth_verifier=" . OAuth1Util::urlencode_rfc3986($verifier)); - killme(); - } - - $tpl = get_markup_template("oauth_authorize_done.tpl"); - $o = replace_macros($tpl, array( - '$title' => t('Authorize application connection'), - '$info' => t('Return to your app and insert this Security Code:'), - '$code' => $verifier, - )); - - return $o; - } - - - if(! local_channel()) { - // TODO: we need login form to redirect to this page - notice( t('Please login to continue.') . EOL ); - return login(false,'api-login',$request->get_parameters()); - } - - $app = $this->oauth_get_client($request); - if (is_null($app)) - return "Invalid request. Unknown token."; - - $tpl = get_markup_template('oauth_authorize.tpl'); - $o = replace_macros($tpl, array( - '$title' => t('Authorize application connection'), - '$app' => $app, - '$authorize' => t('Do you want to authorize this application to access your posts and contacts, and/or create new posts for you?'), - '$yes' => t('Yes'), - '$no' => t('No'), - )); - - // echo "
        "; var_dump($app); killme();
        -			
        -			return $o;
        -		}
        -		
        -		echo api_call();
        -		killme();
        -	}
        +        if (App::$cmd === 'api/oauth/authorize') {
        +            /*
        +             * api/oauth/authorize interact with the user. return a standard page
        +             */
         
        -	function oauth_get_client($request){
        +            App::$page['template'] = 'minimal';
         
        -		$params = $request->get_parameters();
        -		$token  = $params['oauth_token'];
        -	
        -		$r = q("SELECT clients.* FROM clients, tokens WHERE clients.client_id = tokens.client_id 
        +            // get consumer/client from request token
        +            try {
        +                $request = OAuth1Request::from_request();
        +            } catch (Exception $e) {
        +                logger('OAuth exception: ' . print_r($e, true));
        +                // echo "
        "; var_dump($e);
        +                killme();
        +            }
        +
        +
        +            if (x($_POST, 'oauth_yes')) {
        +                $app = $this->oauth_get_client($request);
        +                if (is_null($app)) {
        +                    return "Invalid request. Unknown token.";
        +                }
        +
        +                $consumer = new OAuth1Consumer($app['client_id'], $app['pw'], $app['redirect_uri']);
        +
        +                $verifier = md5($app['secret'] . local_channel());
        +                set_config('oauth', $verifier, local_channel());
        +
        +
        +                if ($consumer->callback_url != null) {
        +                    $params = $request->get_parameters();
        +                    $glue = '?';
        +                    if (strstr($consumer->callback_url, $glue)) {
        +                        $glue = '?';
        +                    }
        +                    goaway($consumer->callback_url . $glue . "oauth_token=" . OAuth1Util::urlencode_rfc3986($params['oauth_token']) . "&oauth_verifier=" . OAuth1Util::urlencode_rfc3986($verifier));
        +                    killme();
        +                }
        +
        +                $tpl = get_markup_template("oauth_authorize_done.tpl");
        +                $o = replace_macros($tpl, array(
        +                    '$title' => t('Authorize application connection'),
        +                    '$info' => t('Return to your app and insert this Security Code:'),
        +                    '$code' => $verifier,
        +                ));
        +
        +                return $o;
        +            }
        +
        +
        +            if (!local_channel()) {
        +                // TODO: we need login form to redirect to this page
        +                notice(t('Please login to continue.') . EOL);
        +                return login(false, 'api-login', $request->get_parameters());
        +            }
        +
        +            $app = $this->oauth_get_client($request);
        +            if (is_null($app)) {
        +                return "Invalid request. Unknown token.";
        +            }
        +
        +            $tpl = get_markup_template('oauth_authorize.tpl');
        +            $o = replace_macros($tpl, array(
        +                '$title' => t('Authorize application connection'),
        +                '$app' => $app,
        +                '$authorize' => t('Do you want to authorize this application to access your posts and contacts, and/or create new posts for you?'),
        +                '$yes' => t('Yes'),
        +                '$no' => t('No'),
        +            ));
        +
        +            // echo "
        "; var_dump($app); killme();
        +
        +            return $o;
        +        }
        +
        +        echo api_call();
        +        killme();
        +    }
        +
        +    public function oauth_get_client($request)
        +    {
        +
        +        $params = $request->get_parameters();
        +        $token = $params['oauth_token'];
        +
        +        $r = q(
        +            "SELECT clients.* FROM clients, tokens WHERE clients.client_id = tokens.client_id 
         			AND tokens.id = '%s' AND tokens.auth_scope = 'request' ",
        -			dbesc($token)
        -		);
        -		if($r)
        -			return $r[0];
        +            dbesc($token)
        +        );
        +        if ($r) {
        +            return $r[0];
        +        }
         
        -		return null;
        -	
        -	}
        -	
        +        return null;
        +    }
         }
        diff --git a/Zotlabs/Module/Appman.php b/Zotlabs/Module/Appman.php
        index eecf1f95b..8963b3f6b 100644
        --- a/Zotlabs/Module/Appman.php
        +++ b/Zotlabs/Module/Appman.php
        @@ -1,149 +1,153 @@
          intval($_REQUEST['uid']),
        -				'url'        => escape_tags($_REQUEST['url']),
        -				'guid'       => escape_tags($_REQUEST['guid']),
        -				'author'     => escape_tags($_REQUEST['author']),
        -				'addr'       => escape_tags($_REQUEST['addr']),
        -				'name'       => escape_tags($_REQUEST['name']),
        -				'desc'       => escape_tags($_REQUEST['desc']),
        -				'photo'      => escape_tags($_REQUEST['photo']),
        -				'version'    => escape_tags($_REQUEST['version']),
        -				'price'      => escape_tags($_REQUEST['price']),
        -				'page'       => escape_tags($_REQUEST['sellpage']), // do not use 'page' as a request variable here as it conflicts with pagination
        -				'requires'   => escape_tags($_REQUEST['requires']),
        -				'system'     => intval($_REQUEST['system']),
        -				'plugin'     => escape_tags($_REQUEST['plugin']),
        -				'sig'        => escape_tags($_REQUEST['sig']),
        -				'categories' => escape_tags($_REQUEST['categories'])
        -			];
        -	
        -			$_REQUEST['appid'] = Apps::app_install(local_channel(),$arr);
        -	
        -			if (Apps::app_installed(local_channel(),$arr)) {
        -				info( t('App installed.') . EOL);
        -			}
        +    public function post()
        +    {
         
        -			goaway(z_root() . '/apps');
        -		}
        -	
        -	
        -		$papp = Apps::app_decode($_POST['papp']);
        -	
        -		if (! is_array($papp)) {
        -			notice( t('Malformed app.') . EOL);
        -			return;
        -		}
        -	
        -		if ($_POST['install']) {
        -			Apps::app_install(local_channel(),$papp);
        -			if (Apps::app_installed(local_channel(),$papp))
        -				info( t('App installed.') . EOL);
        -		}
        -	
        -		if ($_POST['delete']) {
        -			Apps::app_destroy(local_channel(),$papp);
        -		}
        +        if (!local_channel()) {
        +            return;
        +        }
         
        -		if ($_POST['edit']) {
        -			return;
        -		}
        +        if ($_POST['url']) {
        +            $arr = [
        +                'uid' => intval($_REQUEST['uid']),
        +                'url' => escape_tags($_REQUEST['url']),
        +                'guid' => escape_tags($_REQUEST['guid']),
        +                'author' => escape_tags($_REQUEST['author']),
        +                'addr' => escape_tags($_REQUEST['addr']),
        +                'name' => escape_tags($_REQUEST['name']),
        +                'desc' => escape_tags($_REQUEST['desc']),
        +                'photo' => escape_tags($_REQUEST['photo']),
        +                'version' => escape_tags($_REQUEST['version']),
        +                'price' => escape_tags($_REQUEST['price']),
        +                'page' => escape_tags($_REQUEST['sellpage']), // do not use 'page' as a request variable here as it conflicts with pagination
        +                'requires' => escape_tags($_REQUEST['requires']),
        +                'system' => intval($_REQUEST['system']),
        +                'plugin' => escape_tags($_REQUEST['plugin']),
        +                'sig' => escape_tags($_REQUEST['sig']),
        +                'categories' => escape_tags($_REQUEST['categories'])
        +            ];
         
        -		if ($_POST['feature']) {
        -			Apps::app_feature(local_channel(), $papp, $_POST['feature']);
        -		}
        +            $_REQUEST['appid'] = Apps::app_install(local_channel(), $arr);
         
        -		if ($_POST['pin']) {
        -			Apps::app_feature(local_channel(), $papp, $_POST['pin']);
        -		}
        +            if (Apps::app_installed(local_channel(), $arr)) {
        +                info(t('App installed.') . EOL);
        +            }
         
        -		if ($_SESSION['return_url']) {
        -			goaway(z_root() . '/' . $_SESSION['return_url']);
        -		}
        -		
        -		goaway(z_root() . '/apps');
        -	}
        -	
        -	
        -	function get() {
        -	
        -		if (! local_channel()) {
        -			notice( t('Permission denied.') . EOL);
        -			return;
        -		}
        +            goaway(z_root() . '/apps');
        +        }
         
        -		$channel = App::get_channel();
         
        -		if (argc() > 3) {
        -			if(argv(2) === 'moveup') {
        -				Apps::moveup(local_channel(),argv(1),argv(3));
        -			}
        -			if(argv(2) === 'movedown') {
        -				Apps::movedown(local_channel(),argv(1),argv(3));
        -			}
        -			goaway(z_root() . '/apporder');
        -		}
        +        $papp = Apps::app_decode($_POST['papp']);
         
        -		$app = null;
        -		$embed = null;
        -		if ($_REQUEST['appid']) {
        -			$r = q("select * from app where app_id = '%s' and app_channel = %d limit 1",
        -				dbesc($_REQUEST['appid']),
        -				dbesc(local_channel())
        -			);
        -			if ($r) {
        -				$app = $r[0];
        +        if (!is_array($papp)) {
        +            notice(t('Malformed app.') . EOL);
        +            return;
        +        }
         
        -				$term = q("select * from term where otype = %d and oid = %d and uid = %d",
        -					intval(TERM_OBJ_APP),
        -					intval($r[0]['id']),
        -					intval(local_channel())
        -				);
        -				if ($term) {
        -					$app['categories'] = array_elm_to_str($term,'term');
        -				}
        -			}
        +        if ($_POST['install']) {
        +            Apps::app_install(local_channel(), $papp);
        +            if (Apps::app_installed(local_channel(), $papp)) {
        +                info(t('App installed.') . EOL);
        +            }
        +        }
         
        -			$embed = [ 'embed', t('Embed code'), Apps::app_encode($app,true), EMPTY_STR, 'onclick="this.select();"' ];
        -		}
        -				
        -		return replace_macros(get_markup_template('app_create.tpl'), [
        -			'$banner'     => (($app) ? t('Edit App') : t('Create App')),
        -			'$app'        => $app,
        -			'$guid'       => (($app) ? $app['app_id'] : EMPTY_STR),
        -			'$author'     => (($app) ? $app['app_author'] : $channel['channel_hash']),
        -			'$addr'       => (($app) ? $app['app_addr'] : $channel['xchan_addr']),
        -			'$name'       => [ 'name', t('Name of app'),(($app) ? $app['app_name'] : EMPTY_STR), t('Required') ],
        -			'$url'        => [ 'url', t('Location (URL) of app'),(($app) ? $app['app_url'] : EMPTY_STR), t('Required') ],
        -	 		'$desc'       => [ 'desc', t('Description'),(($app) ? $app['app_desc'] : EMPTY_STR), EMPTY_STR],
        -			'$photo'      => [ 'photo', t('Photo icon URL'),(($app) ? $app['app_photo'] : EMPTY_STR), t('80 x 80 pixels - optional') ],
        -			'$categories' => [ 'categories',t('Categories (optional, comma separated list)'),(($app) ? $app['categories'] : EMPTY_STR), EMPTY_STR ],
        -			'$version'    => [ 'version', t('Version ID'),(($app) ? $app['app_version'] : EMPTY_STR), EMPTY_STR ],
        -			'$price'      => [ 'price', t('Price of app'),(($app) ? $app['app_price'] : EMPTY_STR), EMPTY_STR ],
        -			'$page'       => [ 'sellpage', t('Location (URL) to purchase app'),(($app) ? $app['app_page'] : EMPTY_STR), EMPTY_STR ],
        -			'$system'     => (($app) ? intval($app['app_system']) : 0),
        -			'$plugin'     => (($app) ? $app['app_plugin'] : EMPTY_STR),
        -			'$requires'   => (($app) ? $app['app_requires'] : EMPTY_STR),
        -			'$embed'      => $embed,
        -			'$submit'     => t('Submit')
        -		]);
        -	
        -	}
        -	
        +        if ($_POST['delete']) {
        +            Apps::app_destroy(local_channel(), $papp);
        +        }
        +
        +        if ($_POST['edit']) {
        +            return;
        +        }
        +
        +        if ($_POST['feature']) {
        +            Apps::app_feature(local_channel(), $papp, $_POST['feature']);
        +        }
        +
        +        if ($_POST['pin']) {
        +            Apps::app_feature(local_channel(), $papp, $_POST['pin']);
        +        }
        +
        +        if ($_SESSION['return_url']) {
        +            goaway(z_root() . '/' . $_SESSION['return_url']);
        +        }
        +
        +        goaway(z_root() . '/apps');
        +    }
        +
        +
        +    public function get()
        +    {
        +
        +        if (!local_channel()) {
        +            notice(t('Permission denied.') . EOL);
        +            return;
        +        }
        +
        +        $channel = App::get_channel();
        +
        +        if (argc() > 3) {
        +            if (argv(2) === 'moveup') {
        +                Apps::moveup(local_channel(), argv(1), argv(3));
        +            }
        +            if (argv(2) === 'movedown') {
        +                Apps::movedown(local_channel(), argv(1), argv(3));
        +            }
        +            goaway(z_root() . '/apporder');
        +        }
        +
        +        $app = null;
        +        $embed = null;
        +        if ($_REQUEST['appid']) {
        +            $r = q(
        +                "select * from app where app_id = '%s' and app_channel = %d limit 1",
        +                dbesc($_REQUEST['appid']),
        +                dbesc(local_channel())
        +            );
        +            if ($r) {
        +                $app = $r[0];
        +
        +                $term = q(
        +                    "select * from term where otype = %d and oid = %d and uid = %d",
        +                    intval(TERM_OBJ_APP),
        +                    intval($r[0]['id']),
        +                    intval(local_channel())
        +                );
        +                if ($term) {
        +                    $app['categories'] = array_elm_to_str($term, 'term');
        +                }
        +            }
        +
        +            $embed = ['embed', t('Embed code'), Apps::app_encode($app, true), EMPTY_STR, 'onclick="this.select();"'];
        +        }
        +
        +        return replace_macros(get_markup_template('app_create.tpl'), [
        +            '$banner' => (($app) ? t('Edit App') : t('Create App')),
        +            '$app' => $app,
        +            '$guid' => (($app) ? $app['app_id'] : EMPTY_STR),
        +            '$author' => (($app) ? $app['app_author'] : $channel['channel_hash']),
        +            '$addr' => (($app) ? $app['app_addr'] : $channel['xchan_addr']),
        +            '$name' => ['name', t('Name of app'), (($app) ? $app['app_name'] : EMPTY_STR), t('Required')],
        +            '$url' => ['url', t('Location (URL) of app'), (($app) ? $app['app_url'] : EMPTY_STR), t('Required')],
        +            '$desc' => ['desc', t('Description'), (($app) ? $app['app_desc'] : EMPTY_STR), EMPTY_STR],
        +            '$photo' => ['photo', t('Photo icon URL'), (($app) ? $app['app_photo'] : EMPTY_STR), t('80 x 80 pixels - optional')],
        +            '$categories' => ['categories', t('Categories (optional, comma separated list)'), (($app) ? $app['categories'] : EMPTY_STR), EMPTY_STR],
        +            '$version' => ['version', t('Version ID'), (($app) ? $app['app_version'] : EMPTY_STR), EMPTY_STR],
        +            '$price' => ['price', t('Price of app'), (($app) ? $app['app_price'] : EMPTY_STR), EMPTY_STR],
        +            '$page' => ['sellpage', t('Location (URL) to purchase app'), (($app) ? $app['app_page'] : EMPTY_STR), EMPTY_STR],
        +            '$system' => (($app) ? intval($app['app_system']) : 0),
        +            '$plugin' => (($app) ? $app['app_plugin'] : EMPTY_STR),
        +            '$requires' => (($app) ? $app['app_requires'] : EMPTY_STR),
        +            '$embed' => $embed,
        +            '$submit' => t('Submit')
        +        ]);
        +    }
         }
        diff --git a/Zotlabs/Module/Apporder.php b/Zotlabs/Module/Apporder.php
        index f7a2463a4..a4f90ba2d 100644
        --- a/Zotlabs/Module/Apporder.php
        +++ b/Zotlabs/Module/Apporder.php
        @@ -5,49 +5,50 @@ namespace Zotlabs\Module;
         use Zotlabs\Web\Controller;
         use Zotlabs\Lib\Apps;
         
        -class Apporder extends \Zotlabs\Web\Controller {
        +class Apporder extends Controller
        +{
         
         
        -	function get() {
        +    public function get()
        +    {
         
        -		if (! local_channel()) {
        -			return;
        -		}
        +        if (!local_channel()) {
        +            return;
        +        }
         
        -		nav_set_selected('Order Apps');
        +        nav_set_selected('Order Apps');
         
        -		foreach ( [ 'nav_featured_app', 'nav_pinned_app' ] as $l ) {
        -			$syslist = [];
        -			$list = Apps::app_list(local_channel(), false, [ $l ]);
        -			if ($list) {
        -				foreach ($list as $li) {
        -					$syslist[] = Apps::app_encode($li);
        -				}
        -			}
        -		
        -			Apps::translate_system_apps($syslist);
        +        foreach (['nav_featured_app', 'nav_pinned_app'] as $l) {
        +            $syslist = [];
        +            $list = Apps::app_list(local_channel(), false, [$l]);
        +            if ($list) {
        +                foreach ($list as $li) {
        +                    $syslist[] = Apps::app_encode($li);
        +                }
        +            }
         
        -			usort($syslist,'Zotlabs\\Lib\\Apps::app_name_compare');
        +            Apps::translate_system_apps($syslist);
         
        -			$syslist = Apps::app_order(local_channel(),$syslist, $l);
        +            usort($syslist, 'Zotlabs\\Lib\\Apps::app_name_compare');
         
        -			foreach ($syslist as $app) {
        -				if ($l === 'nav_pinned_app') {
        -					$navbar_apps[] = Apps::app_render($app,'nav-order-pinned');
        -				}
        -				else {
        -					$nav_apps[] = Apps::app_render($app,'nav-order');
        -				}
        -			}
        -		}
        +            $syslist = Apps::app_order(local_channel(), $syslist, $l);
         
        -		return replace_macros(get_markup_template('apporder.tpl'), [
        -				'$arrange'     => t('Arrange Apps'),
        -				'$header'      => [ t('Change order of pinned navbar apps'), t('Change order of app tray apps') ],
        -				'$desc'        => [ t('Use arrows to move the corresponding app left (top) or right (bottom) in the navbar'),
        -					t('Use arrows to move the corresponding app up or down in the app tray') ],
        -				'$nav_apps'    => $nav_apps,
        -				'$navbar_apps' => $navbar_apps
        -		]);
        -	}
        +            foreach ($syslist as $app) {
        +                if ($l === 'nav_pinned_app') {
        +                    $navbar_apps[] = Apps::app_render($app, 'nav-order-pinned');
        +                } else {
        +                    $nav_apps[] = Apps::app_render($app, 'nav-order');
        +                }
        +            }
        +        }
        +
        +        return replace_macros(get_markup_template('apporder.tpl'), [
        +            '$arrange'     => t('Arrange Apps'),
        +            '$header' => [t('Change order of pinned navbar apps'), t('Change order of app tray apps')],
        +            '$desc' => [t('Use arrows to move the corresponding app left (top) or right (bottom) in the navbar'),
        +                t('Use arrows to move the corresponding app up or down in the app tray')],
        +            '$nav_apps' => $nav_apps,
        +            '$navbar_apps' => $navbar_apps
        +        ]);
        +    }
         }
        diff --git a/Zotlabs/Module/Apps.php b/Zotlabs/Module/Apps.php
        index 45e2af577..86c5a4de7 100644
        --- a/Zotlabs/Module/Apps.php
        +++ b/Zotlabs/Module/Apps.php
        @@ -1,60 +1,66 @@
          get_config('system','sitename'),
        -			'$cat' => $cat,
        -			'$title' => (($available) ? t('Available Apps') : t('Installed Apps')),
        -			'$apps' => $apps,
        -			'$authed' => ((local_channel()) ? true : false),
        -			'$manage' => (($available) ? EMPTY_STR : t('Manage apps')),
        -			'$create' => (($mode == 'edit') ? t('Create Custom App') : '')
        -		));
        -	
        -	}
        -	
        +        $apps = [];
        +
        +        if (local_channel()) {
        +            Zlib\Apps::import_system_apps();
        +            $syslist = [];
        +            $cat = ((array_key_exists('cat', $_GET) && $_GET['cat']) ? [escape_tags($_GET['cat'])] : '');
        +            $list = Zlib\Apps::app_list((($available) ? 0 : local_channel()), (($mode == 'edit') ? true : false), $cat);
        +            if ($list) {
        +                foreach ($list as $x) {
        +                    $syslist[] = Zlib\Apps::app_encode($x);
        +                }
        +            }
        +            Zlib\Apps::translate_system_apps($syslist);
        +        } else {
        +            $syslist = Zlib\Apps::get_system_apps(true);
        +        }
        +
        +        usort($syslist, 'Zotlabs\\Lib\\Apps::app_name_compare');
        +
        +        //  logger('apps: ' . print_r($syslist,true));
        +
        +        foreach ($syslist as $app) {
        +            $apps[] = Zlib\Apps::app_render($app, (($available) ? 'install' : $mode));
        +        }
        +		
        +		// @TODO not ready for prime time. The manage url redirects to installed apps
        +		// and we need to edit available apps
        +		$sys_edit = false; // (local_channel() && is_sys_channel(local_channel()));
        +
        +        return replace_macros(get_markup_template('myapps.tpl'), array(
        +            '$sitename' => get_config('system', 'sitename'),
        +            '$cat' => $cat,
        +            '$title' => (($available) ? t('Available Apps') : t('Installed Apps')),
        +            '$apps' => $apps,
        +            '$authed' => ((local_channel()) ? true : false),
        +            '$manage' => (($available && ! $sys_edit) ? EMPTY_STR : t('Manage apps')),
        +            '$create' => (($mode == 'edit') ? t('Create Custom App') : '')
        +        ));
        +    }
         }
        diff --git a/Zotlabs/Module/Apschema.php b/Zotlabs/Module/Apschema.php
        index 0b0d15bcc..9b5c68e0b 100644
        --- a/Zotlabs/Module/Apschema.php
        +++ b/Zotlabs/Module/Apschema.php
        @@ -5,21 +5,18 @@ namespace Zotlabs\Module;
         use Zotlabs\Web\Controller;
         use Zotlabs\Lib\Activity;
         
        -class Apschema extends Controller {
        +class Apschema extends Controller
        +{
         
        -	function init() {
        +    public function init()
        +    {
         
        -		$arr = [
        -			'@context' => array_merge(['as' => 'https://www.w3.org/ns/activitystreams#'], Activity::ap_schema())
        -		];
        +        $arr = [
        +            '@context' => array_merge(['as' => 'https://www.w3.org/ns/activitystreams#'], Activity::ap_schema())
        +        ];
         
        -		header('Content-Type: application/ld+json');
        -		echo json_encode($arr,JSON_UNESCAPED_SLASHES);
        -		killme();
        -
        -	}
        -
        -
        -
        -
        -}
        \ No newline at end of file
        +        header('Content-Type: application/ld+json');
        +        echo json_encode($arr, JSON_UNESCAPED_SLASHES);
        +        killme();
        +    }
        +}
        diff --git a/Zotlabs/Module/Attach.php b/Zotlabs/Module/Attach.php
        index 20b74614a..4c45b2bee 100644
        --- a/Zotlabs/Module/Attach.php
        +++ b/Zotlabs/Module/Attach.php
        @@ -1,52 +1,58 @@
          2) ? intval(argv(2)) : 0));
        -	
        -		if(! $r['success']) {
        -			notice( $r['message'] . EOL);
        -			return;
        -		}
        -	
        -		$c = q("select channel_address from channel where channel_id = %d limit 1",
        -			intval($r['data']['uid'])
        -		);
        -	
        -		if(! $c)
        -			return;
        -	
        -		header('Content-type: ' . $r['data']['filetype']);
        -		header('Content-Disposition: attachment; filename="' . $r['data']['filename'] . '"');
        -		if(intval($r['data']['os_storage'])) {
        -			$fname = dbunescbin($r['data']['content']);
        -			if(strpos($fname,'store') !== false)
        -				$istream = fopen($fname,'rb');
        -			else
        -				$istream = fopen('store/' . $c[0]['channel_address'] . '/' . $fname,'rb');
        -			$ostream = fopen('php://output','wb');
        -			if($istream && $ostream) {
        -				pipe_streams($istream,$ostream);
        -				fclose($istream);
        -				fclose($ostream);
        -			}
        -		}
        -		else
        -			echo dbunescbin($r['data']['content']);
        -		killme();
        -	
        -	}
        -	
        +    public function init()
        +    {
        +
        +        if (argc() < 2) {
        +            notice(t('Item not available.') . EOL);
        +            return;
        +        }
        +
        +        $r = attach_by_hash(argv(1), get_observer_hash(), ((argc() > 2) ? intval(argv(2)) : 0));
        +
        +        if (!$r['success']) {
        +            notice($r['message'] . EOL);
        +            return;
        +        }
        +
        +        $c = q(
        +            "select channel_address from channel where channel_id = %d limit 1",
        +            intval($r['data']['uid'])
        +        );
        +
        +        if (!$c) {
        +            return;
        +        }
        +
        +        header('Content-type: ' . $r['data']['filetype']);
        +        header('Content-Disposition: attachment; filename="' . $r['data']['filename'] . '"');
        +        if (intval($r['data']['os_storage'])) {
        +            $fname = dbunescbin($r['data']['content']);
        +            if (strpos($fname, 'store') !== false) {
        +                $istream = fopen($fname, 'rb');
        +            } else {
        +                $istream = fopen('store/' . $c[0]['channel_address'] . '/' . $fname, 'rb');
        +            }
        +            $ostream = fopen('php://output', 'wb');
        +            if ($istream && $ostream) {
        +                pipe_streams($istream, $ostream);
        +                fclose($istream);
        +                fclose($ostream);
        +            }
        +        } else {
        +            echo dbunescbin($r['data']['content']);
        +        }
        +        killme();
        +    }
         }
        diff --git a/Zotlabs/Module/Authorize.php b/Zotlabs/Module/Authorize.php
        index 709d76c18..25f04e3d3 100644
        --- a/Zotlabs/Module/Authorize.php
        +++ b/Zotlabs/Module/Authorize.php
        @@ -10,134 +10,133 @@ use Zotlabs\Identity\OAuth2Storage;
         use OAuth2\Request;
         use OAuth2\Response;
         
        +class Authorize extends Controller
        +{
         
        -class Authorize extends Controller {
        +    public function get()
        +    {
        +        if (!local_channel()) {
        +            return login();
        +        } else {
        +            $name = $_REQUEST['client_name'];
        +            if (!$name) {
        +                $name = (($_REQUEST['client_id']) ?: t('Unknown App'));
        +            }
         
        -	function get() {
        -		if (! local_channel()) {
        -			return login();
        -		} 
        -		else {
        +            $app = [
        +                'name' => $name,
        +                'icon' => (x($_REQUEST, 'logo_uri') ? $_REQUEST['logo_uri'] : z_root() . '/images/icons/plugin.png'),
        +                'url' => (x($_REQUEST, 'client_uri') ? $_REQUEST['client_uri'] : ''),
        +            ];
         
        -			$name = $_REQUEST['client_name'];
        -			if(! $name) {
        -				$name = (($_REQUEST['client_id']) ?: t('Unknown App'));
        -			}
        +            $link = (($app['url']) ? '' . $app['name'] . ' ' : $app['name']);
         
        -			$app = [
        -				'name' => $name,
        -				'icon' => (x($_REQUEST, 'logo_uri')    ? $_REQUEST['logo_uri'] : z_root() . '/images/icons/plugin.png'),
        -				'url'  => (x($_REQUEST, 'client_uri')  ? $_REQUEST['client_uri'] : ''),
        -			];
        +            $o .= replace_macros(get_markup_template('oauth_authorize.tpl'), [
        +                '$title' => t('Authorize'),
        +                '$authorize' => sprintf(t('Do you authorize the app %s to access your channel data?'), $link),
        +                '$app' => $app,
        +                '$yes' => t('Allow'),
        +                '$no' => t('Deny'),
        +                '$client_id' => (x($_REQUEST, 'client_id') ? $_REQUEST['client_id'] : ''),
        +                '$redirect_uri' => (x($_REQUEST, 'redirect_uri') ? $_REQUEST['redirect_uri'] : ''),
        +                '$state' => (x($_REQUEST, 'state') ? $_REQUEST['state'] : ''),
        +            ]);
        +            return $o;
        +        }
        +    }
         
        -			$link = (($app['url']) ? '' . $app['name'] . ' ' : $app['name']);
        +    public function post()
        +    {
        +        if (!local_channel()) {
        +            return;
        +        }
         
        -			$o .= replace_macros(get_markup_template('oauth_authorize.tpl'), [
        -				'$title'        => t('Authorize'),
        -				'$authorize'    => sprintf( t('Do you authorize the app %s to access your channel data?'), $link ),
        -				'$app'          => $app,
        -				'$yes'          => t('Allow'),
        -				'$no'           => t('Deny'),
        -				'$client_id'    => (x($_REQUEST, 'client_id') ? $_REQUEST['client_id'] : ''),
        -				'$redirect_uri' => (x($_REQUEST, 'redirect_uri') ? $_REQUEST['redirect_uri'] : ''),
        -				'$state'        => (x($_REQUEST, 'state') ? $_REQUEST['state'] : ''),
        -			]);
        -			return $o;
        -		}
        -	}
        +        $storage = new OAuth2Storage(DBA::$dba->db);
        +        $s = new OAuth2Server($storage);
         
        -	function post() {
        -		if (! local_channel()) {
        -			return;
        -		}
        +        // TODO: The automatic client registration protocol below should adhere more
        +        // closely to "OAuth 2.0 Dynamic Client Registration Protocol" defined
        +        // at https://tools.ietf.org/html/rfc7591
         
        -		$storage = new OAuth2Storage(DBA::$dba->db);
        -		$s = new OAuth2Server($storage);
        +        // If no client_id was provided, generate a new one.
        +        if (x($_POST, 'client_name')) {
        +            $client_name = $_POST['client_name'];
        +        } else {
        +            $client_name = $_POST['client_name'] = EMPTY_STR;
        +        }
         
        -		// TODO: The automatic client registration protocol below should adhere more
        -		// closely to "OAuth 2.0 Dynamic Client Registration Protocol" defined
        -		// at https://tools.ietf.org/html/rfc7591
        +        // If no client_id was provided, generate a new one.
        +        if (x($_POST, 'client_id')) {
        +            $client_id = $_POST['client_id'];
        +        } else {
        +            $client_id = $_POST['client_id'] = random_string(16);
        +        }
         
        -		// If no client_id was provided, generate a new one.
        -		if (x($_POST, 'client_name')) {
        -			$client_name = $_POST['client_name'];
        -		} else {
        -			$client_name = $_POST['client_name'] = EMPTY_STR;
        -		}
        +        // If no redirect_uri was provided, generate a fake one.
        +        if (x($_POST, 'redirect_uri')) {
        +            $redirect_uri = $_POST['redirect_uri'];
        +        } else {
        +            $redirect_uri = $_POST['redirect_uri'] = 'https://fake.example.com/oauth';
        +        }
         
        -		// If no client_id was provided, generate a new one.
        -		if (x($_POST, 'client_id')) {
        -			$client_id = $_POST['client_id'];
        -		} else {
        -			$client_id = $_POST['client_id'] = random_string(16);
        -		}
        +        $request = Request::createFromGlobals();
        +        $response = new Response();
         
        -		// If no redirect_uri was provided, generate a fake one.
        -		if (x($_POST, 'redirect_uri')) {
        -			$redirect_uri = $_POST['redirect_uri'];
        -		} else {
        -			$redirect_uri = $_POST['redirect_uri'] = 'https://fake.example.com/oauth';
        -		}
        +        // Note, "sub" field must match type and content. $user_id is used to populate - make sure it's a string.
        +        $channel = channelx_by_n(local_channel());
        +        $user_id = $channel['channel_id'];
         
        -		$request = Request::createFromGlobals();
        -		$response = new Response();
        +        $client_found = false;
        +        $client = $storage->getClientDetails($client_id);
         
        -		// Note, "sub" field must match type and content. $user_id is used to populate - make sure it's a string. 
        -		$channel = channelx_by_n(local_channel());
        -		$user_id = $channel['channel_id'];
        +        logger('client: ' . print_r($client, true), LOGGER_DATA);
         
        -		$client_found = false;
        -		$client = $storage->getClientDetails($client_id);
        -
        -		logger('client: ' . print_r($client,true),LOGGER_DATA);
        -
        -		if ($client) {
        -			if (intval($client['user_id']) === 0 || intval($client['user_id']) === intval($user_id)) {
        -				$client_found = true;
        -				$client_name = $client['client_name'];
        -				$client_secret = $client['client_secret'];
        -				// Until "Dynamic Client Registration" is fully tested - allow new clients to assign their own secret in the REQUEST
        -				if (! $client_secret) {
        -					$client_secret = ((isset($_REQUEST['client_secret'])) ? $_REQUEST['client_secret'] : random_string(16));
        -				}
        -				$grant_types = $client['grant_types'];
        -				// Client apps are registered per channel
        +        if ($client) {
        +            if (intval($client['user_id']) === 0 || intval($client['user_id']) === intval($user_id)) {
        +                $client_found = true;
        +                $client_name = $client['client_name'];
        +                $client_secret = $client['client_secret'];
        +                // Until "Dynamic Client Registration" is fully tested - allow new clients to assign their own secret in the REQUEST
        +                if (!$client_secret) {
        +                    $client_secret = ((isset($_REQUEST['client_secret'])) ? $_REQUEST['client_secret'] : random_string(16));
        +                }
        +                $grant_types = $client['grant_types'];
        +                // Client apps are registered per channel
         
         
        -logger('client_id: ' . $client_id);
        -logger('client_secret: ' . $client_secret);
        -logger('redirect_uri: ' . $redirect_uri);
        -logger('grant_types: ' . $_REQUEST['grant_types']);
        -logger('scope: ' . $_REQUEST['scope']);
        -logger('user_id: ' . $user_id);
        -logger('client_name: ' . $client_name);
        +                logger('client_id: ' . $client_id);
        +                logger('client_secret: ' . $client_secret);
        +                logger('redirect_uri: ' . $redirect_uri);
        +                logger('grant_types: ' . $_REQUEST['grant_types']);
        +                logger('scope: ' . $_REQUEST['scope']);
        +                logger('user_id: ' . $user_id);
        +                logger('client_name: ' . $client_name);
         
        -				$storage->setClientDetails($client_id, $client_secret, $redirect_uri, $grant_types, $_REQUEST['scope'], $user_id, $client_name);
        -			}
        -		}
        -		if (! $client_found) {
        -			$response->send();
        -			killme();
        -		}
        +                $storage->setClientDetails($client_id, $client_secret, $redirect_uri, $grant_types, $_REQUEST['scope'], $user_id, $client_name);
        +            }
        +        }
        +        if (!$client_found) {
        +            $response->send();
        +            killme();
        +        }
         
        -		$response->setParameter('client_secret', $client['client_secret']);
        +        $response->setParameter('client_secret', $client['client_secret']);
         
        -		// validate the authorize request
        -		if (!$s->validateAuthorizeRequest($request, $response)) {
        -			$response->send();
        -			killme();
        -		}
        +        // validate the authorize request
        +        if (!$s->validateAuthorizeRequest($request, $response)) {
        +            $response->send();
        +            killme();
        +        }
         
        -		// print the authorization code if the user has authorized your client
        -		$is_authorized = ($_POST['authorize'] === 'allow');
        -		$s->handleAuthorizeRequest($request, $response, $is_authorized, $user_id);
        -		if ($is_authorized) {
        -			$code = substr($response->getHttpHeader('Location'), strpos($response->getHttpHeader('Location'), 'code=') + 5, 40);
        -			logger('Authorization Code: ' .  $code);
        -		}
        -
        -		$response->send();
        -		killme();
        -	}
        +        // print the authorization code if the user has authorized your client
        +        $is_authorized = ($_POST['authorize'] === 'allow');
        +        $s->handleAuthorizeRequest($request, $response, $is_authorized, $user_id);
        +        if ($is_authorized) {
        +            $code = substr($response->getHttpHeader('Location'), strpos($response->getHttpHeader('Location'), 'code=') + 5, 40);
        +            logger('Authorization Code: ' . $code);
        +        }
         
        +        $response->send();
        +        killme();
        +    }
         }
        diff --git a/Zotlabs/Module/Block.php b/Zotlabs/Module/Block.php
        index 72dc3a7b3..4b16d03bd 100644
        --- a/Zotlabs/Module/Block.php
        +++ b/Zotlabs/Module/Block.php
        @@ -1,91 +1,97 @@
          1 && argv(1) === 'sys' && is_site_admin()) {
        -			$sys = get_sys_channel();
        -			if($sys && intval($sys['channel_id'])) {
        -				\App::$is_sys = true;
        -			}
        -		}
        -	
        -		if(argc() > 1)
        -			$which = argv(1);
        -		else
        -			return;
        -	
        -		Libprofile::load($which);
        -	
        -	}
        -	
        -	
        -	function get() {
        -	
        -		if(! \App::$profile) {
        -			notice( t('Requested profile is not available.') . EOL );
        -			\App::$error = 404;
        -			return;
        -		}
        -	
        -		$which = argv(1);
        -	
        -		$_SESSION['return_url'] = \App::$query_string;
        -	
        -		$uid = local_channel();
        -		$owner = 0;
        -		$channel = null;
        -		$observer = \App::get_observer();
        -	
        -		$channel = \App::get_channel();
        -	
        -		if(\App::$is_sys && is_site_admin()) {
        -			$sys = get_sys_channel();
        -			if($sys && intval($sys['channel_id'])) {
        -				$uid = $owner = intval($sys['channel_id']);
        -				$channel = $sys;
        -				$observer = $sys;
        -			}
        -		}
        -	
        -		if(! $owner) {
        -			// Figure out who the page owner is.
        -			$r = q("select channel_id from channel where channel_address = '%s'",
        -				dbesc($which)
        -			);
        -			if($r) {
        -				$owner = intval($r[0]['channel_id']);
        -			}
        -		}
        -	
        -		$ob_hash = (($observer) ? $observer['xchan_hash'] : '');
        -	
        -		$perms = get_all_perms($owner,$ob_hash);
        -	
        -		if(! $perms['write_pages']) {
        -			notice( t('Permission denied.') . EOL);
        -			return;
        -		}
        -	
        -		// Block design features from visitors 
        -	
        -		if((! $uid) || ($uid != $owner)) {
        -			notice( t('Permission denied.') . EOL);
        -			return;
        -		}
        -	
        -		$mimetype = (($_REQUEST['mimetype']) ? $_REQUEST['mimetype'] : get_pconfig($owner,'system','page_mimetype'));
        +    public function init()
        +    {
         
        -		$x = array(
        -			'webpage' => ITEM_TYPE_BLOCK,
        -			'is_owner' => true,
        -			'nickname' => \App::$profile['channel_address'],
        -			'lockstate' => (($channel['channel_allow_cid'] || $channel['channel_allow_gid'] || $channel['channel_deny_cid'] || $channel['channel_deny_gid']) ? 'lock' : 'unlock'),
        -			'bang' => '',
        -			'showacl' => false,
        -			'visitor' => true,
        -			'mimetype' => $mimetype,
        -			'mimeselect' => true,
        -			'hide_location' => true,
        -			'ptlabel' => t('Block Name'),
        -			'profile_uid' => intval($owner),
        -			'expanded' => true,
        -			'novoting' => true,
        -			'bbco_autocomplete' => 'bbcode',
        -			'bbcode' => true
        -		);
        -	
        -		if($_REQUEST['title'])
        -			$x['title'] = $_REQUEST['title'];
        -		if($_REQUEST['body'])
        -			$x['body'] = $_REQUEST['body'];
        -		if($_REQUEST['pagetitle'])
        -			$x['pagetitle'] = $_REQUEST['pagetitle'];
        -	
        -		$editor = status_editor($x);
        -	
        +        if (argc() > 1 && argv(1) === 'sys' && is_site_admin()) {
        +            $sys = get_sys_channel();
        +            if ($sys && intval($sys['channel_id'])) {
        +                App::$is_sys = true;
        +            }
        +        }
         
        -		$r = q("select iconfig.iid, iconfig.k, iconfig.v, mid, title, body, mimetype, created, edited from iconfig 
        +        if (argc() > 1) {
        +            $which = argv(1);
        +        } else {
        +            return;
        +        }
        +
        +        Libprofile::load($which);
        +    }
        +
        +
        +    public function get()
        +    {
        +
        +        if (!App::$profile) {
        +            notice(t('Requested profile is not available.') . EOL);
        +            App::$error = 404;
        +            return;
        +        }
        +
        +        $which = argv(1);
        +
        +        $_SESSION['return_url'] = App::$query_string;
        +
        +        $uid = local_channel();
        +        $owner = 0;
        +        $channel = null;
        +        $observer = App::get_observer();
        +
        +        $channel = App::get_channel();
        +
        +        if (App::$is_sys && is_site_admin()) {
        +            $sys = get_sys_channel();
        +            if ($sys && intval($sys['channel_id'])) {
        +                $uid = $owner = intval($sys['channel_id']);
        +                $channel = $sys;
        +                $observer = $sys;
        +            }
        +        }
        +
        +        if (!$owner) {
        +            // Figure out who the page owner is.
        +            $r = q(
        +                "select channel_id from channel where channel_address = '%s'",
        +                dbesc($which)
        +            );
        +            if ($r) {
        +                $owner = intval($r[0]['channel_id']);
        +            }
        +        }
        +
        +        $ob_hash = (($observer) ? $observer['xchan_hash'] : '');
        +
        +        $perms = get_all_perms($owner, $ob_hash);
        +
        +        if (!$perms['write_pages']) {
        +            notice(t('Permission denied.') . EOL);
        +            return;
        +        }
        +
        +        // Block design features from visitors
        +
        +        if ((!$uid) || ($uid != $owner)) {
        +            notice(t('Permission denied.') . EOL);
        +            return;
        +        }
        +
        +        $mimetype = (($_REQUEST['mimetype']) ? $_REQUEST['mimetype'] : get_pconfig($owner, 'system', 'page_mimetype'));
        +
        +        $x = array(
        +            'webpage' => ITEM_TYPE_BLOCK,
        +            'is_owner' => true,
        +            'nickname' => App::$profile['channel_address'],
        +            'lockstate' => (($channel['channel_allow_cid'] || $channel['channel_allow_gid'] || $channel['channel_deny_cid'] || $channel['channel_deny_gid']) ? 'lock' : 'unlock'),
        +            'bang' => '',
        +            'showacl' => false,
        +            'visitor' => true,
        +            'mimetype' => $mimetype,
        +            'mimeselect' => true,
        +            'hide_location' => true,
        +            'ptlabel' => t('Block Name'),
        +            'profile_uid' => intval($owner),
        +            'expanded' => true,
        +            'novoting' => true,
        +            'bbco_autocomplete' => 'bbcode',
        +            'bbcode' => true
        +        );
        +
        +        if ($_REQUEST['title']) {
        +            $x['title'] = $_REQUEST['title'];
        +        }
        +        if ($_REQUEST['body']) {
        +            $x['body'] = $_REQUEST['body'];
        +        }
        +        if ($_REQUEST['pagetitle']) {
        +            $x['pagetitle'] = $_REQUEST['pagetitle'];
        +        }
        +
        +        $editor = status_editor($x);
        +
        +
        +        $r = q(
        +            "select iconfig.iid, iconfig.k, iconfig.v, mid, title, body, mimetype, created, edited from iconfig 
         			left join item on iconfig.iid = item.id
         			where uid = %d and iconfig.cat = 'system' and iconfig.k = 'BUILDBLOCK' 
         			and item_type = %d order by item.created desc",
        -			intval($owner),
        -			intval(ITEM_TYPE_BLOCK)
        -		);
        -	
        -		$pages = null;
        -	
        -		if($r) {
        -			$pages = [];
        -			foreach($r as $rr) {
        -				$element_arr = array(
        -					'type'      => 'block',
        -					'title'	    => $rr['title'],
        -					'body'      => $rr['body'],
        -					'created'   => $rr['created'],
        -					'edited'    => $rr['edited'],
        -					'mimetype'  => $rr['mimetype'],
        -					'pagetitle' => $rr['v'],
        -					'mid'       => $rr['mid']
        -				);
        -				$pages[$rr['iid']][] = array(
        -					'url' => $rr['iid'],
        -					'name' => $rr['v'],
        -					'title' => $rr['title'],
        -					'created' => $rr['created'],
        -					'edited' => $rr['edited'],
        -					'bb_element' => '[element]' . base64url_encode(json_encode($element_arr)) . '[/element]'
        -				);
        -			} 
        -		}
        -	
        -		//Build the base URL for edit links
        -		$url = z_root() . '/editblock/' . $which; 
        -	
        -		$o .= replace_macros(get_markup_template('blocklist.tpl'), array(
        -			'$baseurl'    => $url,
        -			'$title'      => t('Blocks'),
        -			'$name'       => t('Block Name'),
        -			'$blocktitle' => t('Block Title'),
        -			'$created'    => t('Created'),
        -			'$edited'     => t('Edited'),
        -			'$create'     => t('Create'),
        -			'$edit'       => t('Edit'),
        -			'$share'      => t('Share'),
        -			'$delete'     => t('Delete'),
        -			'$editor'     => $editor,
        -			'$pages'      => $pages,
        -			'$channel'    => $which,
        -			'$view'       => t('View'),
        -			'$preview'    => '1',
        -		));
        -	    
        -		return $o;
        -	}
        -	
        +            intval($owner),
        +            intval(ITEM_TYPE_BLOCK)
        +        );
        +
        +        $pages = null;
        +
        +        if ($r) {
        +            $pages = [];
        +            foreach ($r as $rr) {
        +                $element_arr = array(
        +                    'type' => 'block',
        +                    'title' => $rr['title'],
        +                    'body' => $rr['body'],
        +                    'created' => $rr['created'],
        +                    'edited' => $rr['edited'],
        +                    'mimetype' => $rr['mimetype'],
        +                    'pagetitle' => $rr['v'],
        +                    'mid' => $rr['mid']
        +                );
        +                $pages[$rr['iid']][] = array(
        +                    'url' => $rr['iid'],
        +                    'name' => $rr['v'],
        +                    'title' => $rr['title'],
        +                    'created' => $rr['created'],
        +                    'edited' => $rr['edited'],
        +                    'bb_element' => '[element]' . base64url_encode(json_encode($element_arr)) . '[/element]'
        +                );
        +            }
        +        }
        +
        +        //Build the base URL for edit links
        +        $url = z_root() . '/editblock/' . $which;
        +
        +        $o .= replace_macros(get_markup_template('blocklist.tpl'), array(
        +            '$baseurl' => $url,
        +            '$title' => t('Blocks'),
        +            '$name' => t('Block Name'),
        +            '$blocktitle' => t('Block Title'),
        +            '$created' => t('Created'),
        +            '$edited' => t('Edited'),
        +            '$create' => t('Create'),
        +            '$edit' => t('Edit'),
        +            '$share' => t('Share'),
        +            '$delete' => t('Delete'),
        +            '$editor' => $editor,
        +            '$pages' => $pages,
        +            '$channel' => $which,
        +            '$view' => t('View'),
        +            '$preview' => '1',
        +        ));
        +
        +        return $o;
        +    }
         }
        diff --git a/Zotlabs/Module/Ca.php b/Zotlabs/Module/Ca.php
        index 93d6438ca..96caace69 100644
        --- a/Zotlabs/Module/Ca.php
        +++ b/Zotlabs/Module/Ca.php
        @@ -1,44 +1,66 @@
          1) {
        -			$path = 'cache/img/' . substr(argv(1),0,2) . '/' . argv(1);
        +    /**
        +     * get
        +     *
        +     * @return void
        +     */
        +    public function get()
        +    {
        +        if (argc() > 1) {
        +            $path = 'cache/img/' . substr(argv(1), 0, 2) . '/' . argv(1);
         
        -			if (file_exists($path) && filesize($path)) {
        -				$x = @getimagesize($path);
        -				if ($x) {
        -					header('Content-Type: ' . $x['mime']);
        -				}
        +            if (file_exists($path) && filesize($path)) {
        +                $x = @getimagesize($path);
        +                if ($x) {
        +                    header('Content-Type: ' . $x['mime']);
        +                }
         
        -				$cache = intval(get_config('system','photo_cache_time'));
        -				if (! $cache) {
        -					$cache = (3600 * 24); // 1 day
        -				}
        -			 	header('Expires: ' . gmdate('D, d M Y H:i:s', time() + $cache) . ' GMT');
        -				// Set browser cache age as $cache.  But set timeout of 'shared caches'
        -				// much lower in the event that infrastructure caching is present.
        -				$smaxage = intval($cache/12);
        -				header('Cache-Control: s-maxage=' . $smaxage . '; max-age=' . $cache . ';');
        -	
        -				$infile = fopen($path,'rb');
        -				$outfile = fopen('php://output','wb');
        -				if ($infile && $outfile) {
        -					pipe_streams($infile,$outfile);
        -				}
        -				fclose($infile);
        -				fclose($outfile);
        -				killme();
        -			}
        +                $cache = intval(get_config('system', 'photo_cache_time'));
        +                if (!$cache) {
        +                    $cache = (3600 * 24); // 1 day
        +                }
        +                header(
        +                    'Expires: ' . gmdate('D, d M Y H:i:s', time() + $cache)
        +                    . ' GMT'
        +                );
        +                // Set browser cache age as $cache.  But set timeout of
        +                // 'shared caches' much lower in the event that infrastructure
        +                // caching is present.
        +                $smaxage = intval($cache / 12);
        +                header(
        +                    'Cache-Control: s-maxage=' . $smaxage
        +                    . '; max-age=' . $cache . ';'
        +                );
         
        -			if ($_GET['url']) {
        -				goaway($url);
        -			}
        -		}
        -		http_status_exit(404,'Not found');
        -	}
        -}
        \ No newline at end of file
        +                $infile = fopen($path, 'rb');
        +                $outfile = fopen('php://output', 'wb');
        +                if ($infile && $outfile) {
        +                    pipe_streams($infile, $outfile);
        +                }
        +                fclose($infile);
        +                fclose($outfile);
        +                killme();
        +            }
        +
        +            if ($_GET['url']) {
        +                goaway($url);
        +            }
        +        }
        +        http_status_exit(404, 'Not found');
        +    }
        +}
        diff --git a/Zotlabs/Module/Cal.php b/Zotlabs/Module/Cal.php
        index d40f8a4f5..877356004 100644
        --- a/Zotlabs/Module/Cal.php
        +++ b/Zotlabs/Module/Cal.php
        @@ -1,12 +1,10 @@
          1) {
        -			$nick = argv(1);
        -	
        -			Libprofile::load($nick);
        -	
        -			$channelx = channelx_by_nick($nick);
        -	
        -			if(! $channelx)
        -				return;
        -	
        -			\App::$data['channel'] = $channelx;
        -	
        -			$observer = \App::get_observer();
        -			\App::$data['observer'] = $observer;
        -	
        -			$observer_xchan = (($observer) ? $observer['xchan_hash'] : '');
        -	
        -			head_set_icon(\App::$data['channel']['xchan_photo_s']);
        -	
        -			\App::$page['htmlhead'] .= "" ;
        -	
        -		}
        -	
        -		return;
        -	}
        -	
        -	
        -	
        -	function get() {
        -	
        -		if(observer_prohibited()) {
        -			return;
        -		}
        -		
        -		$channel = null;
        -	
        -		if(argc() > 1) {
        -			$channel = channelx_by_nick(argv(1));
        -		}
        -	
        -	
        -		if(! $channel) {
        -			notice( t('Channel not found.') . EOL);
        -			return;
        -		}
        -	
        -		// since we don't currently have an event permission - use the stream permission
        -	
        -		if(! perm_is_allowed($channel['channel_id'], get_observer_hash(), 'view_stream')) {
        -			notice( t('Permissions denied.') . EOL);
        -			return;
        -		}
        +    public function init()
        +    {
        +        if (observer_prohibited()) {
        +            return;
        +        }
         
        -		nav_set_selected('Calendar');
        -	
        -		$sql_extra = permissions_sql($channel['channel_id'],get_observer_hash(),'event');
        +        $o = '';
         
        -		$first_day = intval(get_pconfig($channel['channel_id'],'system','cal_first_day',0));
        -	
        -		$htpl = get_markup_template('event_head.tpl');
        -		\App::$page['htmlhead'] .= replace_macros($htpl,array(
        -			'$baseurl' => z_root(),
        -			'$module_url' => '/cal/' . $channel['channel_address'],
        -			'$modparams' => 2,
        -			'$lang' => \App::$language,
        -			'$first_day' => $first_day
        -		));
        -	
        -		$o = '';
        -	
        -		$mode = 'view';
        -		$y = 0;
        -		$m = 0;
        -		$ignored = ((x($_REQUEST,'ignored')) ? " and dismissed = " . intval($_REQUEST['ignored']) . " "  : '');
        -	
        -		// logger('args: ' . print_r(\App::$argv,true));
        -	
        -		if(argc() > 3 && intval(argv(2)) && intval(argv(3))) {
        -			$mode = 'view';
        -			$y = intval(argv(2));
        -			$m = intval(argv(3));
        -		}
        -		if(argc() <= 3) {
        -			$mode = 'view';
        -			$event_id = argv(2);
        -		}
        -	
        -		if($mode == 'view') {
        -	
        -			/* edit/create form */
        -			if($event_id) {
        -				$r = q("SELECT * FROM event WHERE event_hash = '%s' AND uid = %d LIMIT 1",
        -					dbesc($event_id),
        -					intval($channel['channel_id'])
        -				);
        -				if(count($r))
        -					$orig_event = $r[0];
        -			}
        -	
        -	
        -			// Passed parameters overrides anything found in the DB
        -			if(!x($orig_event))
        -				$orig_event = [];
        -	
        -	
        -	
        -			$tz = date_default_timezone_get();
        -			if(x($orig_event))
        -				$tz = (($orig_event['adjust']) ? date_default_timezone_get() : 'UTC');
        -	
        -			$syear = datetime_convert('UTC', $tz, $sdt, 'Y');
        -			$smonth = datetime_convert('UTC', $tz, $sdt, 'm');
        -			$sday = datetime_convert('UTC', $tz, $sdt, 'd');
        -			$shour = datetime_convert('UTC', $tz, $sdt, 'H');
        -			$sminute = datetime_convert('UTC', $tz, $sdt, 'i');
        -	
        -			$stext = datetime_convert('UTC',$tz,$sdt);
        -			$stext = substr($stext,0,14) . "00:00";
        -	
        -			$fyear = datetime_convert('UTC', $tz, $fdt, 'Y');
        -			$fmonth = datetime_convert('UTC', $tz, $fdt, 'm');
        -			$fday = datetime_convert('UTC', $tz, $fdt, 'd');
        -			$fhour = datetime_convert('UTC', $tz, $fdt, 'H');
        -			$fminute = datetime_convert('UTC', $tz, $fdt, 'i');
        -	
        -			$ftext = datetime_convert('UTC',$tz,$fdt);
        -			$ftext = substr($ftext,0,14) . "00:00";
        -	
        -			$type = ((x($orig_event)) ? $orig_event['etype'] : 'event');
        -	
        -			$f = get_config('system','event_input_format');
        -			if(! $f)
        -				$f = 'ymd';
        -	
        -			$catsenabled = Apps::system_app_installed(local_channel(),'Categories');
        -	
        -	
        -			$show_bd = perm_is_allowed($channel['channel_id'], get_observer_hash(), 'view_contacts');
        -			if(! $show_bd) {
        -				$sql_extra .= " and event.etype != 'birthday' ";
        -			}
        -	
        -	
        -			$category = '';
        -	
        -			$thisyear = datetime_convert('UTC',date_default_timezone_get(),'now','Y');
        -			$thismonth = datetime_convert('UTC',date_default_timezone_get(),'now','m');
        -			if(! $y)
        -				$y = intval($thisyear);
        -			if(! $m)
        -				$m = intval($thismonth);
        -	
        -			// Put some limits on dates. The PHP date functions don't seem to do so well before 1900.
        -			// An upper limit was chosen to keep search engines from exploring links millions of years in the future. 
        -	
        -			if($y < 1901)
        -				$y = 1900;
        -			if($y > 2099)
        -				$y = 2100;
        -	
        -			$nextyear = $y;
        -			$nextmonth = $m + 1;
        -			if($nextmonth > 12) {
        -					$nextmonth = 1;
        -				$nextyear ++;
        -			}
        -	
        -			$prevyear = $y;
        -			if($m > 1)
        -				$prevmonth = $m - 1;
        -			else {
        -				$prevmonth = 12;
        -				$prevyear --;
        -			}
        -				
        -			$dim    = get_dim($y,$m);
        -			$start  = sprintf('%d-%d-%d %d:%d:%d',$y,$m,1,0,0,0);
        -			$finish = sprintf('%d-%d-%d %d:%d:%d',$y,$m,$dim,23,59,59);
        -	
        -	
        -			if (argv(2) === 'json'){
        -				if (x($_GET,'start'))	$start = $_GET['start'];
        -				if (x($_GET,'end'))	$finish = $_GET['end'];
        -			}
        -	
        -			$start  = datetime_convert('UTC','UTC',$start);
        -			$finish = datetime_convert('UTC','UTC',$finish);
        -	
        -			$adjust_start = datetime_convert('UTC', date_default_timezone_get(), $start);
        -			$adjust_finish = datetime_convert('UTC', date_default_timezone_get(), $finish);
        -	
        +        if (argc() > 1) {
        +            $nick = argv(1);
         
        -			if(! perm_is_allowed(\App::$profile['uid'],get_observer_hash(),'view_contacts'))
        -				$sql_extra .= " and etype != 'birthday' ";
        +            Libprofile::load($nick);
         
        -			if (x($_GET,'id')){
        -			  	$r = q("SELECT event.*, item.plink, item.item_flags, item.author_xchan, item.owner_xchan
        +            $channelx = channelx_by_nick($nick);
        +
        +            if (!$channelx) {
        +                return;
        +            }
        +
        +            App::$data['channel'] = $channelx;
        +
        +            $observer = App::get_observer();
        +            App::$data['observer'] = $observer;
        +
        +            $observer_xchan = (($observer) ? $observer['xchan_hash'] : '');
        +
        +            head_set_icon(App::$data['channel']['xchan_photo_s']);
        +
        +            App::$page['htmlhead'] .= "";
        +        }
        +
        +        return;
        +    }
        +
        +
        +    public function get()
        +    {
        +
        +        if (observer_prohibited()) {
        +            return;
        +        }
        +
        +        $channel = null;
        +
        +        if (argc() > 1) {
        +            $channel = channelx_by_nick(argv(1));
        +        }
        +
        +
        +        if (!$channel) {
        +            notice(t('Channel not found.') . EOL);
        +            return;
        +        }
        +
        +        // since we don't currently have an event permission - use the stream permission
        +
        +        if (!perm_is_allowed($channel['channel_id'], get_observer_hash(), 'view_stream')) {
        +            notice(t('Permissions denied.') . EOL);
        +            return;
        +        }
        +
        +        nav_set_selected('Calendar');
        +
        +        $sql_extra = permissions_sql($channel['channel_id'], get_observer_hash(), 'event');
        +
        +        $first_day = intval(get_pconfig($channel['channel_id'], 'system', 'cal_first_day', 0));
        +
        +        $htpl = get_markup_template('event_head.tpl');
        +        App::$page['htmlhead'] .= replace_macros($htpl, array(
        +            '$baseurl' => z_root(),
        +            '$module_url' => '/cal/' . $channel['channel_address'],
        +            '$modparams' => 2,
        +            '$lang' => App::$language,
        +            '$first_day' => $first_day
        +        ));
        +
        +        $o = '';
        +
        +        $mode = 'view';
        +        $y = 0;
        +        $m = 0;
        +        $ignored = ((x($_REQUEST, 'ignored')) ? " and dismissed = " . intval($_REQUEST['ignored']) . " " : '');
        +
        +        // logger('args: ' . print_r(\App::$argv,true));
        +
        +        if (argc() > 3 && intval(argv(2)) && intval(argv(3))) {
        +            $mode = 'view';
        +            $y = intval(argv(2));
        +            $m = intval(argv(3));
        +        }
        +        if (argc() <= 3) {
        +            $mode = 'view';
        +            $event_id = argv(2);
        +        }
        +
        +        if ($mode == 'view') {
        +            /* edit/create form */
        +            if ($event_id) {
        +                $r = q(
        +                    "SELECT * FROM event WHERE event_hash = '%s' AND uid = %d LIMIT 1",
        +                    dbesc($event_id),
        +                    intval($channel['channel_id'])
        +                );
        +                if (count($r)) {
        +                    $orig_event = $r[0];
        +                }
        +            }
        +
        +
        +            // Passed parameters overrides anything found in the DB
        +            if (!x($orig_event)) {
        +                $orig_event = [];
        +            }
        +
        +
        +            $tz = date_default_timezone_get();
        +            if (x($orig_event)) {
        +                $tz = (($orig_event['adjust']) ? date_default_timezone_get() : 'UTC');
        +            }
        +
        +            $syear = datetime_convert('UTC', $tz, $sdt, 'Y');
        +            $smonth = datetime_convert('UTC', $tz, $sdt, 'm');
        +            $sday = datetime_convert('UTC', $tz, $sdt, 'd');
        +            $shour = datetime_convert('UTC', $tz, $sdt, 'H');
        +            $sminute = datetime_convert('UTC', $tz, $sdt, 'i');
        +
        +            $stext = datetime_convert('UTC', $tz, $sdt);
        +            $stext = substr($stext, 0, 14) . "00:00";
        +
        +            $fyear = datetime_convert('UTC', $tz, $fdt, 'Y');
        +            $fmonth = datetime_convert('UTC', $tz, $fdt, 'm');
        +            $fday = datetime_convert('UTC', $tz, $fdt, 'd');
        +            $fhour = datetime_convert('UTC', $tz, $fdt, 'H');
        +            $fminute = datetime_convert('UTC', $tz, $fdt, 'i');
        +
        +            $ftext = datetime_convert('UTC', $tz, $fdt);
        +            $ftext = substr($ftext, 0, 14) . "00:00";
        +
        +            $type = ((x($orig_event)) ? $orig_event['etype'] : 'event');
        +
        +            $f = get_config('system', 'event_input_format');
        +            if (!$f) {
        +                $f = 'ymd';
        +            }
        +
        +            $catsenabled = Apps::system_app_installed(local_channel(), 'Categories');
        +
        +
        +            $show_bd = perm_is_allowed($channel['channel_id'], get_observer_hash(), 'view_contacts');
        +            if (!$show_bd) {
        +                $sql_extra .= " and event.etype != 'birthday' ";
        +            }
        +
        +
        +            $category = '';
        +
        +            $thisyear = datetime_convert('UTC', date_default_timezone_get(), 'now', 'Y');
        +            $thismonth = datetime_convert('UTC', date_default_timezone_get(), 'now', 'm');
        +            if (!$y) {
        +                $y = intval($thisyear);
        +            }
        +            if (!$m) {
        +                $m = intval($thismonth);
        +            }
        +
        +            // Put some limits on dates. The PHP date functions don't seem to do so well before 1900.
        +            // An upper limit was chosen to keep search engines from exploring links millions of years in the future.
        +
        +            if ($y < 1901) {
        +                $y = 1900;
        +            }
        +            if ($y > 2099) {
        +                $y = 2100;
        +            }
        +
        +            $nextyear = $y;
        +            $nextmonth = $m + 1;
        +            if ($nextmonth > 12) {
        +                $nextmonth = 1;
        +                $nextyear++;
        +            }
        +
        +            $prevyear = $y;
        +            if ($m > 1) {
        +                $prevmonth = $m - 1;
        +            } else {
        +                $prevmonth = 12;
        +                $prevyear--;
        +            }
        +
        +            $dim = get_dim($y, $m);
        +            $start = sprintf('%d-%d-%d %d:%d:%d', $y, $m, 1, 0, 0, 0);
        +            $finish = sprintf('%d-%d-%d %d:%d:%d', $y, $m, $dim, 23, 59, 59);
        +
        +
        +            if (argv(2) === 'json') {
        +                if (x($_GET, 'start')) {
        +                    $start = $_GET['start'];
        +                }
        +                if (x($_GET, 'end')) {
        +                    $finish = $_GET['end'];
        +                }
        +            }
        +
        +            $start = datetime_convert('UTC', 'UTC', $start);
        +            $finish = datetime_convert('UTC', 'UTC', $finish);
        +
        +            $adjust_start = datetime_convert('UTC', date_default_timezone_get(), $start);
        +            $adjust_finish = datetime_convert('UTC', date_default_timezone_get(), $finish);
        +
        +
        +            if (!perm_is_allowed(App::$profile['uid'], get_observer_hash(), 'view_contacts')) {
        +                $sql_extra .= " and etype != 'birthday' ";
        +            }
        +
        +            if (x($_GET, 'id')) {
        +                $r = q(
        +                    "SELECT event.*, item.plink, item.item_flags, item.author_xchan, item.owner_xchan
         	                                from event left join item on resource_id = event_hash where resource_type = 'event' and event.uid = %d and event.id = %d $sql_extra limit 1",
        -					intval($channel['channel_id']),
        -					intval($_GET['id'])
        -				);
        -			} 
        -			else {
        -				// fixed an issue with "nofinish" events not showing up in the calendar.
        -				// There's still an issue if the finish date crosses the end of month.
        -				// Noting this for now - it will need to be fixed here and in Friendica.
        -				// Ultimately the finish date shouldn't be involved in the query. 
        -	
        -				$r = q("SELECT event.*, item.plink, item.item_flags, item.author_xchan, item.owner_xchan
        +                    intval($channel['channel_id']),
        +                    intval($_GET['id'])
        +                );
        +            } else {
        +                // fixed an issue with "nofinish" events not showing up in the calendar.
        +                // There's still an issue if the finish date crosses the end of month.
        +                // Noting this for now - it will need to be fixed here and in Friendica.
        +                // Ultimately the finish date shouldn't be involved in the query.
        +
        +                $r = q(
        +                    "SELECT event.*, item.plink, item.item_flags, item.author_xchan, item.owner_xchan
         	                              from event left join item on event_hash = resource_id 
         					where resource_type = 'event' and event.uid = %d and event.uid = item.uid $ignored 
         					AND (( adjust = 0 AND ( dtend >= '%s' or nofinish = 1 ) AND dtstart <= '%s' ) 
         					OR  (  adjust = 1 AND ( dtend >= '%s' or nofinish = 1 ) AND dtstart <= '%s' )) $sql_extra ",
        -					intval($channel['channel_id']),
        -					dbesc($start),
        -					dbesc($finish),
        -					dbesc($adjust_start),
        -					dbesc($adjust_finish)
        -				);
        -	
        -			}
        -	
        -			$links = [];
        -	
        -			if($r) {
        -				xchan_query($r);
        -				$r = fetch_post_tags($r,true);
        -	
        -				$r = sort_by_date($r);
        -			}
        -	
        -			if($r) {
        -				foreach($r as $rr) {
        -					$j = (($rr['adjust']) ? datetime_convert('UTC',date_default_timezone_get(),$rr['dtstart'], 'j') : datetime_convert('UTC','UTC',$rr['dtstart'],'j'));
        -					if(! x($links,$j)) 
        -						$links[$j] = z_root() . '/' . \App::$cmd . '#link-' . $j;
        -				}
        -			}
        -	
        -			$events=[];
        -	
        -			$last_date = '';
        -			$fmt = t('l, F j');
        -	
        -			if($r) {
        -	
        -				foreach($r as $rr) {
        -					
        -					$j = (($rr['adjust']) ? datetime_convert('UTC',date_default_timezone_get(),$rr['dtstart'], 'j') : datetime_convert('UTC','UTC',$rr['dtstart'],'j'));
        -					$d = (($rr['adjust']) ? datetime_convert('UTC',date_default_timezone_get(),$rr['dtstart'], $fmt) : datetime_convert('UTC','UTC',$rr['dtstart'],$fmt));
        -					$d = day_translate($d);
        -					
        -					$start = (($rr['adjust']) ? datetime_convert('UTC',date_default_timezone_get(),$rr['dtstart'], 'c') : datetime_convert('UTC','UTC',$rr['dtstart'],'c'));
        -					if ($rr['nofinish']){
        -						$end = null;
        -					} else {
        -						$end = (($rr['adjust']) ? datetime_convert('UTC',date_default_timezone_get(),$rr['dtend'], 'c') : datetime_convert('UTC','UTC',$rr['dtend'],'c'));
        -					}
        -					
        -					
        -					$is_first = ($d !== $last_date);
        -						
        -					$last_date = $d;
        -	
        -					$edit = false;
        -	
        -					$drop = false;
        -	
        -					$title = strip_tags(html_entity_decode(bbcode($rr['summary']),ENT_QUOTES,'UTF-8'));
        -					if(! $title) {
        -						list($title, $_trash) = explode("$rr['id'],
        -						'hash' => $rr['event_hash'],
        -						'start'=> $start,
        -						'end' => $end,
        -						'drop' => $drop,
        -						'allDay' => false,
        -						'title' => $title,
        -						
        -						'j' => $j,
        -						'd' => $d,
        -						'edit' => $edit,
        -						'is_first'=>$is_first,
        -						'item'=>$rr,
        -						'html'=>$html,
        -						'plink' => array($rr['plink'],t('Link to Source'),'',''),
        -					);
        -	
        -	
        -				}
        -			}
        -			
        -			if (argv(2) === 'json'){
        -				echo json_encode($events); killme();
        -			}
        -			
        -			// links: array('href', 'text', 'extra css classes', 'title')
        -			if (x($_GET,'id')){
        -				$tpl =  get_markup_template("event_cal.tpl");
        -			} 
        -			else {
        -				$tpl = get_markup_template("events_cal-js.tpl");
        -			}
        -	
        -			$nick = $channel['channel_address'];
        -	
        -			$o = replace_macros($tpl, array(
        -				'$baseurl'	=> z_root(),
        -				'$new_event'	=> array(z_root().'/cal',(($event_id) ? t('Edit Event') : t('Create Event')),'',''),
        -				'$previus'	=> array(z_root()."/cal/$nick/$prevyear/$prevmonth",t('Previous'),'',''),
        -				'$next'		=> array(z_root()."/cal/$nick/$nextyear/$nextmonth",t('Next'),'',''),
        -				'$export'	=> array(z_root()."/cal/$nick/$y/$m/export",t('Export'),'',''),
        -				'$calendar'	=> cal($y,$m,$links, ' eventcal'),
        -				'$events'	=> $events,
        -				'$upload'	=> t('Import'),
        -				'$submit'	=> t('Submit'),
        -				'$prev'		=> t('Previous'),
        -				'$next'		=> t('Next'),
        -				'$today'	=> t('Today'),
        -				'$form'		=> $form,
        -				'$expandform'	=> ((x($_GET,'expandform')) ? true : false)
        -			));
        -			
        -			if (x($_GET,'id')){ echo $o; killme(); }
        -			
        -			return $o;
        -		}
        -	
        -	}
        -	
        +                    intval($channel['channel_id']),
        +                    dbesc($start),
        +                    dbesc($finish),
        +                    dbesc($adjust_start),
        +                    dbesc($adjust_finish)
        +                );
        +            }
        +
        +            $links = [];
        +
        +            if ($r) {
        +                xchan_query($r);
        +                $r = fetch_post_tags($r, true);
        +
        +                $r = sort_by_date($r);
        +            }
        +
        +            if ($r) {
        +                foreach ($r as $rr) {
        +                    $j = (($rr['adjust']) ? datetime_convert('UTC', date_default_timezone_get(), $rr['dtstart'], 'j') : datetime_convert('UTC', 'UTC', $rr['dtstart'], 'j'));
        +                    if (!x($links, $j)) {
        +                        $links[$j] = z_root() . '/' . App::$cmd . '#link-' . $j;
        +                    }
        +                }
        +            }
        +
        +            $events = [];
        +
        +            $last_date = '';
        +            $fmt = t('l, F j');
        +
        +            if ($r) {
        +                foreach ($r as $rr) {
        +                    $j = (($rr['adjust']) ? datetime_convert('UTC', date_default_timezone_get(), $rr['dtstart'], 'j') : datetime_convert('UTC', 'UTC', $rr['dtstart'], 'j'));
        +                    $d = (($rr['adjust']) ? datetime_convert('UTC', date_default_timezone_get(), $rr['dtstart'], $fmt) : datetime_convert('UTC', 'UTC', $rr['dtstart'], $fmt));
        +                    $d = day_translate($d);
        +
        +                    $start = (($rr['adjust']) ? datetime_convert('UTC', date_default_timezone_get(), $rr['dtstart'], 'c') : datetime_convert('UTC', 'UTC', $rr['dtstart'], 'c'));
        +                    if ($rr['nofinish']) {
        +                        $end = null;
        +                    } else {
        +                        $end = (($rr['adjust']) ? datetime_convert('UTC', date_default_timezone_get(), $rr['dtend'], 'c') : datetime_convert('UTC', 'UTC', $rr['dtend'], 'c'));
        +                    }
        +
        +
        +                    $is_first = ($d !== $last_date);
        +
        +                    $last_date = $d;
        +
        +                    $edit = false;
        +
        +                    $drop = false;
        +
        +                    $title = strip_tags(html_entity_decode(bbcode($rr['summary']), ENT_QUOTES, 'UTF-8'));
        +                    if (!$title) {
        +                        list($title, $_trash) = explode(" $rr['id'],
        +                        'hash' => $rr['event_hash'],
        +                        'start' => $start,
        +                        'end' => $end,
        +                        'drop' => $drop,
        +                        'allDay' => false,
        +                        'title' => $title,
        +
        +                        'j' => $j,
        +                        'd' => $d,
        +                        'edit' => $edit,
        +                        'is_first' => $is_first,
        +                        'item' => $rr,
        +                        'html' => $html,
        +                        'plink' => array($rr['plink'], t('Link to Source'), '', ''),
        +                    );
        +                }
        +            }
        +
        +            if (argv(2) === 'json') {
        +                echo json_encode($events);
        +                killme();
        +            }
        +
        +            // links: array('href', 'text', 'extra css classes', 'title')
        +            if (x($_GET, 'id')) {
        +                $tpl = get_markup_template("event_cal.tpl");
        +            } else {
        +                $tpl = get_markup_template("events_cal-js.tpl");
        +            }
        +
        +            $nick = $channel['channel_address'];
        +
        +            $o = replace_macros($tpl, array(
        +                '$baseurl' => z_root(),
        +                '$new_event' => array(z_root() . '/cal', (($event_id) ? t('Edit Event') : t('Create Event')), '', ''),
        +                '$previus' => array(z_root() . "/cal/$nick/$prevyear/$prevmonth", t('Previous'), '', ''),
        +                '$next' => array(z_root() . "/cal/$nick/$nextyear/$nextmonth", t('Next'), '', ''),
        +                '$export' => array(z_root() . "/cal/$nick/$y/$m/export", t('Export'), '', ''),
        +                '$calendar' => cal($y, $m, $links, ' eventcal'),
        +                '$events' => $events,
        +                '$upload' => t('Import'),
        +                '$submit' => t('Submit'),
        +                '$prev' => t('Previous'),
        +                '$next' => t('Next'),
        +                '$today' => t('Today'),
        +                '$form' => $form,
        +                '$expandform' => ((x($_GET, 'expandform')) ? true : false)
        +            ));
        +
        +            if (x($_GET, 'id')) {
        +                echo $o;
        +                killme();
        +            }
        +
        +            return $o;
        +        }
        +    }
         }
        diff --git a/Zotlabs/Module/Calendar.php b/Zotlabs/Module/Calendar.php
        index 0e979f44c..5794d0bbf 100644
        --- a/Zotlabs/Module/Calendar.php
        +++ b/Zotlabs/Module/Calendar.php
        @@ -1,4 +1,5 @@
         set($x[0]);
        -	
        -			$created = $x[0]['created'];
        -			$edited = datetime_convert();
        -		}
        -		else {
        -			$created = $edited = datetime_convert();
        -			$acl->set_from_array($_POST);
        -		}
        -	
        -		$post_tags = [];
        -		$ac = $acl->get();
        -
        -		$str_contact_allow = $ac['allow_cid'];
        -		$str_group_allow   = $ac['allow_gid'];
        -		$str_contact_deny  = $ac['deny_cid'];
        -		$str_group_deny    = $ac['deny_gid'];
        -	
        -		$private = $acl->is_private();
        -	
        -		$results = linkify_tags($desc, local_channel());
        -
        -		if ($results) {
        -			// Set permissions based on tag replacements
        -	
        -			set_linkified_perms($results, $str_contact_allow, $str_group_allow, local_channel(), false, $private);
        -
        -			foreach ($results as $result) {
        -				$success = $result['success'];
        -				if ($success['replaced']) {
        -					$post_tags[] = [
        -						'uid'   => local_channel(),
        -						'ttype' => $success['termtype'],
        -						'otype' => TERM_OBJ_POST,
        -						'term'  => $success['term'],
        -						'url'   => $success['url']
        -					];	
        -				}
        -			}
        -		}
        +        if (($xchan) && ($xchan !== get_observer_hash())) {
        +            return;
        +        }
         
         
        -		if (strlen($categories)) {
        -			$cats = explode(',',$categories);
        -			foreach ($cats as $cat) {
        -				$post_tags[] = array(
        -					'uid'   => local_channel(), 
        -					'ttype' => TERM_CATEGORY,
        -					'otype' => TERM_OBJ_POST,
        -					'term'  => trim($cat),
        -					'url'   => $channel['xchan_url'] . '?f=&cat=' . urlencode(trim($cat))
        -				);
        -			}
        -		}
        -	
        -		$datarray = [ 
        -			'dtstart'     => $start,
        -			'dtend'       => $finish,
        -			'summary'     => $summary,
        -			'description' => $desc,
        -			'location'    => $location,
        -			'etype'       => $type,
        -			'adjust'      => $adjust,
        -			'nofinish'    => $nofinish,
        -			'uid'         => local_channel(),
        -			'account'     => get_account_id(),
        -			'event_xchan' => $channel['channel_hash'],
        -			'allow_cid'   => $str_contact_allow,
        -			'allow_gid'   => $str_group_allow,
        -			'deny_cid'    => $str_contact_deny,
        -			'deny_gid'    => $str_group_deny,
        -			'private'     => intval($private),
        -			'id'          => $event_id,
        -			'created'     => $created,
        -			'edited'      => $edited
        -		];
        -	
        -		if (intval($_REQUEST['preview'])) {
        -			$html = format_event_html($datarray);
        -			echo $html;
        -			killme();
        -		}
        -	
        -		$event = event_store_event($datarray);
        -	
        -		if ($post_tags)	{
        -			$datarray['term'] = $post_tags;
        -		}
        -	
        -		$item_id = event_store_item($datarray,$event);
        -	
        -		if ($item_id) {
        -			$r = q("select * from item where id = %d",
        -				intval($item_id)
        -			);
        -			if ($r) {
        -				xchan_query($r);
        -				$sync_item = fetch_post_tags($r);
        -				$z = q("select * from event where event_hash = '%s' and uid = %d limit 1",
        -					dbesc($r[0]['resource_id']),
        -					intval($channel['channel_id'])
        -				);
        -				if ($z) {
        -					Libsync::build_sync_packet($channel['channel_id'], [ 'event_item' => [ encode_item($sync_item[0],true) ], 'event' => $z]);
        -				}
        -			}
        -		}
        -	
        -		Run::Summon( [ 'Notifier', 'event', $item_id ] );
        -		killme();
        -	
        -	}
        -	
        -	
        -	
        -	function get() {
        -	
        -		if (argc() > 2 && argv(1) == 'ical') {
        -			$event_id = argv(2);
        -	
        -			$sql_extra = permissions_sql(local_channel());
        -	
        -			$r = q("select * from event where event_hash = '%s' $sql_extra limit 1",
        -				dbesc($event_id)
        -			);
        -			if ($r) { 
        -				header('Content-type: text/calendar');
        -				header('Content-Disposition: attachment; filename="' . t('event') . '-' . $event_id . '.ics"' );
        -				echo ical_wrapper($r);
        -				killme();
        -			}
        -			else {
        -				notice( t('Event not found.') . EOL );
        -				return;
        -			}
        -		}
        -	
        -		if (! local_channel()) {
        -			notice( t('Permission denied.') . EOL);
        -			return;
        -		}
        +        if ($start_text) {
        +            $start = $start_text;
        +        } else {
        +            $start = sprintf('%d-%d-%d %d:%d:0', $startyear, $startmonth, $startday, $starthour, $startminute);
        +        }
         
        -		if ((argc() > 2) && (argv(1) === 'ignore') && intval(argv(2))) {
        -			$r = q("update event set dismissed = 1 where id = %d and uid = %d",
        -				intval(argv(2)),
        -				intval(local_channel())
        -			);
        -		}
        -	
        -		if ((argc() > 2) && (argv(1) === 'unignore') && intval(argv(2))) {
        -			$r = q("update event set dismissed = 0 where id = %d and uid = %d",
        -				intval(argv(2)),
        -				intval(local_channel())
        -			);
        -		}
        +        if ($finish_text) {
        +            $finish = $finish_text;
        +        } else {
        +            $finish = sprintf('%d-%d-%d %d:%d:0', $finishyear, $finishmonth, $finishday, $finishhour, $finishminute);
        +        }
         
        -		$channel = App::get_channel();
        -	
        -		$mode   = 'view';
        -		$export = false;
        +        if ($nofinish) {
        +            $finish = NULL_DATE;
        +        }
         
        -		$ignored = ((x($_REQUEST,'ignored')) ? " and dismissed = " . intval($_REQUEST['ignored']) . " "  : '');
        +        if ($adjust) {
        +            $start = datetime_convert($tz, 'UTC', $start);
        +            if (!$nofinish) {
        +                $finish = datetime_convert($tz, 'UTC', $finish);
        +            }
        +        } else {
        +            $start = datetime_convert('UTC', 'UTC', $start);
        +            if (!$nofinish) {
        +                $finish = datetime_convert('UTC', 'UTC', $finish);
        +            }
        +        }
         
        -		if (argc() > 1) {
        -			if (argc() > 2 && argv(1) === 'add') {
        -				$mode = 'add';
        -				$item_id = intval(argv(2));
        -			}
        -			if (argc() > 2 && argv(1) === 'drop') {
        -				$mode = 'drop';
        -				$event_id = argv(2);
        -			}
        -			if (argc() <= 2 && argv(1) === 'export') {
        -				$export = true;
        -			}
        -			if (argc() > 2 && intval(argv(1)) && intval(argv(2))) {
        -				$mode = 'view';
        -			}
        -			if(argc() <= 2) {
        -				$mode = 'view';
        -				$event_id = argv(1);
        -			}
        -		}
        -	
        -		if ($mode === 'add') {
        -			event_addtocal($item_id,local_channel());
        -			killme();
        -		}
        -	
        -		if ($mode == 'view') {
        -	
        -			/* edit/create form */
        -			if ($event_id) {
        -				$r = q("SELECT * FROM event WHERE event_hash = '%s' AND uid = %d LIMIT 1",
        -					dbesc($event_id),
        -					intval(local_channel())
        -				);
        -				if ($r) {
        -					$orig_event = $r[0];
        -				}
        -			}
        -	
        -			$channel = App::get_channel();
        -	
        -			if (argv(1) === 'json'){
        -				if (x($_GET,'start')) {
        -					$start = $_GET['start'];
        -				}
        -				if (x($_GET,'end'))	{
        -					$finish = $_GET['end'];
        -				}
        -			}
        -	
        -			$start  = datetime_convert('UTC','UTC',$start);
        -			$finish = datetime_convert('UTC','UTC',$finish);
        -	
        -			$adjust_start = datetime_convert('UTC', date_default_timezone_get(), $start);
        -			$adjust_finish = datetime_convert('UTC', date_default_timezone_get(), $finish);
        -	
        -			if (x($_GET,'id')){
        -			  	$r = q("SELECT event.*, item.plink, item.item_flags, item.author_xchan, item.owner_xchan, item.id as item_id
        +        linkify_tags($location, local_channel());
        +
        +        // Don't allow the event to finish before it begins.
        +        // It won't hurt anything, but somebody will file a bug report
        +        // and we'll waste a bunch of time responding to it. Time that
        +        // could've been spent doing something else.
        +
        +        if (strcmp($finish, $start) < 0 && (!$nofinish)) {
        +            notice(t('Event can not end before it has started.') . EOL);
        +            if (intval($_REQUEST['preview'])) {
        +                echo(t('Unable to generate preview.'));
        +            }
        +            killme();
        +        }
        +
        +        if ((!$summary) || (!$start)) {
        +            notice(t('Event title and start time are required.') . EOL);
        +            if (intval($_REQUEST['preview'])) {
        +                echo(t('Unable to generate preview.'));
        +            }
        +            killme();
        +        }
        +
        +        $channel = App::get_channel();
        +        $acl = new AccessControl(false);
        +
        +        if ($event_id) {
        +            $x = q(
        +                "select * from event where id = %d and uid = %d limit 1",
        +                intval($event_id),
        +                intval(local_channel())
        +            );
        +            if (!$x) {
        +                notice(t('Event not found.') . EOL);
        +                if (intval($_REQUEST['preview'])) {
        +                    echo(t('Unable to generate preview.'));
        +                    killme();
        +                }
        +                return;
        +            }
        +
        +            $acl->set($x[0]);
        +
        +            $created = $x[0]['created'];
        +            $edited = datetime_convert();
        +        } else {
        +            $created = $edited = datetime_convert();
        +            $acl->set_from_array($_POST);
        +        }
        +
        +        $post_tags = [];
        +        $ac = $acl->get();
        +
        +        $str_contact_allow = $ac['allow_cid'];
        +        $str_group_allow = $ac['allow_gid'];
        +        $str_contact_deny = $ac['deny_cid'];
        +        $str_group_deny = $ac['deny_gid'];
        +
        +        $private = $acl->is_private();
        +
        +        $results = linkify_tags($desc, local_channel());
        +
        +        if ($results) {
        +            // Set permissions based on tag replacements
        +
        +            set_linkified_perms($results, $str_contact_allow, $str_group_allow, local_channel(), false, $private);
        +
        +            foreach ($results as $result) {
        +                $success = $result['success'];
        +                if ($success['replaced']) {
        +                    $post_tags[] = [
        +                        'uid' => local_channel(),
        +                        'ttype' => $success['termtype'],
        +                        'otype' => TERM_OBJ_POST,
        +                        'term' => $success['term'],
        +                        'url' => $success['url']
        +                    ];
        +                }
        +            }
        +        }
        +
        +
        +        if (strlen($categories)) {
        +            $cats = explode(',', $categories);
        +            foreach ($cats as $cat) {
        +                $post_tags[] = array(
        +                    'uid' => local_channel(),
        +                    'ttype' => TERM_CATEGORY,
        +                    'otype' => TERM_OBJ_POST,
        +                    'term' => trim($cat),
        +                    'url' => $channel['xchan_url'] . '?f=&cat=' . urlencode(trim($cat))
        +                );
        +            }
        +        }
        +
        +        $datarray = [
        +            'dtstart' => $start,
        +            'dtend' => $finish,
        +            'summary' => $summary,
        +            'description' => $desc,
        +            'location' => $location,
        +            'etype' => $type,
        +            'adjust' => $adjust,
        +            'nofinish' => $nofinish,
        +            'uid' => local_channel(),
        +            'account' => get_account_id(),
        +            'event_xchan' => $channel['channel_hash'],
        +            'allow_cid' => $str_contact_allow,
        +            'allow_gid' => $str_group_allow,
        +            'deny_cid' => $str_contact_deny,
        +            'deny_gid' => $str_group_deny,
        +            'private' => intval($private),
        +            'id' => $event_id,
        +            'created' => $created,
        +            'edited' => $edited
        +        ];
        +
        +        if (intval($_REQUEST['preview'])) {
        +            $html = format_event_html($datarray);
        +            echo $html;
        +            killme();
        +        }
        +
        +        $event = event_store_event($datarray);
        +
        +        if ($post_tags) {
        +            $datarray['term'] = $post_tags;
        +        }
        +
        +        $item_id = event_store_item($datarray, $event);
        +
        +        if ($item_id) {
        +            $r = q(
        +                "select * from item where id = %d",
        +                intval($item_id)
        +            );
        +            if ($r) {
        +                xchan_query($r);
        +                $sync_item = fetch_post_tags($r);
        +                $z = q(
        +                    "select * from event where event_hash = '%s' and uid = %d limit 1",
        +                    dbesc($r[0]['resource_id']),
        +                    intval($channel['channel_id'])
        +                );
        +                if ($z) {
        +                    Libsync::build_sync_packet($channel['channel_id'], ['event_item' => [encode_item($sync_item[0], true)], 'event' => $z]);
        +                }
        +            }
        +        }
        +
        +        Run::Summon(['Notifier', 'event', $item_id]);
        +        killme();
        +    }
        +
        +
        +    public function get()
        +    {
        +
        +        if (argc() > 2 && argv(1) == 'ical') {
        +            $event_id = argv(2);
        +
        +            $sql_extra = permissions_sql(local_channel());
        +
        +            $r = q(
        +                "select * from event where event_hash = '%s' $sql_extra limit 1",
        +                dbesc($event_id)
        +            );
        +            if ($r) {
        +                header('Content-type: text/calendar');
        +                header('Content-Disposition: attachment; filename="' . t('event') . '-' . $event_id . '.ics"');
        +                echo ical_wrapper($r);
        +                killme();
        +            } else {
        +                notice(t('Event not found.') . EOL);
        +                return;
        +            }
        +        }
        +
        +        if (!local_channel()) {
        +            notice(t('Permission denied.') . EOL);
        +            return;
        +        }
        +
        +        if ((argc() > 2) && (argv(1) === 'ignore') && intval(argv(2))) {
        +            $r = q(
        +                "update event set dismissed = 1 where id = %d and uid = %d",
        +                intval(argv(2)),
        +                intval(local_channel())
        +            );
        +        }
        +
        +        if ((argc() > 2) && (argv(1) === 'unignore') && intval(argv(2))) {
        +            $r = q(
        +                "update event set dismissed = 0 where id = %d and uid = %d",
        +                intval(argv(2)),
        +                intval(local_channel())
        +            );
        +        }
        +
        +        $channel = App::get_channel();
        +
        +        $mode = 'view';
        +        $export = false;
        +
        +        $ignored = ((x($_REQUEST, 'ignored')) ? " and dismissed = " . intval($_REQUEST['ignored']) . " " : '');
        +
        +        if (argc() > 1) {
        +            if (argc() > 2 && argv(1) === 'add') {
        +                $mode = 'add';
        +                $item_id = intval(argv(2));
        +            }
        +            if (argc() > 2 && argv(1) === 'drop') {
        +                $mode = 'drop';
        +                $event_id = argv(2);
        +            }
        +            if (argc() <= 2 && argv(1) === 'export') {
        +                $export = true;
        +            }
        +            if (argc() > 2 && intval(argv(1)) && intval(argv(2))) {
        +                $mode = 'view';
        +            }
        +            if (argc() <= 2) {
        +                $mode = 'view';
        +                $event_id = argv(1);
        +            }
        +        }
        +
        +        if ($mode === 'add') {
        +            event_addtocal($item_id, local_channel());
        +            killme();
        +        }
        +
        +        if ($mode == 'view') {
        +            /* edit/create form */
        +            if ($event_id) {
        +                $r = q(
        +                    "SELECT * FROM event WHERE event_hash = '%s' AND uid = %d LIMIT 1",
        +                    dbesc($event_id),
        +                    intval(local_channel())
        +                );
        +                if ($r) {
        +                    $orig_event = $r[0];
        +                }
        +            }
        +
        +            $channel = App::get_channel();
        +
        +            if (argv(1) === 'json') {
        +                if (x($_GET, 'start')) {
        +                    $start = $_GET['start'];
        +                }
        +                if (x($_GET, 'end')) {
        +                    $finish = $_GET['end'];
        +                }
        +            }
        +
        +            $start = datetime_convert('UTC', 'UTC', $start);
        +            $finish = datetime_convert('UTC', 'UTC', $finish);
        +
        +            $adjust_start = datetime_convert('UTC', date_default_timezone_get(), $start);
        +            $adjust_finish = datetime_convert('UTC', date_default_timezone_get(), $finish);
        +
        +            if (x($_GET, 'id')) {
        +                $r = q(
        +                    "SELECT event.*, item.plink, item.item_flags, item.author_xchan, item.owner_xchan, item.id as item_id
         	                                from event left join item on item.resource_id = event.event_hash
         					where item.resource_type = 'event' and event.uid = %d and event.id = %d limit 1",
        -					intval(local_channel()),
        -					intval($_GET['id'])
        -				);
        -			}
        -			elseif ($export) {
        -				$r = q("SELECT * from event where uid = %d",
        -					intval(local_channel())
        -				);
        -			}
        -			else {
        -				// fixed an issue with "nofinish" events not showing up in the calendar.
        -				// There's still an issue if the finish date crosses the end of month.
        -				// Noting this for now - it will need to be fixed here and in Friendica.
        -				// Ultimately the finish date shouldn't be involved in the query. 
        +                    intval(local_channel()),
        +                    intval($_GET['id'])
        +                );
        +            } elseif ($export) {
        +                $r = q(
        +                    "SELECT * from event where uid = %d",
        +                    intval(local_channel())
        +                );
        +            } else {
        +                // fixed an issue with "nofinish" events not showing up in the calendar.
        +                // There's still an issue if the finish date crosses the end of month.
        +                // Noting this for now - it will need to be fixed here and in Friendica.
        +                // Ultimately the finish date shouldn't be involved in the query.
         
        -				$r = q("SELECT event.*, item.plink, item.item_flags, item.author_xchan, item.owner_xchan, item.id as item_id
        +                $r = q(
        +                    "SELECT event.*, item.plink, item.item_flags, item.author_xchan, item.owner_xchan, item.id as item_id
         					from event left join item on event.event_hash = item.resource_id 
         					where item.resource_type = 'event' and event.uid = %d and event.uid = item.uid $ignored 
         					AND (( event.adjust = 0 AND ( event.dtend >= '%s' or event.nofinish = 1 ) AND event.dtstart <= '%s' ) 
         					OR  (  event.adjust = 1 AND ( event.dtend >= '%s' or event.nofinish = 1 ) AND event.dtstart <= '%s' )) ",
        -					intval(local_channel()),
        -					dbesc($start),
        -					dbesc($finish),
        -					dbesc($adjust_start),
        -					dbesc($adjust_finish)
        -				);
        +                    intval(local_channel()),
        +                    dbesc($start),
        +                    dbesc($finish),
        +                    dbesc($adjust_start),
        +                    dbesc($adjust_finish)
        +                );
        +            }
         
        -			}
        -		
        -			if($r && ! $export) {
        -				xchan_query($r);
        -				$r = fetch_post_tags($r,true);
        +            if ($r && !$export) {
        +                xchan_query($r);
        +                $r = fetch_post_tags($r, true);
         
        -				$r = sort_by_date($r);
        -			}
        -	
        -			$events = [];
        -		
        -			if ($r) {
        -	
        -				foreach ($r as $rr) {					
        -					$start = (($rr['adjust']) ? datetime_convert('UTC',date_default_timezone_get(),$rr['dtstart'], 'c') : datetime_convert('UTC','UTC',$rr['dtstart'],'c'));
        -					if ($rr['nofinish']) {
        -						$end = null;
        -					}
        -					else {
        -						$end = (($rr['adjust']) ? datetime_convert('UTC',date_default_timezone_get(),$rr['dtend'], 'c') : datetime_convert('UTC','UTC',$rr['dtend'],'c'));
        +                $r = sort_by_date($r);
        +            }
         
        -						// give a fake end to birthdays so they get crammed into a 
        -						// single day on the calendar
        +            $events = [];
         
        -						if ($rr['etype'] === 'birthday')
        -							$end = null;
        -					}
        +            if ($r) {
        +                foreach ($r as $rr) {
        +                    $start = (($rr['adjust']) ? datetime_convert('UTC', date_default_timezone_get(), $rr['dtstart'], 'c') : datetime_convert('UTC', 'UTC', $rr['dtstart'], 'c'));
        +                    if ($rr['nofinish']) {
        +                        $end = null;
        +                    } else {
        +                        $end = (($rr['adjust']) ? datetime_convert('UTC', date_default_timezone_get(), $rr['dtend'], 'c') : datetime_convert('UTC', 'UTC', $rr['dtend'], 'c'));
         
        -					$catsenabled = Apps::system_app_installed($x['profile_uid'], 'Categories');
        -					$categories = '';
        -					if ($catsenabled){
        -						if ($rr['term']) {
        -							$categories = array_elm_to_str(get_terms_oftype($rr['term'], TERM_CATEGORY), 'term');
        -						}
        -					}
        +                        // give a fake end to birthdays so they get crammed into a
        +                        // single day on the calendar
         
        -					$allDay = false;
        +                        if ($rr['etype'] === 'birthday') {
        +                            $end = null;
        +                        }
        +                    }
         
        -					// allDay event rules
        -					if (!strpos($start, 'T') && !strpos($end, 'T'))
        -						$allDay = true;
        -					if (strpos($start, 'T00:00:00') && strpos($end, 'T00:00:00'))
        -						$allDay = true;
        -	
        -					$edit = ((local_channel() && $rr['author_xchan'] == get_observer_hash()) ? array(z_root().'/events/'.$rr['event_hash'].'?expandform=1',t('Edit event'),'','') : false);
        -	
        -					$drop = [ z_root() . '/events/drop/' . $rr['event_hash'], t('Delete event'), '', '' ];
        -	
        -					$events[] = [
        -						'calendar_id' => 'calendar',
        -						'rw'          => true,
        -						'id'          => $rr['id'],
        -						'uri'         => $rr['event_hash'],
        -						'start'       => $start,
        -						'end'         => $end,
        -						'drop'        => $drop,
        -						'allDay'      => $allDay,
        -						'title'       => html_entity_decode($rr['summary'], ENT_COMPAT, 'UTF-8'),
        -						'editable'    => $edit ? true : false,
        -						'item'        => $rr,
        -						'plink'       => [ $rr['plink'], t('Link to source') ],
        -						'description' => htmlentities($rr['description'], ENT_COMPAT, 'UTF-8',false),
        -						'location'    => htmlentities($rr['location'], ENT_COMPAT, 'UTF-8',false),
        -						'allow_cid'   => expand_acl($rr['allow_cid']),
        -						'allow_gid'   => expand_acl($rr['allow_gid']),
        -						'deny_cid'    => expand_acl($rr['deny_cid']),
        -						'deny_gid'    => expand_acl($rr['deny_gid']),
        -						'categories'  => $categories
        -					];
        -				}
        -			}
        +                    $catsenabled = Apps::system_app_installed($x['profile_uid'], 'Categories');
        +                    $categories = '';
        +                    if ($catsenabled) {
        +                        if ($rr['term']) {
        +                            $categories = array_elm_to_str(get_terms_oftype($rr['term'], TERM_CATEGORY), 'term');
        +                        }
        +                    }
         
        -			if ($export) {
        -				header('Content-type: text/calendar');
        -				header('Content-Disposition: attachment; filename="' . t('calendar') . '-' . $channel['channel_address'] . '.ics"' );
        -				echo ical_wrapper($r);
        -				killme();
        -			}
        -	
        -			if (App::$argv[1] === 'json'){
        -				json_return_and_die($events);
        -			}
        -		}
        +                    $allDay = false;
         
        -	
        -		if ($mode === 'drop' && $event_id) {
        -			$r = q("SELECT * FROM event WHERE event_hash = '%s' AND uid = %d LIMIT 1",
        -				dbesc($event_id),
        -				intval(local_channel())
        -			);
        -	
        -			$sync_event = $r[0];
        -	
        -			if ($r) {
        -				$r = q("delete from event where event_hash = '%s' and uid = %d",
        -					dbesc($event_id),
        -					intval(local_channel())
        -				);
        -				if ($r) {
        -					$r = q("update item set resource_type = '', resource_id = '' where resource_type = 'event' and resource_id = '%s' and uid = %d",
        -						dbesc($event_id),
        -						intval(local_channel())
        -					);
        -					$sync_event['event_deleted'] = 1;
        -					Libsync::build_sync_packet(0, [ 'event' => [ $sync_event ] ]);
        -					killme();
        -				}
        -				notice( t('Failed to remove event' ) . EOL);
        -				killme();
        -			}
        -		}
        -	
        -	}
        -	
        +                    // allDay event rules
        +                    if (!strpos($start, 'T') && !strpos($end, 'T')) {
        +                        $allDay = true;
        +                    }
        +                    if (strpos($start, 'T00:00:00') && strpos($end, 'T00:00:00')) {
        +                        $allDay = true;
        +                    }
        +
        +                    $edit = ((local_channel() && $rr['author_xchan'] == get_observer_hash()) ? array(z_root() . '/events/' . $rr['event_hash'] . '?expandform=1', t('Edit event'), '', '') : false);
        +
        +                    $drop = [z_root() . '/events/drop/' . $rr['event_hash'], t('Delete event'), '', ''];
        +
        +                    $events[] = [
        +                        'calendar_id' => 'calendar',
        +                        'rw' => true,
        +                        'id' => $rr['id'],
        +                        'uri' => $rr['event_hash'],
        +                        'start' => $start,
        +                        'end' => $end,
        +                        'drop' => $drop,
        +                        'allDay' => $allDay,
        +                        'title' => html_entity_decode($rr['summary'], ENT_COMPAT, 'UTF-8'),
        +                        'editable' => $edit ? true : false,
        +                        'item' => $rr,
        +                        'plink' => [$rr['plink'], t('Link to source')],
        +                        'description' => htmlentities($rr['description'], ENT_COMPAT, 'UTF-8', false),
        +                        'location' => htmlentities($rr['location'], ENT_COMPAT, 'UTF-8', false),
        +                        'allow_cid' => expand_acl($rr['allow_cid']),
        +                        'allow_gid' => expand_acl($rr['allow_gid']),
        +                        'deny_cid' => expand_acl($rr['deny_cid']),
        +                        'deny_gid' => expand_acl($rr['deny_gid']),
        +                        'categories' => $categories
        +                    ];
        +                }
        +            }
        +
        +            if ($export) {
        +                header('Content-type: text/calendar');
        +                header('Content-Disposition: attachment; filename="' . t('calendar') . '-' . $channel['channel_address'] . '.ics"');
        +                echo ical_wrapper($r);
        +                killme();
        +            }
        +
        +            if (App::$argv[1] === 'json') {
        +                json_return_and_die($events);
        +            }
        +        }
        +
        +
        +        if ($mode === 'drop' && $event_id) {
        +            $r = q(
        +                "SELECT * FROM event WHERE event_hash = '%s' AND uid = %d LIMIT 1",
        +                dbesc($event_id),
        +                intval(local_channel())
        +            );
        +
        +            $sync_event = $r[0];
        +
        +            if ($r) {
        +                $r = q(
        +                    "delete from event where event_hash = '%s' and uid = %d",
        +                    dbesc($event_id),
        +                    intval(local_channel())
        +                );
        +                if ($r) {
        +                    $r = q(
        +                        "update item set resource_type = '', resource_id = '' where resource_type = 'event' and resource_id = '%s' and uid = %d",
        +                        dbesc($event_id),
        +                        intval(local_channel())
        +                    );
        +                    $sync_event['event_deleted'] = 1;
        +                    Libsync::build_sync_packet(0, ['event' => [$sync_event]]);
        +                    killme();
        +                }
        +                notice(t('Failed to remove event') . EOL);
        +                killme();
        +            }
        +        }
        +    }
         }
        diff --git a/Zotlabs/Module/Card_edit.php b/Zotlabs/Module/Card_edit.php
        index 415cdad3f..5da63639b 100644
        --- a/Zotlabs/Module/Card_edit.php
        +++ b/Zotlabs/Module/Card_edit.php
        @@ -1,141 +1,146 @@
          1) ? intval(argv(1)) : 0);
        +        // Figure out which post we're editing
        +        $post_id = ((argc() > 1) ? intval(argv(1)) : 0);
         
        -		if(! $post_id) {
        -			notice( t('Item not found') . EOL);
        -			return;
        -		}
        +        if (!$post_id) {
        +            notice(t('Item not found') . EOL);
        +            return;
        +        }
         
        -		$itm = q("SELECT * FROM item WHERE id = %d and item_type = %d LIMIT 1",
        -			intval($post_id),
        -			intval(ITEM_TYPE_CARD)
        -		);
        -		if($itm) {
        -			$item_id = q("select * from iconfig where cat = 'system' and k = 'CARD' and iid = %d limit 1",
        -				intval($itm[0]['id'])
        -			);
        -			if($item_id)
        -				$card_title = $item_id[0]['v'];
        -		}
        -		else {
        -			notice( t('Item not found') . EOL);
        -			return;
        -		}
        +        $itm = q(
        +            "SELECT * FROM item WHERE id = %d and item_type = %d LIMIT 1",
        +            intval($post_id),
        +            intval(ITEM_TYPE_CARD)
        +        );
        +        if ($itm) {
        +            $item_id = q(
        +                "select * from iconfig where cat = 'system' and k = 'CARD' and iid = %d limit 1",
        +                intval($itm[0]['id'])
        +            );
        +            if ($item_id) {
        +                $card_title = $item_id[0]['v'];
        +            }
        +        } else {
        +            notice(t('Item not found') . EOL);
        +            return;
        +        }
         
        -		$owner = $itm[0]['uid'];
        -		$uid = local_channel();
        +        $owner = $itm[0]['uid'];
        +        $uid = local_channel();
         
        -		$observer = \App::get_observer();
        +        $observer = App::get_observer();
         
        -		$channel = channelx_by_n($owner);
        -		if(! $channel) {
        -			notice( t('Channel not found.') . EOL);
        -			return;
        -		}
        +        $channel = channelx_by_n($owner);
        +        if (!$channel) {
        +            notice(t('Channel not found.') . EOL);
        +            return;
        +        }
         
        -		$ob_hash = (($observer) ? $observer['xchan_hash'] : '');
        +        $ob_hash = (($observer) ? $observer['xchan_hash'] : '');
         
        -		if(! perm_is_allowed($owner,$ob_hash,'write_pages')) {
        -			notice( t('Permission denied.') . EOL);
        -			return;
        -		}
        +        if (!perm_is_allowed($owner, $ob_hash, 'write_pages')) {
        +            notice(t('Permission denied.') . EOL);
        +            return;
        +        }
         
        -		$is_owner = (($uid && $uid == $owner) ? true : false);
        +        $is_owner = (($uid && $uid == $owner) ? true : false);
         
        -		$o = '';
        +        $o = '';
         
         
        +        $category = '';
        +        $catsenabled = ((Apps::system_app_installed($owner, 'Categories')) ? 'categories' : '');
         
        -		$category = '';
        -		$catsenabled = ((Apps::system_app_installed($owner,'Categories')) ? 'categories' : '');
        +        if ($catsenabled) {
        +            $itm = fetch_post_tags($itm);
         
        -		if ($catsenabled){
        -		        $itm = fetch_post_tags($itm);
        -	
        -	                $cats = get_terms_oftype($itm[0]['term'], TERM_CATEGORY);
        -	
        -		        foreach ($cats as $cat) {
        -		                if (strlen($category))
        -		                        $category .= ', ';
        -		                $category .= $cat['term'];
        -		        }
        -		}
        +            $cats = get_terms_oftype($itm[0]['term'], TERM_CATEGORY);
         
        -		if($itm[0]['attach']) {
        -			$j = json_decode($itm[0]['attach'],true);
        -			if($j) {
        -				foreach($j as $jj) {
        -					$itm[0]['body'] .= "\n" . '[attachment]' . basename($jj['href']) . ',' . $jj['revision'] . '[/attachment]' . "\n";
        -				}
        -			}
        -		}
        +            foreach ($cats as $cat) {
        +                if (strlen($category)) {
        +                    $category .= ', ';
        +                }
        +                $category .= $cat['term'];
        +            }
        +        }
        +
        +        if ($itm[0]['attach']) {
        +            $j = json_decode($itm[0]['attach'], true);
        +            if ($j) {
        +                foreach ($j as $jj) {
        +                    $itm[0]['body'] .= "\n" . '[attachment]' . basename($jj['href']) . ',' . $jj['revision'] . '[/attachment]' . "\n";
        +                }
        +            }
        +        }
         
         
        -		$mimetype = $itm[0]['mimetype'];
        +        $mimetype = $itm[0]['mimetype'];
         
        -		$content = $itm[0]['body'];
        +        $content = $itm[0]['body'];
         
         
        +        $rp = 'cards/' . $channel['channel_address'];
         
        -		$rp = 'cards/' . $channel['channel_address'];
        -
        -		$x = array(
        -			'nickname' => $channel['channel_address'],
        -			'bbco_autocomplete'=> 'bbcode',
        -			'return_path' => $rp,
        -			'webpage' => ITEM_TYPE_CARD,
        -			'button' => t('Edit'),
        -			'writefiles' => perm_is_allowed($owner, get_observer_hash(), 'write_pages'),
        -			'weblink' => t('Insert web link'),
        -			'hide_voting' => false,
        -			'hide_future' => false,
        -			'hide_location' => false,
        -			'hide_expire' => false,
        -			'showacl' => true,
        -			'acl' => populate_acl($itm[0],false,\Zotlabs\Lib\PermissionDescription::fromGlobalPermission('view_pages')),
        -			'permissions' => $itm[0],
        -			'lockstate' => (($itm[0]['allow_cid'] || $itm[0]['allow_gid'] || $itm[0]['deny_cid'] || $itm[0]['deny_gid']) ? 'lock' : 'unlock'),
        -			'ptyp' => $itm[0]['type'],
        -			'mimeselect' => false,
        -			'mimetype' => $itm[0]['mimetype'],
        -			'body' => undo_post_tagging($content),
        -			'post_id' => $post_id,
        -			'visitor' => true,
        -			'title' => htmlspecialchars($itm[0]['title'],ENT_COMPAT,'UTF-8'),
        -			'placeholdertitle' => t('Title (optional)'),
        -			'pagetitle' => $card_title,
        -			'profile_uid' => (intval($channel['channel_id'])),
        -			'catsenabled' => $catsenabled,
        -			'category' => $category,
        +        $x = array(
        +            'nickname' => $channel['channel_address'],
        +            'bbco_autocomplete' => 'bbcode',
        +            'return_path' => $rp,
        +            'webpage' => ITEM_TYPE_CARD,
        +            'button' => t('Edit'),
        +            'writefiles' => perm_is_allowed($owner, get_observer_hash(), 'write_pages'),
        +            'weblink' => t('Insert web link'),
        +            'hide_voting' => false,
        +            'hide_future' => false,
        +            'hide_location' => false,
        +            'hide_expire' => false,
        +            'showacl' => true,
        +            'acl' => populate_acl($itm[0], false, PermissionDescription::fromGlobalPermission('view_pages')),
        +            'permissions' => $itm[0],
        +            'lockstate' => (($itm[0]['allow_cid'] || $itm[0]['allow_gid'] || $itm[0]['deny_cid'] || $itm[0]['deny_gid']) ? 'lock' : 'unlock'),
        +            'ptyp' => $itm[0]['type'],
        +            'mimeselect' => false,
        +            'mimetype' => $itm[0]['mimetype'],
        +            'body' => undo_post_tagging($content),
        +            'post_id' => $post_id,
        +            'visitor' => true,
        +            'title' => htmlspecialchars($itm[0]['title'], ENT_COMPAT, 'UTF-8'),
        +            'placeholdertitle' => t('Title (optional)'),
        +            'pagetitle' => $card_title,
        +            'profile_uid' => (intval($channel['channel_id'])),
        +            'catsenabled' => $catsenabled,
        +            'category' => $category,
         			'bbcode' => ((in_array($mimetype, [ 'text/bbcode', 'text/x-multicode' ])) ? true : false)
        -		);
        +        );
         
        -		$editor = status_editor($x);
        +        $editor = status_editor($x);
         
        -		$o .= replace_macros(get_markup_template('edpost_head.tpl'), array(
        -			'$title' => t('Edit Card'),
        -			'$delete' => ((($itm[0]['author_xchan'] === $ob_hash) || ($itm[0]['owner_xchan'] === $ob_hash)) ? t('Delete') : false),
        -			'$id' => $itm[0]['id'],
        -			'$cancel' => t('Cancel'),
        -			'$editor' => $editor
        -		));
        -
        -		return $o;
        -
        -	}
        +        $o .= replace_macros(get_markup_template('edpost_head.tpl'), array(
        +            '$title' => t('Edit Card'),
        +            '$delete' => ((($itm[0]['author_xchan'] === $ob_hash) || ($itm[0]['owner_xchan'] === $ob_hash)) ? t('Delete') : false),
        +            '$id' => $itm[0]['id'],
        +            '$cancel' => t('Cancel'),
        +            '$editor' => $editor
        +        ));
         
        +        return $o;
        +    }
         }
        diff --git a/Zotlabs/Module/Cards.php b/Zotlabs/Module/Cards.php
        index fe2d93d81..aae29c2be 100644
        --- a/Zotlabs/Module/Cards.php
        +++ b/Zotlabs/Module/Cards.php
        @@ -1,4 +1,5 @@
          1)
        -			$which = argv(1);
        -		else
        -			return;
        +        if (argc() > 1) {
        +            $which = argv(1);
        +        } else {
        +            return;
        +        }
         
        -		Libprofile::load($which);
        +        Libprofile::load($which);
        +    }
         
        -	}
        +    /**
        +     * {@inheritDoc}
        +     * @see \Zotlabs\Web\Controller::get()
        +     */
        +    public function get()
        +    {
         
        -	/**
        -	 * {@inheritDoc}
        -	 * @see \Zotlabs\Web\Controller::get()
        -	 */
        -	function get() {
        +        if (observer_prohibited(true)) {
        +            return login();
        +        }
         
        -		if(observer_prohibited(true)) {
        -			return login();
        -		}
        +        if (!App::$profile) {
        +            notice(t('Requested profile is not available.') . EOL);
        +            App::$error = 404;
        +            return;
        +        }
         
        -		if(! App::$profile) {
        -			notice( t('Requested profile is not available.') . EOL );
        -			App::$error = 404;
        -			return;
        -		}
        +        if (!Apps::system_app_installed(App::$profile_uid, 'Cards')) {
        +            //Do not display any associated widgets at this point
        +            App::$pdl = '';
         
        -		if(! Apps::system_app_installed(App::$profile_uid, 'Cards')) {
        -			//Do not display any associated widgets at this point
        -			App::$pdl = '';
        +            $o = 'Cards App (Not Installed):
        '; + $o .= t('Create personal planning cards'); + return $o; + } - $o = 'Cards App (Not Installed):
        '; - $o .= t('Create personal planning cards'); - return $o; - } + nav_set_selected('Cards'); - nav_set_selected('Cards'); - - head_add_link([ - 'rel' => 'alternate', - 'type' => 'application/json+oembed', - 'href' => z_root() . '/oep?f=&url=' . urlencode(z_root() . '/' . App::$query_string), - 'title' => 'oembed' - ]); + head_add_link([ + 'rel' => 'alternate', + 'type' => 'application/json+oembed', + 'href' => z_root() . '/oep?f=&url=' . urlencode(z_root() . '/' . App::$query_string), + 'title' => 'oembed' + ]); - $category = (($_REQUEST['cat']) ? escape_tags(trim($_REQUEST['cat'])) : ''); + $category = (($_REQUEST['cat']) ? escape_tags(trim($_REQUEST['cat'])) : ''); - if($category) { - $sql_extra2 .= protect_sprintf(term_item_parent_query(App::$profile['profile_uid'], 'item', $category, TERM_CATEGORY)); - } + if ($category) { + $sql_extra2 .= protect_sprintf(term_item_parent_query(App::$profile['profile_uid'], 'item', $category, TERM_CATEGORY)); + } - $which = argv(1); + $which = argv(1); - $selected_card = ((argc() > 2) ? argv(2) : ''); + $selected_card = ((argc() > 2) ? argv(2) : ''); - $_SESSION['return_url'] = App::$query_string; + $_SESSION['return_url'] = App::$query_string; - $uid = local_channel(); - $owner = App::$profile_uid; - $observer = App::get_observer(); + $uid = local_channel(); + $owner = App::$profile_uid; + $observer = App::get_observer(); - $ob_hash = (($observer) ? $observer['xchan_hash'] : ''); + $ob_hash = (($observer) ? $observer['xchan_hash'] : ''); - if(! perm_is_allowed($owner, $ob_hash, 'view_pages')) { - notice( t('Permission denied.') . EOL); - return; - } + if (!perm_is_allowed($owner, $ob_hash, 'view_pages')) { + notice(t('Permission denied.') . EOL); + return; + } - $is_owner = ($uid && $uid == $owner); + $is_owner = ($uid && $uid == $owner); - $channel = channelx_by_n($owner); + $channel = channelx_by_n($owner); - if($channel) { - $channel_acl = [ - 'allow_cid' => $channel['channel_allow_cid'], - 'allow_gid' => $channel['channel_allow_gid'], - 'deny_cid' => $channel['channel_deny_cid'], - 'deny_gid' => $channel['channel_deny_gid'] - ]; - } - else { - $channel_acl = [ 'allow_cid' => '', 'allow_gid' => '', 'deny_cid' => '', 'deny_gid' => '' ]; - } + if ($channel) { + $channel_acl = [ + 'allow_cid' => $channel['channel_allow_cid'], + 'allow_gid' => $channel['channel_allow_gid'], + 'deny_cid' => $channel['channel_deny_cid'], + 'deny_gid' => $channel['channel_deny_gid'] + ]; + } else { + $channel_acl = ['allow_cid' => '', 'allow_gid' => '', 'deny_cid' => '', 'deny_gid' => '']; + } + if (perm_is_allowed($owner, $ob_hash, 'write_pages')) { + $x = [ + 'webpage' => ITEM_TYPE_CARD, + 'is_owner' => true, + 'content_label' => t('Add Card'), + 'button' => t('Create'), + 'nickname' => $channel['channel_address'], + 'lockstate' => (($channel['channel_allow_cid'] || $channel['channel_allow_gid'] + || $channel['channel_deny_cid'] || $channel['channel_deny_gid']) ? 'lock' : 'unlock'), + 'acl' => (($is_owner) ? populate_acl( + $channel_acl, + false, + PermissionDescription::fromGlobalPermission('view_pages') + ) : ''), + 'permissions' => $channel_acl, + 'showacl' => (($is_owner) ? true : false), + 'visitor' => true, + 'hide_location' => false, + 'hide_voting' => false, + 'profile_uid' => intval($owner), + 'mimetype' => 'text/x-multicode', + 'mimeselect' => false, + 'layoutselect' => false, + 'expanded' => false, + 'novoting' => false, + 'catsenabled' => Apps::system_app_installed($owner, 'Categories'), + 'bbco_autocomplete' => 'bbcode', + 'bbcode' => true + ]; - if(perm_is_allowed($owner, $ob_hash, 'write_pages')) { + if ($_REQUEST['title']) { + $x['title'] = $_REQUEST['title']; + } + if ($_REQUEST['body']) { + $x['body'] = $_REQUEST['body']; + } - $x = [ - 'webpage' => ITEM_TYPE_CARD, - 'is_owner' => true, - 'content_label' => t('Add Card'), - 'button' => t('Create'), - 'nickname' => $channel['channel_address'], - 'lockstate' => (($channel['channel_allow_cid'] || $channel['channel_allow_gid'] - || $channel['channel_deny_cid'] || $channel['channel_deny_gid']) ? 'lock' : 'unlock'), - 'acl' => (($is_owner) ? populate_acl($channel_acl, false, - PermissionDescription::fromGlobalPermission('view_pages')) : ''), - 'permissions' => $channel_acl, - 'showacl' => (($is_owner) ? true : false), - 'visitor' => true, - 'hide_location' => false, - 'hide_voting' => false, - 'profile_uid' => intval($owner), - 'mimetype' => 'text/x-multicode', - 'mimeselect' => false, - 'layoutselect' => false, - 'expanded' => false, - 'novoting' => false, - 'catsenabled' => Apps::system_app_installed($owner, 'Categories'), - 'bbco_autocomplete' => 'bbcode', - 'bbcode' => true - ]; - - if($_REQUEST['title']) - $x['title'] = $_REQUEST['title']; - if($_REQUEST['body']) - $x['body'] = $_REQUEST['body']; - - $editor = status_editor($x); - } - else { - $editor = ''; - } + $editor = status_editor($x); + } else { + $editor = ''; + } - $itemspage = get_pconfig(local_channel(),'system','itemspage'); - App::set_pager_itemspage(((intval($itemspage)) ? $itemspage : 20)); - $pager_sql = sprintf(" LIMIT %d OFFSET %d ", intval(App::$pager['itemspage']), intval(App::$pager['start'])); + $itemspage = get_pconfig(local_channel(), 'system', 'itemspage'); + App::set_pager_itemspage(((intval($itemspage)) ? $itemspage : 20)); + $pager_sql = sprintf(" LIMIT %d OFFSET %d ", intval(App::$pager['itemspage']), intval(App::$pager['start'])); - $sql_extra = item_permissions_sql($owner); - $sql_item = ''; + $sql_extra = item_permissions_sql($owner); + $sql_item = ''; - if($selected_card) { - $r = q("select * from iconfig where iconfig.cat = 'system' and iconfig.k = 'CARD' and iconfig.v = '%s' limit 1", - dbesc($selected_card) - ); - if($r) { - $sql_item = "and item.id = " . intval($r[0]['iid']) . " "; - } - } + if ($selected_card) { + $r = q( + "select * from iconfig where iconfig.cat = 'system' and iconfig.k = 'CARD' and iconfig.v = '%s' limit 1", + dbesc($selected_card) + ); + if ($r) { + $sql_item = "and item.id = " . intval($r[0]['iid']) . " "; + } + } - $r = q("select * from item + $r = q( + "select * from item where uid = %d and item_type = %d $sql_extra $sql_item order by item.created desc $pager_sql", - intval($owner), - intval(ITEM_TYPE_CARD) - ); + intval($owner), + intval(ITEM_TYPE_CARD) + ); - $item_normal = " and item.item_hidden = 0 and item.item_type in (0,6) and item.item_deleted = 0 + $item_normal = " and item.item_hidden = 0 and item.item_type in (0,6) and item.item_deleted = 0 and item.item_unpublished = 0 and item.item_delayed = 0 and item.item_pending_remove = 0 and item.item_blocked = 0 "; - $items_result = []; - if($r) { + $items_result = []; + if ($r) { + $pager_total = count($r); - $pager_total = count($r); + $parents_str = ids_to_querystr($r, 'id'); - $parents_str = ids_to_querystr($r, 'id'); - - $items = q("SELECT item.*, item.id AS item_id + $items = q( + "SELECT item.*, item.id AS item_id FROM item WHERE item.uid = %d $item_normal AND item.parent IN ( %s ) $sql_extra $sql_extra2 ", - intval(App::$profile['profile_uid']), - dbesc($parents_str) - ); - if($items) { - xchan_query($items); - $items = fetch_post_tags($items, true); - $items_result = conv_sort($items, 'updated'); - } - } + intval(App::$profile['profile_uid']), + dbesc($parents_str) + ); + if ($items) { + xchan_query($items); + $items = fetch_post_tags($items, true); + $items_result = conv_sort($items, 'updated'); + } + } - $mode = 'cards'; + $mode = 'cards'; - if(get_pconfig(local_channel(),'system','articles_list_mode') && (! $selected_card)) - $page_mode = 'pager_list'; - else - $page_mode = 'traditional'; + if (get_pconfig(local_channel(), 'system', 'articles_list_mode') && (!$selected_card)) { + $page_mode = 'pager_list'; + } else { + $page_mode = 'traditional'; + } - $content = conversation($items_result, $mode, false, $page_mode); + $content = conversation($items_result, $mode, false, $page_mode); - $o = replace_macros(get_markup_template('cards.tpl'), [ - '$title' => t('Cards'), - '$editor' => $editor, - '$content' => $content, - '$pager' => alt_pager($pager_total) - ]); - - return $o; - } + $o = replace_macros(get_markup_template('cards.tpl'), [ + '$title' => t('Cards'), + '$editor' => $editor, + '$content' => $content, + '$pager' => alt_pager($pager_total) + ]); + return $o; + } } diff --git a/Zotlabs/Module/Categories.php b/Zotlabs/Module/Categories.php index 4d0af6a6a..e168bc6fe 100644 --- a/Zotlabs/Module/Categories.php +++ b/Zotlabs/Module/Categories.php @@ -9,35 +9,34 @@ use Zotlabs\Lib\Libprofile; use Zotlabs\Web\Controller; use Zotlabs\Render\Comanche; -class Categories extends Controller { +class Categories extends Controller +{ - function init() { + public function init() + { - if(local_channel()) { - $channel = App::get_channel(); - if($channel && $channel['channel_address']) { - $which = $channel['channel_address']; - } - Libprofile::load($which,0); - } - - } + if (local_channel()) { + $channel = App::get_channel(); + if ($channel && $channel['channel_address']) { + $which = $channel['channel_address']; + } + Libprofile::load($which, 0); + } + } - function get() { + public function get() + { $desc = t('This app allows you to add categories to posts and events.'); $text = ''; - if(! ( local_channel() && Apps::system_app_installed(local_channel(),'Categories'))) { + if (!(local_channel() && Apps::system_app_installed(local_channel(), 'Categories'))) { return $text; } - $c = new Comanche; - return $c->widget('catcloud',EMPTY_STR); - - } - - + $c = new Comanche(); + return $c->widget('catcloud', EMPTY_STR); + } } diff --git a/Zotlabs/Module/Cdav.php b/Zotlabs/Module/Cdav.php index 40626e5bb..ceb364c6e 100644 --- a/Zotlabs/Module/Cdav.php +++ b/Zotlabs/Module/Cdav.php @@ -1,10 +1,30 @@ $c, 'account' => $a[0] ]; - $channel_login = $c['channel_id']; - } - } - } - if(! $record) - continue; - - if($record) { - $verified = HTTPSig::verify('',$record['channel']['channel_pubkey']); - if(! ($verified && $verified['header_signed'] && $verified['header_valid'])) { - $record = null; - } - if($record['account']) { - authenticate_success($record['account']); - if($channel_login) { - change_channel($channel_login); - } - } - break; - } - } - } - } - } - - - /** - * This server combines both CardDAV and CalDAV functionality into a single - * server. It is assumed that the server runs at the root of a HTTP domain (be - * that a domainname-based vhost or a specific TCP port. - * - * This example also assumes that you're using SQLite and the database has - * already been setup (along with the database tables). - * - * You may choose to use MySQL instead, just change the PDO connection - * statement. - */ - - /** - * UTC or GMT is easy to work with, and usually recommended for any - * application. - */ - date_default_timezone_set('UTC'); - - /** - * Make sure this setting is turned on and reflect the root url for your WebDAV - * server. - * - * This can be for example the root / or a complete path to your server script. - */ - - $baseUri = '/cdav/'; - - /** - * Database - * - */ - - $pdo = DBA::$dba->db; - - // Autoloader - require_once 'vendor/autoload.php'; - - /** - * The backends. Yes we do really need all of them. - * - * This allows any developer to subclass just any of them and hook into their - * own backend systems. - */ - - $auth = new BasicAuth(); - $auth->setRealm(ucfirst(System::get_platform_name()) . ' ' . 'CalDAV/CardDAV'); - - if (local_channel()) { - - logger('loggedin'); - - if ((argv(1) == 'addressbooks') && (! Apps::system_app_installed(local_channel(), 'CardDAV'))) { - killme(); - } - - $channel = App::get_channel(); - $auth->setCurrentUser($channel['channel_address']); - $auth->channel_id = $channel['channel_id']; - $auth->channel_hash = $channel['channel_hash']; - $auth->channel_account_id = $channel['channel_account_id']; - if ($channel['channel_timezone']) { - $auth->setTimezone($channel['channel_timezone']); - } - $auth->observer = $channel['channel_hash']; - - $principalUri = 'principals/' . $channel['channel_address']; - if (!cdav_principal($principalUri)) { - $this->activate($pdo, $channel); - if (!cdav_principal($principalUri)) { - return; - } - } - - } - - - $principalBackend = new \Sabre\DAVACL\PrincipalBackend\PDO($pdo); - $carddavBackend = new \Sabre\CardDAV\Backend\PDO($pdo); - $caldavBackend = new \Sabre\CalDAV\Backend\PDO($pdo); - - /** - * The directory tree - * - * Basically this is an array which contains the 'top-level' directories in the - * WebDAV server. - */ - - $nodes = [ - // /principals - new \Sabre\CalDAV\Principal\Collection($principalBackend), +class Cdav extends Controller +{ + + public function init() + { + + $record = null; + $channel_login = false; + + if ((argv(1) !== 'calendar') && (argv(1) !== 'addressbook')) { + foreach (['REDIRECT_REMOTE_USER', 'HTTP_AUTHORIZATION'] as $head) { + /* Basic authentication */ + + if (array_key_exists($head, $_SERVER) && substr(trim($_SERVER[$head]), 0, 5) === 'Basic') { + $userpass = @base64_decode(substr(trim($_SERVER[$head]), 6)); + if (strlen($userpass)) { + list($name, $password) = explode(':', $userpass); + $_SERVER['PHP_AUTH_USER'] = $name; + $_SERVER['PHP_AUTH_PW'] = $password; + } + break; + } + + /* Signature authentication */ + + if (array_key_exists($head, $_SERVER) && substr(trim($_SERVER[$head]), 0, 9) === 'Signature') { + if ($head !== 'HTTP_AUTHORIZATION') { + $_SERVER['HTTP_AUTHORIZATION'] = $_SERVER[$head]; + continue; + } + + $sigblock = HTTPSig::parse_sigheader($_SERVER[$head]); + if ($sigblock) { + $keyId = str_replace('acct:', '', $sigblock['keyId']); + if ($keyId) { + $r = q( + "select * from hubloc where hubloc_addr = '%s' limit 1", + dbesc($keyId) + ); + if ($r) { + $c = channelx_by_hash($r[0]['hubloc_hash']); + if ($c) { + $a = q( + "select * from account where account_id = %d limit 1", + intval($c['channel_account_id']) + ); + if ($a) { + $record = ['channel' => $c, 'account' => $a[0]]; + $channel_login = $c['channel_id']; + } + } + } + if (!$record) { + continue; + } + + if ($record) { + $verified = HTTPSig::verify('', $record['channel']['channel_pubkey']); + if (!($verified && $verified['header_signed'] && $verified['header_valid'])) { + $record = null; + } + if ($record['account']) { + authenticate_success($record['account']); + if ($channel_login) { + change_channel($channel_login); + } + } + break; + } + } + } + } + } + + + /** + * This server combines both CardDAV and CalDAV functionality into a single + * server. It is assumed that the server runs at the root of a HTTP domain (be + * that a domainname-based vhost or a specific TCP port. + * + * This example also assumes that you're using SQLite and the database has + * already been setup (along with the database tables). + * + * You may choose to use MySQL instead, just change the PDO connection + * statement. + */ + + /** + * UTC or GMT is easy to work with, and usually recommended for any + * application. + */ + date_default_timezone_set('UTC'); + + /** + * Make sure this setting is turned on and reflect the root url for your WebDAV + * server. + * + * This can be for example the root / or a complete path to your server script. + */ + + $baseUri = '/cdav/'; + + /** + * Database + * + */ + + $pdo = DBA::$dba->db; + + // Autoloader + require_once 'vendor/autoload.php'; + + /** + * The backends. Yes we do really need all of them. + * + * This allows any developer to subclass just any of them and hook into their + * own backend systems. + */ + + $auth = new BasicAuth(); + $auth->setRealm(ucfirst(System::get_platform_name()) . ' ' . 'CalDAV/CardDAV'); + + if (local_channel()) { + logger('loggedin'); + + if ((argv(1) == 'addressbooks') && (!Apps::system_app_installed(local_channel(), 'CardDAV'))) { + killme(); + } + + $channel = App::get_channel(); + $auth->setCurrentUser($channel['channel_address']); + $auth->channel_id = $channel['channel_id']; + $auth->channel_hash = $channel['channel_hash']; + $auth->channel_account_id = $channel['channel_account_id']; + if ($channel['channel_timezone']) { + $auth->setTimezone($channel['channel_timezone']); + } + $auth->observer = $channel['channel_hash']; + + $principalUri = 'principals/' . $channel['channel_address']; + if (!cdav_principal($principalUri)) { + $this->activate($pdo, $channel); + if (!cdav_principal($principalUri)) { + return; + } + } + } + + + $principalBackend = new \Sabre\DAVACL\PrincipalBackend\PDO($pdo); + $carddavBackend = new PDO($pdo); + $caldavBackend = new \Sabre\CalDAV\Backend\PDO($pdo); + + /** + * The directory tree + * + * Basically this is an array which contains the 'top-level' directories in the + * WebDAV server. + */ + + $nodes = [ + // /principals + new Collection($principalBackend), + + // /calendars + new CalendarRoot($principalBackend, $caldavBackend), + + // /addressbook + new AddressBookRoot($principalBackend, $carddavBackend) + ]; + + + // The object tree needs in turn to be passed to the server class + + $server = new Server($nodes); + + if (isset($baseUri)) { + $server->setBaseUri($baseUri); + } + + // Plugins + $server->addPlugin(new \Sabre\DAV\Auth\Plugin($auth)); + //$server->addPlugin(new \Sabre\DAV\Browser\Plugin()); + $server->addPlugin(new \Sabre\DAV\Sync\Plugin()); + $server->addPlugin(new \Sabre\DAV\Sharing\Plugin()); + $server->addPlugin(new \Sabre\DAVACL\Plugin()); + + // CalDAV plugins + $server->addPlugin(new \Sabre\CalDAV\Plugin()); + $server->addPlugin(new SharingPlugin()); + //$server->addPlugin(new \Sabre\CalDAV\Schedule\Plugin()); + $server->addPlugin(new ICSExportPlugin()); + + // CardDAV plugins + $server->addPlugin(new Plugin()); + $server->addPlugin(new VCFExportPlugin()); + + // And off we go! + $server->exec(); + + killme(); + } + } + + public function post() + { + + if (!local_channel()) { + return; + } + + if ((argv(1) === 'addressbook') && (!Apps::system_app_installed(local_channel(), 'CardDAV'))) { + return; + } + + $channel = App::get_channel(); + $principalUri = 'principals/' . $channel['channel_address']; + + if (!cdav_principal($principalUri)) { + return; + } + + $pdo = DBA::$dba->db; + + require_once 'vendor/autoload.php'; + + if (argc() == 2 && argv(1) === 'calendar') { + $caldavBackend = new \Sabre\CalDAV\Backend\PDO($pdo); + $calendars = $caldavBackend->getCalendarsForUser($principalUri); + + // create new calendar + if ($_REQUEST['{DAV:}displayname'] && $_REQUEST['create']) { + do { + $duplicate = false; + $calendarUri = random_string(40); + + $r = q( + "SELECT uri FROM calendarinstances WHERE principaluri = '%s' AND uri = '%s' LIMIT 1", + dbesc($principalUri), + dbesc($calendarUri) + ); + + if ($r) { + $duplicate = true; + } + } while ($duplicate == true); - // /calendars - new \Sabre\CalDAV\CalendarRoot($principalBackend, $caldavBackend), + $properties = [ + '{DAV:}displayname' => $_REQUEST['{DAV:}displayname'], + '{http://apple.com/ns/ical/}calendar-color' => $_REQUEST['color'], + '{urn:ietf:params:xml:ns:caldav}calendar-description' => $channel['channel_name'] + ]; + + $id = $caldavBackend->createCalendar($principalUri, $calendarUri, $properties); + + // set new calendar to be visible + set_pconfig(local_channel(), 'cdav_calendar', $id[0], 1); + } + + //create new calendar object via ajax request + if ($_REQUEST['submit'] === 'create_event' && $_REQUEST['title'] && $_REQUEST['target'] && $_REQUEST['dtstart']) { + $id = explode(':', $_REQUEST['target']); + + if (!cdav_perms($id[0], $calendars, true)) { + return; + } + + $title = $_REQUEST['title']; + $start = datetime_convert(App::$timezone, 'UTC', $_REQUEST['dtstart']); + $dtstart = new DateTime($start); + if ($_REQUEST['dtend']) { + $end = datetime_convert(App::$timezone, 'UTC', $_REQUEST['dtend']); + $dtend = new DateTime($end); + } + $description = $_REQUEST['description']; + $location = $_REQUEST['location']; + + do { + $duplicate = false; + $objectUri = random_string(40) . '.ics'; + + $r = q( + "SELECT uri FROM calendarobjects WHERE calendarid = %s AND uri = '%s' LIMIT 1", + intval($id[0]), + dbesc($objectUri) + ); + + if (count($r)) { + $duplicate = true; + } + } while ($duplicate == true); + + + $vcalendar = new VCalendar([ + 'VEVENT' => [ + 'SUMMARY' => $title, + 'DTSTART' => $dtstart + ] + ]); + if ($dtend) { + $vcalendar->VEVENT->add('DTEND', $dtend); + $vcalendar->VEVENT->DTEND['TZID'] = App::$timezone; + } + if ($description) { + $vcalendar->VEVENT->add('DESCRIPTION', $description); + } + if ($location) { + $vcalendar->VEVENT->add('LOCATION', $location); + } + + $vcalendar->VEVENT->DTSTART['TZID'] = App::$timezone; + + $calendarData = $vcalendar->serialize(); + + $caldavBackend->createCalendarObject($id, $objectUri, $calendarData); + + killme(); + } + + // edit calendar name and color + if ($_REQUEST['{DAV:}displayname'] && $_REQUEST['edit'] && $_REQUEST['id']) { + $id = explode(':', $_REQUEST['id']); + + if (!cdav_perms($id[0], $calendars)) { + return; + } + + $mutations = [ + '{DAV:}displayname' => $_REQUEST['{DAV:}displayname'], + '{http://apple.com/ns/ical/}calendar-color' => $_REQUEST['color'] + ]; + + $patch = new PropPatch($mutations); + + $caldavBackend->updateCalendar($id, $patch); + + $patch->commit(); + } + + // edit calendar object via ajax request + if ($_REQUEST['submit'] === 'update_event' && $_REQUEST['uri'] && $_REQUEST['title'] && $_REQUEST['target'] && $_REQUEST['dtstart']) { + $id = explode(':', $_REQUEST['target']); + + if (!cdav_perms($id[0], $calendars, true)) { + return; + } + + $uri = $_REQUEST['uri']; + $title = $_REQUEST['title']; + $start = datetime_convert(App::$timezone, 'UTC', $_REQUEST['dtstart']); + $dtstart = new DateTime($start); + if ($_REQUEST['dtend']) { + $end = datetime_convert(App::$timezone, 'UTC', $_REQUEST['dtend']); + $dtend = new DateTime($end); + } + $description = $_REQUEST['description']; + $location = $_REQUEST['location']; + + $object = $caldavBackend->getCalendarObject($id, $uri); + + $vcalendar = Reader::read($object['calendardata']); + + if ($title) { + $vcalendar->VEVENT->SUMMARY = $title; + } + if ($dtstart) { + $vcalendar->VEVENT->DTSTART = $dtstart; + } + if ($dtend) { + $vcalendar->VEVENT->DTEND = $dtend; + } else { + unset($vcalendar->VEVENT->DTEND); + } + if ($description) { + $vcalendar->VEVENT->DESCRIPTION = $description; + } + if ($location) { + $vcalendar->VEVENT->LOCATION = $location; + } + + $calendarData = $vcalendar->serialize(); + + $caldavBackend->updateCalendarObject($id, $uri, $calendarData); + killme(); + } + + // delete calendar object via ajax request + if ($_REQUEST['delete'] && $_REQUEST['uri'] && $_REQUEST['target']) { + $id = explode(':', $_REQUEST['target']); + + if (!cdav_perms($id[0], $calendars, true)) { + return; + } + + $uri = $_REQUEST['uri']; + + $caldavBackend->deleteCalendarObject($id, $uri); + killme(); + } + + // edit calendar object date/timeme via ajax request (drag and drop) + if ($_REQUEST['update'] && $_REQUEST['id'] && $_REQUEST['uri']) { + $id = [$_REQUEST['id'][0], $_REQUEST['id'][1]]; - // /addressbook - new \Sabre\CardDAV\AddressBookRoot($principalBackend, $carddavBackend) - ]; + if (!cdav_perms($id[0], $calendars, true)) { + return; + } + $uri = $_REQUEST['uri']; + $start = datetime_convert(App::$timezone, 'UTC', $_REQUEST['dtstart']); + $dtstart = new DateTime($start); + if ($_REQUEST['dtend']) { + $end = datetime_convert(App::$timezone, 'UTC', $_REQUEST['dtend']); + $dtend = new DateTime($end); + } - // The object tree needs in turn to be passed to the server class + $object = $caldavBackend->getCalendarObject($id, $uri); - $server = new \Sabre\DAV\Server($nodes); - - if (isset($baseUri)) { - $server->setBaseUri($baseUri); - } - - // Plugins - $server->addPlugin(new \Sabre\DAV\Auth\Plugin($auth)); - //$server->addPlugin(new \Sabre\DAV\Browser\Plugin()); - $server->addPlugin(new \Sabre\DAV\Sync\Plugin()); - $server->addPlugin(new \Sabre\DAV\Sharing\Plugin()); - $server->addPlugin(new \Sabre\DAVACL\Plugin()); - - // CalDAV plugins - $server->addPlugin(new \Sabre\CalDAV\Plugin()); - $server->addPlugin(new \Sabre\CalDAV\SharingPlugin()); - //$server->addPlugin(new \Sabre\CalDAV\Schedule\Plugin()); - $server->addPlugin(new \Sabre\CalDAV\ICSExportPlugin()); - - // CardDAV plugins - $server->addPlugin(new \Sabre\CardDAV\Plugin()); - $server->addPlugin(new \Sabre\CardDAV\VCFExportPlugin()); - - // And off we go! - $server->exec(); - - killme(); - - } - - } - - function post() { - - if (! local_channel()) - return; - - if ((argv(1) === 'addressbook') && (! Apps::system_app_installed(local_channel(), 'CardDAV'))) { - return; - } - - $channel = App::get_channel(); - $principalUri = 'principals/' . $channel['channel_address']; - - if (!cdav_principal($principalUri)) - return; - - $pdo = DBA::$dba->db; - - require_once 'vendor/autoload.php'; - - if (argc() == 2 && argv(1) === 'calendar') { - - $caldavBackend = new \Sabre\CalDAV\Backend\PDO($pdo); - $calendars = $caldavBackend->getCalendarsForUser($principalUri); - - // create new calendar - if ($_REQUEST['{DAV:}displayname'] && $_REQUEST['create']) { - do { - $duplicate = false; - $calendarUri = random_string(40); - - $r = q("SELECT uri FROM calendarinstances WHERE principaluri = '%s' AND uri = '%s' LIMIT 1", - dbesc($principalUri), - dbesc($calendarUri) - ); - - if ($r) { - $duplicate = true; - } - } while ($duplicate == true); - - $properties = [ - '{DAV:}displayname' => $_REQUEST['{DAV:}displayname'], - '{http://apple.com/ns/ical/}calendar-color' => $_REQUEST['color'], - '{urn:ietf:params:xml:ns:caldav}calendar-description' => $channel['channel_name'] - ]; - - $id = $caldavBackend->createCalendar($principalUri, $calendarUri, $properties); - - // set new calendar to be visible - set_pconfig(local_channel(), 'cdav_calendar' , $id[0], 1); - } - - //create new calendar object via ajax request - if ($_REQUEST['submit'] === 'create_event' && $_REQUEST['title'] && $_REQUEST['target'] && $_REQUEST['dtstart']) { - - $id = explode(':', $_REQUEST['target']); - - if (!cdav_perms($id[0],$calendars,true)) - return; - - $title = $_REQUEST['title']; - $start = datetime_convert(App::$timezone, 'UTC', $_REQUEST['dtstart']); - $dtstart = new DateTime($start); - if ($_REQUEST['dtend']) { - $end = datetime_convert(App::$timezone, 'UTC', $_REQUEST['dtend']); - $dtend = new DateTime($end); - } - $description = $_REQUEST['description']; - $location = $_REQUEST['location']; - - do { - $duplicate = false; - $objectUri = random_string(40) . '.ics'; - - $r = q("SELECT uri FROM calendarobjects WHERE calendarid = %s AND uri = '%s' LIMIT 1", - intval($id[0]), - dbesc($objectUri) - ); - - if (count($r)) - $duplicate = true; - } while ($duplicate == true); - - - $vcalendar = new \Sabre\VObject\Component\VCalendar([ - 'VEVENT' => [ - 'SUMMARY' => $title, - 'DTSTART' => $dtstart - ] - ]); - if($dtend) { - $vcalendar->VEVENT->add('DTEND', $dtend); - $vcalendar->VEVENT->DTEND['TZID'] = App::$timezone; - } - if($description) - $vcalendar->VEVENT->add('DESCRIPTION', $description); - if($location) - $vcalendar->VEVENT->add('LOCATION', $location); - - $vcalendar->VEVENT->DTSTART['TZID'] = App::$timezone; - - $calendarData = $vcalendar->serialize(); - - $caldavBackend->createCalendarObject($id, $objectUri, $calendarData); - - killme(); - } - - // edit calendar name and color - if ($_REQUEST['{DAV:}displayname'] && $_REQUEST['edit'] && $_REQUEST['id']) { - - $id = explode(':', $_REQUEST['id']); - - if (! cdav_perms($id[0],$calendars)) { - return; - } - - $mutations = [ - '{DAV:}displayname' => $_REQUEST['{DAV:}displayname'], - '{http://apple.com/ns/ical/}calendar-color' => $_REQUEST['color'] - ]; - - $patch = new \Sabre\DAV\PropPatch($mutations); - - $caldavBackend->updateCalendar($id, $patch); - - $patch->commit(); - } - - // edit calendar object via ajax request - if ($_REQUEST['submit'] === 'update_event' && $_REQUEST['uri'] && $_REQUEST['title'] && $_REQUEST['target'] && $_REQUEST['dtstart']) { - - $id = explode(':', $_REQUEST['target']); - - if (!cdav_perms($id[0],$calendars,true)) - return; - - $uri = $_REQUEST['uri']; - $title = $_REQUEST['title']; - $start = datetime_convert(App::$timezone, 'UTC', $_REQUEST['dtstart']); - $dtstart = new DateTime($start); - if ($_REQUEST['dtend']) { - $end = datetime_convert(App::$timezone, 'UTC', $_REQUEST['dtend']); - $dtend = new DateTime($end); - } - $description = $_REQUEST['description']; - $location = $_REQUEST['location']; - - $object = $caldavBackend->getCalendarObject($id, $uri); - - $vcalendar = \Sabre\VObject\Reader::read($object['calendardata']); - - if ($title) { - $vcalendar->VEVENT->SUMMARY = $title; - } - if ($dtstart) { - $vcalendar->VEVENT->DTSTART = $dtstart; - } - if ($dtend) { - $vcalendar->VEVENT->DTEND = $dtend; - } - else { - unset($vcalendar->VEVENT->DTEND); - } - if ($description) { - $vcalendar->VEVENT->DESCRIPTION = $description; - } - if ($location) { - $vcalendar->VEVENT->LOCATION = $location; - } - - $calendarData = $vcalendar->serialize(); - - $caldavBackend->updateCalendarObject($id, $uri, $calendarData); - killme(); - } - - // delete calendar object via ajax request - if ($_REQUEST['delete'] && $_REQUEST['uri'] && $_REQUEST['target']) { - - $id = explode(':', $_REQUEST['target']); - - if (!cdav_perms($id[0],$calendars,true)) { - return; - } - - $uri = $_REQUEST['uri']; - - $caldavBackend->deleteCalendarObject($id, $uri); - killme(); - } - - // edit calendar object date/timeme via ajax request (drag and drop) - if ($_REQUEST['update'] && $_REQUEST['id'] && $_REQUEST['uri']) { - - $id = [$_REQUEST['id'][0], $_REQUEST['id'][1]]; - - if (! cdav_perms($id[0],$calendars,true)) { - return; - } - - $uri = $_REQUEST['uri']; - $start = datetime_convert(App::$timezone, 'UTC', $_REQUEST['dtstart']); - $dtstart = new DateTime($start); - if($_REQUEST['dtend']) { - $end = datetime_convert(App::$timezone, 'UTC', $_REQUEST['dtend']); - $dtend = new DateTime($end); - } - - $object = $caldavBackend->getCalendarObject($id, $uri); - - $vcalendar = \Sabre\VObject\Reader::read($object['calendardata']); - - if ($dtstart) { - $vcalendar->VEVENT->DTSTART = $dtstart; - } - if ($dtend) { - $vcalendar->VEVENT->DTEND = $dtend; - } - else { - unset($vcalendar->VEVENT->DTEND); - } - - $calendarData = $vcalendar->serialize(); - - $caldavBackend->updateCalendarObject($id, $uri, $calendarData); - - killme(); - } - - // share a calendar - this only works on local system (with channels on the same server) - if ($_REQUEST['sharee'] && $_REQUEST['share']) { - - $id = [intval($_REQUEST['calendarid']), intval($_REQUEST['instanceid'])]; - - if (! cdav_perms($id[0],$calendars)) { - return; - } - - $hash = $_REQUEST['sharee']; - - $sharee_arr = channelx_by_hash($hash); - - $sharee = new \Sabre\DAV\Xml\Element\Sharee(); - - $sharee->href = 'mailto:' . $sharee_arr['xchan_addr']; - $sharee->principal = 'principals/' . $sharee_arr['channel_address']; - $sharee->access = intval($_REQUEST['access']); - $sharee->properties = ['{DAV:}displayname' => $channel['channel_name']]; - - $caldavBackend->updateInvites($id, [$sharee]); - } - } - - if (argc() >= 2 && argv(1) === 'addressbook') { - - $carddavBackend = new \Sabre\CardDAV\Backend\PDO($pdo); - $addressbooks = $carddavBackend->getAddressBooksForUser($principalUri); - - // create new addressbook - if ($_REQUEST['{DAV:}displayname'] && $_REQUEST['create']) { - do { - $duplicate = false; - $addressbookUri = random_string(20); - - $r = q("SELECT uri FROM addressbooks WHERE principaluri = '%s' AND uri = '%s' LIMIT 1", - dbesc($principalUri), - dbesc($addressbookUri) - ); - - if ($r) { - $duplicate = true; - } - } while ($duplicate == true); - - $properties = ['{DAV:}displayname' => $_REQUEST['{DAV:}displayname']]; - - $carddavBackend->createAddressBook($principalUri, $addressbookUri, $properties); - } - - // edit addressbook - if ($_REQUEST['{DAV:}displayname'] && $_REQUEST['edit'] && intval($_REQUEST['id'])) { - - $id = $_REQUEST['id']; - - if (! cdav_perms($id,$addressbooks)) { - return; - } - - $mutations = [ - '{DAV:}displayname' => $_REQUEST['{DAV:}displayname'] - ]; - - $patch = new \Sabre\DAV\PropPatch($mutations); - - $carddavBackend->updateAddressBook($id, $patch); - - $patch->commit(); - } - - // create addressbook card - if ($_REQUEST['create'] && $_REQUEST['target'] && $_REQUEST['fn']) { - $id = $_REQUEST['target']; - - do { - $duplicate = false; - $uri = random_string(40) . '.vcf'; - - $r = q("SELECT uri FROM cards WHERE addressbookid = %s AND uri = '%s' LIMIT 1", - intval($id), - dbesc($uri) - ); - - if ($r) { - $duplicate = true; - } - } while ($duplicate == true); - - // TODO: this mostly duplictes the procedure in update addressbook card. - // Should move this part to a function to avoid duplication - $fn = $_REQUEST['fn']; - - $vcard = new \Sabre\VObject\Component\VCard([ - 'FN' => $fn, - 'N' => array_reverse(explode(' ', $fn)) - ]); - - $org = $_REQUEST['org']; - if ($org) { - $vcard->ORG = $org; - } - - $title = $_REQUEST['title']; - if ($title) { - $vcard->TITLE = $title; - } - - $tel = $_REQUEST['tel']; - $tel_type = $_REQUEST['tel_type']; - if ($tel) { - $i = 0; - foreach ($tel as $item) { - if ($item) { - $vcard->add('TEL', $item, ['type' => $tel_type[$i]]); - } - $i++; - } - } - - $email = $_REQUEST['email']; - $email_type = $_REQUEST['email_type']; - if ($email) { - $i = 0; - foreach ($email as $item) { - if ($item) { - $vcard->add('EMAIL', $item, ['type' => $email_type[$i]]); - } - $i++; - } - } - - $impp = $_REQUEST['impp']; - $impp_type = $_REQUEST['impp_type']; - if ($impp) { - $i = 0; - foreach ($impp as $item) { - if ($item) { - $vcard->add('IMPP', $item, ['type' => $impp_type[$i]]); - } - $i++; - } - } - - $url = $_REQUEST['url']; - $url_type = $_REQUEST['url_type']; - if ($url) { - $i = 0; - foreach ($url as $item) { - if ($item) { - $vcard->add('URL', $item, ['type' => $url_type[$i]]); - } - $i++; - } - } - - $adr = $_REQUEST['adr']; - $adr_type = $_REQUEST['adr_type']; - - if ($adr) { - $i = 0; - foreach ($adr as $item) { - if ($item) { - $vcard->add('ADR', $item, ['type' => $adr_type[$i]]); - } - $i++; - } - } - - $note = $_REQUEST['note']; - if ($note) { - $vcard->NOTE = $note; - } - - $cardData = $vcard->serialize(); - - $carddavBackend->createCard($id, $uri, $cardData); - - } - - // edit addressbook card - if ($_REQUEST['update'] && $_REQUEST['uri'] && $_REQUEST['target']) { - - $id = $_REQUEST['target']; - - if (!cdav_perms($id,$addressbooks)) { - return; - } - - $uri = $_REQUEST['uri']; - - $object = $carddavBackend->getCard($id, $uri); - $vcard = \Sabre\VObject\Reader::read($object['carddata']); - - $fn = $_REQUEST['fn']; - if ($fn) { - $vcard->FN = $fn; - $vcard->N = array_reverse(explode(' ', $fn)); - } - - $org = $_REQUEST['org']; - if ($org) { - $vcard->ORG = $org; - } - else { - unset($vcard->ORG); - } - - $title = $_REQUEST['title']; - if ($title) { - $vcard->TITLE = $title; - } - else { - unset($vcard->TITLE); - } - - $tel = $_REQUEST['tel']; - $tel_type = $_REQUEST['tel_type']; - if ($tel) { - $i = 0; - unset($vcard->TEL); - foreach ($tel as $item) { - if ($item) { - $vcard->add('TEL', $item, ['type' => $tel_type[$i]]); - } - $i++; - } - } - else { - unset($vcard->TEL); - } - - $email = $_REQUEST['email']; - $email_type = $_REQUEST['email_type']; - if ($email) { - $i = 0; - unset($vcard->EMAIL); - foreach ($email as $item) { - if ($item) { - $vcard->add('EMAIL', $item, ['type' => $email_type[$i]]); - } - $i++; - } - } - else { - unset($vcard->EMAIL); - } - - $impp = $_REQUEST['impp']; - $impp_type = $_REQUEST['impp_type']; - if ($impp) { - $i = 0; - unset($vcard->IMPP); - foreach ($impp as $item) { - if ($item) { - $vcard->add('IMPP', $item, ['type' => $impp_type[$i]]); - } - $i++; - } - } - else { - unset($vcard->IMPP); - } - - $url = $_REQUEST['url']; - $url_type = $_REQUEST['url_type']; - if ($url) { - $i = 0; - unset($vcard->URL); - foreach ($url as $item) { - if ($item) { - $vcard->add('URL', $item, ['type' => $url_type[$i]]); - } - $i++; - } - } - else { - unset($vcard->URL); - } - - $adr = $_REQUEST['adr']; - $adr_type = $_REQUEST['adr_type']; - if ($adr) { - $i = 0; - unset($vcard->ADR); - foreach ($adr as $item) { - if ($item) { - $vcard->add('ADR', $item, ['type' => $adr_type[$i]]); - } - $i++; - } - } - else { - unset($vcard->ADR); - } - - $note = $_REQUEST['note']; - if ($note) { - $vcard->NOTE = $note; - } - else { - unset($vcard->NOTE); - } - - $cardData = $vcard->serialize(); - - $carddavBackend->updateCard($id, $uri, $cardData); - } - - // delete addressbook card - if ($_REQUEST['delete'] && $_REQUEST['uri'] && $_REQUEST['target']) { - - $id = $_REQUEST['target']; - - if (!cdav_perms($id,$addressbooks)) { - return; - } - - $uri = $_REQUEST['uri']; - - $carddavBackend->deleteCard($id, $uri); - } - } - - // Import calendar or addressbook - if (($_FILES) && array_key_exists('userfile',$_FILES) && intval($_FILES['userfile']['size']) && $_REQUEST['target']) { - - $src = $_FILES['userfile']['tmp_name']; - - if ($src) { - - if ($_REQUEST['c_upload']) { - if ($_REQUEST['target'] == 'calendar') { - $result = parse_ical_file($src,local_channel()); - if ($result) { - info( t('Calendar entries imported.') . EOL); - } - else { - notice( t('No calendar entries found.') . EOL); - } - - @unlink($src); - return; - } - - $id = explode(':', $_REQUEST['target']); - $ext = 'ics'; - $table = 'calendarobjects'; - $column = 'calendarid'; - $objects = new \Sabre\VObject\Splitter\ICalendar(@file_get_contents($src)); - $profile = \Sabre\VObject\Node::PROFILE_CALDAV; - $backend = new \Sabre\CalDAV\Backend\PDO($pdo); - } - - if ($_REQUEST['a_upload']) { - $id[] = intval($_REQUEST['target']); - $ext = 'vcf'; - $table = 'cards'; - $column = 'addressbookid'; - $objects = new \Sabre\VObject\Splitter\VCard(@file_get_contents($src)); - $profile = \Sabre\VObject\Node::PROFILE_CARDDAV; - $backend = new \Sabre\CardDAV\Backend\PDO($pdo); - } - - while ($object = $objects->getNext()) { - - if ($_REQUEST['a_upload']) { - $object = $object->convert(\Sabre\VObject\Document::VCARD40); - } - - $ret = $object->validate($profile & \Sabre\VObject\Node::REPAIR); - - // level 3 Means that the document is invalid, - // level 2 means a warning. A warning means it's valid but it could cause interopability issues, - // level 1 means that there was a problem earlier, but the problem was automatically repaired. - - if ($ret[0]['level'] < 3) { - do { - $duplicate = false; - $objectUri = random_string(40) . '.' . $ext; - - $r = q("SELECT uri FROM $table WHERE $column = %d AND uri = '%s' LIMIT 1", - dbesc($id[0]), - dbesc($objectUri) - ); - - if ($r) { - $duplicate = true; - } - } while ($duplicate == true); - - if ($_REQUEST['c_upload']) { - $backend->createCalendarObject($id, $objectUri, $object->serialize()); - } - - if ($_REQUEST['a_upload']) { - $backend->createCard($id[0], $objectUri, $object->serialize()); - } - } - else { - if ($_REQUEST['c_upload']) { - notice( '' . t('INVALID EVENT DISMISSED!') . '' . EOL . - '' . t('Summary: ') . '' . (($object->VEVENT->SUMMARY) ? $object->VEVENT->SUMMARY : t('Unknown')) . EOL . - '' . t('Date: ') . '' . (($object->VEVENT->DTSTART) ? $object->VEVENT->DTSTART : t('Unknown')) . EOL . - '' . t('Reason: ') . '' . $ret[0]['message'] . EOL - ); - } - - if ($_REQUEST['a_upload']) { - notice( '' . t('INVALID CARD DISMISSED!') . '' . EOL . - '' . t('Name: ') . '' . (($object->FN) ? $object->FN : t('Unknown')) . EOL . - '' . t('Reason: ') . '' . $ret[0]['message'] . EOL - ); - } - } - } - } - @unlink($src); - } - } - - function get() { - - if (! local_channel()) { - return; - } - - if ((argv(1) === 'addressbook') && (! Apps::system_app_installed(local_channel(), 'CardDAV'))) { - // Do not display any associated widgets at this point - App::$pdl = ''; - - $o = '' . t('CardDAV App') . ' (' . t('Not Installed') . '):
        '; - $o .= t('CalDAV capable addressbook'); - return $o; - } - - App::$profile_uid = local_channel(); - - $channel = App::get_channel(); - $principalUri = 'principals/' . $channel['channel_address']; - - $pdo = DBA::$dba->db; - - require_once 'vendor/autoload.php'; - - head_add_css('cdav.css'); - - if (! cdav_principal($principalUri)) { - $this->activate($pdo, $channel); - if (! cdav_principal($principalUri)) { - return; - } - } - - if (argv(1) === 'calendar') { - nav_set_selected('Calendar'); - $caldavBackend = new \Sabre\CalDAV\Backend\PDO($pdo); - $calendars = $caldavBackend->getCalendarsForUser($principalUri); - } - - // Display calendar(s) here - if(argc() <= 3 && argv(1) === 'calendar') { - - head_add_css('/library/fullcalendar/packages/core/main.min.css'); - head_add_css('/library/fullcalendar/packages/daygrid/main.min.css'); - head_add_css('/library/fullcalendar/packages/timegrid/main.min.css'); - head_add_css('/library/fullcalendar/packages/list/main.min.css'); - head_add_css('cdav_calendar.css'); - - head_add_js('/library/fullcalendar/packages/core/main.min.js'); - head_add_js('/library/fullcalendar/packages/interaction/main.min.js'); - head_add_js('/library/fullcalendar/packages/daygrid/main.min.js'); - head_add_js('/library/fullcalendar/packages/timegrid/main.min.js'); - head_add_js('/library/fullcalendar/packages/list/main.min.js'); - - $sources = ''; - $resource_id = ''; - $resource = null; - - if (argc() == 3) { - $resource_id = argv(2); - } - - if ($resource_id) { - $r = q("SELECT event.*, item.author_xchan, item.owner_xchan, item.plink, item.id as item_id FROM event LEFT JOIN item ON event.event_hash = item.resource_id + $vcalendar = Reader::read($object['calendardata']); + + if ($dtstart) { + $vcalendar->VEVENT->DTSTART = $dtstart; + } + if ($dtend) { + $vcalendar->VEVENT->DTEND = $dtend; + } else { + unset($vcalendar->VEVENT->DTEND); + } + + $calendarData = $vcalendar->serialize(); + + $caldavBackend->updateCalendarObject($id, $uri, $calendarData); + + killme(); + } + + // share a calendar - this only works on local system (with channels on the same server) + if ($_REQUEST['sharee'] && $_REQUEST['share']) { + $id = [intval($_REQUEST['calendarid']), intval($_REQUEST['instanceid'])]; + + if (!cdav_perms($id[0], $calendars)) { + return; + } + + $hash = $_REQUEST['sharee']; + + $sharee_arr = channelx_by_hash($hash); + + $sharee = new Sharee(); + + $sharee->href = 'mailto:' . $sharee_arr['xchan_addr']; + $sharee->principal = 'principals/' . $sharee_arr['channel_address']; + $sharee->access = intval($_REQUEST['access']); + $sharee->properties = ['{DAV:}displayname' => $channel['channel_name']]; + + $caldavBackend->updateInvites($id, [$sharee]); + } + } + + if (argc() >= 2 && argv(1) === 'addressbook') { + $carddavBackend = new PDO($pdo); + $addressbooks = $carddavBackend->getAddressBooksForUser($principalUri); + + // create new addressbook + if ($_REQUEST['{DAV:}displayname'] && $_REQUEST['create']) { + do { + $duplicate = false; + $addressbookUri = random_string(20); + + $r = q( + "SELECT uri FROM addressbooks WHERE principaluri = '%s' AND uri = '%s' LIMIT 1", + dbesc($principalUri), + dbesc($addressbookUri) + ); + + if ($r) { + $duplicate = true; + } + } while ($duplicate == true); + + $properties = ['{DAV:}displayname' => $_REQUEST['{DAV:}displayname']]; + + $carddavBackend->createAddressBook($principalUri, $addressbookUri, $properties); + } + + // edit addressbook + if ($_REQUEST['{DAV:}displayname'] && $_REQUEST['edit'] && intval($_REQUEST['id'])) { + $id = $_REQUEST['id']; + + if (!cdav_perms($id, $addressbooks)) { + return; + } + + $mutations = [ + '{DAV:}displayname' => $_REQUEST['{DAV:}displayname'] + ]; + + $patch = new PropPatch($mutations); + + $carddavBackend->updateAddressBook($id, $patch); + + $patch->commit(); + } + + // create addressbook card + if ($_REQUEST['create'] && $_REQUEST['target'] && $_REQUEST['fn']) { + $id = $_REQUEST['target']; + + do { + $duplicate = false; + $uri = random_string(40) . '.vcf'; + + $r = q( + "SELECT uri FROM cards WHERE addressbookid = %s AND uri = '%s' LIMIT 1", + intval($id), + dbesc($uri) + ); + + if ($r) { + $duplicate = true; + } + } while ($duplicate == true); + + // TODO: this mostly duplictes the procedure in update addressbook card. + // Should move this part to a function to avoid duplication + $fn = $_REQUEST['fn']; + + $vcard = new \Sabre\VObject\Component\VCard([ + 'FN' => $fn, + 'N' => array_reverse(explode(' ', $fn)) + ]); + + $org = $_REQUEST['org']; + if ($org) { + $vcard->ORG = $org; + } + + $title = $_REQUEST['title']; + if ($title) { + $vcard->TITLE = $title; + } + + $tel = $_REQUEST['tel']; + $tel_type = $_REQUEST['tel_type']; + if ($tel) { + $i = 0; + foreach ($tel as $item) { + if ($item) { + $vcard->add('TEL', $item, ['type' => $tel_type[$i]]); + } + $i++; + } + } + + $email = $_REQUEST['email']; + $email_type = $_REQUEST['email_type']; + if ($email) { + $i = 0; + foreach ($email as $item) { + if ($item) { + $vcard->add('EMAIL', $item, ['type' => $email_type[$i]]); + } + $i++; + } + } + + $impp = $_REQUEST['impp']; + $impp_type = $_REQUEST['impp_type']; + if ($impp) { + $i = 0; + foreach ($impp as $item) { + if ($item) { + $vcard->add('IMPP', $item, ['type' => $impp_type[$i]]); + } + $i++; + } + } + + $url = $_REQUEST['url']; + $url_type = $_REQUEST['url_type']; + if ($url) { + $i = 0; + foreach ($url as $item) { + if ($item) { + $vcard->add('URL', $item, ['type' => $url_type[$i]]); + } + $i++; + } + } + + $adr = $_REQUEST['adr']; + $adr_type = $_REQUEST['adr_type']; + + if ($adr) { + $i = 0; + foreach ($adr as $item) { + if ($item) { + $vcard->add('ADR', $item, ['type' => $adr_type[$i]]); + } + $i++; + } + } + + $note = $_REQUEST['note']; + if ($note) { + $vcard->NOTE = $note; + } + + $cardData = $vcard->serialize(); + + $carddavBackend->createCard($id, $uri, $cardData); + } + + // edit addressbook card + if ($_REQUEST['update'] && $_REQUEST['uri'] && $_REQUEST['target']) { + $id = $_REQUEST['target']; + + if (!cdav_perms($id, $addressbooks)) { + return; + } + + $uri = $_REQUEST['uri']; + + $object = $carddavBackend->getCard($id, $uri); + $vcard = Reader::read($object['carddata']); + + $fn = $_REQUEST['fn']; + if ($fn) { + $vcard->FN = $fn; + $vcard->N = array_reverse(explode(' ', $fn)); + } + + $org = $_REQUEST['org']; + if ($org) { + $vcard->ORG = $org; + } else { + unset($vcard->ORG); + } + + $title = $_REQUEST['title']; + if ($title) { + $vcard->TITLE = $title; + } else { + unset($vcard->TITLE); + } + + $tel = $_REQUEST['tel']; + $tel_type = $_REQUEST['tel_type']; + if ($tel) { + $i = 0; + unset($vcard->TEL); + foreach ($tel as $item) { + if ($item) { + $vcard->add('TEL', $item, ['type' => $tel_type[$i]]); + } + $i++; + } + } else { + unset($vcard->TEL); + } + + $email = $_REQUEST['email']; + $email_type = $_REQUEST['email_type']; + if ($email) { + $i = 0; + unset($vcard->EMAIL); + foreach ($email as $item) { + if ($item) { + $vcard->add('EMAIL', $item, ['type' => $email_type[$i]]); + } + $i++; + } + } else { + unset($vcard->EMAIL); + } + + $impp = $_REQUEST['impp']; + $impp_type = $_REQUEST['impp_type']; + if ($impp) { + $i = 0; + unset($vcard->IMPP); + foreach ($impp as $item) { + if ($item) { + $vcard->add('IMPP', $item, ['type' => $impp_type[$i]]); + } + $i++; + } + } else { + unset($vcard->IMPP); + } + + $url = $_REQUEST['url']; + $url_type = $_REQUEST['url_type']; + if ($url) { + $i = 0; + unset($vcard->URL); + foreach ($url as $item) { + if ($item) { + $vcard->add('URL', $item, ['type' => $url_type[$i]]); + } + $i++; + } + } else { + unset($vcard->URL); + } + + $adr = $_REQUEST['adr']; + $adr_type = $_REQUEST['adr_type']; + if ($adr) { + $i = 0; + unset($vcard->ADR); + foreach ($adr as $item) { + if ($item) { + $vcard->add('ADR', $item, ['type' => $adr_type[$i]]); + } + $i++; + } + } else { + unset($vcard->ADR); + } + + $note = $_REQUEST['note']; + if ($note) { + $vcard->NOTE = $note; + } else { + unset($vcard->NOTE); + } + + $cardData = $vcard->serialize(); + + $carddavBackend->updateCard($id, $uri, $cardData); + } + + // delete addressbook card + if ($_REQUEST['delete'] && $_REQUEST['uri'] && $_REQUEST['target']) { + $id = $_REQUEST['target']; + + if (!cdav_perms($id, $addressbooks)) { + return; + } + + $uri = $_REQUEST['uri']; + + $carddavBackend->deleteCard($id, $uri); + } + } + + // Import calendar or addressbook + if (($_FILES) && array_key_exists('userfile', $_FILES) && intval($_FILES['userfile']['size']) && $_REQUEST['target']) { + $src = $_FILES['userfile']['tmp_name']; + + if ($src) { + if ($_REQUEST['c_upload']) { + if ($_REQUEST['target'] == 'calendar') { + $result = parse_ical_file($src, local_channel()); + if ($result) { + info(t('Calendar entries imported.') . EOL); + } else { + notice(t('No calendar entries found.') . EOL); + } + + @unlink($src); + return; + } + + $id = explode(':', $_REQUEST['target']); + $ext = 'ics'; + $table = 'calendarobjects'; + $column = 'calendarid'; + $objects = new ICalendar(@file_get_contents($src)); + $profile = Node::PROFILE_CALDAV; + $backend = new \Sabre\CalDAV\Backend\PDO($pdo); + } + + if ($_REQUEST['a_upload']) { + $id[] = intval($_REQUEST['target']); + $ext = 'vcf'; + $table = 'cards'; + $column = 'addressbookid'; + $objects = new VCard(@file_get_contents($src)); + $profile = Node::PROFILE_CARDDAV; + $backend = new PDO($pdo); + } + + while ($object = $objects->getNext()) { + if ($_REQUEST['a_upload']) { + $object = $object->convert(Document::VCARD40); + } + + $ret = $object->validate($profile & Node::REPAIR); + + // level 3 Means that the document is invalid, + // level 2 means a warning. A warning means it's valid but it could cause interopability issues, + // level 1 means that there was a problem earlier, but the problem was automatically repaired. + + if ($ret[0]['level'] < 3) { + do { + $duplicate = false; + $objectUri = random_string(40) . '.' . $ext; + + $r = q( + "SELECT uri FROM $table WHERE $column = %d AND uri = '%s' LIMIT 1", + dbesc($id[0]), + dbesc($objectUri) + ); + + if ($r) { + $duplicate = true; + } + } while ($duplicate == true); + + if ($_REQUEST['c_upload']) { + $backend->createCalendarObject($id, $objectUri, $object->serialize()); + } + + if ($_REQUEST['a_upload']) { + $backend->createCard($id[0], $objectUri, $object->serialize()); + } + } else { + if ($_REQUEST['c_upload']) { + notice('' . t('INVALID EVENT DISMISSED!') . '' . EOL . + '' . t('Summary: ') . '' . (($object->VEVENT->SUMMARY) ? $object->VEVENT->SUMMARY : t('Unknown')) . EOL . + '' . t('Date: ') . '' . (($object->VEVENT->DTSTART) ? $object->VEVENT->DTSTART : t('Unknown')) . EOL . + '' . t('Reason: ') . '' . $ret[0]['message'] . EOL); + } + + if ($_REQUEST['a_upload']) { + notice('' . t('INVALID CARD DISMISSED!') . '' . EOL . + '' . t('Name: ') . '' . (($object->FN) ? $object->FN : t('Unknown')) . EOL . + '' . t('Reason: ') . '' . $ret[0]['message'] . EOL); + } + } + } + } + @unlink($src); + } + } + + public function get() + { + + if (!local_channel()) { + return; + } + + if ((argv(1) === 'addressbook') && (!Apps::system_app_installed(local_channel(), 'CardDAV'))) { + // Do not display any associated widgets at this point + App::$pdl = ''; + + $o = '' . t('CardDAV App') . ' (' . t('Not Installed') . '):
        '; + $o .= t('CalDAV capable addressbook'); + return $o; + } + + App::$profile_uid = local_channel(); + + $channel = App::get_channel(); + $principalUri = 'principals/' . $channel['channel_address']; + + $pdo = DBA::$dba->db; + + require_once 'vendor/autoload.php'; + + head_add_css('cdav.css'); + + if (!cdav_principal($principalUri)) { + $this->activate($pdo, $channel); + if (!cdav_principal($principalUri)) { + return; + } + } + + if (argv(1) === 'calendar') { + nav_set_selected('Calendar'); + $caldavBackend = new \Sabre\CalDAV\Backend\PDO($pdo); + $calendars = $caldavBackend->getCalendarsForUser($principalUri); + } + + // Display calendar(s) here + if (argc() <= 3 && argv(1) === 'calendar') { + head_add_css('/library/fullcalendar/packages/core/main.min.css'); + head_add_css('/library/fullcalendar/packages/daygrid/main.min.css'); + head_add_css('/library/fullcalendar/packages/timegrid/main.min.css'); + head_add_css('/library/fullcalendar/packages/list/main.min.css'); + head_add_css('cdav_calendar.css'); + + head_add_js('/library/fullcalendar/packages/core/main.min.js'); + head_add_js('/library/fullcalendar/packages/interaction/main.min.js'); + head_add_js('/library/fullcalendar/packages/daygrid/main.min.js'); + head_add_js('/library/fullcalendar/packages/timegrid/main.min.js'); + head_add_js('/library/fullcalendar/packages/list/main.min.js'); + + $sources = ''; + $resource_id = ''; + $resource = null; + + if (argc() == 3) { + $resource_id = argv(2); + } + + if ($resource_id) { + $r = q( + "SELECT event.*, item.author_xchan, item.owner_xchan, item.plink, item.id as item_id FROM event LEFT JOIN item ON event.event_hash = item.resource_id WHERE event.uid = %d AND event.event_hash = '%s' LIMIT 1", - intval(local_channel()), - dbesc($resource_id) - ); - if ($r) { - xchan_query($r); - $r = fetch_post_tags($r,true); + intval(local_channel()), + dbesc($resource_id) + ); + if ($r) { + xchan_query($r); + $r = fetch_post_tags($r, true); - $r[0]['dtstart'] = (($r[0]['adjust']) ? datetime_convert('UTC',date_default_timezone_get(),$r[0]['dtstart'], 'c') : datetime_convert('UTC','UTC',$r[0]['dtstart'],'c')); - $r[0]['dtend'] = (($r[0]['adjust']) ? datetime_convert('UTC',date_default_timezone_get(),$r[0]['dtend'], 'c') : datetime_convert('UTC','UTC',$r[0]['dtend'],'c')); + $r[0]['dtstart'] = (($r[0]['adjust']) ? datetime_convert('UTC', date_default_timezone_get(), $r[0]['dtstart'], 'c') : datetime_convert('UTC', 'UTC', $r[0]['dtstart'], 'c')); + $r[0]['dtend'] = (($r[0]['adjust']) ? datetime_convert('UTC', date_default_timezone_get(), $r[0]['dtend'], 'c') : datetime_convert('UTC', 'UTC', $r[0]['dtend'], 'c')); - $r[0]['plink'] = [$r[0]['plink'], t('Link to source')]; + $r[0]['plink'] = [$r[0]['plink'], t('Link to source')]; - $resource = $r[0]; + $resource = $r[0]; - $catsenabled = Apps::system_app_installed(local_channel(), 'Categories'); + $catsenabled = Apps::system_app_installed(local_channel(), 'Categories'); - $categories = ''; - if ($catsenabled){ - if($r[0]['term']) { - $categories = array_elm_to_str(get_terms_oftype($r[0]['term'], TERM_CATEGORY), 'term'); - } - } + $categories = ''; + if ($catsenabled) { + if ($r[0]['term']) { + $categories = array_elm_to_str(get_terms_oftype($r[0]['term'], TERM_CATEGORY), 'term'); + } + } - if ($r[0]['dismissed'] == 0) { - q("UPDATE event SET dismissed = 1 WHERE event.uid = %d AND event.event_hash = '%s'", - intval(local_channel()), - dbesc($resource_id) - ); - } - } - } + if ($r[0]['dismissed'] == 0) { + q( + "UPDATE event SET dismissed = 1 WHERE event.uid = %d AND event.event_hash = '%s'", + intval(local_channel()), + dbesc($resource_id) + ); + } + } + } - if (get_pconfig(local_channel(), 'cdav_calendar', 'calendar')) { - $sources .= '{ + if (get_pconfig(local_channel(), 'cdav_calendar', 'calendar')) { + $sources .= '{ id: \'calendar\', url: \'/calendar/json/\', color: \'#3a87ad\' }, '; - } + } - $calendars[] = [ - 'displayname' => $channel['channel_name'], - 'id' => 'calendar' - ]; + $calendars[] = [ + 'displayname' => $channel['channel_name'], + 'id' => 'calendar' + ]; - foreach ($calendars as $calendar) { - $editable = (($calendar['share-access'] == 2) ? 'false' : 'true'); // false/true must be string since we're passing it to javascript - $color = (($calendar['{http://apple.com/ns/ical/}calendar-color']) ? $calendar['{http://apple.com/ns/ical/}calendar-color'] : '#6cad39'); - $sharer = (($calendar['share-access'] == 3) ? $calendar['{urn:ietf:params:xml:ns:caldav}calendar-description'] : ''); - $switch = get_pconfig(local_channel(), 'cdav_calendar', $calendar['id'][0]); - if ($switch) { - $sources .= '{ + foreach ($calendars as $calendar) { + $editable = (($calendar['share-access'] == 2) ? 'false' : 'true'); // false/true must be string since we're passing it to javascript + $color = (($calendar['{http://apple.com/ns/ical/}calendar-color']) ? $calendar['{http://apple.com/ns/ical/}calendar-color'] : '#6cad39'); + $sharer = (($calendar['share-access'] == 3) ? $calendar['{urn:ietf:params:xml:ns:caldav}calendar-description'] : ''); + $switch = get_pconfig(local_channel(), 'cdav_calendar', $calendar['id'][0]); + if ($switch) { + $sources .= '{ id: ' . $calendar['id'][0] . ', url: \'/cdav/calendar/json/' . $calendar['id'][0] . '/' . $calendar['id'][1] . '\', color: \'' . $color . '\' }, '; - } - - if ($calendar['share-access'] != 2) { - $writable_calendars[] = [ - 'displayname' => $calendar['{DAV:}displayname'], - 'sharer' => $sharer, - 'id' => $calendar['id'] - ]; - } - } - - $sources = rtrim($sources, ', '); - - $first_day = feature_enabled(local_channel(), 'cal_first_day'); - $first_day = (($first_day) ? $first_day : 0); - - $title = ['title', t('Event title')]; - $dtstart = ['dtstart', t('Start date and time')]; - $dtend = ['dtend', t('End date and time')]; - $description = ['description', t('Description')]; - $location = ['location', t('Location')]; - - $catsenabled = Apps::system_app_installed(local_channel(), 'Categories'); - - require_once('include/acl_selectors.php'); - - $accesslist = new \Zotlabs\Access\AccessControl($channel); - $perm_defaults = $accesslist->get(); - - $acl = populate_acl($perm_defaults, false, \Zotlabs\Lib\PermissionDescription::fromGlobalPermission('view_stream')); - - $permissions = $perm_defaults; - - $o .= replace_macros(get_markup_template('cdav_calendar.tpl'), [ - '$sources' => $sources, - '$color' => $color, - '$lang' => App::$language, - '$timezone' => App::$timezone, - '$first_day' => $first_day, - '$prev' => t('Previous'), - '$next' => t('Next'), - '$today' => t('Today'), - '$month' => t('Month'), - '$week' => t('Week'), - '$day' => t('Day'), - '$list_month' => t('List month'), - '$list_week' => t('List week'), - '$list_day' => t('List day'), - '$title' => $title, - '$calendars' => $calendars, - '$writable_calendars' => $writable_calendars, - '$dtstart' => $dtstart, - '$dtend' => $dtend, - '$description' => $description, - '$location' => $location, - '$more' => t('More'), - '$less' => t('Less'), - '$update' => t('Update'), - '$calendar_select_label' => t('Select calendar'), - '$calendar_optiopns_label' => [t('Channel Calendars'), t('CalDAV Calendars')], - '$delete' => t('Delete'), - '$delete_all' => t('Delete all'), - '$cancel' => t('Cancel'), - '$create' => t('Create'), - '$recurrence_warning' => t('Sorry! Editing of recurrent events is not yet implemented.'), - '$channel_hash' => $channel['channel_hash'], - '$acl' => $acl, - '$lockstate' => (($accesslist->is_private()) ? 'lock' : 'unlock'), - '$allow_cid' => acl2json($permissions['allow_cid']), - '$allow_gid' => acl2json($permissions['allow_gid']), - '$deny_cid' => acl2json($permissions['deny_cid']), - '$deny_gid' => acl2json($permissions['deny_gid']), - '$catsenabled' => $catsenabled, - '$categories_label' => t('Categories'), - '$resource' => json_encode($resource), - '$categories' => $categories - ]); - - return $o; - - } - - // Provide json data for calendar - if (argc() == 5 && argv(1) === 'calendar' && argv(2) === 'json' && intval(argv(3)) && intval(argv(4))) { - - $events = []; - - $id = [argv(3), argv(4)]; - - if (! cdav_perms($id[0],$calendars)) { - json_return_and_die($events); - } - - if (x($_GET,'start')) { - $start = new \DateTime($_GET['start']); - } - if (x($_GET,'end')) { - $end = new \DateTime($_GET['end']); - } - - $filters['name'] = 'VCALENDAR'; - $filters['prop-filters'][0]['name'] = 'VEVENT'; - $filters['comp-filters'][0]['name'] = 'VEVENT'; - $filters['comp-filters'][0]['time-range']['start'] = $start; - $filters['comp-filters'][0]['time-range']['end'] = $end; - - $uris = $caldavBackend->calendarQuery($id, $filters); - - if ($uris) { - $objects = $caldavBackend->getMultipleCalendarObjects($id, $uris); - foreach ($objects as $object) { - - $vcalendar = \Sabre\VObject\Reader::read($object['calendardata']); - - if (isset($vcalendar->VEVENT->RRULE)) { - // expanding recurrent events seems to loose timezone info - // save it here so we can add it later - $recurrent_timezone = (string)$vcalendar->VEVENT->DTSTART['TZID']; - $vcalendar = $vcalendar->expand($start, $end); - } - - foreach ($vcalendar->VEVENT as $vevent) { - $title = (string)$vevent->SUMMARY; - $dtstart = (string)$vevent->DTSTART; - $dtend = (string)$vevent->DTEND; - $description = (string)$vevent->DESCRIPTION; - $location = (string)$vevent->LOCATION; - $timezone = (string)$vevent->DTSTART['TZID']; - $rw = ((cdav_perms($id[0],$calendars,true)) ? true : false); - $editable = $rw ? true : false; - $recurrent = ((isset($vevent->{'RECURRENCE-ID'})) ? true : false); - - if ($recurrent) { - $editable = false; - $timezone = $recurrent_timezone; - } - - $allDay = false; - - // allDay event rules - if (!strpos($dtstart, 'T') && !strpos($dtend, 'T')) { - $allDay = true; - } - if (strpos($dtstart, 'T000000') && strpos($dtend, 'T000000')) { - $allDay = true; - } - - $events[] = [ - 'calendar_id' => $id, - 'uri' => $object['uri'], - 'title' => $title, - 'start' => datetime_convert($timezone, $timezone, $dtstart, 'c'), - 'end' => (($dtend) ? datetime_convert($timezone, $timezone, $dtend, 'c') : ''), - 'description' => $description, - 'location' => $location, - 'allDay' => $allDay, - 'editable' => $editable, - 'recurrent' => $recurrent, - 'rw' => $rw - ]; - } - } - } - json_return_and_die($events); - } - - // enable/disable calendars - if (argc() == 5 && argv(1) === 'calendar' && argv(2) === 'switch' && argv(3) && (argv(4) == 1 || argv(4) == 0)) { - $id = argv(3); - - if (! cdav_perms($id,$calendars)) { - killme(); - } - - set_pconfig(local_channel(), 'cdav_calendar' , argv(3), argv(4)); - killme(); - } - - // drop calendar - if (argc() == 5 && argv(1) === 'calendar' && argv(2) === 'drop' && intval(argv(3)) && intval(argv(4))) { - $id = [argv(3), argv(4)]; - - if (! cdav_perms($id[0],$calendars)) { - killme(); - } - - $caldavBackend->deleteCalendar($id); - killme(); - } - - // drop sharee - if (argc() == 6 && argv(1) === 'calendar' && argv(2) === 'dropsharee' && intval(argv(3)) && intval(argv(4))) { - - $id = [argv(3), argv(4)]; - $hash = argv(5); - - if (! cdav_perms($id[0],$calendars)) { - killme(); - } - - $sharee_arr = channelx_by_hash($hash); - - $sharee = new \Sabre\DAV\Xml\Element\Sharee(); - - $sharee->href = 'mailto:' . $sharee_arr['xchan_addr']; - $sharee->principal = 'principals/' . $sharee_arr['channel_address']; - $sharee->access = 4; - $caldavBackend->updateInvites($id, [$sharee]); - - killme(); - } - - - if (argv(1) === 'addressbook') { - nav_set_selected('CardDAV'); - $carddavBackend = new \Sabre\CardDAV\Backend\PDO($pdo); - $addressbooks = $carddavBackend->getAddressBooksForUser($principalUri); - } - - // Display Adressbook here - if (argc() == 3 && argv(1) === 'addressbook' && intval(argv(2))) { - - $id = argv(2); - - $displayname = cdav_perms($id,$addressbooks); - - if (! $displayname) { - return; - } - - head_add_css('cdav_addressbook.css'); - - $o = ''; - - $sabrecards = $carddavBackend->getCards($id); - foreach ($sabrecards as $sabrecard) { - $uris[] = $sabrecard['uri']; - } - - if ($uris) { - $objects = $carddavBackend->getMultipleCards($id, $uris); - - foreach ($objects as $object) { - $vcard = \Sabre\VObject\Reader::read($object['carddata']); - - $photo = ''; - if ($vcard->PHOTO) { - $photo_value = strtolower($vcard->PHOTO->getValueType()); // binary or uri - if ($photo_value === 'binary') { - $photo_type = strtolower($vcard->PHOTO['TYPE']); // mime jpeg, png or gif - $photo = 'data:image/' . $photo_type . ';base64,' . base64_encode((string)$vcard->PHOTO); - } - else { - $url = parse_url((string)$vcard->PHOTO); - $photo = 'data:' . $url['path']; - } - } - - $fn = ''; - if ($vcard->FN) { - $fn = (string)$vcard->FN; - } - - $org = ''; - if ($vcard->ORG) { - $org = (string)$vcard->ORG; - } - - $title = ''; - if ($vcard->TITLE) { - $title = (string)$vcard->TITLE; - } - - $tels = []; - if ($vcard->TEL) { - foreach ($vcard->TEL as $tel) { - $type = (($tel['TYPE']) ? translate_type((string)$tel['TYPE']) : ''); - $tels[] = [ - 'type' => $type, - 'nr' => (string)$tel - ]; - } - } - - $emails = []; - if ($vcard->EMAIL) { - foreach ($vcard->EMAIL as $email) { - $type = (($email['TYPE']) ? translate_type((string)$email['TYPE']) : ''); - $emails[] = [ - 'type' => $type, - 'address' => (string)$email - ]; - } - } - - $impps = []; - if ($vcard->IMPP) { - foreach ($vcard->IMPP as $impp) { - $type = (($impp['TYPE']) ? translate_type((string)$impp['TYPE']) : ''); - $impps[] = [ - 'type' => $type, - 'address' => (string)$impp - ]; - } - } - - $urls = []; - if ($vcard->URL) { - foreach ($vcard->URL as $url) { - $type = (($url['TYPE']) ? translate_type((string)$url['TYPE']) : ''); - $urls[] = [ - 'type' => $type, - 'address' => (string)$url - ]; - } - } - - $adrs = []; - if ($vcard->ADR) { - foreach ($vcard->ADR as $adr) { - $type = (($adr['TYPE']) ? translate_type((string)$adr['TYPE']) : ''); - $adrs[] = [ - 'type' => $type, - 'address' => $adr->getParts() - ]; - } - } - - $note = ''; - if ($vcard->NOTE) { - $note = (string)$vcard->NOTE; - } - - $cards[] = [ - 'id' => $object['id'], - 'uri' => $object['uri'], - 'photo' => $photo, - 'fn' => $fn, - 'org' => $org, - 'title' => $title, - 'tels' => $tels, - 'emails' => $emails, - 'impps' => $impps, - 'urls' => $urls, - 'adrs' => $adrs, - 'note' => $note - ]; - } - - usort($cards, function($a, $b) { return strcasecmp($a['fn'], $b['fn']); }); - } - - $o .= replace_macros(get_markup_template('cdav_addressbook.tpl'), [ - '$id' => $id, - '$cards' => $cards, - '$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') - ]); - - return $o; - } - - // delete addressbook - if (argc() > 3 && argv(1) === 'addressbook' && argv(2) === 'drop' && intval(argv(3))) { - $id = argv(3); - - if (! cdav_perms($id,$addressbooks)) { - return; - } - - $carddavBackend->deleteAddressBook($id); - killme(); - } - - } - - function activate($pdo, $channel) { - - if (! $channel) { - return; - } - - $uri = 'principals/' . $channel['channel_address']; - - - $r = q("select * from principals where uri = '%s' limit 1", - dbesc($uri) - ); - if($r) { - $r = q("update principals set email = '%s', displayname = '%s' where uri = '%s' ", - dbesc($channel['xchan_addr']), - dbesc($channel['channel_name']), - dbesc($uri) - ); - } - else { - $r = q("insert into principals ( uri, email, displayname ) values('%s','%s','%s') ", - dbesc($uri), - dbesc($channel['xchan_addr']), - dbesc($channel['channel_name']) - ); - - // create default calendar - $caldavBackend = new \Sabre\CalDAV\Backend\PDO($pdo); - $properties = [ - '{DAV:}displayname' => t('Default Calendar'), - '{http://apple.com/ns/ical/}calendar-color' => '#6cad39', - '{urn:ietf:params:xml:ns:caldav}calendar-description' => $channel['channel_name'] - ]; - - $id = $caldavBackend->createCalendar($uri, 'default', $properties); - set_pconfig(local_channel(), 'cdav_calendar' , $id[0], 1); - set_pconfig(local_channel(), 'cdav_calendar' , 'calendar', 1); - - // create default addressbook - $carddavBackend = new \Sabre\CardDAV\Backend\PDO($pdo); - $properties = ['{DAV:}displayname' => t('Default Addressbook')]; - $carddavBackend->createAddressBook($uri, 'default', $properties); - - } - } - + } + + if ($calendar['share-access'] != 2) { + $writable_calendars[] = [ + 'displayname' => $calendar['{DAV:}displayname'], + 'sharer' => $sharer, + 'id' => $calendar['id'] + ]; + } + } + + $sources = rtrim($sources, ', '); + + $first_day = feature_enabled(local_channel(), 'cal_first_day'); + $first_day = (($first_day) ? $first_day : 0); + + $title = ['title', t('Event title')]; + $dtstart = ['dtstart', t('Start date and time')]; + $dtend = ['dtend', t('End date and time')]; + $description = ['description', t('Description')]; + $location = ['location', t('Location')]; + + $catsenabled = Apps::system_app_installed(local_channel(), 'Categories'); + + require_once('include/acl_selectors.php'); + + $accesslist = new AccessControl($channel); + $perm_defaults = $accesslist->get(); + + $acl = populate_acl($perm_defaults, false, PermissionDescription::fromGlobalPermission('view_stream')); + + $permissions = $perm_defaults; + + $o .= replace_macros(get_markup_template('cdav_calendar.tpl'), [ + '$sources' => $sources, + '$color' => $color, + '$lang' => App::$language, + '$timezone' => App::$timezone, + '$first_day' => $first_day, + '$prev' => t('Previous'), + '$next' => t('Next'), + '$today' => t('Today'), + '$month' => t('Month'), + '$week' => t('Week'), + '$day' => t('Day'), + '$list_month' => t('List month'), + '$list_week' => t('List week'), + '$list_day' => t('List day'), + '$title' => $title, + '$calendars' => $calendars, + '$writable_calendars' => $writable_calendars, + '$dtstart' => $dtstart, + '$dtend' => $dtend, + '$description' => $description, + '$location' => $location, + '$more' => t('More'), + '$less' => t('Less'), + '$update' => t('Update'), + '$calendar_select_label' => t('Select calendar'), + '$calendar_optiopns_label' => [t('Channel Calendars'), t('CalDAV Calendars')], + '$delete' => t('Delete'), + '$delete_all' => t('Delete all'), + '$cancel' => t('Cancel'), + '$create' => t('Create'), + '$recurrence_warning' => t('Sorry! Editing of recurrent events is not yet implemented.'), + '$channel_hash' => $channel['channel_hash'], + '$acl' => $acl, + '$lockstate' => (($accesslist->is_private()) ? 'lock' : 'unlock'), + '$allow_cid' => acl2json($permissions['allow_cid']), + '$allow_gid' => acl2json($permissions['allow_gid']), + '$deny_cid' => acl2json($permissions['deny_cid']), + '$deny_gid' => acl2json($permissions['deny_gid']), + '$catsenabled' => $catsenabled, + '$categories_label' => t('Categories'), + '$resource' => json_encode($resource), + '$categories' => $categories + ]); + + return $o; + } + + // Provide json data for calendar + if (argc() == 5 && argv(1) === 'calendar' && argv(2) === 'json' && intval(argv(3)) && intval(argv(4))) { + $events = []; + + $id = [argv(3), argv(4)]; + + if (!cdav_perms($id[0], $calendars)) { + json_return_and_die($events); + } + + if (x($_GET, 'start')) { + $start = new DateTime($_GET['start']); + } + if (x($_GET, 'end')) { + $end = new DateTime($_GET['end']); + } + + $filters['name'] = 'VCALENDAR'; + $filters['prop-filters'][0]['name'] = 'VEVENT'; + $filters['comp-filters'][0]['name'] = 'VEVENT'; + $filters['comp-filters'][0]['time-range']['start'] = $start; + $filters['comp-filters'][0]['time-range']['end'] = $end; + + $uris = $caldavBackend->calendarQuery($id, $filters); + + if ($uris) { + $objects = $caldavBackend->getMultipleCalendarObjects($id, $uris); + foreach ($objects as $object) { + $vcalendar = Reader::read($object['calendardata']); + + if (isset($vcalendar->VEVENT->RRULE)) { + // expanding recurrent events seems to loose timezone info + // save it here so we can add it later + $recurrent_timezone = (string)$vcalendar->VEVENT->DTSTART['TZID']; + $vcalendar = $vcalendar->expand($start, $end); + } + + foreach ($vcalendar->VEVENT as $vevent) { + $title = (string)$vevent->SUMMARY; + $dtstart = (string)$vevent->DTSTART; + $dtend = (string)$vevent->DTEND; + $description = (string)$vevent->DESCRIPTION; + $location = (string)$vevent->LOCATION; + $timezone = (string)$vevent->DTSTART['TZID']; + $rw = ((cdav_perms($id[0], $calendars, true)) ? true : false); + $editable = $rw ? true : false; + $recurrent = ((isset($vevent->{'RECURRENCE-ID'})) ? true : false); + + if ($recurrent) { + $editable = false; + $timezone = $recurrent_timezone; + } + + $allDay = false; + + // allDay event rules + if (!strpos($dtstart, 'T') && !strpos($dtend, 'T')) { + $allDay = true; + } + if (strpos($dtstart, 'T000000') && strpos($dtend, 'T000000')) { + $allDay = true; + } + + $events[] = [ + 'calendar_id' => $id, + 'uri' => $object['uri'], + 'title' => $title, + 'start' => datetime_convert($timezone, $timezone, $dtstart, 'c'), + 'end' => (($dtend) ? datetime_convert($timezone, $timezone, $dtend, 'c') : ''), + 'description' => $description, + 'location' => $location, + 'allDay' => $allDay, + 'editable' => $editable, + 'recurrent' => $recurrent, + 'rw' => $rw + ]; + } + } + } + json_return_and_die($events); + } + + // enable/disable calendars + if (argc() == 5 && argv(1) === 'calendar' && argv(2) === 'switch' && argv(3) && (argv(4) == 1 || argv(4) == 0)) { + $id = argv(3); + + if (!cdav_perms($id, $calendars)) { + killme(); + } + + set_pconfig(local_channel(), 'cdav_calendar', argv(3), argv(4)); + killme(); + } + + // drop calendar + if (argc() == 5 && argv(1) === 'calendar' && argv(2) === 'drop' && intval(argv(3)) && intval(argv(4))) { + $id = [argv(3), argv(4)]; + + if (!cdav_perms($id[0], $calendars)) { + killme(); + } + + $caldavBackend->deleteCalendar($id); + killme(); + } + + // drop sharee + if (argc() == 6 && argv(1) === 'calendar' && argv(2) === 'dropsharee' && intval(argv(3)) && intval(argv(4))) { + $id = [argv(3), argv(4)]; + $hash = argv(5); + + if (!cdav_perms($id[0], $calendars)) { + killme(); + } + + $sharee_arr = channelx_by_hash($hash); + + $sharee = new Sharee(); + + $sharee->href = 'mailto:' . $sharee_arr['xchan_addr']; + $sharee->principal = 'principals/' . $sharee_arr['channel_address']; + $sharee->access = 4; + $caldavBackend->updateInvites($id, [$sharee]); + + killme(); + } + + + if (argv(1) === 'addressbook') { + nav_set_selected('CardDAV'); + $carddavBackend = new PDO($pdo); + $addressbooks = $carddavBackend->getAddressBooksForUser($principalUri); + } + + // Display Adressbook here + if (argc() == 3 && argv(1) === 'addressbook' && intval(argv(2))) { + $id = argv(2); + + $displayname = cdav_perms($id, $addressbooks); + + if (!$displayname) { + return; + } + + head_add_css('cdav_addressbook.css'); + + $o = ''; + + $sabrecards = $carddavBackend->getCards($id); + foreach ($sabrecards as $sabrecard) { + $uris[] = $sabrecard['uri']; + } + + if ($uris) { + $objects = $carddavBackend->getMultipleCards($id, $uris); + + foreach ($objects as $object) { + $vcard = Reader::read($object['carddata']); + + $photo = ''; + if ($vcard->PHOTO) { + $photo_value = strtolower($vcard->PHOTO->getValueType()); // binary or uri + if ($photo_value === 'binary') { + $photo_type = strtolower($vcard->PHOTO['TYPE']); // mime jpeg, png or gif + $photo = 'data:image/' . $photo_type . ';base64,' . base64_encode((string)$vcard->PHOTO); + } else { + $url = parse_url((string)$vcard->PHOTO); + $photo = 'data:' . $url['path']; + } + } + + $fn = ''; + if ($vcard->FN) { + $fn = (string)$vcard->FN; + } + + $org = ''; + if ($vcard->ORG) { + $org = (string)$vcard->ORG; + } + + $title = ''; + if ($vcard->TITLE) { + $title = (string)$vcard->TITLE; + } + + $tels = []; + if ($vcard->TEL) { + foreach ($vcard->TEL as $tel) { + $type = (($tel['TYPE']) ? translate_type((string)$tel['TYPE']) : ''); + $tels[] = [ + 'type' => $type, + 'nr' => (string)$tel + ]; + } + } + + $emails = []; + if ($vcard->EMAIL) { + foreach ($vcard->EMAIL as $email) { + $type = (($email['TYPE']) ? translate_type((string)$email['TYPE']) : ''); + $emails[] = [ + 'type' => $type, + 'address' => (string)$email + ]; + } + } + + $impps = []; + if ($vcard->IMPP) { + foreach ($vcard->IMPP as $impp) { + $type = (($impp['TYPE']) ? translate_type((string)$impp['TYPE']) : ''); + $impps[] = [ + 'type' => $type, + 'address' => (string)$impp + ]; + } + } + + $urls = []; + if ($vcard->URL) { + foreach ($vcard->URL as $url) { + $type = (($url['TYPE']) ? translate_type((string)$url['TYPE']) : ''); + $urls[] = [ + 'type' => $type, + 'address' => (string)$url + ]; + } + } + + $adrs = []; + if ($vcard->ADR) { + foreach ($vcard->ADR as $adr) { + $type = (($adr['TYPE']) ? translate_type((string)$adr['TYPE']) : ''); + $adrs[] = [ + 'type' => $type, + 'address' => $adr->getParts() + ]; + } + } + + $note = ''; + if ($vcard->NOTE) { + $note = (string)$vcard->NOTE; + } + + $cards[] = [ + 'id' => $object['id'], + 'uri' => $object['uri'], + 'photo' => $photo, + 'fn' => $fn, + 'org' => $org, + 'title' => $title, + 'tels' => $tels, + 'emails' => $emails, + 'impps' => $impps, + 'urls' => $urls, + 'adrs' => $adrs, + 'note' => $note + ]; + } + + usort($cards, function ($a, $b) { + return strcasecmp($a['fn'], $b['fn']); + }); + } + + $o .= replace_macros(get_markup_template('cdav_addressbook.tpl'), [ + '$id' => $id, + '$cards' => $cards, + '$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') + ]); + + return $o; + } + + // delete addressbook + if (argc() > 3 && argv(1) === 'addressbook' && argv(2) === 'drop' && intval(argv(3))) { + $id = argv(3); + + if (!cdav_perms($id, $addressbooks)) { + return; + } + + $carddavBackend->deleteAddressBook($id); + killme(); + } + } + + public function activate($pdo, $channel) + { + + if (!$channel) { + return; + } + + $uri = 'principals/' . $channel['channel_address']; + + + $r = q( + "select * from principals where uri = '%s' limit 1", + dbesc($uri) + ); + if ($r) { + $r = q( + "update principals set email = '%s', displayname = '%s' where uri = '%s' ", + dbesc($channel['xchan_addr']), + dbesc($channel['channel_name']), + dbesc($uri) + ); + } else { + $r = q( + "insert into principals ( uri, email, displayname ) values('%s','%s','%s') ", + dbesc($uri), + dbesc($channel['xchan_addr']), + dbesc($channel['channel_name']) + ); + + // create default calendar + $caldavBackend = new \Sabre\CalDAV\Backend\PDO($pdo); + $properties = [ + '{DAV:}displayname' => t('Default Calendar'), + '{http://apple.com/ns/ical/}calendar-color' => '#6cad39', + '{urn:ietf:params:xml:ns:caldav}calendar-description' => $channel['channel_name'] + ]; + + $id = $caldavBackend->createCalendar($uri, 'default', $properties); + set_pconfig(local_channel(), 'cdav_calendar', $id[0], 1); + set_pconfig(local_channel(), 'cdav_calendar', 'calendar', 1); + + // create default addressbook + $carddavBackend = new PDO($pdo); + $properties = ['{DAV:}displayname' => t('Default Addressbook')]; + $carddavBackend->createAddressBook($uri, 'default', $properties); + } + } } diff --git a/Zotlabs/Module/Changeaddr.php b/Zotlabs/Module/Changeaddr.php index 3ab6495b0..5e4ad254d 100644 --- a/Zotlabs/Module/Changeaddr.php +++ b/Zotlabs/Module/Changeaddr.php @@ -10,103 +10,107 @@ use Zotlabs\Web\Controller; * Provided for those situations which require it. * Must be manually configured by the admin before use. */ - - -class Changeaddr extends Controller { - - function post() { - - if (! get_config('system','allow_nick_change')) { - return; - } - - if (! local_channel()) { - return; - } - - if (isset($_SESSION['delegate']) && $_SESSION['delegate']) { - return; - } - - if ((! x($_POST,'qxz_password')) || (! strlen(trim($_POST['qxz_password'])))) - return; - - if ((! x($_POST,'verify')) || (! strlen(trim($_POST['verify'])))) - return; - - if ($_POST['verify'] !== $_SESSION['remove_account_verify']) - return; - - - $account = App::get_account(); - $channel = App::get_channel(); - - $x = account_verify_password($account['account_email'],$_POST['qxz_password']); - if (! ($x && $x['account'])) { - return; - } - - if ($account['account_password_changed'] > NULL_DATE) { - $d1 = datetime_convert('UTC','UTC','now - 48 hours'); - if ($account['account_password_changed'] > $d1) { - notice( t('Channel name changes are not allowed within 48 hours of changing the account password.') . EOL); - return; - } - } - - $new_address = trim($_POST['newname']); - - if ($new_address === $channel['channel_address']) - return; - - if ($new_address === 'sys') { - notice( t('Reserved nickname. Please choose another.') . EOL); - return; - } - - if (check_webbie(array($new_address)) !== $new_address) { - notice( t('Nickname has unsupported characters or is already being used on this site.') . EOL); - return $ret; - } - - channel_change_address($channel,$new_address); - - goaway(z_root() . '/changeaddr'); - - } - - - function get() { - - if (! get_config('system','allow_nick_change')) { - notice(t('Feature has been disabled') . EOL); - return; - } - if (! local_channel()) { - goaway(z_root()); - } - - $channel = App::get_channel(); +class Changeaddr extends Controller +{ - $hash = random_string(); - - $_SESSION['remove_account_verify'] = $hash; - - $tpl = get_markup_template('channel_rename.tpl'); - $o .= replace_macros($tpl, [ - '$basedir' => z_root(), - '$hash' => $hash, - '$title' => t('Change channel nickname/address'), - '$desc' => array(t('WARNING: '), t('Any/all connections on other networks will be lost!')), - '$passwd' => t('Please enter your password for verification:'), - '$newname' => [ 'newname', t('New channel address'),$channel['channel_address'], ''], - '$submit' => t('Rename Channel') - ]); - - return $o; - - } - + public function post() + { + + if (!get_config('system', 'allow_nick_change')) { + return; + } + + if (!local_channel()) { + return; + } + + if (isset($_SESSION['delegate']) && $_SESSION['delegate']) { + return; + } + + if ((!x($_POST, 'qxz_password')) || (!strlen(trim($_POST['qxz_password'])))) { + return; + } + + if ((!x($_POST, 'verify')) || (!strlen(trim($_POST['verify'])))) { + return; + } + + if ($_POST['verify'] !== $_SESSION['remove_account_verify']) { + return; + } + + + $account = App::get_account(); + $channel = App::get_channel(); + + $x = account_verify_password($account['account_email'], $_POST['qxz_password']); + if (!($x && $x['account'])) { + return; + } + + if ($account['account_password_changed'] > NULL_DATE) { + $d1 = datetime_convert('UTC', 'UTC', 'now - 48 hours'); + if ($account['account_password_changed'] > $d1) { + notice(t('Channel name changes are not allowed within 48 hours of changing the account password.') . EOL); + return; + } + } + + $new_address = trim($_POST['newname']); + + if ($new_address === $channel['channel_address']) { + return; + } + + if ($new_address === 'sys') { + notice(t('Reserved nickname. Please choose another.') . EOL); + return; + } + + if (check_webbie(array($new_address)) !== $new_address) { + notice(t('Nickname has unsupported characters or is already being used on this site.') . EOL); + return $ret; + } + + channel_change_address($channel, $new_address); + + goaway(z_root() . '/changeaddr'); + } + + + public function get() + { + + if (!get_config('system', 'allow_nick_change')) { + notice(t('Feature has been disabled') . EOL); + return; + } + + + if (!local_channel()) { + goaway(z_root()); + } + + $channel = App::get_channel(); + + $hash = random_string(); + + $_SESSION['remove_account_verify'] = $hash; + + $tpl = get_markup_template('channel_rename.tpl'); + $o .= replace_macros($tpl, [ + '$basedir' => z_root(), + '$hash' => $hash, + '$title' => t('Change channel nickname/address'), + '$desc' => array(t('WARNING: '), t('Any/all connections on other networks will be lost!')), + '$passwd' => t('Please enter your password for verification:'), + '$newname' => ['newname', t('New channel address'), $channel['channel_address'], ''], + '$submit' => t('Rename Channel') + ]); + + return $o; + } } diff --git a/Zotlabs/Module/Channel.php b/Zotlabs/Module/Channel.php index 80d3a9e8f..b7265e92d 100644 --- a/Zotlabs/Module/Channel.php +++ b/Zotlabs/Module/Channel.php @@ -10,10 +10,10 @@ use Zotlabs\Lib\LDSignatures; use Zotlabs\Lib\Crypto; use Zotlabs\Lib\PConfig; use Zotlabs\Web\HTTPSig; - use App; use Zotlabs\Web\Controller; use Zotlabs\Lib\PermissionDescription; +use Zotlabs\Widget\Pinned; require_once('include/items.php'); require_once('include/security.php'); @@ -25,605 +25,589 @@ require_once('include/acl_selectors.php'); * @brief Channel Controller * */ +class Channel extends Controller +{ -class Channel extends Controller { + // State passed in from the Update module. - // State passed in from the Update module. - - public $profile_uid = 0; - public $loading = 0; - public $updating = 0; + public $profile_uid = 0; + public $loading = 0; + public $updating = 0; - function init() { + public function init() + { - if (isset($_GET['search']) && (in_array(substr($_GET['search'],0,1),[ '@', '!', '?']) || strpos($_GET['search'],'https://') === 0)) { - goaway(z_root() . '/search' . '?f=&search=' . urlencode($_GET['search'])); - } + if (isset($_GET['search']) && (in_array(substr($_GET['search'], 0, 1), ['@', '!', '?']) || strpos($_GET['search'], 'https://') === 0)) { + goaway(z_root() . '/search' . '?f=&search=' . urlencode($_GET['search'])); + } - $which = null; - if (argc() > 1) { - $which = argv(1); - } - if (! $which) { - if (local_channel()) { - $channel = App::get_channel(); - if ($channel && $channel['channel_address']) { - $which = $channel['channel_address']; - } - } - } - if (! $which) { - notice( t('You must be logged in to see this page.') . EOL ); - return; - } + $which = null; + if (argc() > 1) { + $which = argv(1); + } + if (!$which) { + if (local_channel()) { + $channel = App::get_channel(); + if ($channel && $channel['channel_address']) { + $which = $channel['channel_address']; + } + } + } + if (!$which) { + notice(t('You must be logged in to see this page.') . EOL); + return; + } - $profile = 0; - $channel = App::get_channel(); + $profile = 0; + $channel = App::get_channel(); - if ((local_channel()) && (argc() > 2) && (argv(2) === 'view')) { - $which = $channel['channel_address']; - $profile = argv(1); - } + if ((local_channel()) && (argc() > 2) && (argv(2) === 'view')) { + $which = $channel['channel_address']; + $profile = argv(1); + } - $channel = channelx_by_nick($which, true); - if (! $channel) { - http_status_exit(404, 'Not found'); - } - if ($channel['channel_removed']) { - http_status_exit(410,'Gone'); - } + $channel = channelx_by_nick($which, true); + if (!$channel) { + http_status_exit(404, 'Not found'); + } + if ($channel['channel_removed']) { + http_status_exit(410, 'Gone'); + } - if (get_pconfig($channel['channel_id'],'system','noindex')) { - App::$meta->set('robots', 'noindex, noarchive'); - } + if (get_pconfig($channel['channel_id'], 'system', 'noindex')) { + App::$meta->set('robots', 'noindex, noarchive'); + } - head_add_link( [ - 'rel' => 'alternate', - 'type' => 'application/atom+xml', - 'title' => t('Only posts'), - 'href' => z_root() . '/feed/' . $which - ]); - - - - // An ActivityStreams actor record is more or less required for ActivityStreams compliance - // unless the actor object is inlined into every activity/object. This implies that it - // is more or less required for the Zot6 protocol, which uses ActivityStreams as a content - // serialisation and which doesn't always include the full actor record with every - // activity/object. - - // "more or less" means it isn't spelled out in the ActivityStreams spec, but a number of - // things will break in subtle ways if it isn't provided. - - // The ActivityPub protocol requires an 'inbox', which will not be present in this record - // if/when the ActivityPub protocol is disabled. This will be the case when using the Redmatrix - // fork of Zap; which disables ActivityPub connectivity by default. - - if (ActivityStreams::is_as_request()) { - - // Somebody may attempt an ActivityStreams fetch on one of our message permalinks - // Make it do the right thing. - - $mid = ((x($_REQUEST,'mid')) ? $_REQUEST['mid'] : ''); - $mid = unpack_link_id($mid); - - if ($mid) { - $obj = null; - if (strpos($mid, z_root() . '/item/') === 0) { - App::$argc = 2; - App::$argv = [ 'item', basename($mid) ]; - $obj = new Item(); - } - if (strpos($mid, z_root() . '/activity/') === 0) { - App::$argc = 2; - App::$argv = [ 'activity', basename($mid) ]; - $obj = new Activity(); - } - if ($obj) { - $obj->init(); - } - } - if (intval($channel['channel_system'])) { - goaway(z_root()); - } - as_return_and_die(Activity::encode_person($channel,true,true),$channel); - } - - // handle zot6 channel discovery - - if(Libzot::is_zot_request()) { - - $sigdata = HTTPSig::verify(file_get_contents('php://input'), EMPTY_STR, 'zot6'); - - if($sigdata && $sigdata['signer'] && $sigdata['header_valid']) { - $data = json_encode(Libzot::zotinfo([ 'guid_hash' => $channel['channel_hash'], 'target_url' => $sigdata['signer'] ])); - $s = q("select site_crypto, hubloc_sitekey from site left join hubloc on hubloc_url = site_url where hubloc_id_url = '%s' and hubloc_network in ('nomad','zot6') limit 1", - dbesc($sigdata['signer']) - ); - - if($s && $s[0]['hubloc_sitekey'] && $s[0]['site_crypto']) { - $data = json_encode(Crypto::encapsulate($data,$s[0]['hubloc_sitekey'],Libzot::best_algorithm($s[0]['site_crypto']))); - } - } - else { - $data = json_encode(Libzot::zotinfo([ 'guid_hash' => $channel['channel_hash'] ])); - } - - $headers = [ - 'Content-Type' => 'application/x-nomad+json', - 'Digest' => HTTPSig::generate_digest_header($data), - '(request-target)' => strtolower($_SERVER['REQUEST_METHOD']) . ' ' . $_SERVER['REQUEST_URI'] - ]; - $h = HTTPSig::create_sig($headers,$channel['channel_prvkey'],channel_url($channel)); - HTTPSig::set_headers($h); - echo $data; - killme(); - } + head_add_link([ + 'rel' => 'alternate', + 'type' => 'application/atom+xml', + 'title' => t('Only posts'), + 'href' => z_root() . '/feed/' . $which + ]); - // Run Libprofile::load() here to make sure the theme is set before - // we start loading content + // An ActivityStreams actor record is more or less required for ActivityStreams compliance + // unless the actor object is inlined into every activity/object. This implies that it + // is more or less required for the Zot6 protocol, which uses ActivityStreams as a content + // serialisation and which doesn't always include the full actor record with every + // activity/object. - Libprofile::load($which,$profile); + // "more or less" means it isn't spelled out in the ActivityStreams spec, but a number of + // things will break in subtle ways if it isn't provided. - if (! $_REQUEST['mid']) { + // The ActivityPub protocol requires an 'inbox', which will not be present in this record + // if/when the ActivityPub protocol is disabled. This will be the case when using the Redmatrix + // fork of Zap; which disables ActivityPub connectivity by default. - App::$meta->set('og:title', $channel['channel_name']); - App::$meta->set('og:image', $channel['xchan_photo_l']); - App::$meta->set('og:type','webpage'); - App::$meta->set('og:url:secure_url', channel_url($channel)); - if(App::$profile['about'] && perm_is_allowed($channel['channel_id'],get_observer_hash(),'view_profile')) { - App::$meta->set('og:description', App::$profile['about']); - } - else { - App::$meta->set('og:description', sprintf( t('This is the home page of %s.'), $channel['channel_name'])); - } - } - } + if (ActivityStreams::is_as_request()) { + // Somebody may attempt an ActivityStreams fetch on one of our message permalinks + // Make it do the right thing. - function get() { + $mid = ((x($_REQUEST, 'mid')) ? $_REQUEST['mid'] : ''); + $mid = unpack_link_id($mid); - $noscript_content = get_config('system', 'noscript_content', '1'); + if ($mid) { + $obj = null; + if (strpos($mid, z_root() . '/item/') === 0) { + App::$argc = 2; + App::$argv = ['item', basename($mid)]; + $obj = new Item(); + } + if (strpos($mid, z_root() . '/activity/') === 0) { + App::$argc = 2; + App::$argv = ['activity', basename($mid)]; + $obj = new Activity(); + } + if ($obj) { + $obj->init(); + } + } + if (intval($channel['channel_system'])) { + goaway(z_root()); + } + as_return_and_die(Activity::encode_person($channel, true, true), $channel); + } - $category = $datequery = $datequery2 = ''; + // handle zot6 channel discovery - $mid = ((x($_REQUEST,'mid')) ? $_REQUEST['mid'] : ''); - $mid = unpack_link_id($mid); + if (Libzot::is_zot_request()) { + $sigdata = HTTPSig::verify(file_get_contents('php://input'), EMPTY_STR, 'zot6'); - $datequery = ((x($_GET,'dend') && is_a_date_arg($_GET['dend'])) ? notags($_GET['dend']) : ''); - $datequery2 = ((x($_GET,'dbegin') && is_a_date_arg($_GET['dbegin'])) ? notags($_GET['dbegin']) : ''); + if ($sigdata && $sigdata['signer'] && $sigdata['header_valid']) { + $data = json_encode(Libzot::zotinfo(['guid_hash' => $channel['channel_hash'], 'target_url' => $sigdata['signer']])); + $s = q( + "select site_crypto, hubloc_sitekey from site left join hubloc on hubloc_url = site_url where hubloc_id_url = '%s' and hubloc_network in ('nomad','zot6') limit 1", + dbesc($sigdata['signer']) + ); - if(observer_prohibited(true)) { - return login(); - } + if ($s && $s[0]['hubloc_sitekey'] && $s[0]['site_crypto']) { + $data = json_encode(Crypto::encapsulate($data, $s[0]['hubloc_sitekey'], Libzot::best_algorithm($s[0]['site_crypto']))); + } + } else { + $data = json_encode(Libzot::zotinfo(['guid_hash' => $channel['channel_hash']])); + } - $category = ((x($_REQUEST,'cat')) ? $_REQUEST['cat'] : ''); - $hashtags = ((x($_REQUEST,'tag')) ? $_REQUEST['tag'] : ''); - $order = ((x($_GET,'order')) ? notags($_GET['order']) : 'post'); - $static = ((array_key_exists('static',$_REQUEST)) ? intval($_REQUEST['static']) : 0); - $search = ((x($_GET,'search')) ? $_GET['search'] : EMPTY_STR); - - $groups = []; - - $o = ''; - - if($this->updating) { - // Ensure we've got a profile owner if updating. - App::$profile['profile_uid'] = App::$profile_uid = $this->profile_uid; - } - - $is_owner = (((local_channel()) && (App::$profile['profile_uid'] == local_channel())) ? true : false); - - $channel = App::get_channel(); - $observer = App::get_observer(); - $ob_hash = (($observer) ? $observer['xchan_hash'] : ''); - - $perms = get_all_perms(App::$profile['profile_uid'],$ob_hash); + $headers = [ + 'Content-Type' => 'application/x-nomad+json', + 'Digest' => HTTPSig::generate_digest_header($data), + '(request-target)' => strtolower($_SERVER['REQUEST_METHOD']) . ' ' . $_SERVER['REQUEST_URI'] + ]; + $h = HTTPSig::create_sig($headers, $channel['channel_prvkey'], channel_url($channel)); + HTTPSig::set_headers($h); + echo $data; + killme(); + } - if ($this->loading && ! $mid) { + // Run Libprofile::load() here to make sure the theme is set before + // we start loading content - $_SESSION['loadtime_channel'] = datetime_convert(); - if ($is_owner) { - PConfig::Set(local_channel(),'system','loadtime_channel',$_SESSION['loadtime_channel']); - } - } + Libprofile::load($which, $profile); + + if (!$_REQUEST['mid']) { + App::$meta->set('og:title', $channel['channel_name']); + App::$meta->set('og:image', $channel['xchan_photo_l']); + App::$meta->set('og:type', 'webpage'); + App::$meta->set('og:url:secure_url', channel_url($channel)); + if (App::$profile['about'] && perm_is_allowed($channel['channel_id'], get_observer_hash(), 'view_profile')) { + App::$meta->set('og:description', App::$profile['about']); + } else { + App::$meta->set('og:description', sprintf(t('This is the home page of %s.'), $channel['channel_name'])); + } + } + } + + public function get() + { + + $noscript_content = get_config('system', 'noscript_content', '1'); + + $category = $datequery = $datequery2 = ''; + + $mid = ((x($_REQUEST, 'mid')) ? $_REQUEST['mid'] : ''); + $mid = unpack_link_id($mid); + + $datequery = ((x($_GET, 'dend') && is_a_date_arg($_GET['dend'])) ? notags($_GET['dend']) : ''); + $datequery2 = ((x($_GET, 'dbegin') && is_a_date_arg($_GET['dbegin'])) ? notags($_GET['dbegin']) : ''); + + if (observer_prohibited(true)) { + return login(); + } + + $category = ((x($_REQUEST, 'cat')) ? $_REQUEST['cat'] : ''); + $hashtags = ((x($_REQUEST, 'tag')) ? $_REQUEST['tag'] : ''); + $order = ((x($_GET, 'order')) ? notags($_GET['order']) : 'post'); + $static = ((array_key_exists('static', $_REQUEST)) ? intval($_REQUEST['static']) : 0); + $search = ((x($_GET, 'search')) ? $_GET['search'] : EMPTY_STR); + + $groups = []; + + $o = ''; + + if ($this->updating) { + // Ensure we've got a profile owner if updating. + App::$profile['profile_uid'] = App::$profile_uid = $this->profile_uid; + } + + $is_owner = (((local_channel()) && (App::$profile['profile_uid'] == local_channel())) ? true : false); + + $channel = App::get_channel(); + $observer = App::get_observer(); + $ob_hash = (($observer) ? $observer['xchan_hash'] : ''); + + $perms = get_all_perms(App::$profile['profile_uid'], $ob_hash); - - if(! $perms['view_stream']) { - // We may want to make the target of this redirect configurable - if($perms['view_profile']) { - notice( t('Insufficient permissions. Request redirected to profile page.') . EOL); - goaway (z_root() . "/profile/" . App::$profile['channel_address']); - } - notice( t('Permission denied.') . EOL); - return; - } + if ($this->loading && !$mid) { + $_SESSION['loadtime_channel'] = datetime_convert(); + if ($is_owner) { + PConfig::Set(local_channel(), 'system', 'loadtime_channel', $_SESSION['loadtime_channel']); + } + } - if(! $this->updating) { - - nav_set_selected('Channel Home'); - - $static = channel_manual_conv_update(App::$profile['profile_uid']); - - // search terms header - if($search) { - $o .= replace_macros(get_markup_template("section_title.tpl"),array( - '$title' => t('Search Results For:') . ' ' . htmlspecialchars($search, ENT_COMPAT,'UTF-8') - )); - } - - $role = get_pconfig(App::$profile['profile_uid'],'system','permissions_role'); - if ($role === 'social_restricted' && (! $ob_hash)) { - // provide warning that content is probably hidden ? - } - - if($channel && $is_owner) { - $channel_acl = array( - 'allow_cid' => $channel['channel_allow_cid'], - 'allow_gid' => $channel['channel_allow_gid'], - 'deny_cid' => $channel['channel_deny_cid'], - 'deny_gid' => $channel['channel_deny_gid'] - ); - } - else { - $channel_acl = [ 'allow_cid' => '', 'allow_gid' => '', 'deny_cid' => '', 'deny_gid' => '' ]; - } + if (!$perms['view_stream']) { + // We may want to make the target of this redirect configurable + if ($perms['view_profile']) { + notice(t('Insufficient permissions. Request redirected to profile page.') . EOL); + goaway(z_root() . "/profile/" . App::$profile['channel_address']); + } + notice(t('Permission denied.') . EOL); + return; + } - if($perms['post_wall']) { + if (!$this->updating) { + nav_set_selected('Channel Home'); - $x = array( - 'is_owner' => $is_owner, - 'allow_location' => ((($is_owner || $observer) && (intval(get_pconfig(App::$profile['profile_uid'],'system','use_browser_location')))) ? true : false), - 'default_location' => (($is_owner) ? App::$profile['channel_location'] : ''), - 'nickname' => App::$profile['channel_address'], - 'lockstate' => (((strlen(App::$profile['channel_allow_cid'])) || (strlen(App::$profile['channel_allow_gid'])) || (strlen(App::$profile['channel_deny_cid'])) || (strlen(App::$profile['channel_deny_gid']))) ? 'lock' : 'unlock'), - 'acl' => (($is_owner) ? populate_acl($channel_acl,true, PermissionDescription::fromGlobalPermission('view_stream'), get_post_aclDialogDescription(), 'acl_dialog_post') : ''), - 'permissions' => $channel_acl, - 'showacl' => (($is_owner) ? 'yes' : ''), - 'bang' => '', - 'visitor' => (($is_owner || $observer) ? true : false), - 'profile_uid' => App::$profile['profile_uid'], - 'editor_autocomplete' => true, - 'bbco_autocomplete' => 'bbcode', - 'bbcode' => true, - 'jotnets' => true, - 'reset' => t('Reset form') - ); + $static = channel_manual_conv_update(App::$profile['profile_uid']); - $o .= status_editor($x); - } + // search terms header + if ($search) { + $o .= replace_macros(get_markup_template("section_title.tpl"), array( + '$title' => t('Search Results For:') . ' ' . htmlspecialchars($search, ENT_COMPAT, 'UTF-8') + )); + } - if (! $mid && ! $search) { - $obj = new \Zotlabs\Widget\Pinned; - $o .= $obj->widget([]); - } - } + $role = get_pconfig(App::$profile['profile_uid'], 'system', 'permissions_role'); + if ($role === 'social_restricted' && (!$ob_hash)) { + // provide warning that content is probably hidden ? + } + + if ($channel && $is_owner) { + $channel_acl = array( + 'allow_cid' => $channel['channel_allow_cid'], + 'allow_gid' => $channel['channel_allow_gid'], + 'deny_cid' => $channel['channel_deny_cid'], + 'deny_gid' => $channel['channel_deny_gid'] + ); + } else { + $channel_acl = ['allow_cid' => '', 'allow_gid' => '', 'deny_cid' => '', 'deny_gid' => '']; + } - /** - * Get permissions SQL - */ - - $item_normal = " and item.item_hidden = 0 and item.item_type = 0 and item.item_deleted = 0 + if ($perms['post_wall']) { + $x = array( + 'is_owner' => $is_owner, + 'allow_location' => ((($is_owner || $observer) && (intval(get_pconfig(App::$profile['profile_uid'], 'system', 'use_browser_location')))) ? true : false), + 'default_location' => (($is_owner) ? App::$profile['channel_location'] : ''), + 'nickname' => App::$profile['channel_address'], + 'lockstate' => (((strlen(App::$profile['channel_allow_cid'])) || (strlen(App::$profile['channel_allow_gid'])) || (strlen(App::$profile['channel_deny_cid'])) || (strlen(App::$profile['channel_deny_gid']))) ? 'lock' : 'unlock'), + 'acl' => (($is_owner) ? populate_acl($channel_acl, true, PermissionDescription::fromGlobalPermission('view_stream'), get_post_aclDialogDescription(), 'acl_dialog_post') : ''), + 'permissions' => $channel_acl, + 'showacl' => (($is_owner) ? 'yes' : ''), + 'bang' => '', + 'visitor' => (($is_owner || $observer) ? true : false), + 'profile_uid' => App::$profile['profile_uid'], + 'editor_autocomplete' => true, + 'bbco_autocomplete' => 'bbcode', + 'bbcode' => true, + 'jotnets' => true, + 'reset' => t('Reset form') + ); + + $o .= status_editor($x); + } + + if (!$mid && !$search) { + $obj = new Pinned(); + $o .= $obj->widget([]); + } + } + + + /** + * Get permissions SQL + */ + + $item_normal = " and item.item_hidden = 0 and item.item_type = 0 and item.item_deleted = 0 and item.item_unpublished = 0 and item.item_pending_remove = 0 and item.item_blocked = 0 "; - if (! $is_owner) - $item_normal .= "and item.item_delayed = 0 "; - $item_normal_update = item_normal_update(); - $sql_extra = item_permissions_sql(App::$profile['profile_uid']); + if (!$is_owner) { + $item_normal .= "and item.item_delayed = 0 "; + } + $item_normal_update = item_normal_update(); + $sql_extra = item_permissions_sql(App::$profile['profile_uid']); - if (get_pconfig(App::$profile['profile_uid'],'system','channel_list_mode') && (! $mid)) { - $page_mode = 'list'; - } - else { - $page_mode = 'client'; - } - - $abook_uids = " and abook.abook_channel = " . intval(App::$profile['profile_uid']) . " "; + if (get_pconfig(App::$profile['profile_uid'], 'system', 'channel_list_mode') && (!$mid)) { + $page_mode = 'list'; + } else { + $page_mode = 'client'; + } - $simple_update = (($this->updating) ? " AND item_unseen = 1 " : ''); + $abook_uids = " and abook.abook_channel = " . intval(App::$profile['profile_uid']) . " "; - if ($search) { - $search = escape_tags($search); - if(strpos($search,'#') === 0) { - $sql_extra .= term_query('item',substr($search,1),TERM_HASHTAG,TERM_COMMUNITYTAG); - } - else { - $sql_extra .= sprintf(" AND (item.body like '%s' OR item.title like '%s') ", - dbesc(protect_sprintf('%' . $search . '%')), - dbesc(protect_sprintf('%' . $search . '%')) - ); - } - } + $simple_update = (($this->updating) ? " AND item_unseen = 1 " : ''); + + if ($search) { + $search = escape_tags($search); + if (strpos($search, '#') === 0) { + $sql_extra .= term_query('item', substr($search, 1), TERM_HASHTAG, TERM_COMMUNITYTAG); + } else { + $sql_extra .= sprintf( + " AND (item.body like '%s' OR item.title like '%s') ", + dbesc(protect_sprintf('%' . $search . '%')), + dbesc(protect_sprintf('%' . $search . '%')) + ); + } + } - head_add_link([ - 'rel' => 'alternate', - 'type' => 'application/json+oembed', - 'href' => z_root() . '/oep?f=&url=' . urlencode(z_root() . '/' . App::$query_string), - 'title' => 'oembed' - ]); + head_add_link([ + 'rel' => 'alternate', + 'type' => 'application/json+oembed', + 'href' => z_root() . '/oep?f=&url=' . urlencode(z_root() . '/' . App::$query_string), + 'title' => 'oembed' + ]); - if ($this->updating && isset($_SESSION['loadtime_channel'])) { - $simple_update = " AND item.changed > '" . datetime_convert('UTC','UTC',$_SESSION['loadtime_channel']) . "' "; - } - if ($this->loading) { - $simple_update = ''; - } - - if ($static && $simple_update) { - $simple_update .= " and author_xchan = '" . protect_sprintf(get_observer_hash()) . "' "; - } - - if (($this->updating) && (! $this->loading)) { - if ($mid) { - $r = q("SELECT parent AS item_id from item where mid like '%s' and uid = %d $item_normal_update + if ($this->updating && isset($_SESSION['loadtime_channel'])) { + $simple_update = " AND item.changed > '" . datetime_convert('UTC', 'UTC', $_SESSION['loadtime_channel']) . "' "; + } + if ($this->loading) { + $simple_update = ''; + } + + if ($static && $simple_update) { + $simple_update .= " and author_xchan = '" . protect_sprintf(get_observer_hash()) . "' "; + } + + if (($this->updating) && (!$this->loading)) { + if ($mid) { + $r = q( + "SELECT parent AS item_id from item where mid like '%s' and uid = %d $item_normal_update AND item_wall = 1 $simple_update $sql_extra limit 1", - dbesc($mid . '%'), - intval(App::$profile['profile_uid']) - ); - } - else { - $r = q("SELECT parent AS item_id from item + dbesc($mid . '%'), + intval(App::$profile['profile_uid']) + ); + } else { + $r = q( + "SELECT parent AS item_id from item left join abook on ( item.owner_xchan = abook.abook_xchan $abook_uids ) WHERE uid = %d $item_normal_update AND item_wall = 1 $simple_update AND (abook.abook_blocked = 0 or abook.abook_flags is null) $sql_extra ORDER BY created DESC", - intval(App::$profile['profile_uid']) - ); - } - } - else { + intval(App::$profile['profile_uid']) + ); + } + } else { + if (x($category)) { + $sql_extra2 .= protect_sprintf(term_item_parent_query(App::$profile['profile_uid'], 'item', $category, TERM_CATEGORY)); + } + if (x($hashtags)) { + $sql_extra2 .= protect_sprintf(term_item_parent_query(App::$profile['profile_uid'], 'item', $hashtags, TERM_HASHTAG, TERM_COMMUNITYTAG)); + } - if (x($category)) { - $sql_extra2 .= protect_sprintf(term_item_parent_query(App::$profile['profile_uid'],'item', $category, TERM_CATEGORY)); - } - if (x($hashtags)) { - $sql_extra2 .= protect_sprintf(term_item_parent_query(App::$profile['profile_uid'],'item', $hashtags, TERM_HASHTAG, TERM_COMMUNITYTAG)); - } + if ($datequery) { + $sql_extra2 .= protect_sprintf(sprintf(" AND item.created <= '%s' ", dbesc(datetime_convert(date_default_timezone_get(), '', $datequery)))); + $order = 'post'; + } + if ($datequery2) { + $sql_extra2 .= protect_sprintf(sprintf(" AND item.created >= '%s' ", dbesc(datetime_convert(date_default_timezone_get(), '', $datequery2)))); + } - if ($datequery) { - $sql_extra2 .= protect_sprintf(sprintf(" AND item.created <= '%s' ", dbesc(datetime_convert(date_default_timezone_get(),'',$datequery)))); - $order = 'post'; - } - if ($datequery2) { - $sql_extra2 .= protect_sprintf(sprintf(" AND item.created >= '%s' ", dbesc(datetime_convert(date_default_timezone_get(),'',$datequery2)))); - } + if ($datequery || $datequery2) { + $sql_extra2 .= " and item.item_thread_top != 0 "; + } - if ($datequery || $datequery2) { - $sql_extra2 .= " and item.item_thread_top != 0 "; - } + if ($order === 'post') { + $ordering = "created"; + } else { + $ordering = "commented"; + } - if ($order === 'post') { - $ordering = "created"; - } - else { - $ordering = "commented"; - } + $itemspage = get_pconfig(local_channel(), 'system', 'itemspage'); + App::set_pager_itemspage(((intval($itemspage)) ? $itemspage : 20)); + $pager_sql = sprintf(" LIMIT %d OFFSET %d ", intval(App::$pager['itemspage']), intval(App::$pager['start'])); - $itemspage = get_pconfig(local_channel(),'system','itemspage'); - App::set_pager_itemspage(((intval($itemspage)) ? $itemspage : 20)); - $pager_sql = sprintf(" LIMIT %d OFFSET %d ", intval(App::$pager['itemspage']), intval(App::$pager['start'])); - - if ($noscript_content || $this->loading) { - if ($mid) { - $r = q("SELECT parent AS item_id from item where mid like '%s' and uid = %d $item_normal + if ($noscript_content || $this->loading) { + if ($mid) { + $r = q( + "SELECT parent AS item_id from item where mid like '%s' and uid = %d $item_normal AND item_wall = 1 $sql_extra limit 1", - dbesc($mid . '%'), - intval(App::$profile['profile_uid']) - ); - if (! $r) { - notice( t('Permission denied.') . EOL); - } - } - else { - $r = q("SELECT DISTINCT item.parent AS item_id, $ordering FROM item + dbesc($mid . '%'), + intval(App::$profile['profile_uid']) + ); + if (!$r) { + notice(t('Permission denied.') . EOL); + } + } else { + $r = q( + "SELECT DISTINCT item.parent AS item_id, $ordering FROM item left join abook on ( item.author_xchan = abook.abook_xchan $abook_uids ) WHERE true and item.uid = %d $item_normal AND (abook.abook_blocked = 0 or abook.abook_flags is null) AND item.item_wall = 1 AND item.item_thread_top = 1 $sql_extra $sql_extra2 ORDER BY $ordering DESC $pager_sql ", - intval(App::$profile['profile_uid']) - ); - } - } - else { - $r = []; - } - } - if ($r) { + intval(App::$profile['profile_uid']) + ); + } + } else { + $r = []; + } + } + if ($r) { + $parents_str = ids_to_querystr($r, 'item_id'); - $parents_str = ids_to_querystr($r,'item_id'); - - $items = q("SELECT item.*, item.id AS item_id + $items = q( + "SELECT item.*, item.id AS item_id FROM item WHERE item.uid = %d $item_normal AND item.parent IN ( %s ) $sql_extra ", - intval(App::$profile['profile_uid']), - dbesc($parents_str) - ); + intval(App::$profile['profile_uid']), + dbesc($parents_str) + ); - xchan_query($items); - $items = fetch_post_tags($items, true); - $items = conv_sort($items,$ordering); + xchan_query($items); + $items = fetch_post_tags($items, true); + $items = conv_sort($items, $ordering); - if ($this->loading && $mid && (! count($items))) { - // This will happen if we don't have sufficient permissions - // to view the parent item (or the item itself if it is toplevel) - notice( t('Permission denied.') . EOL); - } + if ($this->loading && $mid && (!count($items))) { + // This will happen if we don't have sufficient permissions + // to view the parent item (or the item itself if it is toplevel) + notice(t('Permission denied.') . EOL); + } + } else { + $items = []; + } - } - else { - $items = []; - } + if ((!$this->updating) && (!$this->loading)) { + // This is ugly, but we can't pass the profile_uid through the session to the ajax updater, + // because browser prefetching might change it on us. We have to deliver it with the page. - if ((! $this->updating) && (! $this->loading)) { + $maxheight = get_pconfig(App::$profile['profile_uid'], 'system', 'channel_divmore_height'); + if (!$maxheight) { + $maxheight = 400; + } - // This is ugly, but we can't pass the profile_uid through the session to the ajax updater, - // because browser prefetching might change it on us. We have to deliver it with the page. + $o .= '
        ' . "\r\n"; + $o .= "\r\n"; - $maxheight = get_pconfig(App::$profile['profile_uid'],'system','channel_divmore_height'); - if(! $maxheight) - $maxheight = 400; + App::$page['htmlhead'] .= replace_macros(get_markup_template("build_query.tpl"), array( + '$baseurl' => z_root(), + '$pgtype' => 'channel', + '$uid' => ((App::$profile['profile_uid']) ? App::$profile['profile_uid'] : '0'), + '$gid' => '0', + '$cid' => '0', + '$cmin' => '(-1)', + '$cmax' => '(-1)', + '$star' => '0', + '$liked' => '0', + '$conv' => '0', + '$spam' => '0', + '$nouveau' => '0', + '$wall' => '1', + '$draft' => '0', + '$fh' => '0', + '$dm' => '0', + '$static' => $static, + '$page' => ((App::$pager['page'] != 1) ? App::$pager['page'] : 1), + '$search' => $search, + '$xchan' => '', + '$order' => (($order) ? urlencode($order) : ''), + '$list' => ((x($_REQUEST, 'list')) ? intval($_REQUEST['list']) : 0), + '$file' => '', + '$cats' => (($category) ? urlencode($category) : ''), + '$tags' => (($hashtags) ? urlencode($hashtags) : ''), + '$mid' => (($mid) ? urlencode($mid) : ''), + '$verb' => '', + '$net' => '', + '$dend' => $datequery, + '$dbegin' => $datequery2 + )); + } - $o .= '
        ' . "\r\n"; - $o .= "\r\n"; + $update_unseen = ''; - App::$page['htmlhead'] .= replace_macros(get_markup_template("build_query.tpl"),array( - '$baseurl' => z_root(), - '$pgtype' => 'channel', - '$uid' => ((App::$profile['profile_uid']) ? App::$profile['profile_uid'] : '0'), - '$gid' => '0', - '$cid' => '0', - '$cmin' => '(-1)', - '$cmax' => '(-1)', - '$star' => '0', - '$liked' => '0', - '$conv' => '0', - '$spam' => '0', - '$nouveau' => '0', - '$wall' => '1', - '$draft' => '0', - '$fh' => '0', - '$dm' => '0', - '$static' => $static, - '$page' => ((App::$pager['page'] != 1) ? App::$pager['page'] : 1), - '$search' => $search, - '$xchan' => '', - '$order' => (($order) ? urlencode($order) : ''), - '$list' => ((x($_REQUEST,'list')) ? intval($_REQUEST['list']) : 0), - '$file' => '', - '$cats' => (($category) ? urlencode($category) : ''), - '$tags' => (($hashtags) ? urlencode($hashtags) : ''), - '$mid' => (($mid) ? urlencode($mid) : ''), - '$verb' => '', - '$net' => '', - '$dend' => $datequery, - '$dbegin' => $datequery2 - )); + if ($page_mode === 'list') { - } + /** + * in "list mode", only mark the parent item and any like activities as "seen". + * We won't distinguish between comment likes and post likes. The important thing + * is that the number of unseen comments will be accurate. The SQL to separate the + * comment likes could also get somewhat hairy. + */ - $update_unseen = ''; + if (isset($parents_str) && $parents_str) { + $update_unseen = " AND ( id IN ( " . dbesc($parents_str) . " )"; + $update_unseen .= " OR ( parent IN ( " . dbesc($parents_str) . " ) AND verb in ( '" . dbesc(ACTIVITY_LIKE) . "','" . dbesc(ACTIVITY_DISLIKE) . "' ))) "; + } + } else { + if (isset($parents_str) && $parents_str) { + $update_unseen = " AND parent IN ( " . dbesc($parents_str) . " )"; + } + } - if ($page_mode === 'list') { + if ($is_owner && $update_unseen && (!$_SESSION['sudo'])) { + $x = ['channel_id' => local_channel(), 'update' => 'unset']; + call_hooks('update_unseen', $x); + if ($x['update'] === 'unset' || intval($x['update'])) { + $r = q( + "UPDATE item SET item_unseen = 0 where item_unseen = 1 and item_wall = 1 AND uid = %d $update_unseen", + intval(local_channel()) + ); + } - /** - * in "list mode", only mark the parent item and any like activities as "seen". - * We won't distinguish between comment likes and post likes. The important thing - * is that the number of unseen comments will be accurate. The SQL to separate the - * comment likes could also get somewhat hairy. - */ + $ids = ids_to_array($items, 'item_id'); + $seen = PConfig::Get(local_channel(), 'system', 'seen_items'); + if (!$seen) { + $seen = []; + } + $seen = array_merge($ids, $seen); + PConfig::Set(local_channel(), 'system', 'seen_items', $seen); + } - if (isset($parents_str) && $parents_str) { - $update_unseen = " AND ( id IN ( " . dbesc($parents_str) . " )"; - $update_unseen .= " OR ( parent IN ( " . dbesc($parents_str) . " ) AND verb in ( '" . dbesc(ACTIVITY_LIKE) . "','" . dbesc(ACTIVITY_DISLIKE) . "' ))) "; - } - } - else { - if (isset($parents_str) && $parents_str) { - $update_unseen = " AND parent IN ( " . dbesc($parents_str) . " )"; - } - } + $mode = (($search) ? 'search' : 'channel'); - if ($is_owner && $update_unseen && (! $_SESSION['sudo'])) { - $x = [ 'channel_id' => local_channel(), 'update' => 'unset' ]; - call_hooks('update_unseen',$x); - if ($x['update'] === 'unset' || intval($x['update'])) { - $r = q("UPDATE item SET item_unseen = 0 where item_unseen = 1 and item_wall = 1 AND uid = %d $update_unseen", - intval(local_channel()) - ); - } + if ($this->updating) { + $o .= conversation($items, $mode, $this->updating, $page_mode); + } else { + $o .= ''; - $ids = ids_to_array($items,'item_id'); - $seen = PConfig::Get(local_channel(),'system','seen_items'); - if (! $seen) { - $seen = []; - } - $seen = array_merge($ids,$seen); - PConfig::Set(local_channel(),'system','seen_items',$seen); - } + $o .= conversation($items, $mode, $this->updating, $page_mode); - $mode = (($search) ? 'search' : 'channel'); + if ($mid && $items[0]['title']) { + App::$page['title'] = $items[0]['title'] . " - " . App::$page['title']; + } + } - if($this->updating) { - $o .= conversation($items,$mode,$this->updating,$page_mode); - } - else { + // We reset $channel so that info can be obtained for unlogged visitors + $channel = channelx_by_n(App::$profile['profile_uid']); - $o .= ''; + if (isset($_REQUEST['mid']) && $_REQUEST['mid']) { + if (preg_match("/\[[zi]mg(.*?)\]([^\[]+)/is", $items[0]['body'], $matches)) { + $ogimage = $matches[2]; + // Will we use og:image:type someday? We keep this just in case + // $ogimagetype = guess_image_type($ogimage); + } - $o .= conversation($items,$mode,$this->updating,$page_mode); + // some work on post content to generate a description + // almost fully based on work done on Hubzilla by Max Kostikov + $ogdesc = $items[0]['body']; - if ($mid && $items[0]['title']) - App::$page['title'] = $items[0]['title'] . " - " . App::$page['title']; + $ogdesc = str_replace("#^[", "[", $ogdesc); - } + $ogdesc = bbcode($ogdesc, ['tryoembed' => false]); + $ogdesc = trim(html2plain($ogdesc, 0, true)); + $ogdesc = html_entity_decode($ogdesc, ENT_QUOTES, 'UTF-8'); - // We reset $channel so that info can be obtained for unlogged visitors - $channel = channelx_by_n(App::$profile['profile_uid']); + // remove all URLs + $ogdesc = preg_replace("/https?\:\/\/[a-zA-Z0-9\:\/\-\?\&\;\.\=\_\~\#\%\$\!\+\,\@]+/", "", $ogdesc); - if (isset($_REQUEST['mid']) && $_REQUEST['mid']) { + // shorten description + $ogdesc = substr($ogdesc, 0, 300); + $ogdesc = str_replace("\n", " ", $ogdesc); + while (strpos($ogdesc, " ") !== false) { + $ogdesc = str_replace(" ", " ", $ogdesc); + } + $ogdesc = (strlen($ogdesc) < 298 ? $ogdesc : rtrim(substr($ogdesc, 0, strrpos($ogdesc, " ")), "?.,:;!-") . "..."); - if(preg_match("/\[[zi]mg(.*?)\]([^\[]+)/is", $items[0]['body'], $matches)) { - $ogimage = $matches[2]; - // Will we use og:image:type someday? We keep this just in case - // $ogimagetype = guess_image_type($ogimage); - } + // we can now start loading content - // some work on post content to generate a description - // almost fully based on work done on Hubzilla by Max Kostikov - $ogdesc = $items[0]['body']; + App::$meta->set('og:title', ($items[0]['title'] ? $items[0]['title'] : $channel['channel_name'])); + App::$meta->set('og:image', ($ogimage ? $ogimage : $channel['xchan_photo_l'])); + App::$meta->set('og:type', 'article'); + App::$meta->set('og:url:secure_url', channel_url($channel)); + App::$meta->set('og:description', ($ogdesc ? $ogdesc : sprintf(t('This post was published on the home page of %s.'), $channel['channel_name']))); + } - $ogdesc = str_replace("#^[", "[", $ogdesc); + if ($mid) { + $o .= '
        '; + } - $ogdesc = bbcode($ogdesc, [ 'tryoembed' => false ]); - $ogdesc = trim(html2plain($ogdesc, 0, true)); - $ogdesc = html_entity_decode($ogdesc, ENT_QUOTES, 'UTF-8'); - - // remove all URLs - $ogdesc = preg_replace("/https?\:\/\/[a-zA-Z0-9\:\/\-\?\&\;\.\=\_\~\#\%\$\!\+\,\@]+/", "", $ogdesc); - - // shorten description - $ogdesc = substr($ogdesc, 0, 300); - $ogdesc = str_replace("\n", " ", $ogdesc); - while (strpos($ogdesc, " ") !== false) - $ogdesc = str_replace(" ", " ", $ogdesc); - $ogdesc = (strlen($ogdesc) < 298 ? $ogdesc : rtrim(substr($ogdesc, 0, strrpos($ogdesc, " ")), "?.,:;!-") . "..."); - - // we can now start loading content - - App::$meta->set('og:title', ($items[0]['title'] ? $items[0]['title'] : $channel['channel_name'])); - App::$meta->set('og:image', ($ogimage ? $ogimage : $channel['xchan_photo_l'])); - App::$meta->set('og:type', 'article'); - App::$meta->set('og:url:secure_url', channel_url($channel)); - App::$meta->set('og:description', ($ogdesc ? $ogdesc : sprintf( t('This post was published on the home page of %s.'), $channel['channel_name']))); - } - - if ($mid) { - $o .= '
        '; - } - - return $o; - } + return $o; + } } diff --git a/Zotlabs/Module/Chanview.php b/Zotlabs/Module/Chanview.php index f5eca7db9..5e1b6722e 100644 --- a/Zotlabs/Module/Chanview.php +++ b/Zotlabs/Module/Chanview.php @@ -1,4 +1,5 @@ $url, - '$photo' => get_xconfig(App::$poi['xchan_hash'],'system','cover_photo'), - '$alt' => t('Cover photo for this channel'), - '$about' => $about, - '$followers_txt' => t('Followers'), - '$following_txt' => t('Following'), - '$followers' => $followers, - '$following' => $following, - '$visit' => t('Visit'), - '$full' => t('toggle full screen mode') - ]); - - return $o; - } - } - + $followers = t('Not available'); + $following = t('Not available'); + + $f = get_xconfig(App::$poi['xchan_hash'], 'activitypub', 'collections'); + if ($f && isset($f['followers'])) { + $m = Activity::fetch($f['followers']); + if (is_array($m) && isset($m['totalItems'])) { + $followers = intval($m['totalItems']); + } + } + if ($f && isset($f['following'])) { + $m = Activity::fetch($f['following']); + if (is_array($m) && isset($m['totalItems'])) { + $following = intval($m['totalItems']); + } + } + + $o = replace_macros(get_markup_template('chanview.tpl'), [ + '$url' => $url, + '$photo' => get_xconfig(App::$poi['xchan_hash'], 'system', 'cover_photo'), + '$alt' => t('Cover photo for this channel'), + '$about' => $about, + '$followers_txt' => t('Followers'), + '$following_txt' => t('Following'), + '$followers' => $followers, + '$following' => $following, + '$visit' => t('Visit'), + '$full' => t('toggle full screen mode') + ]); + + return $o; + } + } } diff --git a/Zotlabs/Module/Chat.php b/Zotlabs/Module/Chat.php index 22ed3958b..3cdcb12cd 100644 --- a/Zotlabs/Module/Chat.php +++ b/Zotlabs/Module/Chat.php @@ -1,4 +1,6 @@ - 1) ? argv(1) : null); - if (local_channel() && (! $which)) { - $channel = App::get_channel(); - if ($channel && $channel['channel_address']) { - $which = $channel['channel_address']; - } - } + public function init() + { - if (! $which) { - notice( t('You must be logged in to see this page.') . EOL ); - return; - } - - $profile = 0; - - // Run Libprofile::load() here to make sure the theme is set before - // we start loading content - - Libprofile::load($which,$profile); - - } - - function post() { - - if ($_POST['room_name']) { - $room = strip_tags(trim($_POST['room_name'])); - } - - if ((! $room) || (! local_channel())) { - return; - } - - $channel = App::get_channel(); - - if ($_POST['action'] === 'drop') { - logger('delete chatroom'); - Chatroom::destroy($channel, [ 'cr_name' => $room ] ); - goaway(z_root() . '/chat/' . $channel['channel_address']); - } - - $acl = new AccessControl($channel); - $acl->set_from_array($_REQUEST); - - $arr = $acl->get(); - $arr['name'] = $room; - $arr['expire'] = intval($_POST['chat_expire']); - if (intval($arr['expire']) < 0) { - $arr['expire'] = 0; - } - - Chatroom::create($channel,$arr); - - $x = q("select * from chatroom where cr_name = '%s' and cr_uid = %d limit 1", - dbesc($room), - intval(local_channel()) - ); - - Libsync::build_sync_packet(0, array('chatroom' => $x)); - - if ($x) { - goaway(z_root() . '/chat/' . $channel['channel_address'] . '/' . $x[0]['cr_id']); - } - - // that failed. Try again perhaps? - - goaway(z_root() . '/chat/' . $channel['channel_address'] . '/new'); - - - } - - - function get() { + $which = ((argc() > 1) ? argv(1) : null); + if (local_channel() && (!$which)) { + $channel = App::get_channel(); + if ($channel && $channel['channel_address']) { + $which = $channel['channel_address']; + } + } -// if(! Apps::system_app_installed(App::$profile_uid, 'Chatrooms')) { -// // Do not display any associated widgets at this point -// App::$pdl = ''; + if (!$which) { + notice(t('You must be logged in to see this page.') . EOL); + return; + } + + $profile = 0; + + // Run Libprofile::load() here to make sure the theme is set before + // we start loading content + + Libprofile::load($which, $profile); + } + + public function post() + { + + if ($_POST['room_name']) { + $room = strip_tags(trim($_POST['room_name'])); + } + + if ((!$room) || (!local_channel())) { + return; + } + + $channel = App::get_channel(); + + if ($_POST['action'] === 'drop') { + logger('delete chatroom'); + Chatroom::destroy($channel, ['cr_name' => $room]); + goaway(z_root() . '/chat/' . $channel['channel_address']); + } + + $acl = new AccessControl($channel); + $acl->set_from_array($_REQUEST); + + $arr = $acl->get(); + $arr['name'] = $room; + $arr['expire'] = intval($_POST['chat_expire']); + if (intval($arr['expire']) < 0) { + $arr['expire'] = 0; + } + + Chatroom::create($channel, $arr); + + $x = q( + "select * from chatroom where cr_name = '%s' and cr_uid = %d limit 1", + dbesc($room), + intval(local_channel()) + ); + + Libsync::build_sync_packet(0, array('chatroom' => $x)); + + if ($x) { + goaway(z_root() . '/chat/' . $channel['channel_address'] . '/' . $x[0]['cr_id']); + } + + // that failed. Try again perhaps? + + goaway(z_root() . '/chat/' . $channel['channel_address'] . '/new'); + } + + + public function get() + { + +// if(! Apps::system_app_installed(App::$profile_uid, 'Chatrooms')) { +// // Do not display any associated widgets at this point +// App::$pdl = ''; // -// $o = 'Chatrooms App (Not Installed):
        '; -// $o .= t('Access Controlled Chatrooms'); -// return $o; -// } - - if (local_channel()) { - $channel = App::get_channel(); - nav_set_selected('Chatrooms'); - } +// $o = 'Chatrooms App (Not Installed):
        '; +// $o .= t('Access Controlled Chatrooms'); +// return $o; +// } - $ob = App::get_observer(); - $observer = get_observer_hash(); - if (! $observer) { - notice( t('Permission denied.') . EOL); - return; - } - - if (! perm_is_allowed(App::$profile['profile_uid'],$observer,'chat')) { - notice( t('Permission denied.') . EOL); - return; - } - - if ((argc() > 3) && intval(argv(2)) && (argv(3) === 'leave')) { - Chatroom::leave($observer,argv(2),$_SERVER['REMOTE_ADDR']); - goaway(z_root() . '/channel/' . argv(1)); - } - - - if ((argc() > 3) && intval(argv(2)) && (argv(3) === 'status')) { - $ret = [ 'success' => false ]; - $room_id = intval(argv(2)); - if (! $room_id || ! $observer) { - return; - } - - $r = q("select * from chatroom where cr_id = %d limit 1", - intval($room_id) - ); - if (! $r) { - json_return_and_die($ret); - } - require_once('include/security.php'); - $sql_extra = permissions_sql($r[0]['cr_uid']); - - $x = q("select * from chatroom where cr_id = %d and cr_uid = %d $sql_extra limit 1", - intval($room_id), - intval($r[0]['cr_uid']) - ); - if (! $x) { - json_return_and_die($ret); - } - $y = q("select count(*) as total from chatpresence where cp_room = %d", - intval($room_id) - ); - if ($y) { - $ret['success'] = true; - $ret['chatroom'] = $r[0]['cr_name']; - $ret['inroom'] = $y[0]['total']; - } - - // figure out how to present a timestamp of the last activity, since we don't know the observer's timezone. - - $z = q("select created from chat where chat_room = %d order by created desc limit 1", - intval($room_id) - ); - if ($z) { - $ret['last'] = $z[0]['created']; - } - json_return_and_die($ret); - } - - - if (argc() > 2 && intval(argv(2))) { - - $room_id = intval(argv(2)); - - $x = Chatroom::enter($observer,$room_id,'online',$_SERVER['REMOTE_ADDR']); - if (! $x) { - return; - } - $x = q("select * from chatroom where cr_id = %d and cr_uid = %d $sql_extra limit 1", - intval($room_id), - intval(App::$profile['profile_uid']) - ); - - if ($x) { - $acl = new AccessControl(false); - $acl->set($x[0]); - - $private = $acl->is_private(); - $room_name = $x[0]['cr_name']; - } - else { - notice( t('Room not found') . EOL); - return; - } + if (local_channel()) { + $channel = App::get_channel(); + nav_set_selected('Chatrooms'); + } - $cipher = get_pconfig(local_channel(),'system','default_cipher'); - if (! $cipher) { - $cipher = 'AES-128-CCM'; - } + $ob = App::get_observer(); + $observer = get_observer_hash(); + if (!$observer) { + notice(t('Permission denied.') . EOL); + return; + } - - $o = replace_macros(get_markup_template('chat.tpl'), [ - '$is_owner' => ((local_channel() && local_channel() == $x[0]['cr_uid']) ? true : false), - '$room_name' => $room_name, - '$room_id' => $room_id, - '$baseurl' => z_root(), - '$nickname' => argv(1), - '$submit' => t('Submit'), - '$leave' => t('Leave Room'), - '$drop' => t('Delete Room'), - '$away' => t('I am away right now'), - '$online' => t('I am online'), - '$feature_encrypt' => ((Apps::system_app_installed(local_channel(),'Secrets')) ? true : false), - '$cipher' => $cipher, - '$linkurl' => t('Please enter a link URL:'), - '$encrypt' => t('Encrypt text'), - '$insert' => t('Insert web link') - ]); - return $o; - } + if (!perm_is_allowed(App::$profile['profile_uid'], $observer, 'chat')) { + notice(t('Permission denied.') . EOL); + return; + } - require_once('include/conversation.php'); - - $o = ''; + if ((argc() > 3) && intval(argv(2)) && (argv(3) === 'leave')) { + Chatroom::leave($observer, argv(2), $_SERVER['REMOTE_ADDR']); + goaway(z_root() . '/channel/' . argv(1)); + } - $acl = new AccessControl($channel); - $channel_acl = $acl->get(); - $lockstate = (($channel_acl['allow_cid'] || $channel_acl['allow_gid'] || $channel_acl['deny_cid'] || $channel_acl['deny_gid']) ? 'lock' : 'unlock'); - require_once('include/acl_selectors.php'); + if ((argc() > 3) && intval(argv(2)) && (argv(3) === 'status')) { + $ret = ['success' => false]; + $room_id = intval(argv(2)); + if (!$room_id || !$observer) { + return; + } - $chatroom_new = ''; - if (local_channel()) { - $chatroom_new = replace_macros(get_markup_template('chatroom_new.tpl'),array( - '$header' => t('New Chatroom'), - '$name' => array('room_name',t('Chatroom name'),'', ''), - '$chat_expire' => array('chat_expire',t('Expiration of chats (minutes)'),120,''), - '$permissions' => t('Permissions'), - '$acl' => populate_acl($channel_acl,false), - '$allow_cid' => acl2json($channel_acl['allow_cid']), - '$allow_gid' => acl2json($channel_acl['allow_gid']), - '$deny_cid' => acl2json($channel_acl['deny_cid']), - '$deny_gid' => acl2json($channel_acl['deny_gid']), - '$lockstate' => $lockstate, - '$submit' => t('Submit') - - )); - } + $r = q( + "select * from chatroom where cr_id = %d limit 1", + intval($room_id) + ); + if (!$r) { + json_return_and_die($ret); + } + require_once('include/security.php'); + $sql_extra = permissions_sql($r[0]['cr_uid']); - $rooms = Chatroom::roomlist(App::$profile['profile_uid']); - - $o .= replace_macros(get_markup_template('chatrooms.tpl'), [ - '$header' => sprintf( t('%1$s\'s Chatrooms'), App::$profile['fullname']), - '$name' => t('Name'), - '$baseurl' => z_root(), - '$nickname' => App::$profile['channel_address'], - '$rooms' => $rooms, - '$norooms' => t('No chatrooms available'), - '$newroom' => t('Create New'), - '$is_owner' => ((local_channel() && local_channel() == App::$profile['profile_uid']) ? 1 : 0), - '$chatroom_new' => $chatroom_new, - '$expire' => t('Expiration'), - '$expire_unit' => t('min') //minutes - ]); - - return $o; - - } - + $x = q( + "select * from chatroom where cr_id = %d and cr_uid = %d $sql_extra limit 1", + intval($room_id), + intval($r[0]['cr_uid']) + ); + if (!$x) { + json_return_and_die($ret); + } + $y = q( + "select count(*) as total from chatpresence where cp_room = %d", + intval($room_id) + ); + if ($y) { + $ret['success'] = true; + $ret['chatroom'] = $r[0]['cr_name']; + $ret['inroom'] = $y[0]['total']; + } + + // figure out how to present a timestamp of the last activity, since we don't know the observer's timezone. + + $z = q( + "select created from chat where chat_room = %d order by created desc limit 1", + intval($room_id) + ); + if ($z) { + $ret['last'] = $z[0]['created']; + } + json_return_and_die($ret); + } + + + if (argc() > 2 && intval(argv(2))) { + $room_id = intval(argv(2)); + + $x = Chatroom::enter($observer, $room_id, 'online', $_SERVER['REMOTE_ADDR']); + if (!$x) { + return; + } + $x = q( + "select * from chatroom where cr_id = %d and cr_uid = %d $sql_extra limit 1", + intval($room_id), + intval(App::$profile['profile_uid']) + ); + + if ($x) { + $acl = new AccessControl(false); + $acl->set($x[0]); + + $private = $acl->is_private(); + $room_name = $x[0]['cr_name']; + } else { + notice(t('Room not found') . EOL); + return; + } + + $cipher = get_pconfig(local_channel(), 'system', 'default_cipher'); + if (!$cipher) { + $cipher = 'AES-128-CCM'; + } + + + $o = replace_macros(get_markup_template('chat.tpl'), [ + '$is_owner' => ((local_channel() && local_channel() == $x[0]['cr_uid']) ? true : false), + '$room_name' => $room_name, + '$room_id' => $room_id, + '$baseurl' => z_root(), + '$nickname' => argv(1), + '$submit' => t('Submit'), + '$leave' => t('Leave Room'), + '$drop' => t('Delete Room'), + '$away' => t('I am away right now'), + '$online' => t('I am online'), + '$feature_encrypt' => ((Apps::system_app_installed(local_channel(), 'Secrets')) ? true : false), + '$cipher' => $cipher, + '$linkurl' => t('Please enter a link URL:'), + '$encrypt' => t('Encrypt text'), + '$insert' => t('Insert web link') + ]); + return $o; + } + + require_once('include/conversation.php'); + + $o = ''; + + $acl = new AccessControl($channel); + $channel_acl = $acl->get(); + + $lockstate = (($channel_acl['allow_cid'] || $channel_acl['allow_gid'] || $channel_acl['deny_cid'] || $channel_acl['deny_gid']) ? 'lock' : 'unlock'); + require_once('include/acl_selectors.php'); + + $chatroom_new = ''; + if (local_channel()) { + $chatroom_new = replace_macros(get_markup_template('chatroom_new.tpl'), array( + '$header' => t('New Chatroom'), + '$name' => array('room_name', t('Chatroom name'), '', ''), + '$chat_expire' => array('chat_expire', t('Expiration of chats (minutes)'), 120, ''), + '$permissions' => t('Permissions'), + '$acl' => populate_acl($channel_acl, false), + '$allow_cid' => acl2json($channel_acl['allow_cid']), + '$allow_gid' => acl2json($channel_acl['allow_gid']), + '$deny_cid' => acl2json($channel_acl['deny_cid']), + '$deny_gid' => acl2json($channel_acl['deny_gid']), + '$lockstate' => $lockstate, + '$submit' => t('Submit') + + )); + } + + $rooms = Chatroom::roomlist(App::$profile['profile_uid']); + + $o .= replace_macros(get_markup_template('chatrooms.tpl'), [ + '$header' => sprintf(t('%1$s\'s Chatrooms'), App::$profile['fullname']), + '$name' => t('Name'), + '$baseurl' => z_root(), + '$nickname' => App::$profile['channel_address'], + '$rooms' => $rooms, + '$norooms' => t('No chatrooms available'), + '$newroom' => t('Create New'), + '$is_owner' => ((local_channel() && local_channel() == App::$profile['profile_uid']) ? 1 : 0), + '$chatroom_new' => $chatroom_new, + '$expire' => t('Expiration'), + '$expire_unit' => t('min') //minutes + ]); + + return $o; + } } diff --git a/Zotlabs/Module/Chatsvc.php b/Zotlabs/Module/Chatsvc.php index a433eca71..c61870934 100644 --- a/Zotlabs/Module/Chatsvc.php +++ b/Zotlabs/Module/Chatsvc.php @@ -1,184 +1,198 @@ - false); - - \App::$data['chat']['room_id'] = intval($_REQUEST['room_id']); - $x = q("select cr_uid from chatroom where cr_id = %d and cr_id != 0 limit 1", - intval(\App::$data['chat']['room_id']) - ); - if(! $x) - json_return_and_die($ret); - - \App::$data['chat']['uid'] = $x[0]['cr_uid']; - - if(! perm_is_allowed(\App::$data['chat']['uid'],get_observer_hash(),'chat')) { - json_return_and_die($ret); - } - - } - - function post() { - - $ret = array('success' => false); - - $room_id = \App::$data['chat']['room_id']; - $text = escape_tags($_REQUEST['chat_text']); - if(! $text) - return; - - $sql_extra = permissions_sql(\App::$data['chat']['uid']); - - $r = q("select * from chatroom where cr_uid = %d and cr_id = %d $sql_extra", - intval(\App::$data['chat']['uid']), - intval(\App::$data['chat']['room_id']) - ); - if(! $r) - json_return_and_die($ret); - - $arr = array( - 'chat_room' => \App::$data['chat']['room_id'], - 'chat_xchan' => get_observer_hash(), - 'chat_text' => $text - ); - - call_hooks('chat_post',$arr); - - $x = q("insert into chat ( chat_room, chat_xchan, created, chat_text ) + public function init() + { + + //logger('chatsvc'); + + $ret = array('success' => false); + + App::$data['chat']['room_id'] = intval($_REQUEST['room_id']); + $x = q( + "select cr_uid from chatroom where cr_id = %d and cr_id != 0 limit 1", + intval(App::$data['chat']['room_id']) + ); + if (!$x) { + json_return_and_die($ret); + } + + App::$data['chat']['uid'] = $x[0]['cr_uid']; + + if (!perm_is_allowed(App::$data['chat']['uid'], get_observer_hash(), 'chat')) { + json_return_and_die($ret); + } + } + + public function post() + { + + $ret = array('success' => false); + + $room_id = App::$data['chat']['room_id']; + $text = escape_tags($_REQUEST['chat_text']); + if (!$text) { + return; + } + + $sql_extra = permissions_sql(App::$data['chat']['uid']); + + $r = q( + "select * from chatroom where cr_uid = %d and cr_id = %d $sql_extra", + intval(App::$data['chat']['uid']), + intval(App::$data['chat']['room_id']) + ); + if (!$r) { + json_return_and_die($ret); + } + + $arr = array( + 'chat_room' => App::$data['chat']['room_id'], + 'chat_xchan' => get_observer_hash(), + 'chat_text' => $text + ); + + call_hooks('chat_post', $arr); + + $x = q( + "insert into chat ( chat_room, chat_xchan, created, chat_text ) values( %d, '%s', '%s', '%s' )", - intval(\App::$data['chat']['room_id']), - dbesc(get_observer_hash()), - dbesc(datetime_convert()), - dbesc(str_rot47(base64url_encode($arr['chat_text']))) - ); - - $ret['success'] = true; - json_return_and_die($ret); - } - - function get() { - - $status = strip_tags($_REQUEST['status']); - $room_id = intval(\App::$data['chat']['room_id']); - $stopped = ((x($_REQUEST,'stopped') && intval($_REQUEST['stopped'])) ? true : false); - - if($status && $room_id) { - - $x = q("select channel_address from channel where channel_id = %d limit 1", - intval(\App::$data['chat']['uid']) - ); - - $r = q("update chatpresence set cp_status = '%s', cp_last = '%s' where cp_room = %d and cp_xchan = '%s' and cp_client = '%s'", - dbesc($status), - dbesc(datetime_convert()), - intval($room_id), - dbesc(get_observer_hash()), - dbesc($_SERVER['REMOTE_ADDR']) - ); - - goaway(z_root() . '/chat/' . $x[0]['channel_address'] . '/' . $room_id); - } - - if(! $stopped) { - - $lastseen = intval($_REQUEST['last']); - - $ret = array('success' => false); - - $sql_extra = permissions_sql(\App::$data['chat']['uid']); - - $r = q("select * from chatroom where cr_uid = %d and cr_id = %d $sql_extra", - intval(\App::$data['chat']['uid']), - intval(\App::$data['chat']['room_id']) - ); - if(! $r) - json_return_and_die($ret); - - $inroom = []; - - $r = q("select * from chatpresence left join xchan on xchan_hash = cp_xchan where cp_room = %d order by xchan_name", - intval(\App::$data['chat']['room_id']) - ); - if($r) { - 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/png'; - $rv['xchan_photo_l'] = z_root() . '/' . get_default_profile_photo(300); - $rv['xchan_photo_m'] = z_root() . '/' . get_default_profile_photo(80); - $rv['xchan_photo_s'] = z_root() . '/' . get_default_profile_photo(48); + intval(App::$data['chat']['room_id']), + dbesc(get_observer_hash()), + dbesc(datetime_convert()), + dbesc(str_rot47(base64url_encode($arr['chat_text']))) + ); - } + $ret['success'] = true; + json_return_and_die($ret); + } - switch($rv['cp_status']) { - case 'away': - $status = t('Away'); - $status_class = 'away'; - break; - case 'online': - default: - $status = t('Online'); - $status_class = 'online'; - break; - } - - $inroom[] = array('img' => zid($rv['xchan_photo_m']), 'img_type' => $rv['xchan_photo_mimetype'],'name' => $rv['xchan_name'], 'status' => $status, 'status_class' => $status_class); - } - } - - $chats = []; - - $r = q("select * from chat left join xchan on chat_xchan = xchan_hash where chat_room = %d and chat_id > %d order by created", - intval(\App::$data['chat']['room_id']), - intval($lastseen) - ); - if($r) { - foreach($r as $rr) { - $chats[] = array( - 'id' => $rr['chat_id'], - 'img' => zid($rr['xchan_photo_m']), - 'img_type' => $rr['xchan_photo_mimetype'], - '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' => zidify_links(smilies(bbcode(base64url_decode(str_rot47($rr['chat_text']))))), - 'self' => ((get_observer_hash() == $rr['chat_xchan']) ? 'self' : '') - ); - } - } - } - - $r = q("update chatpresence set cp_last = '%s' where cp_room = %d and cp_xchan = '%s' and cp_client = '%s'", - dbesc(datetime_convert()), - intval(\App::$data['chat']['room_id']), - dbesc(get_observer_hash()), - dbesc($_SERVER['REMOTE_ADDR']) - ); - - $ret['success'] = true; - if(! $stopped) { - $ret['inroom'] = $inroom; - $ret['chats'] = $chats; - } - json_return_and_die($ret); - - } - - + public function get() + { + + $status = strip_tags($_REQUEST['status']); + $room_id = intval(App::$data['chat']['room_id']); + $stopped = ((x($_REQUEST, 'stopped') && intval($_REQUEST['stopped'])) ? true : false); + + if ($status && $room_id) { + $x = q( + "select channel_address from channel where channel_id = %d limit 1", + intval(App::$data['chat']['uid']) + ); + + $r = q( + "update chatpresence set cp_status = '%s', cp_last = '%s' where cp_room = %d and cp_xchan = '%s' and cp_client = '%s'", + dbesc($status), + dbesc(datetime_convert()), + intval($room_id), + dbesc(get_observer_hash()), + dbesc($_SERVER['REMOTE_ADDR']) + ); + + goaway(z_root() . '/chat/' . $x[0]['channel_address'] . '/' . $room_id); + } + + if (!$stopped) { + $lastseen = intval($_REQUEST['last']); + + $ret = array('success' => false); + + $sql_extra = permissions_sql(App::$data['chat']['uid']); + + $r = q( + "select * from chatroom where cr_uid = %d and cr_id = %d $sql_extra", + intval(App::$data['chat']['uid']), + intval(App::$data['chat']['room_id']) + ); + if (!$r) { + json_return_and_die($ret); + } + + $inroom = []; + + $r = q( + "select * from chatpresence left join xchan on xchan_hash = cp_xchan where cp_room = %d order by xchan_name", + intval(App::$data['chat']['room_id']) + ); + if ($r) { + 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/png'; + $rv['xchan_photo_l'] = z_root() . '/' . get_default_profile_photo(300); + $rv['xchan_photo_m'] = z_root() . '/' . get_default_profile_photo(80); + $rv['xchan_photo_s'] = z_root() . '/' . get_default_profile_photo(48); + } + + switch ($rv['cp_status']) { + case 'away': + $status = t('Away'); + $status_class = 'away'; + break; + case 'online': + default: + $status = t('Online'); + $status_class = 'online'; + break; + } + + $inroom[] = array('img' => zid($rv['xchan_photo_m']), 'img_type' => $rv['xchan_photo_mimetype'], 'name' => $rv['xchan_name'], 'status' => $status, 'status_class' => $status_class); + } + } + + $chats = []; + + $r = q( + "select * from chat left join xchan on chat_xchan = xchan_hash where chat_room = %d and chat_id > %d order by created", + intval(App::$data['chat']['room_id']), + intval($lastseen) + ); + if ($r) { + foreach ($r as $rr) { + $chats[] = array( + 'id' => $rr['chat_id'], + 'img' => zid($rr['xchan_photo_m']), + 'img_type' => $rr['xchan_photo_mimetype'], + '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' => zidify_links(smilies(bbcode(base64url_decode(str_rot47($rr['chat_text']))))), + 'self' => ((get_observer_hash() == $rr['chat_xchan']) ? 'self' : '') + ); + } + } + } + + $r = q( + "update chatpresence set cp_last = '%s' where cp_room = %d and cp_xchan = '%s' and cp_client = '%s'", + dbesc(datetime_convert()), + intval(App::$data['chat']['room_id']), + dbesc(get_observer_hash()), + dbesc($_SERVER['REMOTE_ADDR']) + ); + + $ret['success'] = true; + if (!$stopped) { + $ret['inroom'] = $inroom; + $ret['chats'] = $chats; + } + json_return_and_die($ret); + } } diff --git a/Zotlabs/Module/Clients.php b/Zotlabs/Module/Clients.php index 7cf8facd4..2be5e034e 100644 --- a/Zotlabs/Module/Clients.php +++ b/Zotlabs/Module/Clients.php @@ -6,18 +6,18 @@ use Zotlabs\Lib\Apps; use Zotlabs\Lib\Libsync; use Zotlabs\Web\Controller; -class Clients extends Controller { +class Clients extends Controller +{ - function get() { + public function get() + { $desc = t('This app allows you to authorize mobile apps using OAuth and OpenID to access your channel.'); - if(! Apps::system_app_installed(local_channel(),'Clients')) { - return ''; - } - goaway(z_root() . '/settings/oauth2'); - } - - + if (!Apps::system_app_installed(local_channel(), 'Clients')) { + return ''; + } + goaway(z_root() . '/settings/oauth2'); + } } diff --git a/Zotlabs/Module/Cloud.php b/Zotlabs/Module/Cloud.php index 3c8411420..5694f43fb 100644 --- a/Zotlabs/Module/Cloud.php +++ b/Zotlabs/Module/Cloud.php @@ -1,5 +1,7 @@ 1) { - $which = argv(1); - } - $profile = 0; + $which = null; + if (argc() > 1) { + $which = argv(1); + } + $profile = 0; - if ($which) { - Libprofile::load( $which, $profile); - } + if ($which) { + Libprofile::load($which, $profile); + } - $auth = new BasicAuth(); + $auth = new BasicAuth(); - $ob_hash = get_observer_hash(); + $ob_hash = get_observer_hash(); - if ($ob_hash) { - if (local_channel()) { - $channel = App::get_channel(); - $auth->setCurrentUser($channel['channel_address']); - $auth->channel_id = $channel['channel_id']; - $auth->channel_hash = $channel['channel_hash']; - $auth->channel_account_id = $channel['channel_account_id']; - if ($channel['channel_timezone']) { - $auth->setTimezone($channel['channel_timezone']); - } - } - $auth->observer = $ob_hash; - } + if ($ob_hash) { + if (local_channel()) { + $channel = App::get_channel(); + $auth->setCurrentUser($channel['channel_address']); + $auth->channel_id = $channel['channel_id']; + $auth->channel_hash = $channel['channel_hash']; + $auth->channel_account_id = $channel['channel_account_id']; + if ($channel['channel_timezone']) { + $auth->setTimezone($channel['channel_timezone']); + } + } + $auth->observer = $ob_hash; + } - // if we arrived at this path with any query parameters in the url, build a clean url without - // them and redirect. + // if we arrived at this path with any query parameters in the url, build a clean url without + // them and redirect. - if (! array_key_exists('cloud_sort',$_SESSION)) { - $_SESSION['cloud_sort'] = 'name'; - } + if (!array_key_exists('cloud_sort', $_SESSION)) { + $_SESSION['cloud_sort'] = 'name'; + } - $_SESSION['cloud_sort'] = (($_REQUEST['sort']) ? trim(notags($_REQUEST['sort'])) : $_SESSION['cloud_sort']); + $_SESSION['cloud_sort'] = (($_REQUEST['sort']) ? trim(notags($_REQUEST['sort'])) : $_SESSION['cloud_sort']); - $x = clean_query_string(); - if ($x !== App::$query_string) { - goaway(z_root() . '/' . $x); - } + $x = clean_query_string(); + if ($x !== App::$query_string) { + goaway(z_root() . '/' . $x); + } - $rootDirectory = new Directory('/', $auth); + $rootDirectory = new Directory('/', $auth); - // A SabreDAV server-object - $server = new SDAV\Server($rootDirectory); - // prevent overwriting changes each other with a lock backend - $lockBackend = new SDAV\Locks\Backend\File('cache/locks'); - $lockPlugin = new SDAV\Locks\Plugin($lockBackend); + // A SabreDAV server-object + $server = new SDAV\Server($rootDirectory); + // prevent overwriting changes each other with a lock backend + $lockBackend = new SDAV\Locks\Backend\File('cache/locks'); + $lockPlugin = new SDAV\Locks\Plugin($lockBackend); - $server->addPlugin($lockPlugin); + $server->addPlugin($lockPlugin); - $is_readable = false; + $is_readable = false; - // provide a directory view for the cloud in Hubzilla - $browser = new \Zotlabs\Storage\Browser($auth); - $auth->setBrowserPlugin($browser); + // provide a directory view for the cloud in Hubzilla + $browser = new Browser($auth); + $auth->setBrowserPlugin($browser); - $server->addPlugin($browser); + $server->addPlugin($browser); - // Experimental QuotaPlugin - // require_once('\Zotlabs\Storage/QuotaPlugin.php'); - // $server->addPlugin(new \Zotlabs\Storage\\QuotaPlugin($auth)); + // Experimental QuotaPlugin + // require_once('\Zotlabs\Storage/QuotaPlugin.php'); + // $server->addPlugin(new \Zotlabs\Storage\\QuotaPlugin($auth)); - // over-ride the default XML output on thrown exceptions + // over-ride the default XML output on thrown exceptions - $server->on('exception', [ $this, 'DAVException' ]); + $server->on('exception', [$this, 'DAVException']); - // All we need to do now, is to fire up the server + // All we need to do now, is to fire up the server - $server->exec(); + $server->exec(); - if ($browser->build_page) { - construct_page(); - } - - killme(); - } + if ($browser->build_page) { + construct_page(); + } + + killme(); + } - function DAVException($err) { - - if ($err instanceof \Sabre\DAV\Exception\NotFound) { - notice( t('Not found') . EOL); - } - elseif ($err instanceof \Sabre\DAV\Exception\Forbidden) { - notice( t('Permission denied') . EOL); - } - elseif ($err instanceof \Sabre\DAV\Exception\NotImplemented) { - notice( t('Please refresh page') . EOL); - // It would be nice to do the following on remote authentication - // which provides an unexpected page query param, but we do not - // because if the exception has a different cause, it will loop. - // goaway(z_root() . '/' . App::$query_string); - } - else { - notice( t('Unknown error') . EOL); - } + public function DAVException($err) + { - construct_page(); - - killme(); - } + if ($err instanceof NotFound) { + notice(t('Not found') . EOL); + } elseif ($err instanceof Forbidden) { + notice(t('Permission denied') . EOL); + } elseif ($err instanceof NotImplemented) { + notice(t('Please refresh page') . EOL); + // It would be nice to do the following on remote authentication + // which provides an unexpected page query param, but we do not + // because if the exception has a different cause, it will loop. + // goaway(z_root() . '/' . App::$query_string); + } else { + notice(t('Unknown error') . EOL); + } + construct_page(); + + killme(); + } } - - diff --git a/Zotlabs/Module/Cloud_tiles.php b/Zotlabs/Module/Cloud_tiles.php index da551904f..488d9b0f5 100644 --- a/Zotlabs/Module/Cloud_tiles.php +++ b/Zotlabs/Module/Cloud_tiles.php @@ -2,20 +2,24 @@ namespace Zotlabs\Module; -class Cloud_tiles extends \Zotlabs\Web\Controller { +use Zotlabs\Web\Controller; - function init() { +class Cloud_tiles extends Controller +{ - if(intval($_SESSION['cloud_tiles'])) - $_SESSION['cloud_tiles'] = 0; - else - $_SESSION['cloud_tiles'] = 1; + public function init() + { - if(local_channel()) { - set_pconfig(local_channel(),'system','cloud_tiles',$_SESSION['cloud_tiles']); - } + if (intval($_SESSION['cloud_tiles'])) { + $_SESSION['cloud_tiles'] = 0; + } else { + $_SESSION['cloud_tiles'] = 1; + } - goaway(z_root() . '/' . hex2bin(argv(1))); + if (local_channel()) { + set_pconfig(local_channel(), 'system', 'cloud_tiles', $_SESSION['cloud_tiles']); + } - } -} \ No newline at end of file + goaway(z_root() . '/' . hex2bin(argv(1))); + } +} diff --git a/Zotlabs/Module/Comment_control.php b/Zotlabs/Module/Comment_control.php index 884096e09..7508f069c 100644 --- a/Zotlabs/Module/Comment_control.php +++ b/Zotlabs/Module/Comment_control.php @@ -6,23 +6,24 @@ use Zotlabs\Lib\Apps; use Zotlabs\Lib\Libsync; use Zotlabs\Web\Controller; -class Comment_control extends Controller { +class Comment_control extends Controller +{ - function get() { + public function get() + { $desc = t('This app allows you to select the comment audience and set a length of time that comments on a particular post will be accepted.'); $text = ''; - if(! ( local_channel() && Apps::system_app_installed(local_channel(),'Comment Control'))) { + if (!(local_channel() && Apps::system_app_installed(local_channel(), 'Comment Control'))) { return $text; } - $desc = t('This app is installed. A button to control comments may be found below the post editor.'); + $desc = t('This app is installed. A button to control comments may be found below the post editor.'); - $text = ''; + $text = ''; - return $text; - - } + return $text; + } } diff --git a/Zotlabs/Module/Common.php b/Zotlabs/Module/Common.php index c7ef39879..3e93f7930 100644 --- a/Zotlabs/Module/Common.php +++ b/Zotlabs/Module/Common.php @@ -1,4 +1,5 @@ 1 && intval(argv(1))) - $channel_id = intval(argv(1)); - else { - notice( t('No channel.') . EOL ); - \App::$error = 404; - return; - } - - $x = q("select channel_address from channel where channel_id = %d limit 1", - intval($channel_id) - ); - - if($x) - Libprofile::load($x[0]['channel_address'],0); - - } - - function get() { - - $o = ''; - - if(! \App::$profile['profile_uid']) - return; - - $observer_hash = get_observer_hash(); - - if(! perm_is_allowed(\App::$profile['profile_uid'],$observer_hash,'view_contacts')) { - notice( t('Permission denied.') . EOL); - return; - } - - $t = count_common_friends(\App::$profile['profile_uid'],$observer_hash); - - if(! $t) { - notice( t('No connections in common.') . EOL); - return; - } - - $r = common_friends(\App::$profile['profile_uid'],$observer_hash); - - if($r) { - foreach($r as $rr) { - $items[] = [ - 'url' => $rr['xchan_url'], - 'name' => $rr['xchan_name'], - 'photo' => $rr['xchan_photo_m'], - 'tags' => '' - ]; - } - } + public function init() + { - $tpl = get_markup_template('common_friends.tpl'); + if (argc() > 1 && intval(argv(1))) { + $channel_id = intval(argv(1)); + } else { + notice(t('No channel.') . EOL); + App::$error = 404; + return; + } - $o = replace_macros($tpl, [ - '$title' => t('View Common Connections'), - '$items' => $items - ]); - - return $o; - } - + $x = q( + "select channel_address from channel where channel_id = %d limit 1", + intval($channel_id) + ); + + if ($x) { + Libprofile::load($x[0]['channel_address'], 0); + } + } + + public function get() + { + + $o = ''; + + if (!App::$profile['profile_uid']) { + return; + } + + $observer_hash = get_observer_hash(); + + if (!perm_is_allowed(App::$profile['profile_uid'], $observer_hash, 'view_contacts')) { + notice(t('Permission denied.') . EOL); + return; + } + + $t = count_common_friends(App::$profile['profile_uid'], $observer_hash); + + if (!$t) { + notice(t('No connections in common.') . EOL); + return; + } + + $r = common_friends(App::$profile['profile_uid'], $observer_hash); + + if ($r) { + foreach ($r as $rr) { + $items[] = [ + 'url' => $rr['xchan_url'], + 'name' => $rr['xchan_name'], + 'photo' => $rr['xchan_photo_m'], + 'tags' => '' + ]; + } + } + + $tpl = get_markup_template('common_friends.tpl'); + + $o = replace_macros($tpl, [ + '$title' => t('View Common Connections'), + '$items' => $items + ]); + + return $o; + } } diff --git a/Zotlabs/Module/Connac.php b/Zotlabs/Module/Connac.php index b919b06c3..057084ab3 100644 --- a/Zotlabs/Module/Connac.php +++ b/Zotlabs/Module/Connac.php @@ -1,4 +1,5 @@ 1) { + App::$data['channel'] = channelx_by_nick(argv(1)); + Libprofile::load(argv(1), EMPTY_STR); + } else { + notice(t('Requested profile is not available.') . EOL); + App::$error = 404; + return; + } + } - function init() { - if(argc() > 1) { - App::$data['channel'] = channelx_by_nick(argv(1)); - Libprofile::load(argv(1),EMPTY_STR); - } - else { - notice( t('Requested profile is not available.') . EOL ); - App::$error = 404; - return; - } - } - - function post() { - - if(! array_key_exists('channel', App::$data)) - return; - - $edit = ((local_channel() && (local_channel() == App::$data['channel']['channel_id'])) ? true : false); - - if($edit) { - $has_premium = ((App::$data['channel']['channel_pageflags'] & PAGE_PREMIUM) ? 1 : 0); - $premium = (($_POST['premium']) ? intval($_POST['premium']) : 0); - $text = escape_tags($_POST['text']); - - if($has_premium != $premium) { - $r = q("update channel set channel_pageflags = ( channel_pageflags %s %d ) where channel_id = %d", - db_getfunc('^'), - intval(PAGE_PREMIUM), - intval(local_channel()) - ); - - Run::Summon( [ 'Notifier','refresh_all',App::$data['channel']['channel_id'] ] ); - } - set_pconfig(App::$data['channel']['channel_id'],'system','selltext',$text); - // reload the page completely to get fresh data - goaway(z_root() . '/' . App::$query_string); - - } - - $url = EMPTY_STR; - $observer = App::get_observer(); - if(($observer) && ($_POST['submit'] === t('Continue'))) { - if($observer['xchan_follow']) - $url = sprintf($observer['xchan_follow'],urlencode(channel_reddress(App::$data['channel']))); - if(! $url) { - $r = q("select * from hubloc where hubloc_hash = '%s' order by hubloc_id desc limit 1", - dbesc($observer['xchan_hash']) - ); - if($r) - $url = $r[0]['hubloc_url'] . '/follow?f=&url=' . urlencode(channel_reddress(App::$data['channel'])); - } - } - if($url) - goaway($url . '&confirm=1'); - else - notice('Unable to connect to your home hub location.'); - - } - - - - function get() { - - $edit = ((local_channel() && (local_channel() == App::$data['channel']['channel_id'])) ? true : false); - - $text = get_pconfig(App::$data['channel']['channel_id'],'system','selltext'); - - if($edit) { - - $o = replace_macros(get_markup_template('sellpage_edit.tpl'),array( - '$header' => t('Premium Channel Setup'), - '$address' => App::$data['channel']['channel_address'], - '$premium' => array('premium', t('Enable premium channel connection restrictions'),((App::$data['channel']['channel_pageflags'] & PAGE_PREMIUM) ? '1' : ''),''), - '$lbl_about' => t('Please enter your restrictions or conditions, such as paypal receipt, usage guidelines, etc.'), - '$text' => $text, - '$desc' => t('This channel may require additional steps or acknowledgement of the following conditions prior to connecting:'), - '$lbl2' => t('Potential connections will then see the following text before proceeding:'), - '$desc2' => t('By continuing, I certify that I have complied with any instructions provided on this page.'), - '$submit' => t('Submit'), - - - )); - return $o; - } - else { - if(! $text) - $text = t('(No specific instructions have been provided by the channel owner.)'); - - $submit = replace_macros(get_markup_template('sellpage_submit.tpl'), array( - '$continue' => t('Continue'), - '$address' => App::$data['channel']['channel_address'] - )); - - $o = replace_macros(get_markup_template('sellpage_view.tpl'),array( - '$header' => t('Restricted or Premium Channel'), - '$desc' => t('This channel may require additional steps or acknowledgement of the following conditions prior to connecting:'), - '$text' => prepare_text($text), - - '$desc2' => t('By continuing, I certify that I have complied with any instructions provided on this page.'), - '$submit' => $submit, - - )); - - $arr = array('channel' => App::$data['channel'],'observer' => App::get_observer(), 'sellpage' => $o, 'submit' => $submit); - call_hooks('connect_premium', $arr); - $o = $arr['sellpage']; - - } - - return $o; - } + public function post() + { + + if (!array_key_exists('channel', App::$data)) { + return; + } + + $edit = ((local_channel() && (local_channel() == App::$data['channel']['channel_id'])) ? true : false); + + if ($edit) { + $has_premium = ((App::$data['channel']['channel_pageflags'] & PAGE_PREMIUM) ? 1 : 0); + $premium = (($_POST['premium']) ? intval($_POST['premium']) : 0); + $text = escape_tags($_POST['text']); + + if ($has_premium != $premium) { + $r = q( + "update channel set channel_pageflags = ( channel_pageflags %s %d ) where channel_id = %d", + db_getfunc('^'), + intval(PAGE_PREMIUM), + intval(local_channel()) + ); + + Run::Summon(['Notifier', 'refresh_all', App::$data['channel']['channel_id']]); + } + set_pconfig(App::$data['channel']['channel_id'], 'system', 'selltext', $text); + // reload the page completely to get fresh data + goaway(z_root() . '/' . App::$query_string); + } + + $url = EMPTY_STR; + $observer = App::get_observer(); + if (($observer) && ($_POST['submit'] === t('Continue'))) { + if ($observer['xchan_follow']) { + $url = sprintf($observer['xchan_follow'], urlencode(channel_reddress(App::$data['channel']))); + } + if (!$url) { + $r = q( + "select * from hubloc where hubloc_hash = '%s' order by hubloc_id desc limit 1", + dbesc($observer['xchan_hash']) + ); + if ($r) { + $url = $r[0]['hubloc_url'] . '/follow?f=&url=' . urlencode(channel_reddress(App::$data['channel'])); + } + } + } + if ($url) { + goaway($url . '&confirm=1'); + } else { + notice('Unable to connect to your home hub location.'); + } + } + + + public function get() + { + + $edit = ((local_channel() && (local_channel() == App::$data['channel']['channel_id'])) ? true : false); + + $text = get_pconfig(App::$data['channel']['channel_id'], 'system', 'selltext'); + + if ($edit) { + $o = replace_macros(get_markup_template('sellpage_edit.tpl'), array( + '$header' => t('Premium Channel Setup'), + '$address' => App::$data['channel']['channel_address'], + '$premium' => array('premium', t('Enable premium channel connection restrictions'), ((App::$data['channel']['channel_pageflags'] & PAGE_PREMIUM) ? '1' : ''), ''), + '$lbl_about' => t('Please enter your restrictions or conditions, such as paypal receipt, usage guidelines, etc.'), + '$text' => $text, + '$desc' => t('This channel may require additional steps or acknowledgement of the following conditions prior to connecting:'), + '$lbl2' => t('Potential connections will then see the following text before proceeding:'), + '$desc2' => t('By continuing, I certify that I have complied with any instructions provided on this page.'), + '$submit' => t('Submit'), + + + )); + return $o; + } else { + if (!$text) { + $text = t('(No specific instructions have been provided by the channel owner.)'); + } + + $submit = replace_macros(get_markup_template('sellpage_submit.tpl'), array( + '$continue' => t('Continue'), + '$address' => App::$data['channel']['channel_address'] + )); + + $o = replace_macros(get_markup_template('sellpage_view.tpl'), array( + '$header' => t('Restricted or Premium Channel'), + '$desc' => t('This channel may require additional steps or acknowledgement of the following conditions prior to connecting:'), + '$text' => prepare_text($text), + + '$desc2' => t('By continuing, I certify that I have complied with any instructions provided on this page.'), + '$submit' => $submit, + + )); + + $arr = array('channel' => App::$data['channel'], 'observer' => App::get_observer(), 'sellpage' => $o, 'submit' => $submit); + call_hooks('connect_premium', $arr); + $o = $arr['sellpage']; + } + + return $o; + } } diff --git a/Zotlabs/Module/Connections.php b/Zotlabs/Module/Connections.php index 4671fbbf7..1230daffe 100644 --- a/Zotlabs/Module/Connections.php +++ b/Zotlabs/Module/Connections.php @@ -1,4 +1,5 @@ array( - 'label' => t('Active Connections'), - 'url' => z_root() . '/connections/active', - 'sel' => ($active) ? 'active' : '', - 'title' => t('Show active connections'), - ), + if (!local_channel()) { + return; + } - 'pending' => array( - 'label' => t('New Connections'), - 'url' => z_root() . '/connections/pending', - 'sel' => ($pending) ? 'active' : '', - 'title' => t('Show pending (new) connections'), - ), - - 'blocked' => array( - 'label' => t('Blocked'), - 'url' => z_root() . '/connections/blocked', - 'sel' => ($blocked) ? 'active' : '', - 'title' => t('Only show blocked connections'), - ), - - 'ignored' => array( - 'label' => t('Ignored'), - 'url' => z_root() . '/connections/ignored', - 'sel' => ($ignored) ? 'active' : '', - 'title' => t('Only show ignored connections'), - ), - - 'archived' => array( - 'label' => t('Archived/Unreachable'), - 'url' => z_root() . '/connections/archived', - 'sel' => ($archived) ? 'active' : '', - 'title' => t('Only show archived/unreachable connections'), - ), - - 'hidden' => array( - 'label' => t('Hidden'), - 'url' => z_root() . '/connections/hidden', - 'sel' => ($hidden) ? 'active' : '', - 'title' => t('Only show hidden connections'), - ), - - 'all' => array( - 'label' => t('All Connections'), - 'url' => z_root() . '/connections/all', - 'sel' => ($all) ? 'active' : '', - 'title' => t('Show all connections'), - ), - - ); - - $searching = false; - if($search) { - $search_hdr = $search; - $search_txt = dbesc(protect_sprintf($search)); - $searching = true; - } - $sql_extra .= (($searching) ? " AND ( xchan_name like '%%$search_txt%%' OR abook_alias like '%%$search_txt%%' ) " : ""); - - if (isset($_REQUEST['gid']) && intval($_REQUEST['gid'])) { - $sql_extra .= " and xchan_hash in ( select xchan from pgrp_member where gid = " . intval($_REQUEST['gid']) . " and uid = " . intval(local_channel()) . " ) "; - } - - $r = q("SELECT COUNT(abook.abook_id) AS total FROM abook left join xchan on abook.abook_xchan = xchan.xchan_hash + $channel = App::get_channel(); + if ($channel) { + head_set_icon($channel['xchan_photo_s']); + } + } + + public function get() + { + + $sort_type = 0; + $o = ''; + + + if (!local_channel()) { + notice(t('Permission denied.') . EOL); + return login(); + } + + nav_set_selected('Connections'); + + $active = false; + $blocked = false; + $hidden = false; + $ignored = false; + $archived = false; + $unblocked = false; + $pending = false; + $unconnected = false; + $all = false; + + if (!(isset($_REQUEST['aj']) && $_REQUEST['aj'])) { + $_SESSION['return_url'] = App::$query_string; + } + + $search_flags = ""; + $head = ''; + + if (argc() == 2) { + switch (argv(1)) { + case 'active': + $search_flags = " and abook_blocked = 0 and abook_ignored = 0 and abook_hidden = 0 and abook_archived = 0 AND abook_not_here = 0 "; + $head = t('Active'); + $active = true; + break; + case 'blocked': + $search_flags = " and abook_blocked = 1 "; + $head = t('Blocked'); + $blocked = true; + break; + case 'ignored': + $search_flags = " and abook_ignored = 1 "; + $head = t('Ignored'); + $ignored = true; + break; + case 'hidden': + $search_flags = " and abook_hidden = 1 "; + $head = t('Hidden'); + $hidden = true; + break; + case 'archived': + $search_flags = " and ( abook_archived = 1 OR abook_not_here = 1) "; + $head = t('Archived/Unreachable'); + $archived = true; + break; + case 'all': + $search_flags = ''; + $head = t('All'); + $all = true; + break; + case 'pending': + $search_flags = " and abook_pending = 1 "; + $head = t('New'); + $pending = true; + break; + case 'ifpending': + case intval(argv(1)): + $r = q( + "SELECT COUNT(abook.abook_id) AS total FROM abook left join xchan on abook.abook_xchan = xchan.xchan_hash where abook_channel = %d and abook_pending = 1 and abook_self = 0 and abook_ignored = 0 and xchan_deleted = 0 and xchan_orphan = 0 ", + intval(local_channel()) + ); + if ($r && $r[0]['total']) { + $search_flags = " and abook_pending = 1 "; + if (intval(argv(1))) { + $search_flags .= " and abook_id = " . intval(argv(1)) . " "; + } + $head = t('New'); + $pending = true; + App::$argv[1] = 'pending'; + } else { + $head = t('All'); + $search_flags = ''; + $all = true; + App::$argc = 1; + unset(App::$argv[1]); + } + break; + + default: + $search_flags = " and abook_blocked = 0 and abook_ignored = 0 and abook_hidden = 0 and abook_archived = 0 and abook_not_here = 0 "; + $active = true; + $head = t('Active'); + break; + } + + $sql_extra = $search_flags; + if (argv(1) === 'pending') { + $sql_extra .= " and abook_ignored = 0 "; + } + } else { + $sql_extra = " and abook_blocked = 0 and abook_ignored = 0 and abook_hidden = 0 and abook_archived = 0 and abook_not_here = 0 "; + $active = true; + $head = t('Active'); + } + + $search = ((x($_REQUEST, 'search')) ? notags(trim($_REQUEST['search'])) : ''); + + $tabs = array( + + 'active' => array( + 'label' => t('Active Connections'), + 'url' => z_root() . '/connections/active', + 'sel' => ($active) ? 'active' : '', + 'title' => t('Show active connections'), + ), + + 'pending' => array( + 'label' => t('New Connections'), + 'url' => z_root() . '/connections/pending', + 'sel' => ($pending) ? 'active' : '', + 'title' => t('Show pending (new) connections'), + ), + + 'blocked' => array( + 'label' => t('Blocked'), + 'url' => z_root() . '/connections/blocked', + 'sel' => ($blocked) ? 'active' : '', + 'title' => t('Only show blocked connections'), + ), + + 'ignored' => array( + 'label' => t('Ignored'), + 'url' => z_root() . '/connections/ignored', + 'sel' => ($ignored) ? 'active' : '', + 'title' => t('Only show ignored connections'), + ), + + 'archived' => array( + 'label' => t('Archived/Unreachable'), + 'url' => z_root() . '/connections/archived', + 'sel' => ($archived) ? 'active' : '', + 'title' => t('Only show archived/unreachable connections'), + ), + + 'hidden' => array( + 'label' => t('Hidden'), + 'url' => z_root() . '/connections/hidden', + 'sel' => ($hidden) ? 'active' : '', + 'title' => t('Only show hidden connections'), + ), + + 'all' => array( + 'label' => t('All Connections'), + 'url' => z_root() . '/connections/all', + 'sel' => ($all) ? 'active' : '', + 'title' => t('Show all connections'), + ), + + ); + + $searching = false; + if ($search) { + $search_hdr = $search; + $search_txt = dbesc(protect_sprintf($search)); + $searching = true; + } + $sql_extra .= (($searching) ? " AND ( xchan_name like '%%$search_txt%%' OR abook_alias like '%%$search_txt%%' ) " : ""); + + if (isset($_REQUEST['gid']) && intval($_REQUEST['gid'])) { + $sql_extra .= " and xchan_hash in ( select xchan from pgrp_member where gid = " . intval($_REQUEST['gid']) . " and uid = " . intval(local_channel()) . " ) "; + } + + $r = q( + "SELECT COUNT(abook.abook_id) AS total FROM abook left join xchan on abook.abook_xchan = xchan.xchan_hash where abook_channel = %d and abook_self = 0 and xchan_deleted = 0 and xchan_orphan = 0 $sql_extra ", - intval(local_channel()) - ); - if($r) { - App::set_pager_total($r[0]['total']); - $total = $r[0]['total']; - } + intval(local_channel()) + ); + if ($r) { + App::set_pager_total($r[0]['total']); + $total = $r[0]['total']; + } - $order_q = 'xchan_name'; - if (isset($_REQUEST['order'])) { - switch ($_REQUEST['order']) { - case 'date': - $order_q = 'abook_created desc'; - break; - case 'created': - $order_q = 'abook_created'; - break; - case 'cmax': - $order_q = 'abook_closeness'; - break; - case 'name': - default: - $order_q = 'xchan_name'; - break; - } - } + $order_q = 'xchan_name'; + if (isset($_REQUEST['order'])) { + switch ($_REQUEST['order']) { + case 'date': + $order_q = 'abook_created desc'; + break; + case 'created': + $order_q = 'abook_created'; + break; + case 'cmax': + $order_q = 'abook_closeness'; + break; + case 'name': + default: + $order_q = 'xchan_name'; + break; + } + } - $order = array( - - 'name' => array( - 'label' => t('Name'), - 'url' => z_root() . '/connections' . ((argv(1)) ? '/' . argv(1) : '') . '?order=name', - 'sel' => ((isset($_REQUEST['order']) && $_REQUEST['order'] !== 'name') ? 'active' : ''), - 'title' => t('Order by name'), - ), + $order = array( - 'date' => array( - 'label' => t('Recent'), - 'url' => z_root() . '/connections' . ((argv(1)) ? '/' . argv(1) : '') . '?order=date', - 'sel' => ((isset($_REQUEST['order']) && $_REQUEST['order'] === 'date') ? 'active' : ''), - 'title' => t('Order by recent'), - ), + 'name' => array( + 'label' => t('Name'), + 'url' => z_root() . '/connections' . ((argv(1)) ? '/' . argv(1) : '') . '?order=name', + 'sel' => ((isset($_REQUEST['order']) && $_REQUEST['order'] !== 'name') ? 'active' : ''), + 'title' => t('Order by name'), + ), - 'created' => array( - 'label' => t('Created'), - 'url' => z_root() . '/connections' . ((argv(1)) ? '/' . argv(1) : '') . '?order=created', - 'sel' => ((isset($_REQUEST['order']) && $_REQUEST['order'] === 'created') ? 'active' : ''), - 'title' => t('Order by date'), - ), + 'date' => array( + 'label' => t('Recent'), + 'url' => z_root() . '/connections' . ((argv(1)) ? '/' . argv(1) : '') . '?order=date', + 'sel' => ((isset($_REQUEST['order']) && $_REQUEST['order'] === 'date') ? 'active' : ''), + 'title' => t('Order by recent'), + ), + + 'created' => array( + 'label' => t('Created'), + 'url' => z_root() . '/connections' . ((argv(1)) ? '/' . argv(1) : '') . '?order=created', + 'sel' => ((isset($_REQUEST['order']) && $_REQUEST['order'] === 'created') ? 'active' : ''), + 'title' => t('Order by date'), + ), // reserved for cmax -// 'date' => array( -// 'label' => t(''), -// 'url' => z_root() . '/connections' . ((argv(1)) ? '/' . argv(1) : '') . '?order=date', -// 'sel' => ($_REQUEST['order'] === 'date') ? 'active' : '', -// 'title' => t('Order by recent'), -// ), +// 'date' => array( +// 'label' => t(''), +// 'url' => z_root() . '/connections' . ((argv(1)) ? '/' . argv(1) : '') . '?order=date', +// 'sel' => ($_REQUEST['order'] === 'date') ? 'active' : '', +// 'title' => t('Order by recent'), +// ), - ); + ); - - - $r = q("SELECT abook.*, xchan.* FROM abook left join xchan on abook.abook_xchan = xchan.xchan_hash + $r = q( + "SELECT abook.*, xchan.* FROM abook left join xchan on abook.abook_xchan = xchan.xchan_hash WHERE abook_channel = %d and abook_self = 0 and xchan_deleted = 0 and xchan_orphan = 0 $sql_extra ORDER BY $order_q LIMIT %d OFFSET %d ", - intval(local_channel()), - intval(App::$pager['itemspage']), - intval(App::$pager['start']) - ); - - $contacts = []; - - if($r) { + intval(local_channel()), + intval(App::$pager['itemspage']), + intval(App::$pager['start']) + ); - vcard_query($r); + $contacts = []; + + if ($r) { + vcard_query($r); - foreach($r as $rr) { - if ((! $blocked) && LibBlock::fetch_by_entity(local_channel(),$rr['xchan_hash'])) { - continue; - } - if($rr['xchan_url']) { + foreach ($r as $rr) { + if ((!$blocked) && LibBlock::fetch_by_entity(local_channel(), $rr['xchan_hash'])) { + continue; + } + if ($rr['xchan_url']) { + if ((isset($rr['vcard']) && $rr['vcard']) && is_array($rr['vcard']['tels']) && $rr['vcard']['tels'][0]['nr']) { + $phone = $rr['vcard']['tels'][0]['nr']; + } else { + $phone = ''; + } - if((isset($rr['vcard']) && $rr['vcard']) && is_array($rr['vcard']['tels']) && $rr['vcard']['tels'][0]['nr']) - $phone = $rr['vcard']['tels'][0]['nr']; - else - $phone = ''; - - $status_str = ''; - $status = array( - ((isset($rr['abook_active']) && intval($rr['abook_active'])) ? t('Active') : ''), - ((intval($rr['abook_pending'])) ? t('Pending approval') : ''), - ((intval($rr['abook_archived'])) ? t('Archived') : ''), - ((intval($rr['abook_hidden'])) ? t('Hidden') : ''), - ((intval($rr['abook_ignored'])) ? t('Ignored') : ''), - ((intval($rr['abook_blocked'])) ? t('Blocked') : ''), - ((intval($rr['abook_not_here'])) ? t('Not connected at this location') : '') - ); + $status_str = ''; + $status = array( + ((isset($rr['abook_active']) && intval($rr['abook_active'])) ? t('Active') : ''), + ((intval($rr['abook_pending'])) ? t('Pending approval') : ''), + ((intval($rr['abook_archived'])) ? t('Archived') : ''), + ((intval($rr['abook_hidden'])) ? t('Hidden') : ''), + ((intval($rr['abook_ignored'])) ? t('Ignored') : ''), + ((intval($rr['abook_blocked'])) ? t('Blocked') : ''), + ((intval($rr['abook_not_here'])) ? t('Not connected at this location') : '') + ); - $oneway = false; - if(! their_perms_contains(local_channel(),$rr['xchan_hash'],'post_comments')) { - $oneway = true; - } - - foreach($status as $str) { - if(!$str) - continue; - $status_str .= $str; - $status_str .= ', '; - } - $status_str = rtrim($status_str, ', '); - - $contacts[] = array( - 'img_hover' => sprintf( t('%1$s [%2$s]'),$rr['xchan_name'],$rr['xchan_url']), - 'edit_hover' => t('Edit connection'), - 'edit' => t('Edit'), - 'delete_hover' => t('Delete connection'), - 'id' => $rr['abook_id'], - 'thumb' => $rr['xchan_photo_m'], - 'name' => $rr['xchan_name'] . (($rr['abook_alias']) ? ' <' . $rr['abook_alias'] . '>' : ''), - 'classes' => ((intval($rr['abook_archived']) || intval($rr['abook_not_here'])) ? 'archived' : ''), - 'link' => z_root() . '/connedit/' . $rr['abook_id'], - 'deletelink' => z_root() . '/connedit/' . intval($rr['abook_id']) . '/drop', - 'delete' => t('Delete'), - 'url' => chanlink_hash($rr['xchan_hash']), - 'webbie_label' => t('Channel address'), - 'webbie' => $rr['xchan_addr'], - 'network_label' => t('Network'), - 'network' => network_to_name($rr['xchan_network']), - 'channel_type' => intval($rr['xchan_type']), - 'call' => t('Call'), - 'phone' => $phone, - 'status_label' => t('Status'), - 'status' => $status_str, - 'connected_label' => t('Connected'), - 'connected' => datetime_convert('UTC',date_default_timezone_get(),$rr['abook_created'], 'c'), - 'approve_hover' => t('Approve connection'), - 'approve' => (($rr['abook_pending']) ? t('Approve') : false), - 'ignore_hover' => t('Ignore connection'), - 'ignore' => ((! $rr['abook_ignored']) ? t('Ignore') : false), - 'recent_label' => t('Recent activity'), - 'recentlink' => z_root() . '/stream/?f=&cid=' . intval($rr['abook_id']), - 'oneway' => $oneway, - 'allow_delete' => ($rr['abook_pending'] || get_pconfig(local_channel(),'system','connections_quick_delete')), - ); - } - } - } - - - if($_REQUEST['aj']) { - if($contacts) { - $o = replace_macros(get_markup_template('contactsajax.tpl'),array( - '$contacts' => $contacts, - '$edit' => t('Edit'), - )); - } - else { - $o = '
        '; - } - echo $o; - killme(); - } - else { - $o .= ""; - $o .= replace_macros(get_markup_template('connections.tpl'),array( - '$header' => t('Connections') . (($head) ? ': ' . $head : ''), - '$tabs' => $tabs, - '$order' => $order, - '$sort' => t('Filter by'), - '$sortorder' => t('Sort by'), - '$total' => $total, - '$search' => ((isset($search_hdr)) ? $search_hdr : EMPTY_STR), - '$label' => t('Search'), - '$desc' => t('Search your connections'), - '$finding' => (($searching) ? t('Connections search') . ": '" . $search . "'" : ""), - '$submit' => t('Find'), - '$edit' => t('Edit'), - '$cmd' => App::$cmd, - '$contacts' => $contacts, - '$paginate' => paginate($a), - - )); - } - - if(! $contacts) - $o .= '
        '; - - return $o; - } - + $oneway = false; + if (!their_perms_contains(local_channel(), $rr['xchan_hash'], 'post_comments')) { + $oneway = true; + } + + foreach ($status as $str) { + if (!$str) { + continue; + } + $status_str .= $str; + $status_str .= ', '; + } + $status_str = rtrim($status_str, ', '); + + $contacts[] = array( + 'img_hover' => sprintf(t('%1$s [%2$s]'), $rr['xchan_name'], $rr['xchan_url']), + 'edit_hover' => t('Edit connection'), + 'edit' => t('Edit'), + 'delete_hover' => t('Delete connection'), + 'id' => $rr['abook_id'], + 'thumb' => $rr['xchan_photo_m'], + 'name' => $rr['xchan_name'] . (($rr['abook_alias']) ? ' <' . $rr['abook_alias'] . '>' : ''), + 'classes' => ((intval($rr['abook_archived']) || intval($rr['abook_not_here'])) ? 'archived' : ''), + 'link' => z_root() . '/connedit/' . $rr['abook_id'], + 'deletelink' => z_root() . '/connedit/' . intval($rr['abook_id']) . '/drop', + 'delete' => t('Delete'), + 'url' => chanlink_hash($rr['xchan_hash']), + 'webbie_label' => t('Channel address'), + 'webbie' => $rr['xchan_addr'], + 'network_label' => t('Network'), + 'network' => network_to_name($rr['xchan_network']), + 'channel_type' => intval($rr['xchan_type']), + 'call' => t('Call'), + 'phone' => $phone, + 'status_label' => t('Status'), + 'status' => $status_str, + 'connected_label' => t('Connected'), + 'connected' => datetime_convert('UTC', date_default_timezone_get(), $rr['abook_created'], 'c'), + 'approve_hover' => t('Approve connection'), + 'approve' => (($rr['abook_pending']) ? t('Approve') : false), + 'ignore_hover' => t('Ignore connection'), + 'ignore' => ((!$rr['abook_ignored']) ? t('Ignore') : false), + 'recent_label' => t('Recent activity'), + 'recentlink' => z_root() . '/stream/?f=&cid=' . intval($rr['abook_id']), + 'oneway' => $oneway, + 'allow_delete' => ($rr['abook_pending'] || get_pconfig(local_channel(), 'system', 'connections_quick_delete')), + ); + } + } + } + + + if ($_REQUEST['aj']) { + if ($contacts) { + $o = replace_macros(get_markup_template('contactsajax.tpl'), array( + '$contacts' => $contacts, + '$edit' => t('Edit'), + )); + } else { + $o = '
        '; + } + echo $o; + killme(); + } else { + $o .= ""; + $o .= replace_macros(get_markup_template('connections.tpl'), array( + '$header' => t('Connections') . (($head) ? ': ' . $head : ''), + '$tabs' => $tabs, + '$order' => $order, + '$sort' => t('Filter by'), + '$sortorder' => t('Sort by'), + '$total' => $total, + '$search' => ((isset($search_hdr)) ? $search_hdr : EMPTY_STR), + '$label' => t('Search'), + '$desc' => t('Search your connections'), + '$finding' => (($searching) ? t('Connections search') . ": '" . $search . "'" : ""), + '$submit' => t('Find'), + '$edit' => t('Edit'), + '$cmd' => App::$cmd, + '$contacts' => $contacts, + '$paginate' => paginate($a), + + )); + } + + if (!$contacts) { + $o .= '
        '; + } + + return $o; + } } diff --git a/Zotlabs/Module/Connedit.php b/Zotlabs/Module/Connedit.php index 804f32fd5..ac6ef0742 100644 --- a/Zotlabs/Module/Connedit.php +++ b/Zotlabs/Module/Connedit.php @@ -1,4 +1,5 @@ = 2) && intval(argv(1))) { - $r = q("SELECT abook.*, xchan.* FROM abook left join xchan on abook_xchan = xchan_hash + public function init() + { + + if (!local_channel()) { + return; + } + + if ((argc() >= 2) && intval(argv(1))) { + $r = q( + "SELECT abook.*, xchan.* FROM abook left join xchan on abook_xchan = xchan_hash WHERE abook_channel = %d and abook_id = %d LIMIT 1", - intval(local_channel()), - intval(argv(1)) - ); - // Set the person-of-interest for use by widgets that operate on a single pre-defined channel - if ($r) { - App::$poi = array_shift($r); - } - } - - $channel = App::get_channel(); - if ($channel) { - head_set_icon($channel['xchan_photo_s']); - } - } + intval(local_channel()), + intval(argv(1)) + ); + // Set the person-of-interest for use by widgets that operate on a single pre-defined channel + if ($r) { + App::$poi = array_shift($r); + } + } - - /** - * @brief Evaluate posted values and set changes - */ - - function post() { - - if (! local_channel()) { - return; - } - - $contact_id = intval(argv(1)); - if (! $contact_id) { - return; - } - - $channel = App::get_channel(); - - $orig_record = q("SELECT * FROM abook WHERE abook_id = %d AND abook_channel = %d LIMIT 1", - intval($contact_id), - intval(local_channel()) - ); - - if (! $orig_record) { - notice( t('Could not access contact record.') . EOL); - goaway(z_root() . '/connections'); - } + $channel = App::get_channel(); + if ($channel) { + head_set_icon($channel['xchan_photo_s']); + } + } - $orig_record = array_shift($orig_record); - call_hooks('contact_edit_post', $_POST); - - $vc = get_abconfig(local_channel(),$orig_record['abook_xchan'],'system','vcard'); - $vcard = (($vc) ? Reader::read($vc) : null); - $serialised_vcard = update_vcard($_REQUEST,$vcard); - if ($serialised_vcard) { - set_abconfig(local_channel(),$orig_record['abook_xchan'],'system','vcard',$serialised_vcard); - } + /** + * @brief Evaluate posted values and set changes + */ - $autoperms = null; - $is_self = false; + public function post() + { - if (intval($orig_record['abook_self'])) { - $autoperms = intval($_POST['autoperms']); - $is_self = true; - } - - $profile_id = ((array_key_exists('profile_assign',$_POST)) ? $_POST['profile_assign'] : $orig_record['abook_profile']); + if (!local_channel()) { + return; + } - if ($profile_id) { - $r = q("SELECT profile_guid FROM profile WHERE profile_guid = '%s' AND uid = %d LIMIT 1", - dbesc($profile_id), - intval(local_channel()) - ); - if (! $r) { - notice( t('Could not locate selected profile.') . EOL); - return; - } - } - - $abook_incl = ((array_key_exists('abook_incl',$_POST)) ? escape_tags($_POST['abook_incl']) : $orig_record['abook_incl']); - $abook_excl = ((array_key_exists('abook_excl',$_POST)) ? escape_tags($_POST['abook_excl']) : $orig_record['abook_excl']); - $abook_alias = ((array_key_exists('abook_alias',$_POST)) ? escape_tags(trim($_POST['abook_alias'])) : $orig_record['abook_alias']); + $contact_id = intval(argv(1)); + if (!$contact_id) { + return; + } - $block_announce = ((array_key_exists('block_announce',$_POST)) ? intval($_POST['block_announce']) : 0); - - set_abconfig($channel['channel_id'],$orig_record['abook_xchan'],'system','block_announce',$block_announce); + $channel = App::get_channel(); - $hidden = intval($_POST['hidden']); - - $priority = intval($_POST['poll']); - if ($priority > 5 || $priority < 0) { - $priority = 0; - } - - if (! array_key_exists('closeness',$_POST)) { - $_POST['closeness'] = 80; - } - $closeness = intval($_POST['closeness']); - if ($closeness < 0 || $closeness > 99) { - $closeness = 80; - } - - $all_perms = Permissions::Perms(); + $orig_record = q( + "SELECT * FROM abook WHERE abook_id = %d AND abook_channel = %d LIMIT 1", + intval($contact_id), + intval(local_channel()) + ); - $p = EMPTY_STR; + if (!$orig_record) { + notice(t('Could not access contact record.') . EOL); + goaway(z_root() . '/connections'); + } - if ($all_perms) { - foreach ($all_perms as $perm => $desc) { - if (array_key_exists('perms_' . $perm, $_POST)) { - if ($p) { - $p .= ','; - } - $p .= $perm; - } - } - set_abconfig($channel['channel_id'],$orig_record['abook_xchan'],'system','my_perms',$p); - if ($autoperms) { - set_pconfig($channel['channel_id'],'system','autoperms',$p); - } - } + $orig_record = array_shift($orig_record); - $new_friend = false; - - if (($_REQUEST['pending']) && intval($orig_record['abook_pending'])) { + call_hooks('contact_edit_post', $_POST); - $new_friend = true; - - // @fixme it won't be common, but when you accept a new connection request - // the permissions will now be that of your permissions role and ignore - // any you may have set manually on the form. We'll probably see a bug if somebody - // tries to set the permissions *and* approve the connection in the same - // request. The workaround is to approve the connection, then go back and - // adjust permissions as desired. - - $p = Permissions::connect_perms(local_channel()); - $my_perms = Permissions::serialise($p['perms']); - if ($my_perms) { - set_abconfig($channel['channel_id'],$orig_record['abook_xchan'],'system','my_perms',$my_perms); - } - } + $vc = get_abconfig(local_channel(), $orig_record['abook_xchan'], 'system', 'vcard'); + $vcard = (($vc) ? Reader::read($vc) : null); + $serialised_vcard = update_vcard($_REQUEST, $vcard); + if ($serialised_vcard) { + set_abconfig(local_channel(), $orig_record['abook_xchan'], 'system', 'vcard', $serialised_vcard); + } - $abook_pending = (($new_friend) ? 0 : $orig_record['abook_pending']); + $autoperms = null; + $is_self = false; - $r = q("UPDATE abook SET abook_profile = '%s', abook_closeness = %d, abook_pending = %d, + if (intval($orig_record['abook_self'])) { + $autoperms = intval($_POST['autoperms']); + $is_self = true; + } + + $profile_id = ((array_key_exists('profile_assign', $_POST)) ? $_POST['profile_assign'] : $orig_record['abook_profile']); + + if ($profile_id) { + $r = q( + "SELECT profile_guid FROM profile WHERE profile_guid = '%s' AND uid = %d LIMIT 1", + dbesc($profile_id), + intval(local_channel()) + ); + if (!$r) { + notice(t('Could not locate selected profile.') . EOL); + return; + } + } + + $abook_incl = ((array_key_exists('abook_incl', $_POST)) ? escape_tags($_POST['abook_incl']) : $orig_record['abook_incl']); + $abook_excl = ((array_key_exists('abook_excl', $_POST)) ? escape_tags($_POST['abook_excl']) : $orig_record['abook_excl']); + $abook_alias = ((array_key_exists('abook_alias', $_POST)) ? escape_tags(trim($_POST['abook_alias'])) : $orig_record['abook_alias']); + + $block_announce = ((array_key_exists('block_announce', $_POST)) ? intval($_POST['block_announce']) : 0); + + set_abconfig($channel['channel_id'], $orig_record['abook_xchan'], 'system', 'block_announce', $block_announce); + + $hidden = intval($_POST['hidden']); + + $priority = intval($_POST['poll']); + if ($priority > 5 || $priority < 0) { + $priority = 0; + } + + if (!array_key_exists('closeness', $_POST)) { + $_POST['closeness'] = 80; + } + $closeness = intval($_POST['closeness']); + if ($closeness < 0 || $closeness > 99) { + $closeness = 80; + } + + $all_perms = Permissions::Perms(); + + $p = EMPTY_STR; + + if ($all_perms) { + foreach ($all_perms as $perm => $desc) { + if (array_key_exists('perms_' . $perm, $_POST)) { + if ($p) { + $p .= ','; + } + $p .= $perm; + } + } + set_abconfig($channel['channel_id'], $orig_record['abook_xchan'], 'system', 'my_perms', $p); + if ($autoperms) { + set_pconfig($channel['channel_id'], 'system', 'autoperms', $p); + } + } + + $new_friend = false; + + if (($_REQUEST['pending']) && intval($orig_record['abook_pending'])) { + $new_friend = true; + + // @fixme it won't be common, but when you accept a new connection request + // the permissions will now be that of your permissions role and ignore + // any you may have set manually on the form. We'll probably see a bug if somebody + // tries to set the permissions *and* approve the connection in the same + // request. The workaround is to approve the connection, then go back and + // adjust permissions as desired. + + $p = Permissions::connect_perms(local_channel()); + $my_perms = Permissions::serialise($p['perms']); + if ($my_perms) { + set_abconfig($channel['channel_id'], $orig_record['abook_xchan'], 'system', 'my_perms', $my_perms); + } + } + + $abook_pending = (($new_friend) ? 0 : $orig_record['abook_pending']); + + $r = q( + "UPDATE abook SET abook_profile = '%s', abook_closeness = %d, abook_pending = %d, abook_incl = '%s', abook_excl = '%s', abook_alias = '%s' where abook_id = %d AND abook_channel = %d", - dbesc($profile_id), - intval($closeness), - intval($abook_pending), - dbesc($abook_incl), - dbesc($abook_excl), - dbesc($abook_alias), - intval($contact_id), - intval(local_channel()) - ); - - if ($r) { - info( t('Connection updated.') . EOL); - } - else { - notice( t('Failed to update connection record.') . EOL); - } - - if (! intval(App::$poi['abook_self'])) { - if ($new_friend) { - Run::Summon( [ 'Notifier', 'permissions_accept', $contact_id ] ); - } + dbesc($profile_id), + intval($closeness), + intval($abook_pending), + dbesc($abook_incl), + dbesc($abook_excl), + dbesc($abook_alias), + intval($contact_id), + intval(local_channel()) + ); - Run::Summon( [ - 'Notifier', - (($new_friend) ? 'permissions_create' : 'permissions_update'), - $contact_id - ]); - } - - if ($new_friend) { - $default_group = $channel['channel_default_group']; - if ($default_group) { - $g = AccessList::rec_byhash(local_channel(),$default_group); - if ($g) { - AccessList::member_add(local_channel(),'',App::$poi['abook_xchan'],$g['id']); - } - } - - // Check if settings permit ("post new friend activity" is allowed, and - // friends in general or this friend in particular aren't hidden) - // and send out a new friend activity - - $pr = q("select * from profile where uid = %d and is_default = 1 and hide_friends = 0", - intval($channel['channel_id']) - ); - if (($pr) && (! intval($orig_record['abook_hidden'])) && (intval(get_pconfig($channel['channel_id'],'system','post_newfriend')))) { - $xarr = []; + if ($r) { + info(t('Connection updated.') . EOL); + } else { + notice(t('Failed to update connection record.') . EOL); + } - $xarr['item_wall'] = 1; - $xarr['item_origin'] = 1; - $xarr['item_thread_top'] = 1; - $xarr['owner_xchan'] = $xarr['author_xchan'] = $channel['channel_hash']; - $xarr['allow_cid'] = $channel['channel_allow_cid']; - $xarr['allow_gid'] = $channel['channel_allow_gid']; - $xarr['deny_cid'] = $channel['channel_deny_cid']; - $xarr['deny_gid'] = $channel['channel_deny_gid']; - $xarr['item_private'] = (($xarr['allow_cid']||$xarr['allow_gid']||$xarr['deny_cid']||$xarr['deny_gid']) ? 1 : 0); - - $xarr['body'] = '[zrl=' . $channel['xchan_url'] . ']' . $channel['xchan_name'] . '[/zrl]' . ' ' . t('is now connected to') . ' ' . '[zrl=' . App::$poi['xchan_url'] . ']' . App::$poi['xchan_name'] . '[/zrl]'; - - $xarr['body'] .= "\n\n\n" . '[zrl=' . App::$poi['xchan_url'] . '][zmg=80x80]' . App::$poi['xchan_photo_m'] . '[/zmg][/zrl]'; - - post_activity_item($xarr); - - } - - // pull in a bit of content if there is any to pull in - Run::Summon( [ 'Onepoll', $contact_id ]); - } - - // Refresh the structure in memory with the new data - - $r = q("SELECT abook.*, xchan.* + if (!intval(App::$poi['abook_self'])) { + if ($new_friend) { + Run::Summon(['Notifier', 'permissions_accept', $contact_id]); + } + + Run::Summon([ + 'Notifier', + (($new_friend) ? 'permissions_create' : 'permissions_update'), + $contact_id + ]); + } + + if ($new_friend) { + $default_group = $channel['channel_default_group']; + if ($default_group) { + $g = AccessList::rec_byhash(local_channel(), $default_group); + if ($g) { + AccessList::member_add(local_channel(), '', App::$poi['abook_xchan'], $g['id']); + } + } + + // Check if settings permit ("post new friend activity" is allowed, and + // friends in general or this friend in particular aren't hidden) + // and send out a new friend activity + + $pr = q( + "select * from profile where uid = %d and is_default = 1 and hide_friends = 0", + intval($channel['channel_id']) + ); + if (($pr) && (!intval($orig_record['abook_hidden'])) && (intval(get_pconfig($channel['channel_id'], 'system', 'post_newfriend')))) { + $xarr = []; + + $xarr['item_wall'] = 1; + $xarr['item_origin'] = 1; + $xarr['item_thread_top'] = 1; + $xarr['owner_xchan'] = $xarr['author_xchan'] = $channel['channel_hash']; + $xarr['allow_cid'] = $channel['channel_allow_cid']; + $xarr['allow_gid'] = $channel['channel_allow_gid']; + $xarr['deny_cid'] = $channel['channel_deny_cid']; + $xarr['deny_gid'] = $channel['channel_deny_gid']; + $xarr['item_private'] = (($xarr['allow_cid'] || $xarr['allow_gid'] || $xarr['deny_cid'] || $xarr['deny_gid']) ? 1 : 0); + + $xarr['body'] = '[zrl=' . $channel['xchan_url'] . ']' . $channel['xchan_name'] . '[/zrl]' . ' ' . t('is now connected to') . ' ' . '[zrl=' . App::$poi['xchan_url'] . ']' . App::$poi['xchan_name'] . '[/zrl]'; + + $xarr['body'] .= "\n\n\n" . '[zrl=' . App::$poi['xchan_url'] . '][zmg=80x80]' . App::$poi['xchan_photo_m'] . '[/zmg][/zrl]'; + + post_activity_item($xarr); + } + + // pull in a bit of content if there is any to pull in + Run::Summon(['Onepoll', $contact_id]); + } + + // Refresh the structure in memory with the new data + + $r = q( + "SELECT abook.*, xchan.* FROM abook left join xchan on abook_xchan = xchan_hash WHERE abook_channel = %d and abook_id = %d LIMIT 1", - intval(local_channel()), - intval($contact_id) - ); - if ($r) { - App::$poi = array_shift($r); - } - - if ($new_friend) { - $arr = [ 'channel_id' => local_channel(), 'abook' => App::$poi ]; - call_hooks('accept_follow', $arr); - } - - $this->connedit_clone($a); - - if (($_REQUEST['pending']) && (!$_REQUEST['done'])) { - goaway(z_root() . '/connections/ifpending'); - } - - return; - - } - - /* @brief Clone connection - * - * - */ - - function connedit_clone(&$a) { - - if (! App::$poi) { - return; - } - - $channel = App::get_channel(); - - $r = q("SELECT abook.*, xchan.* + intval(local_channel()), + intval($contact_id) + ); + if ($r) { + App::$poi = array_shift($r); + } + + if ($new_friend) { + $arr = ['channel_id' => local_channel(), 'abook' => App::$poi]; + call_hooks('accept_follow', $arr); + } + + $this->connedit_clone($a); + + if (($_REQUEST['pending']) && (!$_REQUEST['done'])) { + goaway(z_root() . '/connections/ifpending'); + } + + return; + } + + /* @brief Clone connection + * + * + */ + + public function connedit_clone(&$a) + { + + if (!App::$poi) { + return; + } + + $channel = App::get_channel(); + + $r = q( + "SELECT abook.*, xchan.* FROM abook left join xchan on abook_xchan = xchan_hash WHERE abook_channel = %d and abook_id = %d LIMIT 1", - intval(local_channel()), - intval(App::$poi['abook_id']) - ); - if (! $r) { - return; - } + intval(local_channel()), + intval(App::$poi['abook_id']) + ); + if (!$r) { + return; + } - App::$poi = array_shift($r); - $clone = App::$poi; - - unset($clone['abook_id']); - unset($clone['abook_account']); - unset($clone['abook_channel']); - - $abconfig = load_abconfig($channel['channel_id'],$clone['abook_xchan']); - if ($abconfig) { - $clone['abconfig'] = $abconfig; - } - Libsync::build_sync_packet($channel['channel_id'], [ 'abook' => [ $clone ] ] ); - } - - /** - * @brief Generate content of connection edit page - */ - - function get() { - - $sort_type = 0; - $o = EMPTY_STR; - - if (! local_channel()) { - notice( t('Permission denied.') . EOL); - return login(); - } - - $section = ((array_key_exists('section',$_REQUEST)) ? $_REQUEST['section'] : ''); - $channel = App::get_channel(); - - $yes_no = [ t('No'), t('Yes') ]; - - $connect_perms = Permissions::connect_perms(local_channel()); + App::$poi = array_shift($r); + $clone = App::$poi; - $o .= "\n"; - - if (argc() == 3) { - - $contact_id = intval(argv(1)); - if (! $contact_id) { - return; - } - - $cmd = argv(2); - - $orig_record = q("SELECT abook.*, xchan.* FROM abook left join xchan on abook_xchan = xchan_hash + foreach ($connect_perms['perms'] as $p => $v) { + if ($v) { + $o .= "\$('#me_id_perms_" . $p . "').prop('checked', true); \n"; + } + } + $o .= " }\n\n"; + + if (argc() == 3) { + $contact_id = intval(argv(1)); + if (!$contact_id) { + return; + } + + $cmd = argv(2); + + $orig_record = q( + "SELECT abook.*, xchan.* FROM abook left join xchan on abook_xchan = xchan_hash WHERE abook_id = %d AND abook_channel = %d AND abook_self = 0 LIMIT 1", - intval($contact_id), - intval(local_channel()) - ); - - if (! $orig_record) { - notice( t('Could not access address book record.') . EOL); - goaway(z_root() . '/connections'); - } + intval($contact_id), + intval(local_channel()) + ); - $orig_record = array_shift($orig_record); + if (!$orig_record) { + notice(t('Could not access address book record.') . EOL); + goaway(z_root() . '/connections'); + } - if ($cmd === 'update') { - // pull feed and consume it, which should subscribe to the hub. - Run::Summon( [ 'Poller', $contact_id ]); - goaway(z_root() . '/connedit/' . $contact_id); - } + $orig_record = array_shift($orig_record); - if ($cmd === 'fetchvc') { - $url = str_replace('/channel/','/profile/',$orig_record['xchan_url']) . '/vcard'; - $recurse = 0; - $x = z_fetch_url(zid($url),false,$recurse,['session' => true]); - if ($x['success']) { - $h = new HTTPHeaders($x['header']); - $fields = $h->fetcharr(); - if ($fields && array_key_exists('content-type',$fields)) { - $type = explode(';',trim($fields['content-type'])); - if ($type && $type[0] === 'text/vcard' && $x['body']) { - $vc = Reader::read($x['body']); - $vcard = $vc->serialize(); - if ($vcard) { - set_abconfig(local_channel(),$orig_record['abook_xchan'],'system','vcard',$vcard); - $this->connedit_clone($a); - } - } - } - } - goaway(z_root() . '/connedit/' . $contact_id); - } + if ($cmd === 'update') { + // pull feed and consume it, which should subscribe to the hub. + Run::Summon(['Poller', $contact_id]); + goaway(z_root() . '/connedit/' . $contact_id); + } + + if ($cmd === 'fetchvc') { + $url = str_replace('/channel/', '/profile/', $orig_record['xchan_url']) . '/vcard'; + $recurse = 0; + $x = z_fetch_url(zid($url), false, $recurse, ['session' => true]); + if ($x['success']) { + $h = new HTTPHeaders($x['header']); + $fields = $h->fetcharr(); + if ($fields && array_key_exists('content-type', $fields)) { + $type = explode(';', trim($fields['content-type'])); + if ($type && $type[0] === 'text/vcard' && $x['body']) { + $vc = Reader::read($x['body']); + $vcard = $vc->serialize(); + if ($vcard) { + set_abconfig(local_channel(), $orig_record['abook_xchan'], 'system', 'vcard', $vcard); + $this->connedit_clone($a); + } + } + } + } + goaway(z_root() . '/connedit/' . $contact_id); + } - if ($cmd === 'resetphoto') { - q("update xchan set xchan_photo_date = '2001-01-01 00:00:00' where xchan_hash = '%s'", - dbesc($orig_record['xchan_hash']) - ); - $cmd = 'refresh'; - } + if ($cmd === 'resetphoto') { + q( + "update xchan set xchan_photo_date = '2001-01-01 00:00:00' where xchan_hash = '%s'", + dbesc($orig_record['xchan_hash']) + ); + $cmd = 'refresh'; + } - if ($cmd === 'refresh') { + if ($cmd === 'refresh') { if (in_array($orig_record['xchan_network'],['nomad','zot6'])) { - if (! Libzot::refresh($orig_record, App::get_channel())) { - notice( t('Refresh failed - channel is currently unavailable.') ); - } - } - else { - if ($orig_record['xchan_network'] === 'activitypub') { - ActivityPub::discover($orig_record['xchan_hash'],true); - } - // if they are on a different network we'll force a refresh of the connection basic info - Run::Summon( [ 'Notifier', 'permissions_update', $contact_id ]); - } - goaway(z_root() . '/connedit/' . $contact_id); - } + if (!Libzot::refresh($orig_record, App::get_channel())) { + notice(t('Refresh failed - channel is currently unavailable.')); + } + } else { + if ($orig_record['xchan_network'] === 'activitypub') { + ActivityPub::discover($orig_record['xchan_hash'], true); + } + // if they are on a different network we'll force a refresh of the connection basic info + Run::Summon(['Notifier', 'permissions_update', $contact_id]); + } + goaway(z_root() . '/connedit/' . $contact_id); + } - switch ($cmd) { - case 'block': - if (intval($orig_record['abook_blocked'])) { - LibBlock::remove(local_channel(),$orig_record['abook_xchan']); - $sync = [ - 'block_channel_id' => local_channel(), - 'block_entity' => $orig_record['abook_xchan'], - 'block_type' => BLOCKTYPE_CHANNEL, - 'deleted' => true, - ]; - } - else { - LibBlock::store( [ - 'block_channel_id' => local_channel(), - 'block_entity' => $orig_record['abook_xchan'], - 'block_type' => BLOCKTYPE_CHANNEL, - 'block_comment' => t('Added by Connedit') - ]); - $z = q("insert into xign ( uid, xchan ) values ( %d , '%s' ) ", - intval(local_channel()), - dbesc($orig_record['abook_xchan']) - ); - $sync = LibBlock::fetch_by_entity(local_channel(),$orig_record['abook_xchan']); - } - $ignored = [ 'uid' => local_channel(), 'xchan' => $orig_record['abook_xchan'] ]; - Libsync::build_sync_packet(0, [ 'xign' => [ $ignored ], 'block' => [ $sync ]] ); - $flag_result = abook_toggle_flag($orig_record,ABOOK_FLAG_BLOCKED); - break; - case 'ignore': - $flag_result = abook_toggle_flag($orig_record,ABOOK_FLAG_IGNORED); - break; - case 'censor': - $flag_result = abook_toggle_flag($orig_record,ABOOK_FLAG_CENSORED); - break; - case 'archive': - $flag_result = abook_toggle_flag($orig_record,ABOOK_FLAG_ARCHIVED); - break; - case 'hide': - $flag_result = abook_toggle_flag($orig_record,ABOOK_FLAG_HIDDEN); - break; - case 'approve': - if(intval($orig_record['abook_pending'])) { - $flag_result = abook_toggle_flag($orig_record,ABOOK_FLAG_PENDING); - } - break; - default: - break; - } + switch ($cmd) { + case 'block': + if (intval($orig_record['abook_blocked'])) { + LibBlock::remove(local_channel(), $orig_record['abook_xchan']); + $sync = [ + 'block_channel_id' => local_channel(), + 'block_entity' => $orig_record['abook_xchan'], + 'block_type' => BLOCKTYPE_CHANNEL, + 'deleted' => true, + ]; + } else { + LibBlock::store([ + 'block_channel_id' => local_channel(), + 'block_entity' => $orig_record['abook_xchan'], + 'block_type' => BLOCKTYPE_CHANNEL, + 'block_comment' => t('Added by Connedit') + ]); + $z = q( + "insert into xign ( uid, xchan ) values ( %d , '%s' ) ", + intval(local_channel()), + dbesc($orig_record['abook_xchan']) + ); + $sync = LibBlock::fetch_by_entity(local_channel(), $orig_record['abook_xchan']); + } + $ignored = ['uid' => local_channel(), 'xchan' => $orig_record['abook_xchan']]; + Libsync::build_sync_packet(0, ['xign' => [$ignored], 'block' => [$sync]]); + $flag_result = abook_toggle_flag($orig_record, ABOOK_FLAG_BLOCKED); + break; + case 'ignore': + $flag_result = abook_toggle_flag($orig_record, ABOOK_FLAG_IGNORED); + break; + case 'censor': + $flag_result = abook_toggle_flag($orig_record, ABOOK_FLAG_CENSORED); + break; + case 'archive': + $flag_result = abook_toggle_flag($orig_record, ABOOK_FLAG_ARCHIVED); + break; + case 'hide': + $flag_result = abook_toggle_flag($orig_record, ABOOK_FLAG_HIDDEN); + break; + case 'approve': + if (intval($orig_record['abook_pending'])) { + $flag_result = abook_toggle_flag($orig_record, ABOOK_FLAG_PENDING); + } + break; + default: + break; + } - if (isset($flag_result)) { - if ($flag_result) { - $this->connedit_clone($a); - } - else { - notice(t('Unable to set address book parameters.') . EOL); - } - goaway(z_root() . '/connedit/' . $contact_id); - } - - if ($cmd === 'drop') { - if ($orig_record['xchan_network'] === 'activitypub') { - ActivityPub::contact_remove(local_channel(), $orig_record); - } - contact_remove(local_channel(), $orig_record['abook_id'], true); + if (isset($flag_result)) { + if ($flag_result) { + $this->connedit_clone($a); + } else { + notice(t('Unable to set address book parameters.') . EOL); + } + goaway(z_root() . '/connedit/' . $contact_id); + } - // The purge notification is sent to the xchan_hash as the abook record will have just been removed - - Run::Summon( [ 'Notifier' , 'purge', $orig_record['xchan_hash'] ] ); - - Libsync::build_sync_packet(0, [ 'abook' => [ [ 'abook_xchan' => $orig_record['abook_xchan'], 'entry_deleted' => true ] ] ] ); - - info( t('Connection has been removed.') . EOL ); - if (isset($_SESSION['return_url']) && $_SESSION['return_url']) { - goaway(z_root() . '/' . $_SESSION['return_url']); - } - goaway(z_root() . '/contacts'); - } - } - - if (App::$poi) { - - $abook_prev = 0; - $abook_next = 0; + if ($cmd === 'drop') { + if ($orig_record['xchan_network'] === 'activitypub') { + ActivityPub::contact_remove(local_channel(), $orig_record); + } + contact_remove(local_channel(), $orig_record['abook_id'], true); - $contact_id = App::$poi['abook_id']; - $contact = App::$poi; + // The purge notification is sent to the xchan_hash as the abook record will have just been removed - $cn = q("SELECT abook_id, xchan_name from abook left join xchan on abook_xchan = xchan_hash where abook_channel = %d and abook_self = 0 and xchan_deleted = 0 order by xchan_name", - intval(local_channel()) - ); + Run::Summon(['Notifier', 'purge', $orig_record['xchan_hash']]); - if ($cn) { - // store previous/next ids for navigation - $pntotal = count($cn); + Libsync::build_sync_packet(0, ['abook' => [['abook_xchan' => $orig_record['abook_xchan'], 'entry_deleted' => true]]]); - 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']; - } - } - } - } + info(t('Connection has been removed.') . EOL); + if (isset($_SESSION['return_url']) && $_SESSION['return_url']) { + goaway(z_root() . '/' . $_SESSION['return_url']); + } + goaway(z_root() . '/contacts'); + } + } - $tools = array( - - 'view' => array( - 'label' => t('View Profile'), - 'url' => chanlink_cid($contact['abook_id']), - 'sel' => '', - 'title' => sprintf( t('View %s\'s profile'), $contact['xchan_name']), - ), - - 'refresh' => array( - 'label' => t('Refresh Permissions'), - 'url' => z_root() . '/connedit/' . $contact['abook_id'] . '/refresh', - 'sel' => '', - 'title' => t('Fetch updated permissions'), - ), + if (App::$poi) { + $abook_prev = 0; + $abook_next = 0; - 'rephoto' => array( - 'label' => t('Refresh Photo'), - 'url' => z_root() . '/connedit/' . $contact['abook_id'] . '/resetphoto', - 'sel' => '', - 'title' => t('Fetch updated photo'), - ), - - 'recent' => array( - 'label' => t('Recent Activity'), - 'url' => z_root() . '/stream/?f=&cid=' . $contact['abook_id'], - 'sel' => '', - 'title' => t('View recent posts and comments'), - ), - - 'block' => array( - 'label' => (intval($contact['abook_blocked']) ? t('Unblock') : t('Block')), - 'url' => z_root() . '/connedit/' . $contact['abook_id'] . '/block', - 'sel' => (intval($contact['abook_blocked']) ? 'active' : ''), - 'title' => t('Block (or Unblock) all communications with this connection'), - 'info' => (intval($contact['abook_blocked']) ? t('This connection is blocked') : ''), - ), - - 'ignore' => array( - 'label' => (intval($contact['abook_ignored']) ? t('Unignore') : t('Ignore')), - 'url' => z_root() . '/connedit/' . $contact['abook_id'] . '/ignore', - 'sel' => (intval($contact['abook_ignored']) ? 'active' : ''), - 'title' => t('Ignore (or Unignore) all inbound communications from this connection'), - 'info' => (intval($contact['abook_ignored']) ? t('This connection is ignored') : ''), - ), + $contact_id = App::$poi['abook_id']; + $contact = App::$poi; - 'censor' => array( - 'label' => (intval($contact['abook_censor']) ? t('Uncensor') : t('Censor')), - 'url' => z_root() . '/connedit/' . $contact['abook_id'] . '/censor', - 'sel' => (intval($contact['abook_censor']) ? 'active' : ''), - 'title' => t('Censor (or Uncensor) images from this connection'), - 'info' => (intval($contact['abook_censor']) ? t('This connection is censored') : ''), - ), + $cn = q( + "SELECT abook_id, xchan_name from abook left join xchan on abook_xchan = xchan_hash where abook_channel = %d and abook_self = 0 and xchan_deleted = 0 order by xchan_name", + intval(local_channel()) + ); - 'archive' => array( - 'label' => (intval($contact['abook_archived']) ? t('Unarchive') : t('Archive')), - 'url' => z_root() . '/connedit/' . $contact['abook_id'] . '/archive', - 'sel' => (intval($contact['abook_archived']) ? 'active' : ''), - 'title' => t('Archive (or Unarchive) this connection - mark channel dead but keep content'), - 'info' => (intval($contact['abook_archived']) ? t('This connection is archived') : ''), - ), - - 'hide' => array( - 'label' => (intval($contact['abook_hidden']) ? t('Unhide') : t('Hide')), - 'url' => z_root() . '/connedit/' . $contact['abook_id'] . '/hide', - 'sel' => (intval($contact['abook_hidden']) ? 'active' : ''), - 'title' => t('Hide or Unhide this connection from your other connections'), - 'info' => (intval($contact['abook_hidden']) ? t('This connection is hidden') : ''), - ), - - 'delete' => array( - 'label' => t('Delete'), - 'url' => z_root() . '/connedit/' . $contact['abook_id'] . '/drop', - 'sel' => '', - 'title' => t('Delete this connection'), - ), - - ); + if ($cn) { + // store previous/next ids for navigation + $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( + 'label' => t('View Profile'), + 'url' => chanlink_cid($contact['abook_id']), + 'sel' => '', + 'title' => sprintf(t('View %s\'s profile'), $contact['xchan_name']), + ), + + 'refresh' => array( + 'label' => t('Refresh Permissions'), + 'url' => z_root() . '/connedit/' . $contact['abook_id'] . '/refresh', + 'sel' => '', + 'title' => t('Fetch updated permissions'), + ), + + 'rephoto' => array( + 'label' => t('Refresh Photo'), + 'url' => z_root() . '/connedit/' . $contact['abook_id'] . '/resetphoto', + 'sel' => '', + 'title' => t('Fetch updated photo'), + ), + + 'recent' => array( + 'label' => t('Recent Activity'), + 'url' => z_root() . '/stream/?f=&cid=' . $contact['abook_id'], + 'sel' => '', + 'title' => t('View recent posts and comments'), + ), + + 'block' => array( + 'label' => (intval($contact['abook_blocked']) ? t('Unblock') : t('Block')), + 'url' => z_root() . '/connedit/' . $contact['abook_id'] . '/block', + 'sel' => (intval($contact['abook_blocked']) ? 'active' : ''), + 'title' => t('Block (or Unblock) all communications with this connection'), + 'info' => (intval($contact['abook_blocked']) ? t('This connection is blocked') : ''), + ), + + 'ignore' => array( + 'label' => (intval($contact['abook_ignored']) ? t('Unignore') : t('Ignore')), + 'url' => z_root() . '/connedit/' . $contact['abook_id'] . '/ignore', + 'sel' => (intval($contact['abook_ignored']) ? 'active' : ''), + 'title' => t('Ignore (or Unignore) all inbound communications from this connection'), + 'info' => (intval($contact['abook_ignored']) ? t('This connection is ignored') : ''), + ), + + 'censor' => array( + 'label' => (intval($contact['abook_censor']) ? t('Uncensor') : t('Censor')), + 'url' => z_root() . '/connedit/' . $contact['abook_id'] . '/censor', + 'sel' => (intval($contact['abook_censor']) ? 'active' : ''), + 'title' => t('Censor (or Uncensor) images from this connection'), + 'info' => (intval($contact['abook_censor']) ? t('This connection is censored') : ''), + ), + + 'archive' => array( + 'label' => (intval($contact['abook_archived']) ? t('Unarchive') : t('Archive')), + 'url' => z_root() . '/connedit/' . $contact['abook_id'] . '/archive', + 'sel' => (intval($contact['abook_archived']) ? 'active' : ''), + 'title' => t('Archive (or Unarchive) this connection - mark channel dead but keep content'), + 'info' => (intval($contact['abook_archived']) ? t('This connection is archived') : ''), + ), + + 'hide' => array( + 'label' => (intval($contact['abook_hidden']) ? t('Unhide') : t('Hide')), + 'url' => z_root() . '/connedit/' . $contact['abook_id'] . '/hide', + 'sel' => (intval($contact['abook_hidden']) ? 'active' : ''), + 'title' => t('Hide or Unhide this connection from your other connections'), + 'info' => (intval($contact['abook_hidden']) ? t('This connection is hidden') : ''), + ), + + 'delete' => array( + 'label' => t('Delete'), + 'url' => z_root() . '/connedit/' . $contact['abook_id'] . '/drop', + 'sel' => '', + 'title' => t('Delete this connection'), + ), + + ); - if(in_array($contact['xchan_network'],['nomad','zot6'])) { - $tools['fetchvc'] = [ - 'label' => t('Fetch Vcard'), - 'url' => z_root() . '/connedit/' . $contact['abook_id'] . '/fetchvc', - 'sel' => '', - 'title' => t('Fetch electronic calling card for this connection') - ]; - } + if (in_array($contact['xchan_network'], [ 'zot6', 'nomad' ])) { + $tools['fetchvc'] = [ + 'label' => t('Fetch Vcard'), + 'url' => z_root() . '/connedit/' . $contact['abook_id'] . '/fetchvc', + 'sel' => '', + 'title' => t('Fetch electronic calling card for this connection') + ]; + } - $sections = []; + $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'])) { - $self = true; - $abook_prev = $abook_next = 0; - } - - $vc = get_abconfig(local_channel(),$contact['abook_xchan'],'system','vcard'); + $sections['perms'] = [ + 'label' => t('Permissions'), + 'url' => z_root() . '/connedit/' . $contact['abook_id'] . '/?f=§ion=perms', + 'sel' => '', + 'title' => t('Open Individual Permissions section by default'), + ]; - $vctmp = (($vc) ? Reader::read($vc) : null); - $vcard = (($vctmp) ? get_vcard_array($vctmp,$contact['abook_id']) : [] ); - if(! $vcard) - $vcard['fn'] = $contact['xchan_name']; + $self = false; + + if (intval($contact['abook_self'])) { + $self = true; + $abook_prev = $abook_next = 0; + } + + $vc = get_abconfig(local_channel(), $contact['abook_xchan'], 'system', 'vcard'); + + $vctmp = (($vc) ? 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(Apps::system_app_installed(local_channel(),'Friend Zoom')) { + $tpl = get_markup_template("abook_edit.tpl"); - $sections['affinity'] = [ - 'label' => t('Friend Zoom'), - 'url' => z_root() . '/connedit/' . $contact['abook_id'] . '/?f=§ion=affinity', - 'sel' => '', - 'title' => t('Open Friend Zoom section by default'), - ]; - - $labels = [ - 0 => t('Me'), - 20 => t('Family'), - 40 => t('Friends'), - 60 => t('Peers'), - 80 => t('Connections'), - 99 => t('All') - ]; - call_hooks('affinity_labels',$labels); - - $slider_tpl = get_markup_template('contact_slider.tpl'); - - $slideval = intval($contact['abook_closeness']); - - $slide = replace_macros($slider_tpl,array( - '$min' => 1, - '$val' => $slideval, - '$labels' => $labels, - )); - } + if (Apps::system_app_installed(local_channel(), 'Friend Zoom')) { + $sections['affinity'] = [ + 'label' => t('Friend Zoom'), + 'url' => z_root() . '/connedit/' . $contact['abook_id'] . '/?f=§ion=affinity', + 'sel' => '', + 'title' => t('Open Friend Zoom section by default'), + ]; - if(Apps::system_app_installed(local_channel(),'Content Filter')) { - $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 = ''; - - $xl = q("select * from xlink where xlink_xchan = '%s' and xlink_link = '%s' and xlink_static = 1", - dbesc($channel['channel_hash']), - dbesc($contact['xchan_hash']) - ); - - if($xl) { - $rating_val = intval($xl[0]['xlink_rating']); - $rating_text = $xl[0]['xlink_rating_text']; - } - - $rating_enabled = get_config('system','rating_enabled'); - - if($rating_enabled) { - $rating = replace_macros(get_markup_template('rating_slider.tpl'),array( - '$min' => -10, - '$val' => $rating_val - )); - } - else { - $rating = false; - } - - - $perms = []; - $channel = App::get_channel(); - - $global_perms = Permissions::Perms(); + $labels = [ + 0 => t('Me'), + 20 => t('Family'), + 40 => t('Friends'), + 60 => t('Peers'), + 80 => t('Connections'), + 99 => t('All') + ]; + call_hooks('affinity_labels', $labels); - $existing = get_all_perms(local_channel(),$contact['abook_xchan'],false); - - $unapproved = array('pending', t('Approve this connection'), '', t('Accept connection to allow communication'), array(t('No'),t('Yes'))); - - $multiprofs = ((feature_enabled(local_channel(),'multi_profiles')) ? true : false); - - if($slide && !$multiprofs) - $affinity = t('Set Friend Zoom'); - - if(!$slide && $multiprofs) - $affinity = t('Set Profile'); - - if($slide && $multiprofs) - $affinity = t('Set Friend Zoom & Profile'); - - - $theirs = get_abconfig(local_channel(),$contact['abook_xchan'],'system','their_perms',EMPTY_STR); + $slider_tpl = get_markup_template('contact_slider.tpl'); - $their_perms = Permissions::FilledPerms(explode(',',$theirs)); - foreach($global_perms as $k => $v) { - if(! array_key_exists($k,$their_perms)) - $their_perms[$k] = 1; - } + $slideval = intval($contact['abook_closeness']); - $my_perms = explode(',',get_abconfig(local_channel(),$contact['abook_xchan'],'system','my_perms',EMPTY_STR)); + $slide = replace_macros($slider_tpl, array( + '$min' => 1, + '$val' => $slideval, + '$labels' => $labels, + )); + } - foreach($global_perms as $k => $v) { - $thisperm = ((in_array($k,$my_perms)) ? 1 : 0); - - $checkinherited = PermissionLimits::Get(local_channel(),$k); - - // For auto permissions (when $self is true) we don't want to look at existing - // permissions because they are enabled for the channel owner - if((! $self) && ($existing[$k])) - $thisperm = "1"; + if (Apps::system_app_installed(local_channel(), 'Content Filter')) { + $sections['filter'] = [ + 'label' => t('Filter'), + 'url' => z_root() . '/connedit/' . $contact['abook_id'] . '/?f=§ion=filter', + 'sel' => '', + 'title' => t('Open Custom Filter section by default'), + ]; + } - $perms[] = array('perms_' . $k, $v, ((array_key_exists($k,$their_perms)) ? intval($their_perms[$k]) : ''),$thisperm, 1, (($checkinherited & PERMS_SPECIFIC) ? '' : '1'), '', $checkinherited); - } - - $pcat = new Permcat(local_channel()); - $pcatlist = $pcat->listing(); - $permcats = []; - if($pcatlist) { - foreach($pcatlist as $pc) { - $permcats[$pc['name']] = $pc['localname']; - } - } + $rating_val = 0; + $rating_text = ''; - $locstr = locations_by_netid($contact['xchan_hash']); - if(! $locstr) - $locstr = unpunify($contact['xchan_url']); - - $clone_warn = ''; - $clonable = (in_array($contact['xchan_network'],['nomad','zot6','zot','rss']) ? true : false); - if(! $clonable) { - $clone_warn = ''; - $clone_warn .= ((intval($contact['abook_not_here'])) - ? t('This connection is unreachable from this location.') - : t('This connection may be unreachable from other channel locations.') - ); - $clone_warn .= '
        ' . t('Location independence is not supported by their network.'); - } - + $xl = q( + "select * from xlink where xlink_xchan = '%s' and xlink_link = '%s' and xlink_static = 1", + dbesc($channel['channel_hash']), + dbesc($contact['xchan_hash']) + ); + + if ($xl) { + $rating_val = intval($xl[0]['xlink_rating']); + $rating_text = $xl[0]['xlink_rating_text']; + } + + $rating_enabled = get_config('system', 'rating_enabled'); + + if ($rating_enabled) { + $rating = replace_macros(get_markup_template('rating_slider.tpl'), array( + '$min' => -10, + '$val' => $rating_val + )); + } else { + $rating = false; + } - if(intval($contact['abook_not_here']) && ! $clonable) - $not_here = t('This connection is unreachable from this location. Location independence is not supported by their network.'); - - $o .= replace_macros($tpl, [ - '$header' => (($self) ? t('Connection Default Permissions') : sprintf( t('Connection: %s'),$contact['xchan_name']) . (($contact['abook_alias']) ? ' <' . $contact['abook_alias'] . '>' : '')), - '$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), - '$permcat' => [ 'permcat', t('Permission role'), '', '',$permcats ], - '$permcat_new' => t('Add permission role'), - '$permcat_enable' => feature_enabled(local_channel(),'permcats'), - '$addr' => unpunify($contact['xchan_addr']), - '$primeurl' => unpunify($contact['xchan_url']), - '$block_announce' => [ 'block_announce', t('Ignore shares and repeats this connection posts'), get_abconfig(local_channel(),$contact['xchan_hash'],'system','block_announce',false),t('Note: This is not recommended for Groups.'), [ t('No'), t('Yes') ] ], - '$section' => $section, - '$sections' => $sections, - '$vcard' => $vcard, - '$addr_text' => t('This connection\'s primary address is'), - '$loc_text' => t('Available locations:'), - '$locstr' => $locstr, - '$unclonable' => $clone_warn, - '$notself' => (($self) ? '' : '1'), - '$self' => (($self) ? '1' : ''), - '$autolbl' => t('The permissions indicated on this page will be applied to all new connections.'), - '$tools_label' => t('Connection Tools'), - '$tools' => (($self) ? '' : $tools), - '$lbl_slider' => t('Slide to adjust your degree of friendship'), - '$lbl_rating' => t('Rating'), - '$lbl_rating_label' => t('Slide to adjust your rating'), - '$lbl_rating_txt' => t('Optionally explain your rating'), - '$connfilter' => Apps::system_app_installed(local_channel(),'Content Filter'), - '$connfilter_label' => t('Custom Filter'), - '$incl' => array('abook_incl',t('Only import posts with this text'), $contact['abook_incl'],t('words one per line or #tags, $categories, /patterns/, or lang=xx, leave blank to import all posts')), - '$excl' => array('abook_excl',t('Do not import posts with this text'), $contact['abook_excl'],t('words one per line or #tags, $categories, /patterns/, or lang=xx, leave blank to import all posts')), - '$alias' => array('abook_alias',t('Nickname'), $contact['abook_alias'],t('optional - allows you to search by a name that you have chosen')), - '$rating_text' => array('rating_text', t('Optionally explain your rating'),$rating_text,''), - '$rating_info' => t('This information is public!'), - '$rating' => $rating, - '$rating_val' => $rating_val, - '$slide' => $slide, - '$affinity' => $affinity, - '$pending_label' => t('Connection Pending Approval'), - '$is_pending' => (intval($contact['abook_pending']) ? 1 : ''), - '$unapproved' => $unapproved, - '$inherited' => t('inherited'), - '$submit' => t('Submit'), - '$lbl_vis2' => sprintf( t('Please choose the profile you would like to display to %s when viewing your profile securely.'), $contact['xchan_name']), - '$close' => (($contact['abook_closeness']) ? $contact['abook_closeness'] : 80), - '$them' => t('Their Settings'), - '$me' => t('My Settings'), - '$perms' => $perms, - '$permlbl' => t('Individual Permissions'), - '$permnote' => t('Some permissions may be inherited from your channel\'s privacy settings, which have higher priority than individual settings. You can not change those settings here.'), - '$permnote_self' => t('Some permissions may be inherited from your channel\'s privacy settings, 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']), - '$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); - - call_hooks('contact_edit', $arr); - - return $arr['output']; - - } - } + $perms = []; + $channel = App::get_channel(); + + $global_perms = Permissions::Perms(); + + $existing = get_all_perms(local_channel(), $contact['abook_xchan'], false); + + $unapproved = array('pending', t('Approve this connection'), '', t('Accept connection to allow communication'), array(t('No'), t('Yes'))); + + $multiprofs = ((feature_enabled(local_channel(), 'multi_profiles')) ? true : false); + + if ($slide && !$multiprofs) { + $affinity = t('Set Friend Zoom'); + } + + if (!$slide && $multiprofs) { + $affinity = t('Set Profile'); + } + + if ($slide && $multiprofs) { + $affinity = t('Set Friend Zoom & Profile'); + } + + + $theirs = get_abconfig(local_channel(), $contact['abook_xchan'], 'system', 'their_perms', EMPTY_STR); + + $their_perms = Permissions::FilledPerms(explode(',', $theirs)); + foreach ($global_perms as $k => $v) { + if (!array_key_exists($k, $their_perms)) { + $their_perms[$k] = 1; + } + } + + $my_perms = explode(',', get_abconfig(local_channel(), $contact['abook_xchan'], 'system', 'my_perms', EMPTY_STR)); + + foreach ($global_perms as $k => $v) { + $thisperm = ((in_array($k, $my_perms)) ? 1 : 0); + + $checkinherited = PermissionLimits::Get(local_channel(), $k); + + // For auto permissions (when $self is true) we don't want to look at existing + // permissions because they are enabled for the channel owner + if ((!$self) && ($existing[$k])) { + $thisperm = "1"; + } + + $perms[] = array('perms_' . $k, $v, ((array_key_exists($k, $their_perms)) ? intval($their_perms[$k]) : ''), $thisperm, 1, (($checkinherited & PERMS_SPECIFIC) ? '' : '1'), '', $checkinherited); + } + + $pcat = new Permcat(local_channel()); + $pcatlist = $pcat->listing(); + $permcats = []; + if ($pcatlist) { + foreach ($pcatlist as $pc) { + $permcats[$pc['name']] = $pc['localname']; + } + } + + $locstr = locations_by_netid($contact['xchan_hash']); + if (!$locstr) { + $locstr = unpunify($contact['xchan_url']); + } + + $clone_warn = ''; + $clonable = (in_array($contact['xchan_network'], ['nomad', 'zot6', 'zot', 'rss']) ? true : false); + if (!$clonable) { + $clone_warn = ''; + $clone_warn .= ((intval($contact['abook_not_here'])) + ? t('This connection is unreachable from this location.') + : t('This connection may be unreachable from other channel locations.') + ); + $clone_warn .= '
        ' . t('Location independence is not supported by their network.'); + } + + + if (intval($contact['abook_not_here']) && $unclonable) { + $not_here = t('This connection is unreachable from this location. Location independence is not supported by their network.'); + } + + $o .= replace_macros($tpl, [ + '$header' => (($self) ? t('Connection Default Permissions') : sprintf(t('Connection: %s'), $contact['xchan_name']) . (($contact['abook_alias']) ? ' <' . $contact['abook_alias'] . '>' : '')), + '$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), + '$permcat' => ['permcat', t('Permission role'), '', '', $permcats], + '$permcat_new' => t('Add permission role'), + '$permcat_enable' => feature_enabled(local_channel(), 'permcats'), + '$addr' => unpunify($contact['xchan_addr']), + '$primeurl' => unpunify($contact['xchan_url']), + '$block_announce' => ['block_announce', t('Ignore shares and repeats this connection posts'), get_abconfig(local_channel(), $contact['xchan_hash'], 'system', 'block_announce', false), t('Note: This is not recommended for Groups.'), [t('No'), t('Yes')]], + '$section' => $section, + '$sections' => $sections, + '$vcard' => $vcard, + '$addr_text' => t('This connection\'s primary address is'), + '$loc_text' => t('Available locations:'), + '$locstr' => $locstr, + '$unclonable' => $clone_warn, + '$notself' => (($self) ? '' : '1'), + '$self' => (($self) ? '1' : ''), + '$autolbl' => t('The permissions indicated on this page will be applied to all new connections.'), + '$tools_label' => t('Connection Tools'), + '$tools' => (($self) ? '' : $tools), + '$lbl_slider' => t('Slide to adjust your degree of friendship'), + '$lbl_rating' => t('Rating'), + '$lbl_rating_label' => t('Slide to adjust your rating'), + '$lbl_rating_txt' => t('Optionally explain your rating'), + '$connfilter' => Apps::system_app_installed(local_channel(), 'Content Filter'), + '$connfilter_label' => t('Custom Filter'), + '$incl' => array('abook_incl', t('Only import posts with this text'), $contact['abook_incl'], t('words one per line or #tags, $categories, /patterns/, or lang=xx, leave blank to import all posts')), + '$excl' => array('abook_excl', t('Do not import posts with this text'), $contact['abook_excl'], t('words one per line or #tags, $categories, /patterns/, or lang=xx, leave blank to import all posts')), + '$alias' => array('abook_alias', t('Nickname'), $contact['abook_alias'], t('optional - allows you to search by a name that you have chosen')), + '$rating_text' => array('rating_text', t('Optionally explain your rating'), $rating_text, ''), + '$rating_info' => t('This information is public!'), + '$rating' => $rating, + '$rating_val' => $rating_val, + '$slide' => $slide, + '$affinity' => $affinity, + '$pending_label' => t('Connection Pending Approval'), + '$is_pending' => (intval($contact['abook_pending']) ? 1 : ''), + '$unapproved' => $unapproved, + '$inherited' => t('inherited'), + '$submit' => t('Submit'), + '$lbl_vis2' => sprintf(t('Please choose the profile you would like to display to %s when viewing your profile securely.'), $contact['xchan_name']), + '$close' => (($contact['abook_closeness']) ? $contact['abook_closeness'] : 80), + '$them' => t('Their Settings'), + '$me' => t('My Settings'), + '$perms' => $perms, + '$permlbl' => t('Individual Permissions'), + '$permnote' => t('Some permissions may be inherited from your channel\'s privacy settings, which have higher priority than individual settings. You can not change those settings here.'), + '$permnote_self' => t('Some permissions may be inherited from your channel\'s privacy settings, 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']), + '$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); + + call_hooks('contact_edit', $arr); + + return $arr['output']; + } + } } diff --git a/Zotlabs/Module/Contactgroup.php b/Zotlabs/Module/Contactgroup.php index 79b743ac8..50100be78 100644 --- a/Zotlabs/Module/Contactgroup.php +++ b/Zotlabs/Module/Contactgroup.php @@ -1,4 +1,5 @@ 2) && (intval(argv(1))) && (argv(2))) { - $r = abook_by_hash(local_channel(), base64url_decode(argv(2))); - if ($r) { - $change = $r['abook_xchan']; - } - } - - if ((argc() > 1) && (intval(argv(1)))) { - - $group = AccessList::by_id(local_channel(),argv(1)); + public function get() + { - if (! $group) { - killme(); - } - - $members = AccessList::members(local_channel(),$group['id']); - $preselected = ids_to_array($members,'xchan_hash'); - - if ($change) { - if (in_array($change,$preselected)) { - AccessList::member_remove(local_channel(),$group['gname'],$change); - } - else { - AccessList::member_add(local_channel(),$group['gname'],$change); - } - } - } - - killme(); - } + if (!local_channel()) { + killme(); + } + + if ((argc() > 2) && (intval(argv(1))) && (argv(2))) { + $r = abook_by_hash(local_channel(), base64url_decode(argv(2))); + if ($r) { + $change = $r['abook_xchan']; + } + } + + if ((argc() > 1) && (intval(argv(1)))) { + $group = AccessList::by_id(local_channel(), argv(1)); + + if (!$group) { + killme(); + } + + $members = AccessList::members(local_channel(), $group['id']); + $preselected = ids_to_array($members, 'xchan_hash'); + + if ($change) { + if (in_array($change, $preselected)) { + AccessList::member_remove(local_channel(), $group['gname'], $change); + } else { + AccessList::member_add(local_channel(), $group['gname'], $change); + } + } + } + + killme(); + } } diff --git a/Zotlabs/Module/Content_filter.php b/Zotlabs/Module/Content_filter.php index c6d1afc40..a6a3e6915 100644 --- a/Zotlabs/Module/Content_filter.php +++ b/Zotlabs/Module/Content_filter.php @@ -6,69 +6,67 @@ use Zotlabs\Lib\Apps; use Zotlabs\Lib\Libsync; use Zotlabs\Web\Controller; -class Content_filter extends Controller { +class Content_filter extends Controller +{ - function post() { + public function post() + { - if(! ( local_channel() && Apps::system_app_installed(local_channel(),'Content Filter'))) { + if (!(local_channel() && Apps::system_app_installed(local_channel(), 'Content Filter'))) { return; } - if($_POST['content_filter-submit']) { + if ($_POST['content_filter-submit']) { + $incl = ((x($_POST['message_filter_incl'])) ? htmlspecialchars_decode(trim($_POST['message_filter_incl']), ENT_QUOTES) : ''); + $excl = ((x($_POST['message_filter_excl'])) ? htmlspecialchars_decode(trim($_POST['message_filter_excl']), ENT_QUOTES) : ''); - $incl = ((x($_POST['message_filter_incl'])) ? htmlspecialchars_decode(trim($_POST['message_filter_incl']),ENT_QUOTES) : ''); - $excl = ((x($_POST['message_filter_excl'])) ? htmlspecialchars_decode(trim($_POST['message_filter_excl']),ENT_QUOTES) : ''); - - set_pconfig(local_channel(),'system','message_filter_incl',$incl); - set_pconfig(local_channel(),'system','message_filter_excl',$excl); + set_pconfig(local_channel(), 'system', 'message_filter_incl', $incl); + set_pconfig(local_channel(), 'system', 'message_filter_excl', $excl); - info( t('Content Filter settings updated.') . EOL); + info(t('Content Filter settings updated.') . EOL); + } - } - - Libsync::build_sync_packet(); - - } + Libsync::build_sync_packet(); + } - function get() { + public function get() + { $desc = t('This app (when installed) allows you to filter incoming content from all sources or from specific connections. The filtering may be based on words, tags, regular expressions, or language'); $text = ''; - if(! ( local_channel() && Apps::system_app_installed(local_channel(),'Content Filter'))) { + if (!(local_channel() && Apps::system_app_installed(local_channel(), 'Content Filter'))) { return $text; } - $text .= EOL . t('The settings on this page apply to all incoming content. To edit the settings for individual connetions, see the similar settings on the Connection Edit page for that connection.') . EOL . EOL; + $text .= EOL . t('The settings on this page apply to all incoming content. To edit the settings for individual connetions, see the similar settings on the Connection Edit page for that connection.') . EOL . EOL; - $setting_fields = $text; + $setting_fields = $text; - $setting_fields .= replace_macros(get_markup_template('field_textarea.tpl'), array( - '$field' => [ - 'message_filter_incl', - t('Only import posts with this text'), - get_pconfig(local_channel(),'system','message_filter_incl',''), - t('words one per line or #tags, $categories, /patterns/, lang=xx, lang!=xx - leave blank to import all posts') - ] - )); - $setting_fields .= replace_macros(get_markup_template('field_textarea.tpl'), array( - '$field' => [ - 'message_filter_excl', - t('Do not import posts with this text'), - get_pconfig(local_channel(),'system','message_filter_excl',''), - t('words one per line or #tags, $categories, /patterns/, lang=xx, lang!=xx - leave blank to import all posts') - ] - )); + $setting_fields .= replace_macros(get_markup_template('field_textarea.tpl'), array( + '$field' => [ + 'message_filter_incl', + t('Only import posts with this text'), + get_pconfig(local_channel(), 'system', 'message_filter_incl', ''), + t('words one per line or #tags, $categories, /patterns/, lang=xx, lang!=xx - leave blank to import all posts') + ] + )); + $setting_fields .= replace_macros(get_markup_template('field_textarea.tpl'), array( + '$field' => [ + 'message_filter_excl', + t('Do not import posts with this text'), + get_pconfig(local_channel(), 'system', 'message_filter_excl', ''), + t('words one per line or #tags, $categories, /patterns/, lang=xx, lang!=xx - leave blank to import all posts') + ] + )); - $s .= replace_macros(get_markup_template('generic_app_settings.tpl'), array( - '$addon' => array('content_filter', '' . t('Content Filter Settings'), '', t('Submit')), - '$content' => $setting_fields - )); + $s .= replace_macros(get_markup_template('generic_app_settings.tpl'), array( + '$addon' => array('content_filter', '' . t('Content Filter Settings'), '', t('Submit')), + '$content' => $setting_fields + )); - return $s; - } - - -} \ No newline at end of file + return $s; + } +} diff --git a/Zotlabs/Module/Conversation.php b/Zotlabs/Module/Conversation.php index d685bba4b..03f664a0d 100644 --- a/Zotlabs/Module/Conversation.php +++ b/Zotlabs/Module/Conversation.php @@ -1,4 +1,5 @@ 0 order by imgscale asc LIMIT 1", + dbesc($image_id), + intval(local_channel()) + ); + + if ($r) { + $max_thumb = intval(get_config('system', 'max_thumbnail', 1600)); + $iscaled = false; + if (intval($r[0]['height']) > $max_thumb || intval($r[0]['width']) > $max_thumb) { + $imagick_path = get_config('system', 'imagick_convert_path'); + if ($imagick_path && @file_exists($imagick_path) && intval($r[0]['os_storage'])) { + $fname = dbunescbin($r[0]['content']); + $tmp_name = $fname . '-001'; + $newsize = photo_calculate_scale(array_merge(getimagesize($fname), ['max' => $max_thumb])); + $cmd = $imagick_path . ' ' . escapeshellarg(PROJECT_BASE . '/' . $fname) . ' -resize ' . $newsize . ' ' . escapeshellarg(PROJECT_BASE . '/' . $tmp_name); + // logger('imagick thumbnail command: ' . $cmd); + for ($x = 0; $x < 4; $x++) { + exec($cmd); + if (file_exists($tmp_name)) { + break; + } + } + if (file_exists($tmp_name)) { + $base_image = $r[0]; + $gis = getimagesize($tmp_name); + logger('gis: ' . print_r($gis, true)); + $base_image['width'] = $gis[0]; + $base_image['height'] = $gis[1]; + $base_image['content'] = @file_get_contents($tmp_name); + $iscaled = true; + @unlink($tmp_name); + } + } + } + if (!$iscaled) { + $base_image = $r[0]; + $base_image['content'] = (($base_image['os_storage']) ? @file_get_contents(dbunescbin($base_image['content'])) : dbunescbin($base_image['content'])); + } + + $im = photo_factory($base_image['content'], $base_image['mimetype']); + if ($im && $im->is_valid()) { + // We are scaling and cropping the relative pixel locations to the original photo instead of the + // scaled photo we operated on. + + // First load the scaled photo to check its size. (Should probably pass this in the post form and save + // a query.) + + $g = q( + "select width, height from photo where resource_id = '%s' and uid = %d and imgscale = 3", + dbesc($image_id), + intval(local_channel()) + ); - $srcX = intval($_POST['xstart']); - $srcY = intval($_POST['ystart']); - $srcW = intval($_POST['xfinal']) - $srcX; - $srcH = intval($_POST['yfinal']) - $srcY; - - $r = q("select gender from profile where uid = %d and is_default = 1 limit 1", - intval(local_channel()) - ); - if ($r) { - $profile = array_shift($r); - } - - $r = q("SELECT * FROM photo WHERE resource_id = '%s' AND uid = %d AND imgscale > 0 order by imgscale asc LIMIT 1", - dbesc($image_id), - intval(local_channel()) - ); - - if ($r) { + $scaled_width = $g[0]['width']; + $scaled_height = $g[0]['height']; - $max_thumb = intval(get_config('system','max_thumbnail',1600)); - $iscaled = false; - if (intval($r[0]['height']) > $max_thumb || intval($r[0]['width']) > $max_thumb) { - $imagick_path = get_config('system','imagick_convert_path'); - if ($imagick_path && @file_exists($imagick_path) && intval($r[0]['os_storage'])) { + if ((!$scaled_width) || (!$scaled_height)) { + logger('potential divide by zero scaling cover photo'); + return; + } - $fname = dbunescbin($r[0]['content']); - $tmp_name = $fname . '-001'; - $newsize = photo_calculate_scale(array_merge(getimagesize($fname),['max' => $max_thumb])); - $cmd = $imagick_path . ' ' . escapeshellarg(PROJECT_BASE . '/' . $fname) . ' -resize ' . $newsize . ' ' . escapeshellarg(PROJECT_BASE . '/' . $tmp_name); - // logger('imagick thumbnail command: ' . $cmd); - for ($x = 0; $x < 4; $x ++) { - exec($cmd); - if (file_exists($tmp_name)) { - break; - } - } - if (file_exists($tmp_name)) { - $base_image = $r[0]; - $gis = getimagesize($tmp_name); -logger('gis: ' . print_r($gis,true)); - $base_image['width'] = $gis[0]; - $base_image['height'] = $gis[1]; - $base_image['content'] = @file_get_contents($tmp_name); - $iscaled = true; - @unlink($tmp_name); - } - } - } - if (! $iscaled) { - $base_image = $r[0]; - $base_image['content'] = (($base_image['os_storage']) ? @file_get_contents(dbunescbin($base_image['content'])) : dbunescbin($base_image['content'])); - } + // unset all other cover photos - $im = photo_factory($base_image['content'], $base_image['mimetype']); - if ($im && $im->is_valid()) { - - // We are scaling and cropping the relative pixel locations to the original photo instead of the - // scaled photo we operated on. - - // First load the scaled photo to check its size. (Should probably pass this in the post form and save - // a query.) - - $g = q("select width, height from photo where resource_id = '%s' and uid = %d and imgscale = 3", - dbesc($image_id), - intval(local_channel()) - ); - - - $scaled_width = $g[0]['width']; - $scaled_height = $g[0]['height']; - - if ((! $scaled_width) || (! $scaled_height)) { - logger('potential divide by zero scaling cover photo'); - return; - } - - // unset all other cover photos - - q("update photo set photo_usage = %d where photo_usage = %d and uid = %d", - intval(PHOTO_NORMAL), - intval(PHOTO_COVER), - intval(local_channel()) - ); - - $orig_srcx = ( $base_image['width'] / $scaled_width ) * $srcX; - $orig_srcy = ( $base_image['height'] / $scaled_height ) * $srcY; - $orig_srcw = ( $srcW / $scaled_width ) * $base_image['width']; - $orig_srch = ( $srcH / $scaled_height ) * $base_image['height']; - - $im->cropImageRect(1200,435,$orig_srcx, $orig_srcy, $orig_srcw, $orig_srch); - - $aid = get_account_id(); - - $p = [ - 'aid' => $aid, - 'uid' => local_channel(), - 'resource_id' => $base_image['resource_id'], - 'filename' => $base_image['filename'], - 'album' => t('Cover Photos'), - 'os_path' => $base_image['os_path'], - 'display_path' => $base_image['display_path'], - 'created' => $base_image['created'], - 'edited' => $base_image['edited'] - ]; - - $p['imgscale'] = 7; - $p['photo_usage'] = PHOTO_COVER; - - $r1 = $im->storeThumbnail($p, PHOTO_RES_COVER_1200); + q( + "update photo set photo_usage = %d where photo_usage = %d and uid = %d", + intval(PHOTO_NORMAL), + intval(PHOTO_COVER), + intval(local_channel()) + ); - $im->doScaleImage(850,310); - $p['imgscale'] = 8; + $orig_srcx = ($base_image['width'] / $scaled_width) * $srcX; + $orig_srcy = ($base_image['height'] / $scaled_height) * $srcY; + $orig_srcw = ($srcW / $scaled_width) * $base_image['width']; + $orig_srch = ($srcH / $scaled_height) * $base_image['height']; - $r2 = $im->storeThumbnail($p, PHOTO_RES_COVER_850); - - $im->doScaleImage(425,160); - $p['imgscale'] = 9; - - $r3 = $im->storeThumbnail($p, PHOTO_RES_COVER_425); - - if ($r1 === false || $r2 === false || $r3 === false) { - // if one failed, delete them all so we can start over. - notice( t('Image resize failed.') . EOL ); - $x = q("delete from photo where resource_id = '%s' and uid = %d and imgscale >= 7 ", - dbesc($base_image['resource_id']), - local_channel() - ); - return; - } - - $channel = App::get_channel(); - $this->send_cover_photo_activity($channel,$base_image,$profile); - - - } - else - notice( t('Unable to process image') . EOL); - } - - goaway(z_root() . '/channel/' . $channel['channel_address']); - - } - - - $hash = photo_new_resource(); - $smallest = 0; - - $matches = []; - $partial = false; + $im->cropImageRect(1200, 435, $orig_srcx, $orig_srcy, $orig_srcw, $orig_srch); - if (array_key_exists('HTTP_CONTENT_RANGE',$_SERVER)) { - $pm = preg_match('/bytes (\d*)\-(\d*)\/(\d*)/',$_SERVER['HTTP_CONTENT_RANGE'],$matches); - if ($pm) { - logger('Content-Range: ' . print_r($matches,true)); - $partial = true; - } - } + $aid = get_account_id(); - if ($partial) { - $x = save_chunk($channel,$matches[1],$matches[2],$matches[3]); + $p = [ + 'aid' => $aid, + 'uid' => local_channel(), + 'resource_id' => $base_image['resource_id'], + 'filename' => $base_image['filename'], + 'album' => t('Cover Photos'), + 'os_path' => $base_image['os_path'], + 'display_path' => $base_image['display_path'], + 'created' => $base_image['created'], + 'edited' => $base_image['edited'] + ]; - if ($x['partial']) { - header('Range: bytes=0-' . (($x['length']) ? $x['length'] - 1 : 0)); - json_return_and_die($x); - } - else { - header('Range: bytes=0-' . (($x['size']) ? $x['size'] - 1 : 0)); + $p['imgscale'] = 7; + $p['photo_usage'] = PHOTO_COVER; - $_FILES['userfile'] = [ - 'name' => $x['name'], - 'type' => $x['type'], - 'tmp_name' => $x['tmp_name'], - 'error' => $x['error'], - 'size' => $x['size'] - ]; - } - } - else { - if (! array_key_exists('userfile',$_FILES)) { - $_FILES['userfile'] = [ - 'name' => $_FILES['files']['name'], - 'type' => $_FILES['files']['type'], - 'tmp_name' => $_FILES['files']['tmp_name'], - 'error' => $_FILES['files']['error'], - 'size' => $_FILES['files']['size'] - ]; - } - } + $r1 = $im->storeThumbnail($p, PHOTO_RES_COVER_1200); - $res = attach_store(App::get_channel(), get_observer_hash(), '', array('album' => t('Cover Photos'), 'hash' => $hash)); - - logger('attach_store: ' . print_r($res,true),LOGGER_DEBUG); + $im->doScaleImage(850, 310); + $p['imgscale'] = 8; - json_return_and_die([ 'message' => $hash ]); - - } - - function send_cover_photo_activity($channel,$photo,$profile) { - - $arr = []; - $arr['item_thread_top'] = 1; - $arr['item_origin'] = 1; - $arr['item_wall'] = 1; - $arr['uuid'] = new_uuid(); - $arr['mid'] = z_root() . '/item/' . $arr['uuid']; - $arr['obj_type'] = ACTIVITY_OBJ_NOTE; - $arr['verb'] = ACTIVITY_CREATE; - - if ($profile && stripos($profile['gender'],t('female')) !== false) { - $t = t('%1$s updated her %2$s'); - } - elseif ($profile && stripos($profile['gender'],t('male')) !== false) { - $t = t('%1$s updated his %2$s'); - } - else { - $t = t('%1$s updated their %2$s'); - } - - $ptext = '[zrl=' . z_root() . '/photos/' . $channel['channel_address'] . '/image/' . $photo['resource_id'] . ']' . t('cover photo') . '[/zrl]'; - - $ltext = '[zrl=' . z_root() . '/profile/' . $channel['channel_address'] . ']' . '[zmg]' . z_root() . '/photo/' . $photo['resource_id'] . '-8[/zmg][/zrl]'; - - $arr['body'] = sprintf($t,$channel['channel_name'],$ptext) . "\n\n" . $ltext; + $r2 = $im->storeThumbnail($p, PHOTO_RES_COVER_850); - $arr['obj'] = [ - 'type' => ACTIVITY_OBJ_NOTE, - 'published' => datetime_convert('UTC','UTC',$photo['created'],ATOM_TIME), - 'updated' => datetime_convert('UTC','UTC',$photo['edited'],ATOM_TIME), - 'id' => $arr['mid'], - 'url' => [ 'type' => 'Link', 'mediaType' => $photo['mimetype'], 'href' => z_root() . '/photo/' . $photo['resource_id'] . '-7' ], - 'source' => [ 'content' => $arr['body'], 'mediaType' => 'text/x-multicode' ], - 'content' => bbcode($arr['body']), - 'actor' => Activity::encode_person($channel,false), - ]; + $im->doScaleImage(425, 160); + $p['imgscale'] = 9; - $acl = new AccessControl($channel); - $x = $acl->get(); - $arr['allow_cid'] = $x['allow_cid']; - - $arr['allow_gid'] = $x['allow_gid']; - $arr['deny_cid'] = $x['deny_cid']; - $arr['deny_gid'] = $x['deny_gid']; - - $arr['uid'] = $channel['channel_id']; - $arr['aid'] = $channel['channel_account_id']; - - $arr['owner_xchan'] = $channel['channel_hash']; - $arr['author_xchan'] = $channel['channel_hash']; - - post_activity_item($arr); - - - } - - - /** - * @brief Generate content of profile-photo view - * - * @return string - * - */ - - - function get() { - - if (! local_channel()) { - notice( t('Permission denied.') . EOL ); - return; - } - - $channel = App::get_channel(); - - $newuser = false; - - if (argc() == 2 && argv(1) === 'new') - $newuser = true; - - if (argv(1) === 'use') { - if (argc() < 3) { - notice( t('Permission denied.') . EOL ); - return; - }; - - // check_form_security_token_redirectOnErr('/cover_photo', 'cover_photo'); - - $resource_id = argv(2); - - $r = q("SELECT id, album, imgscale FROM photo WHERE uid = %d AND resource_id = '%s' and imgscale > 0 ORDER BY imgscale ASC", - intval(local_channel()), - dbesc($resource_id) - ); - if (! $r) { - notice( t('Photo not available.') . EOL ); - return; - } - $havescale = false; - foreach ($r as $rr) { - if ($rr['imgscale'] == 7) { - $havescale = true; - } - } - - $r = q("SELECT content, mimetype, resource_id, os_storage FROM photo WHERE id = %d and uid = %d limit 1", - intval($r[0]['id']), - intval(local_channel()) - - ); - if (! $r) { - notice( t('Photo not available.') . EOL ); - return; - } - - if (intval($r[0]['os_storage'])) { - $data = @file_get_contents(dbunescbin($r[0]['content'])); - } - else { - $data = dbunescbin($r[0]['content']); - } - - $ph = photo_factory($data, $r[0]['mimetype']); - $smallest = 0; - if ($ph && $ph->is_valid()) { - // go ahead as if we have just uploaded a new photo to crop - $i = q("select resource_id, imgscale from photo where resource_id = '%s' and uid = %d and imgscale = 0", - dbesc($r[0]['resource_id']), - intval(local_channel()) - ); - - if ($i) { - $hash = $i[0]['resource_id']; - foreach ($i as $ii) { - $smallest = intval($ii['imgscale']); - } - } - } - - $this->cover_photo_crop_ui_head($ph, $hash, $smallest); - } - - - if(! array_key_exists('imagecrop',App::$data)) { - - $o .= replace_macros(get_markup_template('cover_photo.tpl'), [ - '$user' => App::$channel['channel_address'], - '$info' => t('Your cover photo may be visible to anybody on the internet'), - '$existing' => get_cover_photo(local_channel(),'array',PHOTO_RES_COVER_850), - '$lbl_upfile' => t('Upload File:'), - '$lbl_profiles' => t('Select a profile:'), - '$title' => t('Change Cover Photo'), - '$submit' => t('Upload'), - '$profiles' => $profiles, - '$embedPhotos' => t('Use a photo from your albums'), - '$embedPhotosModalTitle' => t('Use a photo from your albums'), - '$embedPhotosModalCancel' => t('Cancel'), - '$embedPhotosModalOK' => t('OK'), - '$modalchooseimages' => t('Choose images to embed'), - '$modalchoosealbum' => t('Choose an album'), - '$modaldiffalbum' => t('Choose a different album'), - '$modalerrorlist' => t('Error getting album list'), - '$modalerrorlink' => t('Error getting photo link'), - '$modalerroralbum' => t('Error getting album'), - '$form_security_token' => get_form_security_token("cover_photo"), - '$select' => t('Select previously uploaded photo'), + $r3 = $im->storeThumbnail($p, PHOTO_RES_COVER_425); - ]); - - call_hooks('cover_photo_content_end', $o); - - return $o; - } - else { - $filename = App::$data['imagecrop'] . '-3'; - $resolution = 3; + if ($r1 === false || $r2 === false || $r3 === false) { + // if one failed, delete them all so we can start over. + notice(t('Image resize failed.') . EOL); + $x = q( + "delete from photo where resource_id = '%s' and uid = %d and imgscale >= 7 ", + dbesc($base_image['resource_id']), + local_channel() + ); + return; + } - $o .= replace_macros(get_markup_template('cropcover.tpl'), [ - '$filename' => $filename, - '$profile' => intval($_REQUEST['profile']), - '$resource' => \App::$data['imagecrop'] . '-3', - '$image_url' => z_root() . '/photo/' . $filename, - '$title' => t('Crop Image'), - '$desc' => t('Please adjust the image cropping for optimum viewing.'), - '$form_security_token' => get_form_security_token("cover_photo"), - '$done' => t('Done Editing') - ]); - return $o; - } - } - - /* @brief Generate the UI for photo-cropping - * - * @param $a Current application - * @param $ph Photo-Factory - * @return void - * - */ - - function cover_photo_crop_ui_head($ph, $hash, $smallest){ - - $max_length = get_config('system','max_image_length', MAX_IMAGE_LENGTH); - if ($max_length > 0) { - $ph->scaleImage($max_length); - } - - $width = $ph->getWidth(); - $height = $ph->getHeight(); - - if ($width < 300 || $height < 300) { - $ph->scaleImageUp(240); - $width = $ph->getWidth(); - $height = $ph->getHeight(); - } - - - App::$data['imagecrop'] = $hash; - App::$data['imagecrop_resolution'] = $smallest; - App::$page['htmlhead'] .= replace_macros(get_markup_template('crophead.tpl'), []); - return; - } - - + $channel = App::get_channel(); + $this->send_cover_photo_activity($channel, $base_image, $profile); + } else { + notice(t('Unable to process image') . EOL); + } + } + + goaway(z_root() . '/channel/' . $channel['channel_address']); + } + + + $hash = photo_new_resource(); + $smallest = 0; + + $matches = []; + $partial = false; + + if (array_key_exists('HTTP_CONTENT_RANGE', $_SERVER)) { + $pm = preg_match('/bytes (\d*)\-(\d*)\/(\d*)/', $_SERVER['HTTP_CONTENT_RANGE'], $matches); + if ($pm) { + logger('Content-Range: ' . print_r($matches, true)); + $partial = true; + } + } + + if ($partial) { + $x = save_chunk($channel, $matches[1], $matches[2], $matches[3]); + + if ($x['partial']) { + header('Range: bytes=0-' . (($x['length']) ? $x['length'] - 1 : 0)); + json_return_and_die($x); + } else { + header('Range: bytes=0-' . (($x['size']) ? $x['size'] - 1 : 0)); + + $_FILES['userfile'] = [ + 'name' => $x['name'], + 'type' => $x['type'], + 'tmp_name' => $x['tmp_name'], + 'error' => $x['error'], + 'size' => $x['size'] + ]; + } + } else { + if (!array_key_exists('userfile', $_FILES)) { + $_FILES['userfile'] = [ + 'name' => $_FILES['files']['name'], + 'type' => $_FILES['files']['type'], + 'tmp_name' => $_FILES['files']['tmp_name'], + 'error' => $_FILES['files']['error'], + 'size' => $_FILES['files']['size'] + ]; + } + } + + $res = attach_store(App::get_channel(), get_observer_hash(), '', array('album' => t('Cover Photos'), 'hash' => $hash)); + + logger('attach_store: ' . print_r($res, true), LOGGER_DEBUG); + + json_return_and_die(['message' => $hash]); + } + + public function send_cover_photo_activity($channel, $photo, $profile) + { + + $arr = []; + $arr['item_thread_top'] = 1; + $arr['item_origin'] = 1; + $arr['item_wall'] = 1; + $arr['uuid'] = new_uuid(); + $arr['mid'] = z_root() . '/item/' . $arr['uuid']; + $arr['obj_type'] = ACTIVITY_OBJ_NOTE; + $arr['verb'] = ACTIVITY_CREATE; + + if ($profile && stripos($profile['gender'], t('female')) !== false) { + $t = t('%1$s updated her %2$s'); + } elseif ($profile && stripos($profile['gender'], t('male')) !== false) { + $t = t('%1$s updated his %2$s'); + } else { + $t = t('%1$s updated their %2$s'); + } + + $ptext = '[zrl=' . z_root() . '/photos/' . $channel['channel_address'] . '/image/' . $photo['resource_id'] . ']' . t('cover photo') . '[/zrl]'; + + $ltext = '[zrl=' . z_root() . '/profile/' . $channel['channel_address'] . ']' . '[zmg]' . z_root() . '/photo/' . $photo['resource_id'] . '-8[/zmg][/zrl]'; + + $arr['body'] = sprintf($t, $channel['channel_name'], $ptext) . "\n\n" . $ltext; + + $arr['obj'] = [ + 'type' => ACTIVITY_OBJ_NOTE, + 'published' => datetime_convert('UTC', 'UTC', $photo['created'], ATOM_TIME), + 'updated' => datetime_convert('UTC', 'UTC', $photo['edited'], ATOM_TIME), + 'id' => $arr['mid'], + 'url' => ['type' => 'Link', 'mediaType' => $photo['mimetype'], 'href' => z_root() . '/photo/' . $photo['resource_id'] . '-7'], + 'source' => ['content' => $arr['body'], 'mediaType' => 'text/x-multicode'], + 'content' => bbcode($arr['body']), + 'actor' => Activity::encode_person($channel, false), + ]; + + $acl = new AccessControl($channel); + $x = $acl->get(); + $arr['allow_cid'] = $x['allow_cid']; + + $arr['allow_gid'] = $x['allow_gid']; + $arr['deny_cid'] = $x['deny_cid']; + $arr['deny_gid'] = $x['deny_gid']; + + $arr['uid'] = $channel['channel_id']; + $arr['aid'] = $channel['channel_account_id']; + + $arr['owner_xchan'] = $channel['channel_hash']; + $arr['author_xchan'] = $channel['channel_hash']; + + post_activity_item($arr); + } + + + /** + * @brief Generate content of profile-photo view + * + * @return string + * + */ + + + public function get() + { + + if (!local_channel()) { + notice(t('Permission denied.') . EOL); + return; + } + + $channel = App::get_channel(); + + $newuser = false; + + if (argc() == 2 && argv(1) === 'new') { + $newuser = true; + } + + if (argv(1) === 'use') { + if (argc() < 3) { + notice(t('Permission denied.') . EOL); + return; + } + + // check_form_security_token_redirectOnErr('/cover_photo', 'cover_photo'); + + $resource_id = argv(2); + + $r = q( + "SELECT id, album, imgscale FROM photo WHERE uid = %d AND resource_id = '%s' and imgscale > 0 ORDER BY imgscale ASC", + intval(local_channel()), + dbesc($resource_id) + ); + if (!$r) { + notice(t('Photo not available.') . EOL); + return; + } + $havescale = false; + foreach ($r as $rr) { + if ($rr['imgscale'] == 7) { + $havescale = true; + } + } + + $r = q( + "SELECT content, mimetype, resource_id, os_storage FROM photo WHERE id = %d and uid = %d limit 1", + intval($r[0]['id']), + intval(local_channel()) + ); + if (!$r) { + notice(t('Photo not available.') . EOL); + return; + } + + if (intval($r[0]['os_storage'])) { + $data = @file_get_contents(dbunescbin($r[0]['content'])); + } else { + $data = dbunescbin($r[0]['content']); + } + + $ph = photo_factory($data, $r[0]['mimetype']); + $smallest = 0; + if ($ph && $ph->is_valid()) { + // go ahead as if we have just uploaded a new photo to crop + $i = q( + "select resource_id, imgscale from photo where resource_id = '%s' and uid = %d and imgscale = 0", + dbesc($r[0]['resource_id']), + intval(local_channel()) + ); + + if ($i) { + $hash = $i[0]['resource_id']; + foreach ($i as $ii) { + $smallest = intval($ii['imgscale']); + } + } + } + + $this->cover_photo_crop_ui_head($ph, $hash, $smallest); + } + + + if (!array_key_exists('imagecrop', App::$data)) { + $o .= replace_macros(get_markup_template('cover_photo.tpl'), [ + '$user' => App::$channel['channel_address'], + '$info' => t('Your cover photo may be visible to anybody on the internet'), + '$existing' => get_cover_photo(local_channel(), 'array', PHOTO_RES_COVER_850), + '$lbl_upfile' => t('Upload File:'), + '$lbl_profiles' => t('Select a profile:'), + '$title' => t('Change Cover Photo'), + '$submit' => t('Upload'), + '$profiles' => $profiles, + '$embedPhotos' => t('Use a photo from your albums'), + '$embedPhotosModalTitle' => t('Use a photo from your albums'), + '$embedPhotosModalCancel' => t('Cancel'), + '$embedPhotosModalOK' => t('OK'), + '$modalchooseimages' => t('Choose images to embed'), + '$modalchoosealbum' => t('Choose an album'), + '$modaldiffalbum' => t('Choose a different album'), + '$modalerrorlist' => t('Error getting album list'), + '$modalerrorlink' => t('Error getting photo link'), + '$modalerroralbum' => t('Error getting album'), + '$form_security_token' => get_form_security_token("cover_photo"), + '$select' => t('Select previously uploaded photo'), + + ]); + + call_hooks('cover_photo_content_end', $o); + + return $o; + } else { + $filename = App::$data['imagecrop'] . '-3'; + $resolution = 3; + + $o .= replace_macros(get_markup_template('cropcover.tpl'), [ + '$filename' => $filename, + '$profile' => intval($_REQUEST['profile']), + '$resource' => App::$data['imagecrop'] . '-3', + '$image_url' => z_root() . '/photo/' . $filename, + '$title' => t('Crop Image'), + '$desc' => t('Please adjust the image cropping for optimum viewing.'), + '$form_security_token' => get_form_security_token("cover_photo"), + '$done' => t('Done Editing') + ]); + return $o; + } + } + + /* @brief Generate the UI for photo-cropping + * + * @param $a Current application + * @param $ph Photo-Factory + * @return void + * + */ + + public function cover_photo_crop_ui_head($ph, $hash, $smallest) + { + + $max_length = get_config('system', 'max_image_length', MAX_IMAGE_LENGTH); + if ($max_length > 0) { + $ph->scaleImage($max_length); + } + + $width = $ph->getWidth(); + $height = $ph->getHeight(); + + if ($width < 300 || $height < 300) { + $ph->scaleImageUp(240); + $width = $ph->getWidth(); + $height = $ph->getHeight(); + } + + + App::$data['imagecrop'] = $hash; + App::$data['imagecrop_resolution'] = $smallest; + App::$page['htmlhead'] .= replace_macros(get_markup_template('crophead.tpl'), []); + return; + } } diff --git a/Zotlabs/Module/Dav.php b/Zotlabs/Module/Dav.php index 4e2fea562..d98b4680c 100644 --- a/Zotlabs/Module/Dav.php +++ b/Zotlabs/Module/Dav.php @@ -1,4 +1,5 @@ $c, 'account' => $a[0]]; + $channel_login = $c['channel_id']; + } + } + } + if (!$record) { + continue; + } - $sigblock = HTTPSig::parse_sigheader($_SERVER[$head]); - if ($sigblock) { - $keyId = str_replace('acct:','',$sigblock['keyId']); - if ($keyId) { - $r = q("select * from hubloc where ( hubloc_addr = '%s' OR hubloc_id_url = '%s' OR hubloc_hash = '%s') limit 1", - dbesc($keyId), - dbesc($keyId), - dbesc($keyId) - ); - if ($r) { - $c = channelx_by_hash($r[0]['hubloc_hash']); - if ($c) { - $a = q("select * from account where account_id = %d limit 1", - intval($c['channel_account_id']) - ); - if ($a) { - $record = [ 'channel' => $c, 'account' => $a[0] ]; - $channel_login = $c['channel_id']; - } - } - } - if (! $record) { - continue; - } + if ($record) { + $verified = HTTPSig::verify('', $record['channel']['channel_pubkey']); + if (!($verified && $verified['header_signed'] && $verified['header_valid'])) { + $record = null; + } + if ($record['account']) { + authenticate_success($record['account']); + if ($channel_login) { + change_channel($channel_login); + } + } + break; + } + } + } + } + } - if ($record) { - $verified = HTTPSig::verify('',$record['channel']['channel_pubkey']); - if (! ($verified && $verified['header_signed'] && $verified['header_valid'])) { - $record = null; - } - if ($record['account']) { - authenticate_success($record['account']); - if ($channel_login) { - change_channel($channel_login); - } - } - break; - } - } - } - } - } + if (!is_dir('store')) { + os_mkdir('store', STORAGE_DEFAULT_PERMISSIONS, false); + } - if (! is_dir('store')) - os_mkdir('store', STORAGE_DEFAULT_PERMISSIONS, false); - - if (argc() > 1) - Libprofile::load(argv(1),0); + if (argc() > 1) { + Libprofile::load(argv(1), 0); + } - $auth = new \Zotlabs\Storage\BasicAuth(); -// $auth->observer = get_observer_hash(); + $auth = new BasicAuth(); +// $auth->observer = get_observer_hash(); - $auth->setRealm(ucfirst(\Zotlabs\Lib\System::get_platform_name()) . ' ' . 'WebDAV'); + $auth->setRealm(ucfirst(System::get_platform_name()) . ' ' . 'WebDAV'); - $rootDirectory = new \Zotlabs\Storage\Directory('/', $auth); + $rootDirectory = new \Zotlabs\Storage\Directory('/', $auth); - // A SabreDAV server-object - $server = new SDAV\Server($rootDirectory); + // A SabreDAV server-object + $server = new SDAV\Server($rootDirectory); - $authPlugin = new \Sabre\DAV\Auth\Plugin($auth); - $server->addPlugin($authPlugin); + $authPlugin = new Plugin($auth); + $server->addPlugin($authPlugin); - // prevent overwriting changes each other with a lock backend - $lockBackend = new SDAV\Locks\Backend\File('cache/locks'); - $lockPlugin = new SDAV\Locks\Plugin($lockBackend); + // prevent overwriting changes each other with a lock backend + $lockBackend = new SDAV\Locks\Backend\File('cache/locks'); + $lockPlugin = new SDAV\Locks\Plugin($lockBackend); - $server->addPlugin($lockPlugin); + $server->addPlugin($lockPlugin); - // provide a directory view for the cloud in Hubzilla - $browser = new \Zotlabs\Storage\Browser($auth); - $auth->setBrowserPlugin($browser); + // provide a directory view for the cloud in Hubzilla + $browser = new Browser($auth); + $auth->setBrowserPlugin($browser); - // Experimental QuotaPlugin - // $server->addPlugin(new \Zotlabs\Storage\QuotaPlugin($auth)); + // Experimental QuotaPlugin + // $server->addPlugin(new \Zotlabs\Storage\QuotaPlugin($auth)); - // All we need to do now, is to fire up the server - $server->exec(); - - killme(); - } + // All we need to do now, is to fire up the server + $server->exec(); + killme(); + } } diff --git a/Zotlabs/Module/Defperms.php b/Zotlabs/Module/Defperms.php index 9255bbb6a..ec714b10e 100644 --- a/Zotlabs/Module/Defperms.php +++ b/Zotlabs/Module/Defperms.php @@ -1,4 +1,5 @@ $desc) { - if (array_key_exists('perms_' . $perm, $_POST)) { - if ($p) { - $p .= ','; - } - $p .= $perm; - } - } - set_abconfig($channel['channel_id'],$orig_record[0]['abook_xchan'],'system','my_perms',$p); - if ($autoperms) { - set_pconfig($channel['channel_id'],'system','autoperms',$p); - } - } - - notice( t('Settings updated.') . EOL); + if (!local_channel()) { + return; + } - - // Refresh the structure in memory with the new data - - $r = q("SELECT abook.*, xchan.* + $contact_id = intval(argv(1)); + if (!$contact_id) { + return; + } + + $channel = App::get_channel(); + + $orig_record = q( + "SELECT * FROM abook WHERE abook_id = %d AND abook_channel = %d LIMIT 1", + intval($contact_id), + intval(local_channel()) + ); + + if (!$orig_record) { + notice(t('Could not access contact record.') . EOL); + goaway(z_root() . '/connections'); + } + + + if (intval($orig_record[0]['abook_self'])) { + $autoperms = intval($_POST['autoperms']); + $is_self = true; + } else { + $autoperms = null; + $is_self = false; + } + + $all_perms = Permissions::Perms(); + + $p = EMPTY_STR; + + if ($all_perms) { + foreach ($all_perms as $perm => $desc) { + if (array_key_exists('perms_' . $perm, $_POST)) { + if ($p) { + $p .= ','; + } + $p .= $perm; + } + } + set_abconfig($channel['channel_id'], $orig_record[0]['abook_xchan'], 'system', 'my_perms', $p); + if ($autoperms) { + set_pconfig($channel['channel_id'], 'system', 'autoperms', $p); + } + } + + notice(t('Settings updated.') . EOL); + + + // Refresh the structure in memory with the new data + + $r = q( + "SELECT abook.*, xchan.* FROM abook left join xchan on abook_xchan = xchan_hash WHERE abook_channel = %d and abook_id = %d LIMIT 1", - intval(local_channel()), - intval($contact_id) - ); - if ($r) { - App::$poi = $r[0]; - } - - $this->defperms_clone($a); - - goaway(z_root() . '/defperms'); - - return; - - } - - /* @brief Clone connection - * - * - */ - - function defperms_clone(&$a) { - - if (! App::$poi) { - return; - } - - $channel = App::get_channel(); - - $r = q("SELECT abook.*, xchan.* + intval(local_channel()), + intval($contact_id) + ); + if ($r) { + App::$poi = $r[0]; + } + + $this->defperms_clone($a); + + goaway(z_root() . '/defperms'); + + return; + } + + /* @brief Clone connection + * + * + */ + + public function defperms_clone(&$a) + { + + if (!App::$poi) { + return; + } + + $channel = App::get_channel(); + + $r = q( + "SELECT abook.*, xchan.* FROM abook left join xchan on abook_xchan = xchan_hash WHERE abook_channel = %d and abook_id = %d LIMIT 1", - intval(local_channel()), - intval(App::$poi['abook_id']) - ); - if ($r) { - App::$poi = array_shift($r); - } - - $clone = App::$poi; - - unset($clone['abook_id']); - unset($clone['abook_account']); - unset($clone['abook_channel']); - - $abconfig = load_abconfig($channel['channel_id'],$clone['abook_xchan']); - if ($abconfig) { - $clone['abconfig'] = $abconfig; - } - Libsync::build_sync_packet(0 /* use the current local_channel */, array('abook' => array($clone))); - } - - /* @brief Generate content of connection default permissions page - * - * - */ - - function get() { - - $sort_type = 0; - $o = ''; - - if (! local_channel()) { - notice( t('Permission denied.') . EOL); - return login(); - } + intval(local_channel()), + intval(App::$poi['abook_id']) + ); + if ($r) { + App::$poi = array_shift($r); + } - $role = get_pconfig(local_channel(),'system','permissions_role'); - if ($role) { - notice( t('Permission denied.') . EOL); - return; - } + $clone = App::$poi; - $section = ((array_key_exists('section',$_REQUEST)) ? $_REQUEST['section'] : ''); - $channel = App::get_channel(); - - $yes_no = [ t('No'),t('Yes') ]; - - $connect_perms = Permissions::connect_perms(local_channel()); + unset($clone['abook_id']); + unset($clone['abook_account']); + unset($clone['abook_channel']); - $o .= "\n"; - - if (App::$poi) { - - $sections = []; + foreach ($connect_perms['perms'] as $p => $v) { + if ($v) { + $o .= "\$('#me_id_perms_" . $p . "').prop('checked', true); \n"; + } + } + $o .= " }\n\n"; - $self = false; - - $tpl = get_markup_template('defperms.tpl'); - - $perms = []; - $channel = App::get_channel(); + if (App::$poi) { + $sections = []; - $contact = App::$poi; - - $global_perms = Permissions::Perms(); + $self = false; - $hidden_perms = []; - - foreach ($global_perms as $k => $v) { - $thisperm = get_abconfig(local_channel(),$contact['abook_xchan'],'my_perms',$k); - - $checkinherited = PermissionLimits::Get(local_channel(),$k); + $tpl = get_markup_template('defperms.tpl'); - $inherited = (($checkinherited & PERMS_SPECIFIC) ? false : true); + $perms = []; + $channel = App::get_channel(); - $perms[] = [ 'perms_' . $k, $v, intval($thisperm), '', $yes_no, (($inherited) ? ' disabled="disabled" ' : '') ]; - if ($inherited) { - $hidden_perms[] = [ 'perms_' . $k, intval($thisperm) ]; - } - } - - $pcat = new Permcat(local_channel()); - $pcatlist = $pcat->listing(); - $permcats = []; - if ($pcatlist) { - foreach ($pcatlist as $pc) { - $permcats[$pc['name']] = $pc['localname']; - } - } + $contact = App::$poi; - $o .= replace_macros($tpl, [ - '$header' => t('Connection Default Permissions'), - '$autoperms' => array('autoperms',t('Apply these permissions automatically'), ((get_pconfig(local_channel(),'system','autoperms')) ? 1 : 0), t('If enabled, connection requests will be approved without your interaction'), $yes_no), - '$permcat' => [ 'permcat', t('Permission role'), '', '',$permcats ], - '$permcat_new' => t('Add permission role'), - '$permcat_enable' => feature_enabled(local_channel(),'permcats'), - '$section' => $section, - '$sections' => $sections, - '$autolbl' => t('The permissions indicated on this page will be applied to all new connections.'), - '$autoapprove' => t('Automatic approval settings'), - '$unapproved' => $unapproved, - '$inherited' => t('inherited'), - '$submit' => t('Submit'), - '$me' => t('My Settings'), - '$perms' => $perms, - '$hidden_perms' => $hidden_perms, - '$permlbl' => t('Individual Permissions'), - '$permnote_self' => t('Some individual permissions may have been preset or locked based on your channel type and privacy settings.'), - '$contact_id' => $contact['abook_id'], - '$name' => $contact['xchan_name'], - ]); - - $arr = array('contact' => $contact,'output' => $o); - - call_hooks('contact_edit', $arr); - - return $arr['output']; - - } - } + $global_perms = Permissions::Perms(); + + $hidden_perms = []; + + foreach ($global_perms as $k => $v) { + $thisperm = get_abconfig(local_channel(), $contact['abook_xchan'], 'my_perms', $k); + + $checkinherited = PermissionLimits::Get(local_channel(), $k); + + $inherited = (($checkinherited & PERMS_SPECIFIC) ? false : true); + + $perms[] = ['perms_' . $k, $v, intval($thisperm), '', $yes_no, (($inherited) ? ' disabled="disabled" ' : '')]; + if ($inherited) { + $hidden_perms[] = ['perms_' . $k, intval($thisperm)]; + } + } + + $pcat = new Permcat(local_channel()); + $pcatlist = $pcat->listing(); + $permcats = []; + if ($pcatlist) { + foreach ($pcatlist as $pc) { + $permcats[$pc['name']] = $pc['localname']; + } + } + + $o .= replace_macros($tpl, [ + '$header' => t('Connection Default Permissions'), + '$autoperms' => array('autoperms', t('Apply these permissions automatically'), ((get_pconfig(local_channel(), 'system', 'autoperms')) ? 1 : 0), t('If enabled, connection requests will be approved without your interaction'), $yes_no), + '$permcat' => ['permcat', t('Permission role'), '', '', $permcats], + '$permcat_new' => t('Add permission role'), + '$permcat_enable' => feature_enabled(local_channel(), 'permcats'), + '$section' => $section, + '$sections' => $sections, + '$autolbl' => t('The permissions indicated on this page will be applied to all new connections.'), + '$autoapprove' => t('Automatic approval settings'), + '$unapproved' => $unapproved, + '$inherited' => t('inherited'), + '$submit' => t('Submit'), + '$me' => t('My Settings'), + '$perms' => $perms, + '$hidden_perms' => $hidden_perms, + '$permlbl' => t('Individual Permissions'), + '$permnote_self' => t('Some individual permissions may have been preset or locked based on your channel type and privacy settings.'), + '$contact_id' => $contact['abook_id'], + '$name' => $contact['xchan_name'], + ]); + + $arr = array('contact' => $contact, 'output' => $o); + + call_hooks('contact_edit', $arr); + + return $arr['output']; + } + } } diff --git a/Zotlabs/Module/Dircensor.php b/Zotlabs/Module/Dircensor.php index 0dab52cb6..a4348b862 100644 --- a/Zotlabs/Module/Dircensor.php +++ b/Zotlabs/Module/Dircensor.php @@ -5,48 +5,48 @@ namespace Zotlabs\Module; use App; use Zotlabs\Web\Controller; +class Dircensor extends Controller +{ -class Dircensor extends Controller { + public function get() + { + if (!is_site_admin()) { + return; + } - function get() { - if(! is_site_admin()) { - return; - } + $dirmode = intval(get_config('system', 'directory_mode')); - $dirmode = intval(get_config('system','directory_mode')); + if (!($dirmode == DIRECTORY_MODE_PRIMARY || $dirmode == DIRECTORY_MODE_STANDALONE)) { + return; + } - if (! ($dirmode == DIRECTORY_MODE_PRIMARY || $dirmode == DIRECTORY_MODE_STANDALONE)) { - return; - } + $xchan = argv(1); + if (!$xchan) { + return; + } - $xchan = argv(1); - if(! $xchan) { - return; - } + $r = q( + "select * from xchan where xchan_hash = '%s'", + dbesc($xchan) + ); - $r = q("select * from xchan where xchan_hash = '%s'", - dbesc($xchan) - ); + if (!$r) { + return; + } - if(! $r) { - return; - } + $val = (($r[0]['xchan_censored']) ? 0 : 1); - $val = (($r[0]['xchan_censored']) ? 0 : 1); + q( + "update xchan set xchan_censored = $val where xchan_hash = '%s'", + dbesc($xchan) + ); - q("update xchan set xchan_censored = $val where xchan_hash = '%s'", - dbesc($xchan) - ); + if ($val) { + info(t('Entry censored') . EOL); + } else { + info(t('Entry uncensored') . EOL); + } - if( $val) { - info( t('Entry censored') . EOL); - } - else { - info( t('Entry uncensored') . EOL); - } - - goaway(z_root() . '/directory'); - - } - -} \ No newline at end of file + goaway(z_root() . '/directory'); + } +} diff --git a/Zotlabs/Module/Directory.php b/Zotlabs/Module/Directory.php index bfe44b2b7..ba3944bfd 100644 --- a/Zotlabs/Module/Directory.php +++ b/Zotlabs/Module/Directory.php @@ -12,536 +12,528 @@ require_once('include/socgraph.php'); require_once('include/bbcode.php'); require_once('include/html2plain.php'); -define( 'DIRECTORY_PAGESIZE', 60); +define('DIRECTORY_PAGESIZE', 60); -class Directory extends Controller { +class Directory extends Controller +{ - function init() { - App::set_pager_itemspage(DIRECTORY_PAGESIZE); - - if (x($_GET,'ignore') && local_channel()) { - q("insert into xign ( uid, xchan ) values ( %d, '%s' ) ", - intval(local_channel()), - dbesc($_GET['ignore']) - ); - - Libsync::build_sync_packet(local_channel(), [ 'xign' => [ [ 'uid' => local_channel(), 'xchan' => $_GET['ignore'] ]]] ); - if ($_REQUEST['return']) { - goaway(z_root() . '/' . base64_decode($_REQUEST['return'])); - } - goaway(z_root() . '/directory?f=&suggest=1'); - } - - $observer = get_observer_hash(); - $global_changed = false; - $safe_changed = false; - $type_changed = false; - $active_changed = false; - - if (array_key_exists('global',$_REQUEST)) { - $globaldir = intval($_REQUEST['global']); - if (get_config('system','localdir_hide')) { - $globaldir = 1; - } - $global_changed = true; - } - if ($global_changed) { - $_SESSION['globaldir'] = $globaldir; - if ($observer) { - set_xconfig($observer,'directory','globaldir',$globaldir); - } - } - - if (array_key_exists('safe',$_REQUEST)) { - $safemode = intval($_REQUEST['safe']); - $safe_changed = true; - } - if ($safe_changed) { - $_SESSION['safemode'] = $safemode; - if ($observer) { - set_xconfig($observer,'directory','safemode',$safemode); - } - } - - if (array_key_exists('type',$_REQUEST)) { - $type = intval($_REQUEST['type']); - $type_changed = true; - } - if ($type_changed) { - $_SESSION['chantype'] = $type; - if ($observer) { - set_xconfig($observer,'directory','chantype',$type); - } - } + public function init() + { + App::set_pager_itemspage(DIRECTORY_PAGESIZE); - if (array_key_exists('active',$_REQUEST)) { - $active = intval($_REQUEST['active']); - $active_changed = true; - } - if ($active_changed) { - $_SESSION['activedir'] = $active; - if ($observer) { - set_xconfig($observer,'directory','activedir',$active); - } - } + if (x($_GET, 'ignore') && local_channel()) { + q( + "insert into xign ( uid, xchan ) values ( %d, '%s' ) ", + intval(local_channel()), + dbesc($_GET['ignore']) + ); - } - - function get() { - - if (observer_prohibited()) { - notice( t('Public access denied.') . EOL); - return; - } + Libsync::build_sync_packet(local_channel(), ['xign' => [['uid' => local_channel(), 'xchan' => $_GET['ignore']]]]); + if ($_REQUEST['return']) { + goaway(z_root() . '/' . base64_decode($_REQUEST['return'])); + } + goaway(z_root() . '/directory?f=&suggest=1'); + } - $observer = get_observer_hash(); + $observer = get_observer_hash(); + $global_changed = false; + $safe_changed = false; + $type_changed = false; + $active_changed = false; - if (get_config('system','block_public_directory',true) && (! $observer)) { - notice( t('Public access denied.') . EOL); - return login(false); - } - - $globaldir = Libzotdir::get_directory_setting($observer, 'globaldir'); + if (array_key_exists('global', $_REQUEST)) { + $globaldir = intval($_REQUEST['global']); + if (get_config('system', 'localdir_hide')) { + $globaldir = 1; + } + $global_changed = true; + } + if ($global_changed) { + $_SESSION['globaldir'] = $globaldir; + if ($observer) { + set_xconfig($observer, 'directory', 'globaldir', $globaldir); + } + } - // override your personal global search pref if we're doing a navbar search of the directory - if (intval($_REQUEST['navsearch'])) { - $globaldir = 1; - } - - $safe_mode = Libzotdir::get_directory_setting($observer, 'safemode'); - - $type = Libzotdir::get_directory_setting($observer, 'chantype'); + if (array_key_exists('safe', $_REQUEST)) { + $safemode = intval($_REQUEST['safe']); + $safe_changed = true; + } + if ($safe_changed) { + $_SESSION['safemode'] = $safemode; + if ($observer) { + set_xconfig($observer, 'directory', 'safemode', $safemode); + } + } - $active = Libzotdir::get_directory_setting($observer, 'activedir'); + if (array_key_exists('type', $_REQUEST)) { + $type = intval($_REQUEST['type']); + $type_changed = true; + } + if ($type_changed) { + $_SESSION['chantype'] = $type; + if ($observer) { + set_xconfig($observer, 'directory', 'chantype', $type); + } + } - $o = ''; - nav_set_selected('Directory'); - - if (x($_POST,'search')) { - $search = notags(trim($_POST['search'])); - } - else { - $search = ((x($_GET,'search')) ? notags(trim(rawurldecode($_GET['search']))) : ''); - } - - if (strpos($search,'=')) { - $advanced = $search; - } - - $keywords = (($_GET['keywords']) ? $_GET['keywords'] : ''); - - // Suggest channels if no search terms or keywords are given - $suggest = (local_channel() && x($_REQUEST,'suggest')) ? $_REQUEST['suggest'] : ''; - - if ($suggest) { + if (array_key_exists('active', $_REQUEST)) { + $active = intval($_REQUEST['active']); + $active_changed = true; + } + if ($active_changed) { + $_SESSION['activedir'] = $active; + if ($observer) { + set_xconfig($observer, 'directory', 'activedir', $active); + } + } + } - // the directory options have no effect in suggestion mode - - $globaldir = 1; - $safe_mode = 1; - $active = 1; - $type = 0; + public function get() + { - // only return DIRECTORY_PAGESIZE suggestions as the suggestion sorting - // only works if the suggestion query and the directory query have the - // same number of results + if (observer_prohibited()) { + notice(t('Public access denied.') . EOL); + return; + } - App::set_pager_itemspage(60); - $r = suggestion_query(local_channel(),get_observer_hash(),App::$pager['start'],DIRECTORY_PAGESIZE); + $observer = get_observer_hash(); - if (! $r) { - if ($_REQUEST['aj']) { - echo '
        '; - killme(); - } + if (get_config('system', 'block_public_directory', true) && (!$observer)) { + notice(t('Public access denied.') . EOL); + return login(false); + } - notice( t('No default suggestions were found.') . EOL); - return; - } - - // Remember in which order the suggestions were - $addresses = []; - $common = []; - $index = 0; - foreach ($r as $rr) { - $common[$rr['xchan_hash']] = ((intval($rr['total']) > 0) ? intval($rr['total']) - 1 : 0); - $addresses[$rr['xchan_hash']] = $index++; - } - - // Build query to get info about suggested people - $advanced = ''; - foreach (array_keys($addresses) as $address) { - $advanced .= "xhash=\"$address\" "; - } - // Remove last space in the advanced query - $advanced = rtrim($advanced); - - } - - $tpl = get_markup_template('directory_header.tpl'); - - $dirmode = intval(get_config('system','directory_mode')); + $globaldir = Libzotdir::get_directory_setting($observer, 'globaldir'); - $directory_admin = false; + // override your personal global search pref if we're doing a navbar search of the directory + if (intval($_REQUEST['navsearch'])) { + $globaldir = 1; + } - $url = z_root() . '/dirsearch'; - if (is_sys_channel(local_channel())) { - $directory_admin = true; - } - - $contacts = []; - - if (local_channel()) { - $x = q("select abook_xchan from abook where abook_channel = %d", - intval(local_channel()) - ); - if ($x) { - foreach ($x as $xx) { - $contacts[] = $xx['abook_xchan']; - } - } - } - - if ($url) { - - $numtags = get_config('system','directorytags'); - - $kw = ((intval($numtags) > 0) ? intval($numtags) : 50); - - if (get_config('system','disable_directory_keywords')) { - $kw = 0; - } - - $query = $url . '?f=&kw=' . $kw . (($safe_mode != 1) ? '&safe=' . $safe_mode : ''); - - if ($token) { - $query .= '&t=' . $token; - } - - if (! $globaldir) { - $query .= '&hub=' . App::get_hostname(); - } - if ($search) { - $query .= '&name=' . urlencode($search) . '&keywords=' . urlencode($search); - } - if (strpos($search,'@')) { - $query .= '&address=' . urlencode($search); - } - if (strpos($search,'http') === 0) { - $query .= '&url=' . urlencode($search); - } - if ($keywords) { - $query .= '&keywords=' . urlencode($keywords); - } - if ($advanced) { - $query .= '&query=' . urlencode($advanced); - } - if (! is_null($type)) { - $query .= '&type=' . intval($type); - } - $directory_sort_order = get_config('system','directory_sort_order'); - if (! $directory_sort_order) { - $directory_sort_order = 'date'; - } - - $sort_order = ((x($_REQUEST,'order')) ? $_REQUEST['order'] : $directory_sort_order); - - if ($sort_order) { - $query .= '&order=' . urlencode($sort_order); - } - - if (App::$pager['page'] != 1) { - $query .= '&p=' . App::$pager['page']; - } + $safe_mode = Libzotdir::get_directory_setting($observer, 'safemode'); - if (isset($active)) { - $query .= '&active=' . intval($active); - } + $type = Libzotdir::get_directory_setting($observer, 'chantype'); + + $active = Libzotdir::get_directory_setting($observer, 'activedir'); + + $o = ''; + nav_set_selected('Directory'); + + if (x($_POST, 'search')) { + $search = notags(trim($_POST['search'])); + } else { + $search = ((x($_GET, 'search')) ? notags(trim(rawurldecode($_GET['search']))) : ''); + } + + if (strpos($search, '=')) { + $advanced = $search; + } + + $keywords = (($_GET['keywords']) ? $_GET['keywords'] : ''); + + // Suggest channels if no search terms or keywords are given + $suggest = (local_channel() && x($_REQUEST, 'suggest')) ? $_REQUEST['suggest'] : ''; + + if ($suggest) { + // the directory options have no effect in suggestion mode + + $globaldir = 1; + $safe_mode = 1; + $active = 1; + $type = 0; + + // only return DIRECTORY_PAGESIZE suggestions as the suggestion sorting + // only works if the suggestion query and the directory query have the + // same number of results + + App::set_pager_itemspage(60); + $r = suggestion_query(local_channel(), get_observer_hash(), App::$pager['start'], DIRECTORY_PAGESIZE); + + if (!$r) { + if ($_REQUEST['aj']) { + echo '
        '; + killme(); + } + + notice(t('No default suggestions were found.') . EOL); + return; + } + + // Remember in which order the suggestions were + $addresses = []; + $common = []; + $index = 0; + foreach ($r as $rr) { + $common[$rr['xchan_hash']] = ((intval($rr['total']) > 0) ? intval($rr['total']) - 1 : 0); + $addresses[$rr['xchan_hash']] = $index++; + } + + // Build query to get info about suggested people + $advanced = ''; + foreach (array_keys($addresses) as $address) { + $advanced .= "xhash=\"$address\" "; + } + // Remove last space in the advanced query + $advanced = rtrim($advanced); + } + + $tpl = get_markup_template('directory_header.tpl'); + + $dirmode = intval(get_config('system', 'directory_mode')); + + $directory_admin = false; + + $url = z_root() . '/dirsearch'; + if (is_sys_channel(local_channel())) { + $directory_admin = true; + } + + $contacts = []; + + if (local_channel()) { + $x = q( + "select abook_xchan from abook where abook_channel = %d", + intval(local_channel()) + ); + if ($x) { + foreach ($x as $xx) { + $contacts[] = $xx['abook_xchan']; + } + } + } + + if ($url) { + $numtags = get_config('system', 'directorytags'); + + $kw = ((intval($numtags) > 0) ? intval($numtags) : 50); + + if (get_config('system', 'disable_directory_keywords')) { + $kw = 0; + } + + $query = $url . '?f=&kw=' . $kw . (($safe_mode != 1) ? '&safe=' . $safe_mode : ''); + + if ($token) { + $query .= '&t=' . $token; + } + + if (!$globaldir) { + $query .= '&hub=' . App::get_hostname(); + } + if ($search) { + $query .= '&name=' . urlencode($search) . '&keywords=' . urlencode($search); + } + if (strpos($search, '@')) { + $query .= '&address=' . urlencode($search); + } + if (strpos($search, 'http') === 0) { + $query .= '&url=' . urlencode($search); + } + if ($keywords) { + $query .= '&keywords=' . urlencode($keywords); + } + if ($advanced) { + $query .= '&query=' . urlencode($advanced); + } + if (!is_null($type)) { + $query .= '&type=' . intval($type); + } + $directory_sort_order = get_config('system', 'directory_sort_order'); + if (!$directory_sort_order) { + $directory_sort_order = 'date'; + } + + $sort_order = ((x($_REQUEST, 'order')) ? $_REQUEST['order'] : $directory_sort_order); + + if ($sort_order) { + $query .= '&order=' . urlencode($sort_order); + } + + if (App::$pager['page'] != 1) { + $query .= '&p=' . App::$pager['page']; + } + + if (isset($active)) { + $query .= '&active=' . intval($active); + } - // logger('mod_directory: query: ' . $query); - - $x = z_fetch_url($query); - // logger('directory: return from upstream: ' . print_r($x,true), LOGGER_DATA); - - if ($x['success']) { - $t = 0; - $j = json_decode($x['body'],true); - if ($j) { - - if ($j['results']) { + // logger('mod_directory: query: ' . $query); - $results = $j['results']; - if ($suggest) { - // change order to "number of common friends descending" - $results = self::reorder_results($results,$addresses); - } + $x = z_fetch_url($query); + // logger('directory: return from upstream: ' . print_r($x,true), LOGGER_DATA); - $entries = []; - - $photo = 'thumb'; - - foreach ($results as $rr) { - - $profile_link = chanlink_url($rr['url']); - - $pdesc = (($rr['description']) ? $rr['description'] . '
        ' : ''); - $connect_link = ((local_channel()) ? z_root() . '/follow?f=&url=' . urlencode($rr['address']) : ''); - - // Checking status is disabled ATM until someone checks the performance impact more carefully - //$online = remote_online_status($rr['address']); - $online = ''; - - if (in_array($rr['hash'],$contacts)) { - $connect_link = ''; - } - - $location = ''; - if (strlen($rr['locale'])) { - $location .= $rr['locale']; - } - if (strlen($rr['region'])) { - if (strlen($rr['locale'])) { - $location .= ', '; - } - $location .= $rr['region']; - } - if (strlen($rr['country'])) { - if (strlen($location)) { - $location .= ', '; - } - $location .= $rr['country']; - } - - $age = ''; - if (strlen($rr['birthday'])) { - if (($years = age($rr['birthday'],'UTC','')) > 0) { - $age = $years; - } - } - - $page_type = ''; - - $total_ratings = ''; - - $profile = $rr; - - $gender = ((x($profile,'gender') == 1) ? t('Gender: ') . $profile['gender']: False); - $marital = ((x($profile,'marital') == 1) ? t('Status: ') . $profile['marital']: False); - $homepage = ((x($profile,'homepage') == 1) ? t('Homepage: ') : False); - $homepageurl = ((x($profile,'homepage') == 1) ? html2plain($profile['homepage']) : ''); - $hometown = ((x($profile,'hometown') == 1) ? html2plain($profile['hometown']) : False); - $about = ((x($profile,'about') == 1) ? zidify_links(bbcode($profile['about'])) : False); - - $keywords = ((x($profile,'keywords')) ? $profile['keywords'] : ''); - - $out = ''; - - if ($keywords) { - $keywords = str_replace(',',' ', $keywords); - $keywords = str_replace(' ',' ', $keywords); - $karr = explode(' ', $keywords); - - if ($karr) { - if (local_channel()) { - $pk = q("select keywords from profile where uid = %d and is_default = 1 limit 1", - intval(local_channel()) - ); - if ($pk) { - $keywords = str_replace(',',' ', $pk[0]['keywords']); - $keywords = str_replace(' ',' ', $keywords); - $marr = explode(' ', $keywords); - } - } - foreach ($karr as $k) { - if (strlen($out)) { - $out .= ', '; - } - if ($marr && in_arrayi($k,$marr)) { - $out .= '' . $k . ''; - } - else { - $out .= '' . $k . ''; - } - } - } - } - - $entry = [ - 'id' => ++ $t, - 'profile_link' => $profile_link, - 'type' => $rr['type'], - 'photo' => $rr['photo'], - 'hash' => $rr['hash'], - 'alttext' => $rr['name'] . ((local_channel() || remote_channel()) ? ' ' . $rr['address'] : ''), - 'name' => $rr['name'], - 'age' => $age, - 'age_label' => t('Age:'), - 'profile' => $profile, - 'address' => $rr['address'], - 'nickname' => substr($rr['address'],0,strpos($rr['address'],'@')), - 'location' => $location, - 'location_label' => t('Location:'), - 'gender' => $gender, - 'total_ratings' => $total_ratings, - 'viewrate' => true, - 'canrate' => (($rating_enabled && local_channel()) ? true : false), - // 'network' => network_to_name($rr['network']), - // 'network_label' => t('Network:'), - 'pdesc' => $pdesc, - 'pdesc_label' => t('Description:'), - 'censor' => (($directory_admin) ? 'dircensor/' . $rr['hash'] : ''), - 'censor_label' => (($rr['censored']) ? t('Uncensor') : t('Censor')), - 'marital' => $marital, - 'homepage' => $homepage, - 'homepageurl' => (($safe_mode) ? $homepageurl : linkify($homepageurl)), - 'hometown' => $hometown, - 'hometown_label' => t('Hometown:'), - 'about' => $about, - 'about_label' => t('About:'), - 'conn_label' => t('Connect'), - 'forum_label' => t('Group'), - 'collections_label' => t('Collection'), - 'connect' => $connect_link, - 'online' => $online, - 'kw' => (($out) ? t('Keywords: ') : ''), - 'keywords' => $out, - 'ignlink' => $suggest ? z_root() . '/directory?ignore=' . $rr['hash'] : '', - 'ignore_label' => t('Don\'t suggest'), - 'common_friends' => (($common[$rr['hash']]) ? intval($common[$rr['hash']]) : ''), - 'common_label' => t('Suggestion ranking:'), - 'common_count' => intval($common[$rr['hash']]), - 'safe' => $safe_mode - ]; + if ($x['success']) { + $t = 0; + $j = json_decode($x['body'], true); + if ($j) { + if ($j['results']) { + $results = $j['results']; + if ($suggest) { + // change order to "number of common friends descending" + $results = self::reorder_results($results, $addresses); + } + + $entries = []; + + $photo = 'thumb'; + + foreach ($results as $rr) { + $profile_link = chanlink_url($rr['url']); + + $pdesc = (($rr['description']) ? $rr['description'] . '
        ' : ''); + $connect_link = ((local_channel()) ? z_root() . '/follow?f=&url=' . urlencode($rr['address']) : ''); + + // Checking status is disabled ATM until someone checks the performance impact more carefully + //$online = remote_online_status($rr['address']); + $online = ''; + + if (in_array($rr['hash'], $contacts)) { + $connect_link = ''; + } + + $location = ''; + if (strlen($rr['locale'])) { + $location .= $rr['locale']; + } + if (strlen($rr['region'])) { + if (strlen($rr['locale'])) { + $location .= ', '; + } + $location .= $rr['region']; + } + if (strlen($rr['country'])) { + if (strlen($location)) { + $location .= ', '; + } + $location .= $rr['country']; + } + + $age = ''; + if (strlen($rr['birthday'])) { + if (($years = age($rr['birthday'], 'UTC', '')) > 0) { + $age = $years; + } + } + + $page_type = ''; + + $total_ratings = ''; + + $profile = $rr; + + $gender = ((x($profile, 'gender') == 1) ? t('Gender: ') . $profile['gender'] : false); + $marital = ((x($profile, 'marital') == 1) ? t('Status: ') . $profile['marital'] : false); + $homepage = ((x($profile, 'homepage') == 1) ? t('Homepage: ') : false); + $homepageurl = ((x($profile, 'homepage') == 1) ? html2plain($profile['homepage']) : ''); + $hometown = ((x($profile, 'hometown') == 1) ? html2plain($profile['hometown']) : false); + $about = ((x($profile, 'about') == 1) ? zidify_links(bbcode($profile['about'])) : false); + + $keywords = ((x($profile, 'keywords')) ? $profile['keywords'] : ''); + + $out = ''; + + if ($keywords) { + $keywords = str_replace(',', ' ', $keywords); + $keywords = str_replace(' ', ' ', $keywords); + $karr = explode(' ', $keywords); + + if ($karr) { + if (local_channel()) { + $pk = q( + "select keywords from profile where uid = %d and is_default = 1 limit 1", + intval(local_channel()) + ); + if ($pk) { + $keywords = str_replace(',', ' ', $pk[0]['keywords']); + $keywords = str_replace(' ', ' ', $keywords); + $marr = explode(' ', $keywords); + } + } + foreach ($karr as $k) { + if (strlen($out)) { + $out .= ', '; + } + if ($marr && in_arrayi($k, $marr)) { + $out .= '' . $k . ''; + } else { + $out .= '' . $k . ''; + } + } + } + } + + $entry = [ + 'id' => ++$t, + 'profile_link' => $profile_link, + 'type' => $rr['type'], + 'photo' => $rr['photo'], + 'hash' => $rr['hash'], + 'alttext' => $rr['name'] . ((local_channel() || remote_channel()) ? ' ' . $rr['address'] : ''), + 'name' => $rr['name'], + 'age' => $age, + 'age_label' => t('Age:'), + 'profile' => $profile, + 'address' => $rr['address'], + 'nickname' => substr($rr['address'], 0, strpos($rr['address'], '@')), + 'location' => $location, + 'location_label' => t('Location:'), + 'gender' => $gender, + 'total_ratings' => $total_ratings, + 'viewrate' => true, + 'canrate' => (($rating_enabled && local_channel()) ? true : false), + // 'network' => network_to_name($rr['network']), + // 'network_label' => t('Network:'), + 'pdesc' => $pdesc, + 'pdesc_label' => t('Description:'), + 'censor' => (($directory_admin) ? 'dircensor/' . $rr['hash'] : ''), + 'censor_label' => (($rr['censored']) ? t('Uncensor') : t('Censor')), + 'marital' => $marital, + 'homepage' => $homepage, + 'homepageurl' => (($safe_mode) ? $homepageurl : linkify($homepageurl)), + 'hometown' => $hometown, + 'hometown_label' => t('Hometown:'), + 'about' => $about, + 'about_label' => t('About:'), + 'conn_label' => t('Connect'), + 'forum_label' => t('Group'), + 'collections_label' => t('Collection'), + 'connect' => $connect_link, + 'online' => $online, + 'kw' => (($out) ? t('Keywords: ') : ''), + 'keywords' => $out, + 'ignlink' => $suggest ? z_root() . '/directory?ignore=' . $rr['hash'] : '', + 'ignore_label' => t('Don\'t suggest'), + 'common_friends' => (($common[$rr['hash']]) ? intval($common[$rr['hash']]) : ''), + 'common_label' => t('Suggestion ranking:'), + 'common_count' => intval($common[$rr['hash']]), + 'safe' => $safe_mode + ]; - $blocked = LibBlock::fetch($channel['channel_id'],BLOCKTYPE_SERVER); + $blocked = LibBlock::fetch($channel['channel_id'], BLOCKTYPE_SERVER); - $found_block = false; - if ($blocked) { - foreach ($blocked as $b) { - if (strpos($rr['site_url'],$b['block_entity']) !== false) { - $found_block = true; - break; - } - } - if ($found_block) { - continue; - } - } + $found_block = false; + if ($blocked) { + foreach ($blocked as $b) { + if (strpos($rr['site_url'], $b['block_entity']) !== false) { + $found_block = true; + break; + } + } + if ($found_block) { + continue; + } + } - if (LibBlock::fetch_by_entity(local_channel(), $entry['hash'])) { - continue; - } + if (LibBlock::fetch_by_entity(local_channel(), $entry['hash'])) { + continue; + } - $arr = array('contact' => $rr, 'entry' => $entry); - - call_hooks('directory_item', $arr); - - unset($profile); - unset($location); - - if (! $arr['entry']) { - continue; - } - - if ($sort_order == '' && $suggest) { - $entries[$addresses[$rr['address']]] = $arr['entry']; // Use the same indexes as originally to get the best suggestion first - } - else { - $entries[] = $arr['entry']; - } - } - - ksort($entries); // Sort array by key so that foreach-constructs work as expected - - if ($j['keywords']) { - App::$data['directory_keywords'] = $j['keywords']; - } - - // logger('mod_directory: entries: ' . print_r($entries,true), LOGGER_DATA); - - - if ($_REQUEST['aj']) { - if ($entries) { - $o = replace_macros(get_markup_template('directajax.tpl'), [ '$entries' => $entries ] ); - } - else { - $o = '
        '; - } - echo $o; - killme(); - } - else { - $maxheight = 94; - - $dirtitle = (($globaldir) ? t('Directory') : t('Local Directory')); - - $o .= ""; - $o .= replace_macros($tpl, [ - '$search' => $search, - '$desc' => t('Find'), - '$finddsc' => t('Finding:'), - '$safetxt' => htmlspecialchars($search,ENT_QUOTES,'UTF-8'), - '$entries' => $entries, - '$dirlbl' => $suggest ? t('Channel Suggestions') : $dirtitle, - '$submit' => t('Find'), - '$next' => alt_pager($j['records'], t('next page'), t('previous page')), - '$sort' => t('Sort options'), - '$normal' => t('Alphabetic'), - '$reverse' => t('Reverse Alphabetic'), - '$date' => t('Newest to Oldest'), - '$reversedate' => t('Oldest to Newest'), - '$suggest' => $suggest ? '&suggest=1' : '' - ]); - } - } - else { - if ($_REQUEST['aj']) { - $o = '
        '; - echo $o; - killme(); - } - if ($search && App::$pager['page'] == 1 && $j['records'] == 0) { - if (strpos($search,'@')) { - goaway(z_root() . '/chanview/?f=&address=' . $search); - } - elseif(strpos($search,'http') === 0) { - goaway(z_root() . '/chanview/?f=&url=' . $search); - } - else { - $r = q("select xchan_hash from xchan where xchan_name = '%s' limit 1", - dbesc($search) - ); - if ($r) { - goaway(z_root() . '/chanview/?f=&hash=' . urlencode($r[0]['xchan_hash'])); - } - } - } - info( t("No entries (some entries may be hidden).") . EOL); - } - } - } - } - return $o; - } + $arr = array('contact' => $rr, 'entry' => $entry); + + call_hooks('directory_item', $arr); + + unset($profile); + unset($location); + + if (!$arr['entry']) { + continue; + } + + if ($sort_order == '' && $suggest) { + $entries[$addresses[$rr['address']]] = $arr['entry']; // Use the same indexes as originally to get the best suggestion first + } else { + $entries[] = $arr['entry']; + } + } + + ksort($entries); // Sort array by key so that foreach-constructs work as expected + + if ($j['keywords']) { + App::$data['directory_keywords'] = $j['keywords']; + } + + // logger('mod_directory: entries: ' . print_r($entries,true), LOGGER_DATA); - static public function reorder_results($results,$suggests) { - if (! $suggests) { - return $results; - } + if ($_REQUEST['aj']) { + if ($entries) { + $o = replace_macros(get_markup_template('directajax.tpl'), ['$entries' => $entries]); + } else { + $o = '
        '; + } + echo $o; + killme(); + } else { + $maxheight = 94; - $out = []; - foreach ($suggests as $k => $v) { - foreach ($results as $rv) { - if ($k == $rv['hash']) { - $out[intval($v)] = $rv; - break; - } - } - } - return $out; - } - + $dirtitle = (($globaldir) ? t('Directory') : t('Local Directory')); + + $o .= ""; + $o .= replace_macros($tpl, [ + '$search' => $search, + '$desc' => t('Find'), + '$finddsc' => t('Finding:'), + '$safetxt' => htmlspecialchars($search, ENT_QUOTES, 'UTF-8'), + '$entries' => $entries, + '$dirlbl' => $suggest ? t('Channel Suggestions') : $dirtitle, + '$submit' => t('Find'), + '$next' => alt_pager($j['records'], t('next page'), t('previous page')), + '$sort' => t('Sort options'), + '$normal' => t('Alphabetic'), + '$reverse' => t('Reverse Alphabetic'), + '$date' => t('Newest to Oldest'), + '$reversedate' => t('Oldest to Newest'), + '$suggest' => $suggest ? '&suggest=1' : '' + ]); + } + } else { + if ($_REQUEST['aj']) { + $o = '
        '; + echo $o; + killme(); + } + if ($search && App::$pager['page'] == 1 && $j['records'] == 0) { + if (strpos($search, '@')) { + goaway(z_root() . '/chanview/?f=&address=' . $search); + } elseif (strpos($search, 'http') === 0) { + goaway(z_root() . '/chanview/?f=&url=' . $search); + } else { + $r = q( + "select xchan_hash from xchan where xchan_name = '%s' limit 1", + dbesc($search) + ); + if ($r) { + goaway(z_root() . '/chanview/?f=&hash=' . urlencode($r[0]['xchan_hash'])); + } + } + } + info(t("No entries (some entries may be hidden).") . EOL); + } + } + } + } + return $o; + } + + + public static function reorder_results($results, $suggests) + { + if (!$suggests) { + return $results; + } + + $out = []; + foreach ($suggests as $k => $v) { + foreach ($results as $rv) { + if ($k == $rv['hash']) { + $out[intval($v)] = $rv; + break; + } + } + } + return $out; + } } diff --git a/Zotlabs/Module/Dirsearch.php b/Zotlabs/Module/Dirsearch.php index c6368dfc5..754b004ba 100644 --- a/Zotlabs/Module/Dirsearch.php +++ b/Zotlabs/Module/Dirsearch.php @@ -7,455 +7,460 @@ use Zotlabs\Web\Controller; // This is the primary endpoint for communicating with Zot directory services. -class Dirsearch extends Controller { +class Dirsearch extends Controller +{ - function init() { - App::set_pager_itemspage(60); - } - - function get() { - - $ret = [ 'success' => false ]; - - // logger('request: ' . print_r($_REQUEST,true)); - + public function init() + { + App::set_pager_itemspage(60); + } - if (argc() > 1 && argv(1) === 'sites') { - $ret = $this->list_public_sites(); - json_return_and_die($ret); - } + public function get() + { - $dirmode = intval(get_config('system','directory_mode')); + $ret = ['success' => false]; + + // logger('request: ' . print_r($_REQUEST,true)); - $network = EMPTY_STR; + if (argc() > 1 && argv(1) === 'sites') { + $ret = $this->list_public_sites(); + json_return_and_die($ret); + } - $sql_extra = ''; - - $tables = [ 'name', 'address', 'xhash', 'locale', 'region', 'postcode', - 'country', 'gender', 'marital', 'sexual', 'keywords' ]; - - // parse advanced query if present - - if ($_REQUEST['query']) { - $advanced = $this->dir_parse_query($_REQUEST['query']); - if ($advanced) { - foreach ($advanced as $adv) { - if (in_array($adv['field'],$tables)) { - if ($adv['field'] === 'name') - $sql_extra .= $this->dir_query_build($adv['logic'],'xchan_name',$adv['value']); - elseif ($adv['field'] === 'address') - $sql_extra .= $this->dir_query_build($adv['logic'],'xchan_addr',$adv['value']); - elseif ($adv['field'] === 'xhash') - $sql_extra .= $this->dir_query_build($adv['logic'],'xchan_hash',$adv['value']); - else - $sql_extra .= $this->dir_query_build($adv['logic'],'xprof_' . $adv['field'],$adv['value']); - } - } - } - } - - $hash = ((x($_REQUEST['hash'])) ? $_REQUEST['hash'] : ''); - $name = ((x($_REQUEST,'name')) ? $_REQUEST['name'] : ''); - $url = ((x($_REQUEST,'url')) ? $_REQUEST['url'] : ''); - $hub = ((x($_REQUEST,'hub')) ? $_REQUEST['hub'] : ''); - $address = ((x($_REQUEST,'address')) ? $_REQUEST['address'] : ''); - $locale = ((x($_REQUEST,'locale')) ? $_REQUEST['locale'] : ''); - $region = ((x($_REQUEST,'region')) ? $_REQUEST['region'] : ''); - $postcode = ((x($_REQUEST,'postcode')) ? $_REQUEST['postcode'] : ''); - $country = ((x($_REQUEST,'country')) ? $_REQUEST['country'] : ''); - $gender = ((x($_REQUEST,'gender')) ? $_REQUEST['gender'] : ''); - $marital = ((x($_REQUEST,'marital')) ? $_REQUEST['marital'] : ''); - $sexual = ((x($_REQUEST,'sexual')) ? $_REQUEST['sexual'] : ''); - $keywords = ((x($_REQUEST,'keywords')) ? $_REQUEST['keywords'] : ''); - $agege = ((x($_REQUEST,'agege')) ? intval($_REQUEST['agege']) : 0 ); - $agele = ((x($_REQUEST,'agele')) ? intval($_REQUEST['agele']) : 0 ); - $kw = ((x($_REQUEST,'kw')) ? intval($_REQUEST['kw']) : 0 ); - $active = ((x($_REQUEST,'active')) ? intval($_REQUEST['active']) : 0 ); - $type = ((array_key_exists('type',$_REQUEST)) ? intval($_REQUEST['type']) : 0); - - // allow a site to disable the directory's keyword list - if (get_config('system','disable_directory_keywords')) - $kw = 0; - - // by default use a safe search - $safe = ((x($_REQUEST,'safe'))); - if ($safe === false) { - $safe = 1; - } - - // Directory mirrors will request sync packets, which are lists - // of records that have changed since the sync datetime. - - if (array_key_exists('sync',$_REQUEST)) { - if ($_REQUEST['sync']) - $sync = datetime_convert('UTC','UTC',$_REQUEST['sync']); - else - $sync = datetime_convert('UTC','UTC','2010-01-01 01:01:00'); - } - else - $sync = false; - - if (($dirmode == DIRECTORY_MODE_STANDALONE) && (! $hub)) { - $hub = App::get_hostname(); - } - - if ($hub) { - $hub_query = " and xchan_hash in (select hubloc_hash from hubloc where hubloc_host = '" . protect_sprintf(dbesc($hub)) . "') "; - } - else { - $hub_query = ''; - } - - if ($url) { - $r = q("select xchan_name from hubloc left join xchan on hubloc_hash = xchan_hash where hubloc_url = '%s' or hubloc_id_url = '%s'", - dbesc($url), - dbesc($url) - ); - if ($r && $r[0]['xchan_name']) { - $name = $r[0]['xchan_name']; - } - } - - // The order identifier is validated further below - - $sort_order = ((x($_REQUEST,'order')) ? $_REQUEST['order'] : ''); + $dirmode = intval(get_config('system', 'directory_mode')); - // parse and assemble the query for advanced searches - - $joiner = ' OR '; + $network = EMPTY_STR; - if($_REQUEST['and']) - $joiner = ' AND '; - - if ($name) { - $sql_extra .= $this->dir_query_build($joiner,'xchan_name',$name); - } - if ($address) { - $sql_extra .= $this->dir_query_build($joiner,'xchan_addr',$address); - } - if ($hash) { - $sql_extra .= $this->dir_query_build($joiner,'xchan_hash',$hash); - } - if ($locale) { - $sql_extra .= $this->dir_query_build($joiner,'xprof_locale',$locale); - } - if ($region) { - $sql_extra .= $this->dir_query_build($joiner,'xprof_region',$region); - } - if ($postcode) { - $sql_extra .= $this->dir_query_build($joiner,'xprof_postcode',$postcode); - } - if ($country) { - $sql_extra .= $this->dir_query_build($joiner,'xprof_country',$country); - } - if ($gender) { - $sql_extra .= $this->dir_query_build($joiner,'xprof_gender',$gender); - } - if ($marital) { - $sql_extra .= $this->dir_query_build($joiner,'xprof_marital',$marital); - } - if ($sexual) { - $sql_extra .= $this->dir_query_build($joiner,'xprof_sexual',$sexual); - } - if ($keywords) { - $sql_extra .= $this->dir_query_build($joiner,'xprof_keywords',$keywords); - } - - // we only support an age range currently. You must set both agege - // (greater than or equal) and agele (less than or equal) - - if($agele && $agege) { - $sql_extra .= " $joiner ( xprof_age <= " . intval($agele) . " "; - $sql_extra .= " AND xprof_age >= " . intval($agege) . ") "; - } - - - $perpage = (($_REQUEST['n']) ? $_REQUEST['n'] : 60); - $page = (($_REQUEST['p']) ? intval($_REQUEST['p'] - 1) : 0); - $startrec = (($page+1) * $perpage) - $perpage; - $limit = (($_REQUEST['limit']) ? intval($_REQUEST['limit']) : 0); - $return_total = ((x($_REQUEST,'return_total')) ? intval($_REQUEST['return_total']) : 0); - - // mtime is not currently working - - $mtime = ((x($_REQUEST,'mtime')) ? datetime_convert('UTC','UTC',$_REQUEST['mtime']) : ''); - - // merge them into xprof - - $ret['success'] = true; - - // If &limit=n, return at most n entries - // If &return_total=1, we count matching entries and return that as 'total_items' for use in pagination. - // By default we return one page (default 60 items maximum) and do not count total entries - - $logic = ((strlen($sql_extra)) ? 'false' : 'true'); - - if ($hash) { - $logic = 'true'; - } - - if ($dirmode == DIRECTORY_MODE_STANDALONE) { - $sql_extra .= " and xchan_addr like '%%" . App::get_hostname() . "' "; - } - - $safesql = (($safe > 0) ? " and xchan_censored = 0 and xchan_selfcensored = 0 " : ''); - if ($safe < 0) { - $safesql = " and ( xchan_censored = 1 OR xchan_selfcensored = 1 ) "; - } - - if ($type) { - $safesql .= " and xchan_type = " . intval($type); - } + $sql_extra = ''; - $activesql = EMPTY_STR; - - if ($active) { - $activesql = "and xchan_updated > '" . datetime_convert(date_default_timezone_get(),'UTC','now - 60 days') . "' "; - } + $tables = ['name', 'address', 'xhash', 'locale', 'region', 'postcode', + 'country', 'gender', 'marital', 'sexual', 'keywords']; - if ($limit) { - $qlimit = " LIMIT $limit "; - } - else { - $qlimit = " LIMIT " . intval($perpage) . " OFFSET " . intval($startrec); - if ($return_total) { - $r = q("SELECT COUNT(xchan_hash) AS total FROM xchan left join xprof on xchan_hash = xprof_hash where $logic $sql_extra $network and xchan_hidden = 0 and xchan_orphan = 0 and xchan_deleted = 0 $safesql $activesql "); - if ($r) { - $ret['total_items'] = $r[0]['total']; - } - } - } - - if ($sort_order == 'normal') { - $order = " order by xchan_name asc "; - - // Start the alphabetic search at 'A' - // This will make a handful of channels whose names begin with - // punctuation un-searchable in this mode - - $safesql .= " and ascii(substring(xchan_name FROM 1 FOR 1)) > 64 "; - } - elseif ($sort_order == 'reverse') - $order = " order by xchan_name desc "; - elseif ($sort_order == 'reversedate') - $order = " order by xchan_name_date asc "; - else - $order = " order by xchan_name_date desc "; - - - // normal directory query + // parse advanced query if present - $r = q("SELECT xchan.*, xprof.* from xchan left join xprof on xchan_hash = xprof_hash + if ($_REQUEST['query']) { + $advanced = $this->dir_parse_query($_REQUEST['query']); + if ($advanced) { + foreach ($advanced as $adv) { + if (in_array($adv['field'], $tables)) { + if ($adv['field'] === 'name') { + $sql_extra .= $this->dir_query_build($adv['logic'], 'xchan_name', $adv['value']); + } elseif ($adv['field'] === 'address') { + $sql_extra .= $this->dir_query_build($adv['logic'], 'xchan_addr', $adv['value']); + } elseif ($adv['field'] === 'xhash') { + $sql_extra .= $this->dir_query_build($adv['logic'], 'xchan_hash', $adv['value']); + } else { + $sql_extra .= $this->dir_query_build($adv['logic'], 'xprof_' . $adv['field'], $adv['value']); + } + } + } + } + } + + $hash = ((x($_REQUEST['hash'])) ? $_REQUEST['hash'] : ''); + $name = ((x($_REQUEST, 'name')) ? $_REQUEST['name'] : ''); + $url = ((x($_REQUEST, 'url')) ? $_REQUEST['url'] : ''); + $hub = ((x($_REQUEST, 'hub')) ? $_REQUEST['hub'] : ''); + $address = ((x($_REQUEST, 'address')) ? $_REQUEST['address'] : ''); + $locale = ((x($_REQUEST, 'locale')) ? $_REQUEST['locale'] : ''); + $region = ((x($_REQUEST, 'region')) ? $_REQUEST['region'] : ''); + $postcode = ((x($_REQUEST, 'postcode')) ? $_REQUEST['postcode'] : ''); + $country = ((x($_REQUEST, 'country')) ? $_REQUEST['country'] : ''); + $gender = ((x($_REQUEST, 'gender')) ? $_REQUEST['gender'] : ''); + $marital = ((x($_REQUEST, 'marital')) ? $_REQUEST['marital'] : ''); + $sexual = ((x($_REQUEST, 'sexual')) ? $_REQUEST['sexual'] : ''); + $keywords = ((x($_REQUEST, 'keywords')) ? $_REQUEST['keywords'] : ''); + $agege = ((x($_REQUEST, 'agege')) ? intval($_REQUEST['agege']) : 0); + $agele = ((x($_REQUEST, 'agele')) ? intval($_REQUEST['agele']) : 0); + $kw = ((x($_REQUEST, 'kw')) ? intval($_REQUEST['kw']) : 0); + $active = ((x($_REQUEST, 'active')) ? intval($_REQUEST['active']) : 0); + $type = ((array_key_exists('type', $_REQUEST)) ? intval($_REQUEST['type']) : 0); + + // allow a site to disable the directory's keyword list + if (get_config('system', 'disable_directory_keywords')) { + $kw = 0; + } + + // by default use a safe search + $safe = ((x($_REQUEST, 'safe'))); + if ($safe === false) { + $safe = 1; + } + + // Directory mirrors will request sync packets, which are lists + // of records that have changed since the sync datetime. + + if (array_key_exists('sync', $_REQUEST)) { + if ($_REQUEST['sync']) { + $sync = datetime_convert('UTC', 'UTC', $_REQUEST['sync']); + } else { + $sync = datetime_convert('UTC', 'UTC', '2010-01-01 01:01:00'); + } + } else { + $sync = false; + } + + if (($dirmode == DIRECTORY_MODE_STANDALONE) && (!$hub)) { + $hub = App::get_hostname(); + } + + if ($hub) { + $hub_query = " and xchan_hash in (select hubloc_hash from hubloc where hubloc_host = '" . protect_sprintf(dbesc($hub)) . "') "; + } else { + $hub_query = ''; + } + + if ($url) { + $r = q( + "select xchan_name from hubloc left join xchan on hubloc_hash = xchan_hash where hubloc_url = '%s' or hubloc_id_url = '%s'", + dbesc($url), + dbesc($url) + ); + if ($r && $r[0]['xchan_name']) { + $name = $r[0]['xchan_name']; + } + } + + // The order identifier is validated further below + + $sort_order = ((x($_REQUEST, 'order')) ? $_REQUEST['order'] : ''); + + + // parse and assemble the query for advanced searches + + $joiner = ' OR '; + + if ($_REQUEST['and']) { + $joiner = ' AND '; + } + + if ($name) { + $sql_extra .= $this->dir_query_build($joiner, 'xchan_name', $name); + } + if ($address) { + $sql_extra .= $this->dir_query_build($joiner, 'xchan_addr', $address); + } + if ($hash) { + $sql_extra .= $this->dir_query_build($joiner, 'xchan_hash', $hash); + } + if ($locale) { + $sql_extra .= $this->dir_query_build($joiner, 'xprof_locale', $locale); + } + if ($region) { + $sql_extra .= $this->dir_query_build($joiner, 'xprof_region', $region); + } + if ($postcode) { + $sql_extra .= $this->dir_query_build($joiner, 'xprof_postcode', $postcode); + } + if ($country) { + $sql_extra .= $this->dir_query_build($joiner, 'xprof_country', $country); + } + if ($gender) { + $sql_extra .= $this->dir_query_build($joiner, 'xprof_gender', $gender); + } + if ($marital) { + $sql_extra .= $this->dir_query_build($joiner, 'xprof_marital', $marital); + } + if ($sexual) { + $sql_extra .= $this->dir_query_build($joiner, 'xprof_sexual', $sexual); + } + if ($keywords) { + $sql_extra .= $this->dir_query_build($joiner, 'xprof_keywords', $keywords); + } + + // we only support an age range currently. You must set both agege + // (greater than or equal) and agele (less than or equal) + + if ($agele && $agege) { + $sql_extra .= " $joiner ( xprof_age <= " . intval($agele) . " "; + $sql_extra .= " AND xprof_age >= " . intval($agege) . ") "; + } + + + $perpage = (($_REQUEST['n']) ? $_REQUEST['n'] : 60); + $page = (($_REQUEST['p']) ? intval($_REQUEST['p'] - 1) : 0); + $startrec = (($page + 1) * $perpage) - $perpage; + $limit = (($_REQUEST['limit']) ? intval($_REQUEST['limit']) : 0); + $return_total = ((x($_REQUEST, 'return_total')) ? intval($_REQUEST['return_total']) : 0); + + // mtime is not currently working + + $mtime = ((x($_REQUEST, 'mtime')) ? datetime_convert('UTC', 'UTC', $_REQUEST['mtime']) : ''); + + // merge them into xprof + + $ret['success'] = true; + + // If &limit=n, return at most n entries + // If &return_total=1, we count matching entries and return that as 'total_items' for use in pagination. + // By default we return one page (default 60 items maximum) and do not count total entries + + $logic = ((strlen($sql_extra)) ? 'false' : 'true'); + + if ($hash) { + $logic = 'true'; + } + + if ($dirmode == DIRECTORY_MODE_STANDALONE) { + $sql_extra .= " and xchan_addr like '%%" . App::get_hostname() . "' "; + } + + $safesql = (($safe > 0) ? " and xchan_censored = 0 and xchan_selfcensored = 0 " : ''); + if ($safe < 0) { + $safesql = " and ( xchan_censored = 1 OR xchan_selfcensored = 1 ) "; + } + + if ($type) { + $safesql .= " and xchan_type = " . intval($type); + } + + $activesql = EMPTY_STR; + + if ($active) { + $activesql = "and xchan_updated > '" . datetime_convert(date_default_timezone_get(), 'UTC', 'now - 60 days') . "' "; + } + + if ($limit) { + $qlimit = " LIMIT $limit "; + } else { + $qlimit = " LIMIT " . intval($perpage) . " OFFSET " . intval($startrec); + if ($return_total) { + $r = q("SELECT COUNT(xchan_hash) AS total FROM xchan left join xprof on xchan_hash = xprof_hash where $logic $sql_extra $network and xchan_hidden = 0 and xchan_orphan = 0 and xchan_deleted = 0 $safesql $activesql "); + if ($r) { + $ret['total_items'] = $r[0]['total']; + } + } + } + + if ($sort_order == 'normal') { + $order = " order by xchan_name asc "; + + // Start the alphabetic search at 'A' + // This will make a handful of channels whose names begin with + // punctuation un-searchable in this mode + + $safesql .= " and ascii(substring(xchan_name FROM 1 FOR 1)) > 64 "; + } elseif ($sort_order == 'reverse') { + $order = " order by xchan_name desc "; + } elseif ($sort_order == 'reversedate') { + $order = " order by xchan_name_date asc "; + } else { + $order = " order by xchan_name_date desc "; + } + + + // normal directory query + + $r = q("SELECT xchan.*, xprof.* from xchan left join xprof on xchan_hash = xprof_hash where ( $logic $sql_extra ) $hub_query $network and xchan_hidden = 0 and xchan_orphan = 0 and xchan_deleted = 0 - $safesql $activesql $order $qlimit " - ); + $safesql $activesql $order $qlimit "); - $ret['page'] = $page + 1; - $ret['records'] = count($r); - - if ($r) { - - $entries = []; - $dups = []; - $isdup = EMPTY_STR; + $ret['page'] = $page + 1; + $ret['records'] = count($r); - // Collect activitypub identities and query which also have zot6 identities. - // Do this once per page fetch rather than once per entry. + if ($r) { + $entries = []; + $dups = []; + $isdup = EMPTY_STR; - foreach ($r as $rv) { - if ($rv['xchan_network'] === 'activitypub') { - if ($isdup) { - $isdup .= ','; - } - $isdup .= "'" . dbesc($rv['xchan_url']) . "'"; - } - if ($isdup) { - $isdup = protect_sprintf($isdup); + // Collect activitypub identities and query which also have zot6 identities. + // Do this once per page fetch rather than once per entry. + + foreach ($r as $rv) { + if ($rv['xchan_network'] === 'activitypub') { + if ($isdup) { + $isdup .= ','; + } + $isdup .= "'" . dbesc($rv['xchan_url']) . "'"; + } + if ($isdup) { + $isdup = protect_sprintf($isdup); $z = q("select xchan_url, xchan_hash from xchan where xchan_url in ( $isdup ) and xchan_network in ('nomad','zot6')"); - if ($z) { - foreach($z as $zv) { - $dups[$zv['xchan_url']] = $zv['xchan_hash']; - } - } - } - } - - foreach ($r as $rr) { + if ($z) { + foreach ($z as $zv) { + $dups[$zv['xchan_url']] = $zv['xchan_hash']; + } + } + } + } - // If it's an activitypub record and the channel also has a zot6 address, don't return it. - - if (array_key_exists($rr['xchan_url'],$dups)) { - continue; - } + foreach ($r as $rr) { + // If it's an activitypub record and the channel also has a zot6 address, don't return it. - if (! check_siteallowed($rr['xchan_url'])) { - continue; - } + if (array_key_exists($rr['xchan_url'], $dups)) { + continue; + } - if (! check_channelallowed($rr['xchan_hash'])) { - continue; - } + if (!check_siteallowed($rr['xchan_url'])) { + continue; + } + + if (!check_channelallowed($rr['xchan_hash'])) { + continue; + } - $entry = []; - - $entry['name'] = $rr['xchan_name']; - $entry['hash'] = $rr['xchan_hash']; - $entry['censored'] = $rr['xchan_censored']; - $entry['selfcensored'] = $rr['xchan_selfcensored']; - $entry['type'] = $rr['xchan_type']; - $entry['url'] = $rr['xchan_url']; - $entry['photo_l'] = $rr['xchan_photo_l']; - $entry['photo'] = $rr['xchan_photo_m']; - $entry['address'] = $rr['xchan_addr']; - $entry['network'] = $rr['xchan_network']; - $entry['description'] = $rr['xprof_desc']; - $entry['locale'] = $rr['xprof_locale']; - $entry['region'] = $rr['xprof_region']; - $entry['postcode'] = $rr['xprof_postcode']; - $entry['country'] = $rr['xprof_country']; - $entry['birthday'] = $rr['xprof_dob']; - $entry['age'] = $rr['xprof_age']; - $entry['gender'] = $rr['xprof_gender']; - $entry['marital'] = $rr['xprof_marital']; - $entry['sexual'] = $rr['xprof_sexual']; - $entry['about'] = $rr['xprof_about']; - $entry['homepage'] = $rr['xprof_homepage']; - $entry['hometown'] = $rr['xprof_hometown']; - $entry['keywords'] = $rr['xprof_keywords']; - - $entries[] = $entry; - - } - - $ret['results'] = $entries; - if ($kw) { - $k = dir_tagadelic($kw, $hub, $type,$safesql); - if ($k) { - $ret['keywords'] = []; - foreach ($k as $kv) { - $ret['keywords'][] = [ 'term' => $kv[0], 'weight' => $kv[1], 'normalise' => $kv[2] ]; - } - } - } - } - json_return_and_die($ret); - } - - function dir_query_build($joiner,$field,$s) { - $ret = ''; - if (trim($s)) - $ret .= dbesc($joiner) . " " . dbesc($field) . " like '" . protect_sprintf( '%' . dbesc($s) . '%' ) . "' "; - return $ret; - } - - function dir_flag_build($joiner,$field,$bit,$s) { - return dbesc($joiner) . " ( " . dbesc($field) . " & " . intval($bit) . " ) " . ((intval($s)) ? '>' : '=' ) . " 0 "; - } - - - function dir_parse_query($s) { - - $ret = []; - $curr = []; - $all = explode(' ',$s); - $quoted_string = false; - - if ($all) { - foreach ($all as $q) { - if ($quoted_string === false) { - if ($q === 'and') { - $curr['logic'] = 'and'; - continue; - } - if ($q === 'or') { - $curr['logic'] = 'or'; - continue; - } - if ($q === 'not') { - $curr['logic'] .= ' not'; - continue; - } - if (strpos($q,'=')) { - if (! isset($curr['logic'])) - $curr['logic'] = 'or'; - $curr['field'] = trim(substr($q,0,strpos($q,'='))); - $curr['value'] = trim(substr($q,strpos($q,'=')+1)); - if ($curr['value'][0] == '"' && $curr['value'][strlen($curr['value'])-1] != '"') { - $quoted_string = true; - $curr['value'] = substr($curr['value'],1); - continue; - } - elseif ($curr['value'][0] == '"' && $curr['value'][strlen($curr['value'])-1] == '"') { - $curr['value'] = substr($curr['value'],1,strlen($curr['value'])-2); - $ret[] = $curr; - $curr = []; - continue; - } - else { - $ret[] = $curr; - $curr = []; - continue; - } - } - } - else { - if ($q[strlen($q)-1] == '"') { - $curr['value'] .= ' ' . str_replace('"','',trim($q)); - $ret[] = $curr; - $curr = []; - $quoted_string = false; - } - else - $curr['value'] .= ' ' . trim($q); - } - } - } - logger('dir_parse_query:' . print_r($ret,true),LOGGER_DATA); - return $ret; - } - - - function list_public_sites() { - - $rand = db_getfunc('rand'); - $realm = get_directory_realm(); + $entry = []; - $r = q("select * from site where site_type = %d and site_dead = 0", - intval(SITE_TYPE_ZOT) - ); - - $ret = array('success' => false); - - if ($r) { - $ret['success'] = true; - $ret['sites'] = []; - - foreach ($r as $rr) { - - if ($rr['site_access'] == ACCESS_FREE) - $access = 'free'; - elseif ($rr['site_access'] == ACCESS_PAID) - $access = 'paid'; - elseif ($rr['site_access'] == ACCESS_TIERED) - $access = 'tiered'; - else - $access = 'private'; - - if ($rr['site_register'] == REGISTER_OPEN) - $register = 'open'; - elseif ($rr['site_register'] == REGISTER_APPROVE) - $register = 'approve'; - else - $register = 'closed'; - - $ret['sites'][] = array('url' => $rr['site_url'], 'access' => $access, 'register' => $register, 'sellpage' => $rr['site_sellpage'], 'location' => $rr['site_location'], 'project' => $rr['site_project'], 'version' => $rr['site_version']); + $entry['name'] = $rr['xchan_name']; + $entry['hash'] = $rr['xchan_hash']; + $entry['censored'] = $rr['xchan_censored']; + $entry['selfcensored'] = $rr['xchan_selfcensored']; + $entry['type'] = $rr['xchan_type']; + $entry['url'] = $rr['xchan_url']; + $entry['photo_l'] = $rr['xchan_photo_l']; + $entry['photo'] = $rr['xchan_photo_m']; + $entry['address'] = $rr['xchan_addr']; + $entry['network'] = $rr['xchan_network']; + $entry['description'] = $rr['xprof_desc']; + $entry['locale'] = $rr['xprof_locale']; + $entry['region'] = $rr['xprof_region']; + $entry['postcode'] = $rr['xprof_postcode']; + $entry['country'] = $rr['xprof_country']; + $entry['birthday'] = $rr['xprof_dob']; + $entry['age'] = $rr['xprof_age']; + $entry['gender'] = $rr['xprof_gender']; + $entry['marital'] = $rr['xprof_marital']; + $entry['sexual'] = $rr['xprof_sexual']; + $entry['about'] = $rr['xprof_about']; + $entry['homepage'] = $rr['xprof_homepage']; + $entry['hometown'] = $rr['xprof_hometown']; + $entry['keywords'] = $rr['xprof_keywords']; - } - } - return $ret; - } + $entries[] = $entry; + } + $ret['results'] = $entries; + if ($kw) { + $k = dir_tagadelic($kw, $hub, $type, $safesql); + if ($k) { + $ret['keywords'] = []; + foreach ($k as $kv) { + $ret['keywords'][] = ['term' => $kv[0], 'weight' => $kv[1], 'normalise' => $kv[2]]; + } + } + } + } + json_return_and_die($ret); + } + + public function dir_query_build($joiner, $field, $s) + { + $ret = ''; + if (trim($s)) { + $ret .= dbesc($joiner) . " " . dbesc($field) . " like '" . protect_sprintf('%' . dbesc($s) . '%') . "' "; + } + return $ret; + } + + public function dir_flag_build($joiner, $field, $bit, $s) + { + return dbesc($joiner) . " ( " . dbesc($field) . " & " . intval($bit) . " ) " . ((intval($s)) ? '>' : '=') . " 0 "; + } + + + public function dir_parse_query($s) + { + + $ret = []; + $curr = []; + $all = explode(' ', $s); + $quoted_string = false; + + if ($all) { + foreach ($all as $q) { + if ($quoted_string === false) { + if ($q === 'and') { + $curr['logic'] = 'and'; + continue; + } + if ($q === 'or') { + $curr['logic'] = 'or'; + continue; + } + if ($q === 'not') { + $curr['logic'] .= ' not'; + continue; + } + if (strpos($q, '=')) { + if (!isset($curr['logic'])) { + $curr['logic'] = 'or'; + } + $curr['field'] = trim(substr($q, 0, strpos($q, '='))); + $curr['value'] = trim(substr($q, strpos($q, '=') + 1)); + if ($curr['value'][0] == '"' && $curr['value'][strlen($curr['value']) - 1] != '"') { + $quoted_string = true; + $curr['value'] = substr($curr['value'], 1); + continue; + } elseif ($curr['value'][0] == '"' && $curr['value'][strlen($curr['value']) - 1] == '"') { + $curr['value'] = substr($curr['value'], 1, strlen($curr['value']) - 2); + $ret[] = $curr; + $curr = []; + continue; + } else { + $ret[] = $curr; + $curr = []; + continue; + } + } + } else { + if ($q[strlen($q) - 1] == '"') { + $curr['value'] .= ' ' . str_replace('"', '', trim($q)); + $ret[] = $curr; + $curr = []; + $quoted_string = false; + } else { + $curr['value'] .= ' ' . trim($q); + } + } + } + } + logger('dir_parse_query:' . print_r($ret, true), LOGGER_DATA); + return $ret; + } + + + public function list_public_sites() + { + + $rand = db_getfunc('rand'); + $realm = get_directory_realm(); + + $r = q( + "select * from site where site_type = %d and site_dead = 0", + intval(SITE_TYPE_ZOT) + ); + + $ret = array('success' => false); + + if ($r) { + $ret['success'] = true; + $ret['sites'] = []; + + foreach ($r as $rr) { + if ($rr['site_access'] == ACCESS_FREE) { + $access = 'free'; + } elseif ($rr['site_access'] == ACCESS_PAID) { + $access = 'paid'; + } elseif ($rr['site_access'] == ACCESS_TIERED) { + $access = 'tiered'; + } else { + $access = 'private'; + } + + if ($rr['site_register'] == REGISTER_OPEN) { + $register = 'open'; + } elseif ($rr['site_register'] == REGISTER_APPROVE) { + $register = 'approve'; + } else { + $register = 'closed'; + } + + $ret['sites'][] = array('url' => $rr['site_url'], 'access' => $access, 'register' => $register, 'sellpage' => $rr['site_sellpage'], 'location' => $rr['site_location'], 'project' => $rr['site_project'], 'version' => $rr['site_version']); + } + } + return $ret; + } } diff --git a/Zotlabs/Module/Display.php b/Zotlabs/Module/Display.php index 11fbc4511..a18712805 100644 --- a/Zotlabs/Module/Display.php +++ b/Zotlabs/Module/Display.php @@ -2,8 +2,8 @@ namespace Zotlabs\Module; - use App; +use Zotlabs\Lib\PermissionDescription; use Zotlabs\Lib\System; use Zotlabs\Lib\PConfig; use Zotlabs\Web\Controller; @@ -14,510 +14,505 @@ require_once('include/conversation.php'); require_once('include/acl_selectors.php'); -class Display extends Controller { +class Display extends Controller +{ - // State passed in from the Update module. - - public $profile_uid = 0; - public $loading = 0; - public $updating = 0; + // State passed in from the Update module. + + public $profile_uid = 0; + public $loading = 0; + public $updating = 0; - function get() { + public function get() + { - $noscript_content = (get_config('system', 'noscript_content', '1') && (! $this->updating)); + $noscript_content = (get_config('system', 'noscript_content', '1') && (!$this->updating)); - $module_format = 'html'; + $module_format = 'html'; - if(argc() > 1) { - $module_format = substr(argv(1),strrpos(argv(1),'.') + 1); - if(! in_array($module_format,['atom','zot','json'])) - $module_format = 'html'; - } + if (argc() > 1) { + $module_format = substr(argv(1), strrpos(argv(1), '.') + 1); + if (!in_array($module_format, ['atom', 'zot', 'json'])) { + $module_format = 'html'; + } + } - if($this->loading) - $_SESSION['loadtime_display'] = datetime_convert(); - - if(observer_prohibited()) { - notice( t('Public access denied.') . EOL); - return; - } - - if(argc() > 1) { - $item_hash = argv(1); - if($module_format !== 'html') { - $item_hash = substr($item_hash,0,strrpos($item_hash,'.')); - } - } - - if($_REQUEST['mid']) - $item_hash = $_REQUEST['mid']; + if ($this->loading) { + $_SESSION['loadtime_display'] = datetime_convert(); + } - if(! $item_hash) { - App::$error = 404; - notice( t('Item not found.') . EOL); - return; - } - - $observer_is_owner = false; - $updateable = false; + if (observer_prohibited()) { + notice(t('Public access denied.') . EOL); + return; + } - if(local_channel() && (! $this->updating)) { - - $channel = \App::get_channel(); + if (argc() > 1) { + $item_hash = argv(1); + if ($module_format !== 'html') { + $item_hash = substr($item_hash, 0, strrpos($item_hash, '.')); + } + } - $channel_acl = array( - 'allow_cid' => $channel['channel_allow_cid'], - 'allow_gid' => $channel['channel_allow_gid'], - 'deny_cid' => $channel['channel_deny_cid'], - 'deny_gid' => $channel['channel_deny_gid'] - ); + if ($_REQUEST['mid']) { + $item_hash = $_REQUEST['mid']; + } - $x = array( - 'is_owner' => true, - 'allow_location' => ((intval(get_pconfig($channel['channel_id'],'system','use_browser_location'))) ? '1' : ''), - 'default_location' => $channel['channel_location'], - 'nickname' => $channel['channel_address'], - 'lockstate' => (($group || $cid || $channel['channel_allow_cid'] || $channel['channel_allow_gid'] || $channel['channel_deny_cid'] || $channel['channel_deny_gid']) ? 'lock' : 'unlock'), - 'acl' => populate_acl($channel_acl,true, \Zotlabs\Lib\PermissionDescription::fromGlobalPermission('view_stream'), get_post_aclDialogDescription(), 'acl_dialog_post'), - 'permissions' => $channel_acl, - 'bang' => '', - 'visitor' => true, - 'profile_uid' => local_channel(), - 'return_path' => 'channel/' . $channel['channel_address'], - 'expanded' => true, - 'editor_autocomplete' => true, - 'bbco_autocomplete' => 'bbcode', - 'bbcode' => true, - 'jotnets' => true, - 'reset' => t('Reset form') - ); - - $o = '
        '; - $o .= status_editor($x); - $o .= '
        '; - } - - // This page can be viewed by anybody so the query could be complicated - // First we'll see if there is a copy of the item which is owned by us - if we're logged in locally. - // If that fails (or we aren't logged in locally), - // query an item in which the observer (if logged in remotely) has cid or gid rights - // and if that fails, look for a copy of the post that has no privacy restrictions. - // If we find the post, but we don't find a copy that we're allowed to look at, this fact needs to be reported. - - // find a copy of the item somewhere - - $target_item = null; + if (!$item_hash) { + App::$error = 404; + notice(t('Item not found.') . EOL); + return; + } - $item_hash = unpack_link_id($item_hash); + $observer_is_owner = false; + $updateable = false; - $r = q("select id, uid, mid, parent_mid, thr_parent, verb, item_type, item_deleted, author_xchan, item_blocked from item where mid like '%s' limit 1", - dbesc($item_hash . '%') - ); - - if($r) { - $target_item = $r[0]; - } + if (local_channel() && (!$this->updating)) { + $channel = App::get_channel(); - $x = q("select * from xchan where xchan_hash = '%s' limit 1", - dbesc($target_item['author_xchan']) - ); - if($x) { + $channel_acl = array( + 'allow_cid' => $channel['channel_allow_cid'], + 'allow_gid' => $channel['channel_allow_gid'], + 'deny_cid' => $channel['channel_deny_cid'], + 'deny_gid' => $channel['channel_deny_gid'] + ); + + $x = array( + 'is_owner' => true, + 'allow_location' => ((intval(get_pconfig($channel['channel_id'], 'system', 'use_browser_location'))) ? '1' : ''), + 'default_location' => $channel['channel_location'], + 'nickname' => $channel['channel_address'], + 'lockstate' => (($group || $cid || $channel['channel_allow_cid'] || $channel['channel_allow_gid'] || $channel['channel_deny_cid'] || $channel['channel_deny_gid']) ? 'lock' : 'unlock'), + 'acl' => populate_acl($channel_acl, true, PermissionDescription::fromGlobalPermission('view_stream'), get_post_aclDialogDescription(), 'acl_dialog_post'), + 'permissions' => $channel_acl, + 'bang' => '', + 'visitor' => true, + 'profile_uid' => local_channel(), + 'return_path' => 'channel/' . $channel['channel_address'], + 'expanded' => true, + 'editor_autocomplete' => true, + 'bbco_autocomplete' => 'bbcode', + 'bbcode' => true, + 'jotnets' => true, + 'reset' => t('Reset form') + ); + + $o = '
        '; + $o .= status_editor($x); + $o .= '
        '; + } + + // This page can be viewed by anybody so the query could be complicated + // First we'll see if there is a copy of the item which is owned by us - if we're logged in locally. + // If that fails (or we aren't logged in locally), + // query an item in which the observer (if logged in remotely) has cid or gid rights + // and if that fails, look for a copy of the post that has no privacy restrictions. + // If we find the post, but we don't find a copy that we're allowed to look at, this fact needs to be reported. + + // find a copy of the item somewhere + + $target_item = null; + + $item_hash = unpack_link_id($item_hash); + + $r = q( + "select id, uid, mid, parent_mid, thr_parent, verb, item_type, item_deleted, author_xchan, item_blocked from item where mid like '%s' limit 1", + dbesc($item_hash . '%') + ); + + if ($r) { + $target_item = $r[0]; + } + + $x = q( + "select * from xchan where xchan_hash = '%s' limit 1", + dbesc($target_item['author_xchan']) + ); + if ($x) { // not yet ready for prime time -// \App::$poi = $x[0]; - } +// \App::$poi = $x[0]; + } - // if the item is to be moderated redirect to /moderate - if($target_item['item_blocked'] == ITEM_MODERATED) { - goaway(z_root() . '/moderate/' . $target_item['id']); - } - - $r = null; - - if($target_item['item_type'] == ITEM_TYPE_WEBPAGE) { - $x = q("select * from channel where channel_id = %d limit 1", - intval($target_item['uid']) - ); - $y = q("select * from iconfig left join item on iconfig.iid = item.id + // if the item is to be moderated redirect to /moderate + if ($target_item['item_blocked'] == ITEM_MODERATED) { + goaway(z_root() . '/moderate/' . $target_item['id']); + } + + $r = null; + + if ($target_item['item_type'] == ITEM_TYPE_WEBPAGE) { + $x = q( + "select * from channel where channel_id = %d limit 1", + intval($target_item['uid']) + ); + $y = q( + "select * from iconfig left join item on iconfig.iid = item.id where item.uid = %d and iconfig.cat = 'system' and iconfig.k = 'WEBPAGE' and item.id = %d limit 1", - intval($target_item['uid']), - intval($target_item['parent']) - ); - if($x && $y) { - goaway(z_root() . '/page/' . $x[0]['channel_address'] . '/' . $y[0]['v']); - } - else { - notice( t('Page not found.') . EOL); - return ''; - } - } - if($target_item['item_type'] == ITEM_TYPE_ARTICLE) { - $x = q("select * from channel where channel_id = %d limit 1", - intval($target_item['uid']) - ); - $y = q("select * from iconfig left join item on iconfig.iid = item.id + intval($target_item['uid']), + intval($target_item['parent']) + ); + if ($x && $y) { + goaway(z_root() . '/page/' . $x[0]['channel_address'] . '/' . $y[0]['v']); + } else { + notice(t('Page not found.') . EOL); + return ''; + } + } + if ($target_item['item_type'] == ITEM_TYPE_ARTICLE) { + $x = q( + "select * from channel where channel_id = %d limit 1", + intval($target_item['uid']) + ); + $y = q( + "select * from iconfig left join item on iconfig.iid = item.id where item.uid = %d and iconfig.cat = 'system' and iconfig.k = 'ARTICLE' and item.id = %d limit 1", - intval($target_item['uid']), - intval($target_item['parent']) - ); - if($x && $y) { - goaway(z_root() . '/articles/' . $x[0]['channel_address'] . '/' . $y[0]['v']); - } - else { - notice( t('Page not found.') . EOL); - return ''; - } - } - if($target_item['item_type'] == ITEM_TYPE_CARD) { - $x = q("select * from channel where channel_id = %d limit 1", - intval($target_item['uid']) - ); - $y = q("select * from iconfig left join item on iconfig.iid = item.id + intval($target_item['uid']), + intval($target_item['parent']) + ); + if ($x && $y) { + goaway(z_root() . '/articles/' . $x[0]['channel_address'] . '/' . $y[0]['v']); + } else { + notice(t('Page not found.') . EOL); + return ''; + } + } + if ($target_item['item_type'] == ITEM_TYPE_CARD) { + $x = q( + "select * from channel where channel_id = %d limit 1", + intval($target_item['uid']) + ); + $y = q( + "select * from iconfig left join item on iconfig.iid = item.id where item.uid = %d and iconfig.cat = 'system' and iconfig.k = 'CARD' and item.id = %d limit 1", - intval($target_item['uid']), - intval($target_item['parent']) - ); - if($x && $y) { - goaway(z_root() . '/cards/' . $x[0]['channel_address'] . '/' . $y[0]['v']); - } - else { - notice( t('Page not found.') . EOL); - return ''; - } - } - if ($target_item['item_type'] == ITEM_TYPE_CUSTOM) { - call_hooks('item_custom_display',$target_item); - notice( t('Page not found.') . EOL); - return ''; - } + intval($target_item['uid']), + intval($target_item['parent']) + ); + if ($x && $y) { + goaway(z_root() . '/cards/' . $x[0]['channel_address'] . '/' . $y[0]['v']); + } else { + notice(t('Page not found.') . EOL); + return ''; + } + } + if ($target_item['item_type'] == ITEM_TYPE_CUSTOM) { + call_hooks('item_custom_display', $target_item); + notice(t('Page not found.') . EOL); + return ''; + } - - - $static = ((array_key_exists('static',$_REQUEST)) ? intval($_REQUEST['static']) : 0); - - - $simple_update = (($this->updating) ? " AND item_unseen = 1 " : ''); - - if($this->updating && $_SESSION['loadtime_display']) - $simple_update = " AND item.changed > '" . datetime_convert('UTC','UTC',$_SESSION['loadtime_display']) . "' "; - if($this->loading) - $simple_update = ''; - - if($static && $simple_update) - $simple_update .= " and item_thread_top = 0 and author_xchan = '" . protect_sprintf(get_observer_hash()) . "' "; - - if((! $this->updating) && (! $this->loading)) { - $static = ((local_channel()) ? channel_manual_conv_update(local_channel()) : 1); + $static = ((array_key_exists('static', $_REQUEST)) ? intval($_REQUEST['static']) : 0); - // if the target item is not a post (eg a like) we want to address its thread parent - $mid = ((($target_item['verb'] == ACTIVITY_LIKE) || ($target_item['verb'] == ACTIVITY_DISLIKE)) ? $target_item['thr_parent'] : $target_item['mid']); + $simple_update = (($this->updating) ? " AND item_unseen = 1 " : ''); - // if we received a decoded hash originally we must encode it again before handing to javascript + if ($this->updating && $_SESSION['loadtime_display']) { + $simple_update = " AND item.changed > '" . datetime_convert('UTC', 'UTC', $_SESSION['loadtime_display']) . "' "; + } + if ($this->loading) { + $simple_update = ''; + } - $mid = gen_link_id($mid); + if ($static && $simple_update) { + $simple_update .= " and item_thread_top = 0 and author_xchan = '" . protect_sprintf(get_observer_hash()) . "' "; + } - $o .= '
        ' . "\r\n"; - $o .= "\r\n"; - - App::$page['htmlhead'] .= replace_macros(get_markup_template("build_query.tpl"),array( - '$baseurl' => z_root(), - '$pgtype' => 'display', - '$uid' => '0', - '$gid' => '0', - '$cid' => '0', - '$cmin' => '(-1)', - '$cmax' => '(-1)', - '$star' => '0', - '$liked' => '0', - '$conv' => '0', - '$spam' => '0', - '$fh' => '0', - '$dm' => '0', - '$nouveau' => '0', - '$wall' => '0', - '$draft' => '0', - '$static' => $static, - '$page' => ((App::$pager['page'] != 1) ? App::$pager['page'] : 1), - '$list' => ((x($_REQUEST,'list')) ? intval($_REQUEST['list']) : 0), - '$search' => '', - '$xchan' => '', - '$order' => '', - '$file' => '', - '$cats' => '', - '$tags' => '', - '$dend' => '', - '$dbegin' => '', - '$verb' => '', - '$net' => '', - '$mid' => (($mid) ? urlencode($mid) : '') - )); + if ((!$this->updating) && (!$this->loading)) { + $static = ((local_channel()) ? channel_manual_conv_update(local_channel()) : 1); - head_add_link([ - 'rel' => 'alternate', - 'type' => 'application/json+oembed', - 'href' => z_root() . '/oep?f=&url=' . urlencode(z_root() . '/' . \App::$query_string), - 'title' => 'oembed' - ]); + // if the target item is not a post (eg a like) we want to address its thread parent - } + $mid = ((($target_item['verb'] == ACTIVITY_LIKE) || ($target_item['verb'] == ACTIVITY_DISLIKE)) ? $target_item['thr_parent'] : $target_item['mid']); - $observer_hash = get_observer_hash(); - $item_normal = item_normal(); - $item_normal_update = item_normal_update(); + // if we received a decoded hash originally we must encode it again before handing to javascript - $sql_extra = ((local_channel()) ? EMPTY_STR : item_permissions_sql(0, $observer_hash)); + $mid = gen_link_id($mid); - if($noscript_content || $this->loading) { + $o .= '
        ' . "\r\n"; + $o .= "\r\n"; - $r = null; + App::$page['htmlhead'] .= replace_macros(get_markup_template("build_query.tpl"), array( + '$baseurl' => z_root(), + '$pgtype' => 'display', + '$uid' => '0', + '$gid' => '0', + '$cid' => '0', + '$cmin' => '(-1)', + '$cmax' => '(-1)', + '$star' => '0', + '$liked' => '0', + '$conv' => '0', + '$spam' => '0', + '$fh' => '0', + '$dm' => '0', + '$nouveau' => '0', + '$wall' => '0', + '$draft' => '0', + '$static' => $static, + '$page' => ((App::$pager['page'] != 1) ? App::$pager['page'] : 1), + '$list' => ((x($_REQUEST, 'list')) ? intval($_REQUEST['list']) : 0), + '$search' => '', + '$xchan' => '', + '$order' => '', + '$file' => '', + '$cats' => '', + '$tags' => '', + '$dend' => '', + '$dbegin' => '', + '$verb' => '', + '$net' => '', + '$mid' => (($mid) ? urlencode($mid) : '') + )); - require_once('include/channel.php'); - $sys = get_sys_channel(); - $sysid = $sys['channel_id']; + head_add_link([ + 'rel' => 'alternate', + 'type' => 'application/json+oembed', + 'href' => z_root() . '/oep?f=&url=' . urlencode(z_root() . '/' . App::$query_string), + 'title' => 'oembed' + ]); + } - if(local_channel()) { - $r = q("SELECT item.id as item_id from item WHERE uid = %d and mid = '%s' $item_normal limit 1", - intval(local_channel()), - dbesc($target_item['parent_mid']) - ); - if($r) { - $updateable = true; - } - } + $observer_hash = get_observer_hash(); + $item_normal = item_normal(); + $item_normal_update = item_normal_update(); - if (! (is_array($r) && count($r))) { - $r = q("SELECT item.id as item_id from item WHERE mid = '%s' $sql_extra $item_normal limit 1", - dbesc($target_item['parent_mid']) - ); - } - } - elseif ($this->updating && !$this->loading) { - $r = null; + $sql_extra = ((local_channel()) ? EMPTY_STR : item_permissions_sql(0, $observer_hash)); - require_once('include/channel.php'); - $sys = get_sys_channel(); - $sysid = $sys['channel_id']; + if ($noscript_content || $this->loading) { + $r = null; - if (local_channel()) { - $r = q("SELECT item.parent AS item_id from item WHERE uid = %d and parent_mid = '%s' $item_normal_update $simple_update limit 1", - intval(local_channel()), - dbesc($target_item['parent_mid']) - ); - if($r) { - $updateable = true; - } - } + require_once('include/channel.php'); + $sys = get_sys_channel(); + $sysid = $sys['channel_id']; - if(! $r) { - $r = q("SELECT item.parent AS item_id from item WHERE parent_mid = '%s' $sql_extra $item_normal_update $simple_update limit 1", - dbesc($target_item['parent_mid']) - ); - } - } - - else { - $r = []; - } + if (local_channel()) { + $r = q( + "SELECT item.id as item_id from item WHERE uid = %d and mid = '%s' $item_normal limit 1", + intval(local_channel()), + dbesc($target_item['parent_mid']) + ); + if ($r) { + $updateable = true; + } + } - if($r) { - $parents_str = ids_to_querystr($r,'item_id'); - if($parents_str) { - $items = q("SELECT item.*, item.id AS item_id + if (!(is_array($r) && count($r))) { + $r = q( + "SELECT item.id as item_id from item WHERE mid = '%s' $sql_extra $item_normal limit 1", + dbesc($target_item['parent_mid']) + ); + } + } elseif ($this->updating && !$this->loading) { + $r = null; + + require_once('include/channel.php'); + $sys = get_sys_channel(); + $sysid = $sys['channel_id']; + + if (local_channel()) { + $r = q( + "SELECT item.parent AS item_id from item WHERE uid = %d and parent_mid = '%s' $item_normal_update $simple_update limit 1", + intval(local_channel()), + dbesc($target_item['parent_mid']) + ); + if ($r) { + $updateable = true; + } + } + + if (!$r) { + $r = q( + "SELECT item.parent AS item_id from item WHERE parent_mid = '%s' $sql_extra $item_normal_update $simple_update limit 1", + dbesc($target_item['parent_mid']) + ); + } + } else { + $r = []; + } + + if ($r) { + $parents_str = ids_to_querystr($r, 'item_id'); + if ($parents_str) { + $items = q( + "SELECT item.*, item.id AS item_id FROM item WHERE parent in ( %s ) $item_normal $sql_extra ", - dbesc($parents_str) - ); - xchan_query($items); - $items = fetch_post_tags($items,true); - $items = conv_sort($items,'created'); - } - } - else { - $items = []; - } + dbesc($parents_str) + ); + xchan_query($items); + $items = fetch_post_tags($items, true); + $items = conv_sort($items, 'created'); + } + } else { + $items = []; + } - // see if the top-level post owner chose to block search engines - - if ($items && get_pconfig($items[0]['uid'],'system','noindex')) { - App::$meta->set('robots', 'noindex, noarchive'); - } + // see if the top-level post owner chose to block search engines - foreach ($items as $item) { - if ($item['mid'] === $item_hash) { + if ($items && get_pconfig($items[0]['uid'], 'system', 'noindex')) { + App::$meta->set('robots', 'noindex, noarchive'); + } + + foreach ($items as $item) { + if ($item['mid'] === $item_hash) { + if (preg_match("/\[[zi]mg(.*?)\]([^\[]+)/is", $items[0]['body'], $matches)) { + $ogimage = $matches[2]; + // Will we use og:image:type someday? We keep this just in case + // $ogimagetype = guess_image_type($ogimage); + } + + // some work on post content to generate a description + // almost fully based on work done on Hubzilla by Max Kostikov + $ogdesc = $item['body']; + + $ogdesc = bbcode($ogdesc, ['export' => true]); + $ogdesc = trim(html2plain($ogdesc, 0, true)); + $ogdesc = html_entity_decode($ogdesc, ENT_QUOTES, 'UTF-8'); + + // remove all URLs + $ogdesc = preg_replace("/https?\:\/\/[a-zA-Z0-9\:\/\-\?\&\;\.\=\_\~\#\%\$\!\+\,\@]+/", "", $ogdesc); + + // shorten description + $ogdesc = substr($ogdesc, 0, 300); + $ogdesc = str_replace("\n", " ", $ogdesc); + while (strpos($ogdesc, " ") !== false) { + $ogdesc = str_replace(" ", " ", $ogdesc); + } + $ogdesc = (strlen($ogdesc) < 298 ? $ogdesc : rtrim(substr($ogdesc, 0, strrpos($ogdesc, " ")), "?.,:;!-") . "..."); + + $ogsite = (System::get_site_name()) ? escape_tags(System::get_site_name()) : System::get_platform_name(); + + // we can now start loading content + if ($item['mid'] == $item['parent_mid']) { + App::$meta->set('og:title', ($items[0]['title'] + ? sprintf(t('"%1$s", shared by %2$s with %3$s'), $items[0]['title'], $item['author']['xchan_name'], $ogsite) + : sprintf(t('%1$s shared this post with %2$s'), $item['author']['xchan_name'], $ogsite))); + App::$meta->set('og:image', (isset($ogimage) ? $ogimage : System::get_site_icon())); + App::$meta->set('og:type', 'article'); + App::$meta->set('og:url:secure_url', $item['llink']); + App::$meta->set('og:description', ($ogdesc ? $ogdesc : sprintf(t('Not much to read, click to see the post.')))); + } else { + if (($target_item['verb'] == ACTIVITY_LIKE) || ($target_item['verb'] == ACTIVITY_DISLIKE)) { + App::$meta->set('og:title', ($items[0]['title'] + ? sprintf(t('%1$s shared a reaction to "%2$s"'), $item['author']['xchan_name'], $items[0]['title']) + : sprintf(t('%s shared a reaction to this post/conversation'), $item['author']['xchan_name']))); + App::$meta->set('og:image', (isset($ogimage) ? $ogimage : System::get_site_icon())); + App::$meta->set('og:type', 'article'); + App::$meta->set('og:url:secure_url', $item['llink']); + App::$meta->set('og:description', $ogdesc); + } else { + App::$meta->set('og:title', ($items[0]['title'] + ? sprintf(t('%1$s commented "%2$s"'), $item['author']['xchan_name'], $items[0]['title']) + : sprintf(t('%s shared a comment of this post/conversation'), $item['author']['xchan_name']))); + App::$meta->set('og:image', (isset($ogimage) ? $ogimage : System::get_site_icon())); + App::$meta->set('og:type', 'article'); + App::$meta->set('og:url:secure_url', $item['llink']); + App::$meta->set('og:description', sprintf(t('%1$s wrote this: "%2$s"'), $item['author']['xchan_name'], $ogdesc)); + } + } + } + } + + if (local_channel() && $items) { + $ids = ids_to_array($items, 'item_id'); + $seen = PConfig::Get(local_channel(), 'system', 'seen_items'); + if (!$seen) { + $seen = []; + } + $seen = array_unique(array_merge($ids, $seen)); + PConfig::Set(local_channel(), 'system', 'seen_items', $seen); + } + + switch ($module_format) { + case 'html': + if ($this->updating) { + $o .= conversation($items, 'display', $this->updating, 'client'); + } else { + $o .= ''; + + App::$page['title'] = (($items[0]['title']) ? $items[0]['title'] . " - " . App::$page['title'] : App::$page['title']); + + $o .= conversation($items, 'display', $this->updating, 'client'); + } + + break; + + case 'atom': + $atom = replace_macros(get_markup_template('atom_feed.tpl'), array( + '$version' => xmlify(System::get_project_version()), + '$generator' => xmlify(System::get_platform_name()), + '$generator_uri' => 'https://hubzilla.org', + '$feed_id' => xmlify(App::$cmd), + '$feed_title' => xmlify(t('Article')), + '$feed_updated' => xmlify(datetime_convert('UTC', 'UTC', 'now', ATOM_TIME)), + '$author' => '', + '$owner' => '', + '$profile_page' => xmlify(z_root() . '/display/' . $target_item['mid']), + )); + + $x = ['xml' => $atom, 'channel' => $channel, 'observer_hash' => $observer_hash, 'params' => $params]; + call_hooks('atom_feed_top', $x); + + $atom = $x['xml']; + + // a much simpler interface + call_hooks('atom_feed', $atom); - if(preg_match("/\[[zi]mg(.*?)\]([^\[]+)/is", $items[0]['body'], $matches)) { - $ogimage = $matches[2]; - // Will we use og:image:type someday? We keep this just in case - // $ogimagetype = guess_image_type($ogimage); - } + if ($items) { + $type = 'html'; + foreach ($items as $item) { + if ($item['item_private']) { + continue; + } + $atom .= atom_entry($item, $type, null, '', true, '', false); + } + } - // some work on post content to generate a description - // almost fully based on work done on Hubzilla by Max Kostikov - $ogdesc = $item['body']; + call_hooks('atom_feed_end', $atom); - $ogdesc = bbcode($ogdesc, [ 'export' => true ]); - $ogdesc = trim(html2plain($ogdesc, 0, true)); - $ogdesc = html_entity_decode($ogdesc, ENT_QUOTES, 'UTF-8'); + $atom .= '' . "\r\n"; - // remove all URLs - $ogdesc = preg_replace("/https?\:\/\/[a-zA-Z0-9\:\/\-\?\&\;\.\=\_\~\#\%\$\!\+\,\@]+/", "", $ogdesc); + header('Content-type: application/atom+xml'); + echo $atom; + killme(); + } - // shorten description - $ogdesc = substr($ogdesc, 0, 300); - $ogdesc = str_replace("\n", " ", $ogdesc); - while (strpos($ogdesc, " ") !== false) - $ogdesc = str_replace(" ", " ", $ogdesc); - $ogdesc = (strlen($ogdesc) < 298 ? $ogdesc : rtrim(substr($ogdesc, 0, strrpos($ogdesc, " ")), "?.,:;!-") . "..."); + if ($updateable) { + $x = q( + "UPDATE item SET item_unseen = 0 where item_unseen = 1 AND uid = %d and parent = %d ", + intval(local_channel()), + intval($r[0]['item_id']) + ); + } - $ogsite = (System::get_site_name()) ? escape_tags(System::get_site_name()) : System::get_platform_name(); + $o .= '
        '; - // we can now start loading content - if ($item['mid'] == $item['parent_mid']) { - App::$meta->set('og:title', ($items[0]['title'] - ? sprintf( t('"%1$s", shared by %2$s with %3$s'),$items[0]['title'],$item['author']['xchan_name'],$ogsite) - : sprintf( t('%1$s shared this post with %2$s'),$item['author']['xchan_name'],$ogsite))); - App::$meta->set('og:image', (isset($ogimage) ? $ogimage : System::get_site_icon())); - App::$meta->set('og:type', 'article'); - App::$meta->set('og:url:secure_url', $item['llink']); - App::$meta->set('og:description', ($ogdesc ? $ogdesc : sprintf( t('Not much to read, click to see the post.')))); - } - else { - if (($target_item['verb'] == ACTIVITY_LIKE) || ($target_item['verb'] == ACTIVITY_DISLIKE)) { - App::$meta->set('og:title', ($items[0]['title'] - ? sprintf( t('%1$s shared a reaction to "%2$s"'),$item['author']['xchan_name'],$items[0]['title']) - : sprintf( t('%s shared a reaction to this post/conversation'),$item['author']['xchan_name']))); - App::$meta->set('og:image', (isset($ogimage) ? $ogimage : System::get_site_icon())); - App::$meta->set('og:type', 'article'); - App::$meta->set('og:url:secure_url', $item['llink']); - App::$meta->set('og:description', $ogdesc); - } - else { - App::$meta->set('og:title', ($items[0]['title'] - ? sprintf( t('%1$s commented "%2$s"'),$item['author']['xchan_name'],$items[0]['title']) - : sprintf( t('%s shared a comment of this post/conversation'),$item['author']['xchan_name']))); - App::$meta->set('og:image', (isset($ogimage) ? $ogimage : System::get_site_icon())); - App::$meta->set('og:type', 'article'); - App::$meta->set('og:url:secure_url', $item['llink']); - App::$meta->set('og:description', sprintf( t('%1$s wrote this: "%2$s"'),$item['author']['xchan_name'],$ogdesc)); - } - } - } - } + if ((($this->updating && $this->loading) || $noscript_content) && (!$items)) { + $r = q( + "SELECT id, item_deleted FROM item WHERE mid = '%s' LIMIT 1", + dbesc($item_hash) + ); - if (local_channel() && $items) { - $ids = ids_to_array($items,'item_id'); - $seen = PConfig::Get(local_channel(),'system','seen_items'); - if (! $seen) { - $seen = []; - } - $seen = array_unique(array_merge($ids,$seen)); - PConfig::Set(local_channel(),'system','seen_items',$seen); - } - - switch($module_format) { - - case 'html': - - if ($this->updating) { - $o .= conversation($items, 'display', $this->updating, 'client'); - } - else { - $o .= ''; - - App::$page['title'] = (($items[0]['title']) ? $items[0]['title'] . " - " . App::$page['title'] : App::$page['title']); - - $o .= conversation($items, 'display', $this->updating, 'client'); - } - - break; - - case 'atom': - - $atom = replace_macros(get_markup_template('atom_feed.tpl'), array( - '$version' => xmlify(System::get_project_version()), - '$generator' => xmlify(System::get_platform_name()), - '$generator_uri' => 'https://hubzilla.org', - '$feed_id' => xmlify(App::$cmd), - '$feed_title' => xmlify(t('Article')), - '$feed_updated' => xmlify(datetime_convert('UTC', 'UTC', 'now', ATOM_TIME)), - '$author' => '', - '$owner' => '', - '$profile_page' => xmlify(z_root() . '/display/' . $target_item['mid']), - )); - - $x = [ 'xml' => $atom, 'channel' => $channel, 'observer_hash' => $observer_hash, 'params' => $params ]; - call_hooks('atom_feed_top',$x); - - $atom = $x['xml']; - - // a much simpler interface - call_hooks('atom_feed', $atom); - - - if($items) { - $type = 'html'; - foreach($items as $item) { - if($item['item_private']) - continue; - $atom .= atom_entry($item, $type, null, '', true, '', false); - } - } - - call_hooks('atom_feed_end', $atom); - - $atom .= '' . "\r\n"; - - header('Content-type: application/atom+xml'); - echo $atom; - killme(); - - } - - if($updateable) { - $x = q("UPDATE item SET item_unseen = 0 where item_unseen = 1 AND uid = %d and parent = %d ", - intval(local_channel()), - intval($r[0]['item_id']) - ); - - - } - - $o .= '
        '; - - if((($this->updating && $this->loading) || $noscript_content) && (! $items)) { - - $r = q("SELECT id, item_deleted FROM item WHERE mid = '%s' LIMIT 1", - dbesc($item_hash) - ); - - if($r) { - if(intval($r[0]['item_deleted'])) { - notice( t('Item has been removed.') . EOL ); - } - else { - notice( t('Permission denied.') . EOL ); - } - } - else { - notice( t('Item not found.') . EOL ); - } - - } - - return $o; - - } + if ($r) { + if (intval($r[0]['item_deleted'])) { + notice(t('Item has been removed.') . EOL); + } else { + notice(t('Permission denied.') . EOL); + } + } else { + notice(t('Item not found.') . EOL); + } + } + return $o; + } } diff --git a/Zotlabs/Module/Drafts.php b/Zotlabs/Module/Drafts.php index 0d7431452..722a1139e 100644 --- a/Zotlabs/Module/Drafts.php +++ b/Zotlabs/Module/Drafts.php @@ -6,24 +6,26 @@ use Zotlabs\Lib\Apps; use Zotlabs\Lib\Libsync; use Zotlabs\Web\Controller; -class Drafts extends Controller { +class Drafts extends Controller +{ - function init() { - if (local_channel() && Apps::system_app_installed(local_channel(),'Drafts')) { - goaway(z_root() . '/stream/?draft=1'); + public function init() + { + if (local_channel() && Apps::system_app_installed(local_channel(), 'Drafts')) { + goaway(z_root() . '/stream/?draft=1'); } - } + } - function get() { + public function get() + { $desc = t('This app allows you to save posts you are writing and finish them later prior to sharing/publishing.'); $text = ''; - if(! ( local_channel() && Apps::system_app_installed(local_channel(),'Drafts'))) { + if (!(local_channel() && Apps::system_app_installed(local_channel(), 'Drafts'))) { return $text; } - - } + } } diff --git a/Zotlabs/Module/Dreport.php b/Zotlabs/Module/Dreport.php index 0498f14e7..ebe25016e 100644 --- a/Zotlabs/Module/Dreport.php +++ b/Zotlabs/Module/Dreport.php @@ -1,195 +1,197 @@ 2) { - $cmd = argv(1); - $mid = argv(2); - } - elseif (argc() > 1) { - $cmd = EMPTY_STR; - $mid = argv(1); - } + if (!local_channel()) { + notice(t('Permission denied') . EOL); + return; + } - $message_id = unpack_link_id($mid); + $table = 'item'; - if (! $message_id) { - notice( t('Invalid message') . EOL); - return; - } - - if($cmd === 'push') { - $i = q("select id from item where mid = '%s' and uid = %d and ( author_xchan = '%s' or ( owner_xchan = '%s' and item_wall = 1 )) ", - dbesc($message_id), - intval($channel['channel_id']), - dbesc($channel['channel_hash']), - dbesc($channel['channel_hash']) - ); - if ($i) { - Run::Summon([ 'Notifier', 'edit_post', $i[0]['id'] ]); - } - sleep(3); - goaway(z_root() . '/dreport/' . urlencode($mid)); - } + $channel = App::get_channel(); - if ($cmd === 'log') { - $host = escape_tags($_REQUEST['host']); - $r = q("select * from dreport where dreport_xchan = '%s' and dreport_mid = '%s' and dreport_recip = '%s'", - dbesc($channel['channel_hash']), - dbesc($message_id), - dbesc($host) - ); - if ($r) { - $output = '

        ' . t('Delivery Log') . '

        '; - $output .= EOL . $r[0]['dreport_log']; - return $output; - } - else { - notice( t('Item not found') . EOL); - return; - } - } - - switch ($table) { - case 'item': - $i = q("select id from item where mid = '%s' and ( author_xchan = '%s' or ( owner_xchan = '%s' and item_wall = 1 )) ", - dbesc($message_id), - dbesc($channel['channel_hash']), - dbesc($channel['channel_hash']) - ); - break; - default: - break; - } - - if(! $i) { - notice( t('Permission denied') . EOL); - return; - } - - $r = q("select * from dreport where dreport_xchan = '%s' and dreport_mid = '%s'", - dbesc($channel['channel_hash']), - dbesc($message_id) - ); - - if (! $r) { - notice( t('no results') . EOL); - } - - for($x = 0; $x < count($r); $x++ ) { - - // This has two purposes: 1. make the delivery report strings translateable, and - // 2. assign an ordering to item delivery results so we can group them and provide - // a readable report with more interesting events listed toward the top and lesser - // interesting items towards the bottom - - switch($r[$x]['dreport_result']) { - case 'channel sync processed': - $r[$x]['gravity'] = 0; - $r[$x]['dreport_result'] = t('channel sync processed'); - break; - case 'queued': - $r[$x]['gravity'] = 2; - $r[$x]['dreport_result'] = '' . t('queued') . ''; - break; - case 'site dead': - $r[$x]['gravity'] = 3; - $r[$x]['dreport_result'] = t('site dead'); - break; - case 'site deferred': - $r[$x]['gravity'] = 3; - $r[$x]['dreport_result'] = t('site might be dead - deferred'); - break; - case 'posted': - $r[$x]['gravity'] = 3; - $r[$x]['dreport_result'] = t('posted'); - break; - case 'accepted for delivery': - $r[$x]['gravity'] = 4; - $r[$x]['dreport_result'] = t('accepted for delivery'); - break; - case 'updated': - $r[$x]['gravity'] = 5; - $r[$x]['dreport_result'] = t('updated'); - case 'update ignored': - $r[$x]['gravity'] = 6; - $r[$x]['dreport_result'] = t('update ignored'); - break; - case 'permission denied': - $r[$x]['dreport_result'] = t('permission denied'); - $r[$x]['gravity'] = 6; - break; - case 'recipient not found': - $r[$x]['dreport_result'] = t('recipient not found'); - break; - case 'mail recalled': - $r[$x]['dreport_result'] = t('mail recalled'); - break; - case 'duplicate mail received': - $r[$x]['dreport_result'] = t('duplicate mail received'); - break; - case 'mail delivered': - $r[$x]['dreport_result'] = t('mail delivered'); - break; - default: - if (strpos($r[$x]['dreport_result'],'delivery rejected') === 0) { - $r[$x]['dreport_result'] = t('delivery rejected') . ' ' . substr($r[$x]['dreport_result'],17); - } - $r[$x]['gravity'] = 1; - break; - } - } - - usort($r,'self::dreport_gravity_sort'); + if (argc() > 2) { + $cmd = argv(1); + $mid = argv(2); + } elseif (argc() > 1) { + $cmd = EMPTY_STR; + $mid = argv(1); + } - $entries = []; - foreach($r as $rr) { - $entries[] = [ - 'name' => escape_tags($rr['dreport_name'] ?: $rr['dreport_recip']), - 'result' => $rr['dreport_result'], - 'time' => escape_tags(datetime_convert('UTC',date_default_timezone_get(),$rr['dreport_time'])) - ]; - } + $message_id = unpack_link_id($mid); - $output = replace_macros(get_markup_template('dreport.tpl'), array( - '$title' => sprintf( t('Delivery report for %1$s'),basename($message_id)) . '...', - '$table' => $table, - '$mid' => urlencode($mid), - '$options' => t('Options'), - '$push' => t('Redeliver'), - '$entries' => $entries - )); - - - return $output; - - - - } - - private static function dreport_gravity_sort($a,$b) { - if($a['gravity'] == $b['gravity']) { - if($a['dreport_name'] === $b['dreport_name']) - return strcmp($a['dreport_time'],$b['dreport_time']); - return strcmp($a['dreport_name'],$b['dreport_name']); - } - return (($a['gravity'] > $b['gravity']) ? 1 : (-1)); - } - + if (!$message_id) { + notice(t('Invalid message') . EOL); + return; + } + + if ($cmd === 'push') { + $i = q( + "select id from item where mid = '%s' and uid = %d and ( author_xchan = '%s' or ( owner_xchan = '%s' and item_wall = 1 )) ", + dbesc($message_id), + intval($channel['channel_id']), + dbesc($channel['channel_hash']), + dbesc($channel['channel_hash']) + ); + if ($i) { + Run::Summon(['Notifier', 'edit_post', $i[0]['id']]); + } + sleep(3); + goaway(z_root() . '/dreport/' . urlencode($mid)); + } + + if ($cmd === 'log') { + $host = escape_tags($_REQUEST['host']); + $r = q( + "select * from dreport where dreport_xchan = '%s' and dreport_mid = '%s' and dreport_recip = '%s'", + dbesc($channel['channel_hash']), + dbesc($message_id), + dbesc($host) + ); + if ($r) { + $output = '

        ' . t('Delivery Log') . '

        '; + $output .= EOL . $r[0]['dreport_log']; + return $output; + } else { + notice(t('Item not found') . EOL); + return; + } + } + + switch ($table) { + case 'item': + $i = q( + "select id from item where mid = '%s' and ( author_xchan = '%s' or ( owner_xchan = '%s' and item_wall = 1 )) ", + dbesc($message_id), + dbesc($channel['channel_hash']), + dbesc($channel['channel_hash']) + ); + break; + default: + break; + } + + if (!$i) { + notice(t('Permission denied') . EOL); + return; + } + + $r = q( + "select * from dreport where dreport_xchan = '%s' and dreport_mid = '%s'", + dbesc($channel['channel_hash']), + dbesc($message_id) + ); + + if (!$r) { + notice(t('no results') . EOL); + } + + for ($x = 0; $x < count($r); $x++) { + // This has two purposes: 1. make the delivery report strings translateable, and + // 2. assign an ordering to item delivery results so we can group them and provide + // a readable report with more interesting events listed toward the top and lesser + // interesting items towards the bottom + + switch ($r[$x]['dreport_result']) { + case 'channel sync processed': + $r[$x]['gravity'] = 0; + $r[$x]['dreport_result'] = t('channel sync processed'); + break; + case 'queued': + $r[$x]['gravity'] = 2; + $r[$x]['dreport_result'] = '' . t('queued') . ''; + break; + case 'site dead': + $r[$x]['gravity'] = 3; + $r[$x]['dreport_result'] = t('site dead'); + break; + case 'site deferred': + $r[$x]['gravity'] = 3; + $r[$x]['dreport_result'] = t('site might be dead - deferred'); + break; + case 'posted': + $r[$x]['gravity'] = 3; + $r[$x]['dreport_result'] = t('posted'); + break; + case 'accepted for delivery': + $r[$x]['gravity'] = 4; + $r[$x]['dreport_result'] = t('accepted for delivery'); + break; + case 'updated': + $r[$x]['gravity'] = 5; + $r[$x]['dreport_result'] = t('updated'); + case 'update ignored': + $r[$x]['gravity'] = 6; + $r[$x]['dreport_result'] = t('update ignored'); + break; + case 'permission denied': + $r[$x]['dreport_result'] = t('permission denied'); + $r[$x]['gravity'] = 6; + break; + case 'recipient not found': + $r[$x]['dreport_result'] = t('recipient not found'); + break; + case 'mail recalled': + $r[$x]['dreport_result'] = t('mail recalled'); + break; + case 'duplicate mail received': + $r[$x]['dreport_result'] = t('duplicate mail received'); + break; + case 'mail delivered': + $r[$x]['dreport_result'] = t('mail delivered'); + break; + default: + if (strpos($r[$x]['dreport_result'], 'delivery rejected') === 0) { + $r[$x]['dreport_result'] = t('delivery rejected') . ' ' . substr($r[$x]['dreport_result'], 17); + } + $r[$x]['gravity'] = 1; + break; + } + } + + usort($r, 'self::dreport_gravity_sort'); + + $entries = []; + foreach ($r as $rr) { + $entries[] = [ + 'name' => escape_tags($rr['dreport_name'] ?: $rr['dreport_recip']), + 'result' => $rr['dreport_result'], + 'time' => escape_tags(datetime_convert('UTC', date_default_timezone_get(), $rr['dreport_time'])) + ]; + } + + $output = replace_macros(get_markup_template('dreport.tpl'), array( + '$title' => sprintf(t('Delivery report for %1$s'), basename($message_id)) . '...', + '$table' => $table, + '$mid' => urlencode($mid), + '$options' => t('Options'), + '$push' => t('Redeliver'), + '$entries' => $entries + )); + + + return $output; + } + + private static function dreport_gravity_sort($a, $b) + { + if ($a['gravity'] == $b['gravity']) { + if ($a['dreport_name'] === $b['dreport_name']) { + return strcmp($a['dreport_time'], $b['dreport_time']); + } + return strcmp($a['dreport_name'], $b['dreport_name']); + } + return (($a['gravity'] > $b['gravity']) ? 1 : (-1)); + } } diff --git a/Zotlabs/Module/Editblock.php b/Zotlabs/Module/Editblock.php index 204d8f81f..91fd97675 100644 --- a/Zotlabs/Module/Editblock.php +++ b/Zotlabs/Module/Editblock.php @@ -1,115 +1,124 @@ 1 && argv(1) === 'sys' && is_site_admin()) { - $sys = get_sys_channel(); - if($sys && intval($sys['channel_id'])) { - \App::$is_sys = true; - } - } + if (argc() > 1 && argv(1) === 'sys' && is_site_admin()) { + $sys = get_sys_channel(); + if ($sys && intval($sys['channel_id'])) { + App::$is_sys = true; + } + } - if(argc() > 1) - $which = argv(1); - else - return; + if (argc() > 1) { + $which = argv(1); + } else { + return; + } - Libprofile::load($which); + Libprofile::load($which); + } - } + public function get() + { - function get() { + if (!App::$profile) { + notice(t('Requested profile is not available.') . EOL); + App::$error = 404; + return; + } - if(! \App::$profile) { - notice( t('Requested profile is not available.') . EOL ); - \App::$error = 404; - return; - } + $which = argv(1); - $which = argv(1); + $uid = local_channel(); + $owner = 0; + $channel = null; + $observer = App::get_observer(); - $uid = local_channel(); - $owner = 0; - $channel = null; - $observer = \App::get_observer(); + $channel = App::get_channel(); - $channel = \App::get_channel(); + if (App::$is_sys && is_site_admin()) { + $sys = get_sys_channel(); + if ($sys && intval($sys['channel_id'])) { + $uid = $owner = intval($sys['channel_id']); + $channel = $sys; + $observer = $sys; + } + } - if(\App::$is_sys && is_site_admin()) { - $sys = get_sys_channel(); - if($sys && intval($sys['channel_id'])) { - $uid = $owner = intval($sys['channel_id']); - $channel = $sys; - $observer = $sys; - } - } + if (!$owner) { + // Figure out who the page owner is. + $r = q( + "select channel_id from channel where channel_address = '%s'", + dbesc($which) + ); + if ($r) { + $owner = intval($r[0]['channel_id']); + } + } - if(! $owner) { - // Figure out who the page owner is. - $r = q("select channel_id from channel where channel_address = '%s'", - dbesc($which) - ); - if($r) { - $owner = intval($r[0]['channel_id']); - } - } + $ob_hash = (($observer) ? $observer['xchan_hash'] : ''); - $ob_hash = (($observer) ? $observer['xchan_hash'] : ''); + if (!perm_is_allowed($owner, $ob_hash, 'write_pages')) { + notice(t('Permission denied.') . EOL); + return; + } - if(! perm_is_allowed($owner,$ob_hash,'write_pages')) { - notice( t('Permission denied.') . EOL); - return; - } + $is_owner = (($uid && $uid == $owner) ? true : false); - $is_owner = (($uid && $uid == $owner) ? true : false); + $o = ''; - $o = ''; + // Figure out which post we're editing + $post_id = ((argc() > 2) ? intval(argv(2)) : 0); - // Figure out which post we're editing - $post_id = ((argc() > 2) ? intval(argv(2)) : 0); + if (!($post_id && $owner)) { + notice(t('Item not found') . EOL); + return; + } - if(! ($post_id && $owner)) { - notice( t('Item not found') . EOL); - return; - } + $itm = q( + "SELECT * FROM item WHERE id = %d and uid = %s LIMIT 1", + intval($post_id), + intval($owner) + ); + if ($itm) { + $item_id = q( + "select * from iconfig where cat = 'system' and k = 'BUILDBLOCK' and iid = %d limit 1", + intval($itm[0]['id']) + ); + if ($item_id) { + $block_title = $item_id[0]['v']; + } + } else { + notice(t('Item not found') . EOL); + return; + } - $itm = q("SELECT * FROM item WHERE id = %d and uid = %s LIMIT 1", - intval($post_id), - intval($owner) - ); - if($itm) { - $item_id = q("select * from iconfig where cat = 'system' and k = 'BUILDBLOCK' and iid = %d limit 1", - intval($itm[0]['id']) - ); - if($item_id) - $block_title = $item_id[0]['v']; - } - else { - notice( t('Item not found') . EOL); - return; - } + $mimetype = $itm[0]['mimetype']; - $mimetype = $itm[0]['mimetype']; - - $content = $itm[0]['body']; - if($itm[0]['mimetype'] === 'text/markdown') - $content = \Zotlabs\Lib\MarkdownSoap::unescape($itm[0]['body']); + $content = $itm[0]['body']; + if ($itm[0]['mimetype'] === 'text/markdown') { + $content = MarkdownSoap::unescape($itm[0]['body']); + } - $rp = 'blocks/' . $channel['channel_address']; + $rp = 'blocks/' . $channel['channel_address']; - $x = array( + $x = array( 'nickname' => $channel['channel_address'], 'bbco_autocomplete'=> ((in_array($mimetype, [ 'text/bbcode', 'text/x-multicode' ])) ? 'bbcode' : 'comanche-block'), 'return_path' => $rp, @@ -134,20 +143,18 @@ class Editblock extends Controller { 'pagetitle' => $block_title, 'profile_uid' => (intval($channel['channel_id'])), 'bbcode' => ((in_array($mimetype, [ 'text/bbcode' , 'text/x-multicode' ])) ? true : false) - ); + ); - $editor = status_editor($x); + $editor = status_editor($x); - $o .= replace_macros(get_markup_template('edpost_head.tpl'), array( - '$title' => t('Edit Block'), - '$delete' => ((($itm[0]['author_xchan'] === $ob_hash) || ($itm[0]['owner_xchan'] === $ob_hash)) ? t('Delete') : false), - '$id' => $itm[0]['id'], - '$cancel' => t('Cancel'), - '$editor' => $editor - )); - - return $o; - - } + $o .= replace_macros(get_markup_template('edpost_head.tpl'), array( + '$title' => t('Edit Block'), + '$delete' => ((($itm[0]['author_xchan'] === $ob_hash) || ($itm[0]['owner_xchan'] === $ob_hash)) ? t('Delete') : false), + '$id' => $itm[0]['id'], + '$cancel' => t('Cancel'), + '$editor' => $editor + )); + return $o; + } } diff --git a/Zotlabs/Module/Editlayout.php b/Zotlabs/Module/Editlayout.php index 8d907d168..5eca164da 100644 --- a/Zotlabs/Module/Editlayout.php +++ b/Zotlabs/Module/Editlayout.php @@ -1,4 +1,5 @@ 1 && argv(1) === 'sys' && is_site_admin()) { - $sys = get_sys_channel(); - if($sys && intval($sys['channel_id'])) { - \App::$is_sys = true; - } - } + if (argc() > 1 && argv(1) === 'sys' && is_site_admin()) { + $sys = get_sys_channel(); + if ($sys && intval($sys['channel_id'])) { + App::$is_sys = true; + } + } - if(argc() > 1) - $which = argv(1); - else - return; + if (argc() > 1) { + $which = argv(1); + } else { + return; + } - Libprofile::load($which); + Libprofile::load($which); + } - } + public function get() + { - function get() { + if (!App::$profile) { + notice(t('Requested profile is not available.') . EOL); + App::$error = 404; + return; + } - if(! \App::$profile) { - notice( t('Requested profile is not available.') . EOL ); - \App::$error = 404; - return; - } + $which = argv(1); - $which = argv(1); + $uid = local_channel(); + $owner = 0; + $channel = null; + $observer = App::get_observer(); - $uid = local_channel(); - $owner = 0; - $channel = null; - $observer = \App::get_observer(); + $channel = App::get_channel(); - $channel = \App::get_channel(); + if (App::$is_sys && is_site_admin()) { + $sys = get_sys_channel(); + if ($sys && intval($sys['channel_id'])) { + $uid = $owner = intval($sys['channel_id']); + $channel = $sys; + $observer = $sys; + } + } - if(\App::$is_sys && is_site_admin()) { - $sys = get_sys_channel(); - if($sys && intval($sys['channel_id'])) { - $uid = $owner = intval($sys['channel_id']); - $channel = $sys; - $observer = $sys; - } - } + if (!$owner) { + // Figure out who the page owner is. + $r = q( + "select channel_id from channel where channel_address = '%s'", + dbesc($which) + ); + if ($r) { + $owner = intval($r[0]['channel_id']); + } + } - if(! $owner) { - // Figure out who the page owner is. - $r = q("select channel_id from channel where channel_address = '%s'", - dbesc($which) - ); - if($r) { - $owner = intval($r[0]['channel_id']); - } - } + $ob_hash = (($observer) ? $observer['xchan_hash'] : ''); - $ob_hash = (($observer) ? $observer['xchan_hash'] : ''); + if (!perm_is_allowed($owner, $ob_hash, 'write_pages')) { + notice(t('Permission denied.') . EOL); + return; + } - if(! perm_is_allowed($owner,$ob_hash,'write_pages')) { - notice( t('Permission denied.') . EOL); - return; - } + $is_owner = (($uid && $uid == $owner) ? true : false); - $is_owner = (($uid && $uid == $owner) ? true : false); + $o = ''; - $o = ''; + // Figure out which post we're editing + $post_id = ((argc() > 2) ? intval(argv(2)) : 0); - // Figure out which post we're editing - $post_id = ((argc() > 2) ? intval(argv(2)) : 0); + if (!$post_id) { + notice(t('Item not found') . EOL); + return; + } - if(! $post_id) { - notice( t('Item not found') . EOL); - return; - } + // Now we've got a post and an owner, let's find out if we're allowed to edit it - // Now we've got a post and an owner, let's find out if we're allowed to edit it + $ob_hash = (($observer) ? $observer['xchan_hash'] : ''); - $ob_hash = (($observer) ? $observer['xchan_hash'] : ''); + $perms = get_all_perms($owner, $ob_hash); - $perms = get_all_perms($owner,$ob_hash); + if (!$perms['write_pages']) { + notice(t('Permission denied.') . EOL); + return; + } - if(! $perms['write_pages']) { - notice( t('Permission denied.') . EOL); - return; - } + $itm = q( + "SELECT * FROM item WHERE id = %d and uid = %s LIMIT 1", + intval($post_id), + intval($owner) + ); - $itm = q("SELECT * FROM item WHERE id = %d and uid = %s LIMIT 1", - intval($post_id), - intval($owner) - ); - - $item_id = q("select * from iconfig where cat = 'system' and k = 'PDL' and iid = %d limit 1", - intval($itm[0]['id']) - ); - if($item_id) - $layout_title = $item_id[0]['v']; + $item_id = q( + "select * from iconfig where cat = 'system' and k = 'PDL' and iid = %d limit 1", + intval($itm[0]['id']) + ); + if ($item_id) { + $layout_title = $item_id[0]['v']; + } - $rp = 'layouts/' . $which; + $rp = 'layouts/' . $which; - $x = array( - 'webpage' => ITEM_TYPE_PDL, - 'nickname' => $channel['channel_address'], - 'editor_autocomplete'=> true, - 'bbco_autocomplete'=> 'comanche', - 'return_path' => $rp, - 'button' => t('Edit'), - 'hide_voting' => true, - 'hide_future' => true, - 'hide_expire' => true, - 'hide_location' => true, - 'hide_weblink' => true, - 'hide_attach' => true, - 'hide_preview' => true, - 'disable_comments' => true, - 'ptyp' => $itm[0]['obj_type'], - 'body' => undo_post_tagging($itm[0]['body']), - 'post_id' => $post_id, - 'title' => htmlspecialchars($itm[0]['title'],ENT_COMPAT,'UTF-8'), - 'pagetitle' => $layout_title, - 'ptlabel' => t('Layout Name'), - 'placeholdertitle' => t('Layout Description (Optional)'), - 'showacl' => false, - 'profile_uid' => intval($owner), - ); + $x = array( + 'webpage' => ITEM_TYPE_PDL, + 'nickname' => $channel['channel_address'], + 'editor_autocomplete' => true, + 'bbco_autocomplete' => 'comanche', + 'return_path' => $rp, + 'button' => t('Edit'), + 'hide_voting' => true, + 'hide_future' => true, + 'hide_expire' => true, + 'hide_location' => true, + 'hide_weblink' => true, + 'hide_attach' => true, + 'hide_preview' => true, + 'disable_comments' => true, + 'ptyp' => $itm[0]['obj_type'], + 'body' => undo_post_tagging($itm[0]['body']), + 'post_id' => $post_id, + 'title' => htmlspecialchars($itm[0]['title'], ENT_COMPAT, 'UTF-8'), + 'pagetitle' => $layout_title, + 'ptlabel' => t('Layout Name'), + 'placeholdertitle' => t('Layout Description (Optional)'), + 'showacl' => false, + 'profile_uid' => intval($owner), + ); - $editor = status_editor($x); + $editor = status_editor($x); - $o .= replace_macros(get_markup_template('edpost_head.tpl'), array( - '$title' => t('Edit Layout'), - '$delete' => ((($itm[0]['author_xchan'] === $ob_hash) || ($itm[0]['owner_xchan'] === $ob_hash)) ? t('Delete') : false), - '$id' => $itm[0]['id'], - '$cancel' => t('Cancel'), - '$editor' => $editor - )); - - return $o; - - } + $o .= replace_macros(get_markup_template('edpost_head.tpl'), array( + '$title' => t('Edit Layout'), + '$delete' => ((($itm[0]['author_xchan'] === $ob_hash) || ($itm[0]['owner_xchan'] === $ob_hash)) ? t('Delete') : false), + '$id' => $itm[0]['id'], + '$cancel' => t('Cancel'), + '$editor' => $editor + )); + return $o; + } } diff --git a/Zotlabs/Module/Editpost.php b/Zotlabs/Module/Editpost.php index 6e9754689..f8eacf6f7 100644 --- a/Zotlabs/Module/Editpost.php +++ b/Zotlabs/Module/Editpost.php @@ -1,4 +1,5 @@ 1) ? intval(argv(1)) : 0); + $post_id = ((argc() > 1) ? intval(argv(1)) : 0); - if (! $post_id) { - notice( t('Item not found') . EOL); - return; - } + if (!$post_id) { + notice(t('Item not found') . EOL); + return; + } - $item = q("SELECT * FROM item WHERE id = %d AND ( owner_xchan = '%s' OR author_xchan = '%s' ) LIMIT 1", - intval($post_id), - dbesc(get_observer_hash()), - dbesc(get_observer_hash()) - ); + $item = q( + "SELECT * FROM item WHERE id = %d AND ( owner_xchan = '%s' OR author_xchan = '%s' ) LIMIT 1", + intval($post_id), + dbesc(get_observer_hash()), + dbesc(get_observer_hash()) + ); - // don't allow web editing of potentially binary content (item_obscured = 1) + // don't allow web editing of potentially binary content (item_obscured = 1) - if ((! $item) || intval($item[0]['item_obscured'])) { - notice( t('Item is not editable') . EOL); - return; - } + if ((!$item) || intval($item[0]['item_obscured'])) { + notice(t('Item is not editable') . EOL); + return; + } - $item = array_shift($item); + $item = array_shift($item); - $owner_uid = intval($item['uid']); - $owner = channelx_by_n($owner_uid); + $owner_uid = intval($item['uid']); + $owner = channelx_by_n($owner_uid); - if ($item['resource_type'] === 'photo' && $item['resource_id'] && $owner) { - goaway(z_root() . '/photos/' . $owner['channel_address'] . '/image/' . $item['resource_id'] . '?expandform=1'); - } + if ($item['resource_type'] === 'photo' && $item['resource_id'] && $owner) { + goaway(z_root() . '/photos/' . $owner['channel_address'] . '/image/' . $item['resource_id'] . '?expandform=1'); + } - if ($item['resource_type'] === 'event' && $item['resource_id']) { - goaway(z_root() . '/events/' . $item['resource_id'] . '?expandform=1'); - } + if ($item['resource_type'] === 'event' && $item['resource_id']) { + goaway(z_root() . '/events/' . $item['resource_id'] . '?expandform=1'); + } - $channel = App::get_channel(); + $channel = App::get_channel(); - $category = ''; - $collections = []; - $catsenabled = ((Apps::system_app_installed($owner_uid,'Categories')) ? 'categories' : ''); + $category = ''; + $collections = []; + $catsenabled = ((Apps::system_app_installed($owner_uid, 'Categories')) ? 'categories' : ''); - // we have a single item, but fetch_post_tags expects an array. Convert it before and after. + // we have a single item, but fetch_post_tags expects an array. Convert it before and after. - $item = array_shift(fetch_post_tags([$item])); + $item = array_shift(fetch_post_tags([$item])); - if ($catsenabled) { - $cats = get_terms_oftype($item['term'], TERM_CATEGORY); + if ($catsenabled) { + $cats = get_terms_oftype($item['term'], TERM_CATEGORY); - if ($cats) { - foreach ($cats as $cat) { - if (strlen($category)) { - $category .= ', '; - } - $category .= $cat['term']; - } - } - } + if ($cats) { + foreach ($cats as $cat) { + if (strlen($category)) { + $category .= ', '; + } + $category .= $cat['term']; + } + } + } - $clcts = get_terms_oftype($item['term'], TERM_PCATEGORY); - if ($clcts) { - foreach ($clcts as $clct) { - $collections[] = $clct['term']; - } - } + $clcts = get_terms_oftype($item['term'], TERM_PCATEGORY); + if ($clcts) { + foreach ($clcts as $clct) { + $collections[] = $clct['term']; + } + } - if ($item['attach']) { - $j = json_decode($item['attach'],true); - if ($j) { - foreach ($j as $jj) { - $item['body'] .= "\n" . '[attachment]' . basename($jj['href']) . ',' . $jj['revision'] . '[/attachment]' . "\n"; - } - } - } + if ($item['attach']) { + $j = json_decode($item['attach'], true); + if ($j) { + foreach ($j as $jj) { + $item['body'] .= "\n" . '[attachment]' . basename($jj['href']) . ',' . $jj['revision'] . '[/attachment]' . "\n"; + } + } + } - if (intval($item['item_unpublished'])) { - // clear the old creation date if editing a saved draft. These will always show as just created. - unset($item['created']); - } + if (intval($item['item_unpublished'])) { + // clear the old creation date if editing a saved draft. These will always show as just created. + unset($item['created']); + } - if ($item['summary']) { - $item['body'] = '[summary]' . $item['summary'] . '[/summary]' . "\n\n" . $item['body']; - } + if ($item['summary']) { + $item['body'] = '[summary]' . $item['summary'] . '[/summary]' . "\n\n" . $item['body']; + } - $x = [ - 'nickname' => $channel['channel_address'], - 'item' => $item, - 'editor_autocomplete'=> true, - 'bbco_autocomplete'=> 'bbcode', - 'return_path' => $_SESSION['return_url'], - 'button' => t('Submit'), - 'hide_voting' => true, - 'hide_future' => true, - 'hide_location' => true, - 'is_draft' => ((intval($item['item_unpublished'])) ? true : false), - 'parent' => (($item['mid'] === $item['parent_mid']) ? 0 : $item['parent']), - 'mimetype' => $item['mimetype'], - 'ptyp' => $item['obj_type'], - 'body' => htmlspecialchars_decode(undo_post_tagging($item['body']),ENT_COMPAT), - 'post_id' => $post_id, - 'defloc' => $channel['channel_location'], - 'visitor' => true, - 'title' => htmlspecialchars_decode($item['title'],ENT_COMPAT), - 'category' => $category, - 'showacl' => ((intval($item['item_unpublished'])) ? true : false), - 'lockstate' => (($item['allow_cid'] || $item['allow_gid'] || $item['deny_cid'] || $item['deny_gid']) ? 'lock' : 'unlock'), - 'acl' => populate_acl($item, true, PermissionDescription::fromGlobalPermission('view_stream'), get_post_aclDialogDescription(), 'acl_dialog_post'), - 'bang' => EMPTY_STR, - 'permissions' => $item, - 'profile_uid' => $owner_uid, - 'catsenabled' => $catsenabled, - 'collections' => $collections, - 'jotnets' => true, - 'hide_expire' => true, - 'bbcode' => true - ]; + $x = [ + 'nickname' => $channel['channel_address'], + 'item' => $item, + 'editor_autocomplete' => true, + 'bbco_autocomplete' => 'bbcode', + 'return_path' => $_SESSION['return_url'], + 'button' => t('Submit'), + 'hide_voting' => true, + 'hide_future' => true, + 'hide_location' => true, + 'is_draft' => ((intval($item['item_unpublished'])) ? true : false), + 'parent' => (($item['mid'] === $item['parent_mid']) ? 0 : $item['parent']), + 'mimetype' => $item['mimetype'], + 'ptyp' => $item['obj_type'], + 'body' => htmlspecialchars_decode(undo_post_tagging($item['body']), ENT_COMPAT), + 'post_id' => $post_id, + 'defloc' => $channel['channel_location'], + 'visitor' => true, + 'title' => htmlspecialchars_decode($item['title'], ENT_COMPAT), + 'category' => $category, + 'showacl' => ((intval($item['item_unpublished'])) ? true : false), + 'lockstate' => (($item['allow_cid'] || $item['allow_gid'] || $item['deny_cid'] || $item['deny_gid']) ? 'lock' : 'unlock'), + 'acl' => populate_acl($item, true, PermissionDescription::fromGlobalPermission('view_stream'), get_post_aclDialogDescription(), 'acl_dialog_post'), + 'bang' => EMPTY_STR, + 'permissions' => $item, + 'profile_uid' => $owner_uid, + 'catsenabled' => $catsenabled, + 'collections' => $collections, + 'jotnets' => true, + 'hide_expire' => true, + 'bbcode' => true + ]; - $editor = status_editor($x); + $editor = status_editor($x); - $output .= replace_macros(get_markup_template('edpost_head.tpl'), - [ '$title' => t('Edit post'), '$cancel' => t('Cancel'), '$editor' => $editor ] ); - - return $output; - - } + $output .= replace_macros( + get_markup_template('edpost_head.tpl'), + ['$title' => t('Edit post'), '$cancel' => t('Cancel'), '$editor' => $editor] + ); + return $output; + } } diff --git a/Zotlabs/Module/Editwebpage.php b/Zotlabs/Module/Editwebpage.php index 2ac90718a..39d6fbd3d 100644 --- a/Zotlabs/Module/Editwebpage.php +++ b/Zotlabs/Module/Editwebpage.php @@ -1,7 +1,10 @@ 1 && argv(1) === 'sys' && is_site_admin()) { - $sys = get_sys_channel(); - if($sys && intval($sys['channel_id'])) { - \App::$is_sys = true; - } - } + if (argc() > 1 && argv(1) === 'sys' && is_site_admin()) { + $sys = get_sys_channel(); + if ($sys && intval($sys['channel_id'])) { + App::$is_sys = true; + } + } - if(argc() > 1) - $which = argv(1); - else - return; + if (argc() > 1) { + $which = argv(1); + } else { + return; + } - Libprofile::load($which); + Libprofile::load($which); + } - } + public function get() + { - function get() { + if (!App::$profile) { + notice(t('Requested profile is not available.') . EOL); + App::$error = 404; + return; + } - if(! \App::$profile) { - notice( t('Requested profile is not available.') . EOL ); - \App::$error = 404; - return; - } + $which = argv(1); - $which = argv(1); + $uid = local_channel(); + $owner = 0; + $channel = null; + $observer = App::get_observer(); - $uid = local_channel(); - $owner = 0; - $channel = null; - $observer = \App::get_observer(); + $channel = App::get_channel(); - $channel = \App::get_channel(); + if (App::$is_sys && is_site_admin()) { + $sys = get_sys_channel(); + if ($sys && intval($sys['channel_id'])) { + $uid = $owner = intval($sys['channel_id']); + $channel = $sys; + $observer = $sys; + } + } - if(\App::$is_sys && is_site_admin()) { - $sys = get_sys_channel(); - if($sys && intval($sys['channel_id'])) { - $uid = $owner = intval($sys['channel_id']); - $channel = $sys; - $observer = $sys; - } - } + if (!$owner) { + // Figure out who the page owner is. + $r = q( + "select channel_id from channel where channel_address = '%s'", + dbesc($which) + ); + if ($r) { + $owner = intval($r[0]['channel_id']); + } + } - if(! $owner) { - // Figure out who the page owner is. - $r = q("select channel_id from channel where channel_address = '%s'", - dbesc($which) - ); - if($r) { - $owner = intval($r[0]['channel_id']); - } - } + $ob_hash = (($observer) ? $observer['xchan_hash'] : ''); - $ob_hash = (($observer) ? $observer['xchan_hash'] : ''); + if (!perm_is_allowed($owner, $ob_hash, 'write_pages')) { + notice(t('Permission denied.') . EOL); + return; + } - if(! perm_is_allowed($owner,$ob_hash,'write_pages')) { - notice( t('Permission denied.') . EOL); - return; - } + $is_owner = (($uid && $uid == $owner) ? true : false); - $is_owner = (($uid && $uid == $owner) ? true : false); + $o = ''; - $o = ''; + // Figure out which post we're editing + $post_id = ((argc() > 2) ? intval(argv(2)) : 0); - // Figure out which post we're editing - $post_id = ((argc() > 2) ? intval(argv(2)) : 0); - - if(! $post_id) { - notice( t('Item not found') . EOL); - return; - } + if (!$post_id) { + notice(t('Item not found') . EOL); + return; + } - $ob_hash = (($observer) ? $observer['xchan_hash'] : ''); + $ob_hash = (($observer) ? $observer['xchan_hash'] : ''); - $perms = get_all_perms($owner,$ob_hash); + $perms = get_all_perms($owner, $ob_hash); - if(! $perms['write_pages']) { - notice( t('Permission denied.') . EOL); - return; - } + if (!$perms['write_pages']) { + notice(t('Permission denied.') . EOL); + return; + } - // We've already figured out which item we want and whose copy we need, - // so we don't need anything fancy here + // We've already figured out which item we want and whose copy we need, + // so we don't need anything fancy here - $sql_extra = item_permissions_sql($owner); + $sql_extra = item_permissions_sql($owner); - $itm = q("SELECT * FROM item WHERE id = %d and uid = %s $sql_extra LIMIT 1", - intval($post_id), - intval($owner) - ); + $itm = q( + "SELECT * FROM item WHERE id = %d and uid = %s $sql_extra LIMIT 1", + intval($post_id), + intval($owner) + ); - // don't allow web editing of potentially binary content (item_obscured = 1) - // @FIXME how do we do it instead? + // don't allow web editing of potentially binary content (item_obscured = 1) + // @FIXME how do we do it instead? - if((! $itm) || intval($itm[0]['item_obscured'])) { - notice( t('Permission denied.') . EOL); - return; - } + if ((!$itm) || intval($itm[0]['item_obscured'])) { + notice(t('Permission denied.') . EOL); + return; + } - $item_id = q("select * from iconfig where cat = 'system' and k = 'WEBPAGE' and iid = %d limit 1", - intval($itm[0]['id']) - ); - if($item_id) - $page_title = urldecode($item_id[0]['v']); + $item_id = q( + "select * from iconfig where cat = 'system' and k = 'WEBPAGE' and iid = %d limit 1", + intval($itm[0]['id']) + ); + if ($item_id) { + $page_title = urldecode($item_id[0]['v']); + } - $mimetype = $itm[0]['mimetype']; + $mimetype = $itm[0]['mimetype']; - if($mimetype === 'application/x-php') { - if((! $uid) || ($uid != $itm[0]['uid'])) { - notice( t('Permission denied.') . EOL); - return; - } - } - - $layout = $itm[0]['layout_mid']; + if ($mimetype === 'application/x-php') { + if ((!$uid) || ($uid != $itm[0]['uid'])) { + notice(t('Permission denied.') . EOL); + return; + } + } - $content = $itm[0]['body']; - if($itm[0]['mimetype'] === 'text/markdown') - $content = \Zotlabs\Lib\MarkdownSoap::unescape($itm[0]['body']); - - $rp = 'webpages/' . $which; + $layout = $itm[0]['layout_mid']; - $x = array( - 'nickname' => $channel['channel_address'], - 'bbco_autocomplete'=> ((in_array($mimetype, [ 'text/bbcode', 'text/x-multicode' ])) ? 'bbcode' : ''), - 'return_path' => $rp, - 'webpage' => ITEM_TYPE_WEBPAGE, - 'ptlabel' => t('Page link'), - 'pagetitle' => $page_title, - 'writefiles' => ((in_array($mimetype, [ 'text/bbcode', 'text/x-multicode' ])) ? perm_is_allowed($owner, get_observer_hash(), 'write_storage') : false), - 'button' => t('Edit'), - 'weblink' => ((in_array($mimetype, [ 'text/bbcode', 'text/x-multicode' ])) ? t('Insert web link') : false), - 'hide_location' => true, - 'hide_voting' => true, - 'ptyp' => $itm[0]['type'], - 'body' => undo_post_tagging($content), - 'post_id' => $post_id, - 'visitor' => ($is_owner) ? true : false, - 'acl' => populate_acl($itm[0],false,\Zotlabs\Lib\PermissionDescription::fromGlobalPermission('view_pages')), - 'permissions' => $itm[0], - 'showacl' => ($is_owner) ? true : false, - 'mimetype' => $mimetype, - 'mimeselect' => true, - 'layout' => $layout, - 'layoutselect' => true, - 'title' => htmlspecialchars($itm[0]['title'],ENT_COMPAT,'UTF-8'), - 'lockstate' => (((strlen($itm[0]['allow_cid'])) || (strlen($itm[0]['allow_gid'])) || (strlen($itm[0]['deny_cid'])) || (strlen($itm[0]['deny_gid']))) ? 'lock' : 'unlock'), - 'profile_uid' => (intval($owner)), - 'bbcode' => ((in_array($mimetype, [ 'text/bbcode', 'text/x-multicode' ])) ? true : false) - ); + $content = $itm[0]['body']; + if ($itm[0]['mimetype'] === 'text/markdown') { + $content = MarkdownSoap::unescape($itm[0]['body']); + } - $editor = status_editor($x); + $rp = 'webpages/' . $which; - $o .= replace_macros(get_markup_template('edpost_head.tpl'), array( - '$title' => t('Edit Webpage'), - '$delete' => ((($itm[0]['author_xchan'] === $ob_hash) || ($itm[0]['owner_xchan'] === $ob_hash)) ? t('Delete') : false), - '$editor' => $editor, - '$cancel' => t('Cancel'), - '$id' => $itm[0]['id'] - )); + $x = array( + 'nickname' => $channel['channel_address'], + 'bbco_autocomplete' => ((in_array($mimetype, [ 'text/bbcode', 'text/x-multicode'])) ? 'bbcode' : ''), + 'return_path' => $rp, + 'webpage' => ITEM_TYPE_WEBPAGE, + 'ptlabel' => t('Page link'), + 'pagetitle' => $page_title, + 'writefiles' => ((in_array($mimetype, [ 'text/bbcode', 'text/x-multicode'])) ? perm_is_allowed($owner, get_observer_hash(), 'write_storage') : false), + 'button' => t('Edit'), + 'weblink' => ((in_array($mimetype, [ 'text/bbcode', 'text/x-multicode'])) ? t('Insert web link') : false), + 'hide_location' => true, + 'hide_voting' => true, + 'ptyp' => $itm[0]['type'], + 'body' => undo_post_tagging($content), + 'post_id' => $post_id, + 'visitor' => ($is_owner) ? true : false, + 'acl' => populate_acl($itm[0], false, PermissionDescription::fromGlobalPermission('view_pages')), + 'permissions' => $itm[0], + 'showacl' => ($is_owner) ? true : false, + 'mimetype' => $mimetype, + 'mimeselect' => true, + 'layout' => $layout, + 'layoutselect' => true, + 'title' => htmlspecialchars($itm[0]['title'], ENT_COMPAT, 'UTF-8'), + 'lockstate' => (((strlen($itm[0]['allow_cid'])) || (strlen($itm[0]['allow_gid'])) || (strlen($itm[0]['deny_cid'])) || (strlen($itm[0]['deny_gid']))) ? 'lock' : 'unlock'), + 'profile_uid' => (intval($owner)), + 'bbcode' => ((in_array($mimetype, ['text/bbcode', 'text/x-multicode'])) ? true : false) + ); - return $o; + $editor = status_editor($x); - } + $o .= replace_macros(get_markup_template('edpost_head.tpl'), array( + '$title' => t('Edit Webpage'), + '$delete' => ((($itm[0]['author_xchan'] === $ob_hash) || ($itm[0]['owner_xchan'] === $ob_hash)) ? t('Delete') : false), + '$editor' => $editor, + '$cancel' => t('Cancel'), + '$id' => $itm[0]['id'] + )); + return $o; + } } diff --git a/Zotlabs/Module/Email_resend.php b/Zotlabs/Module/Email_resend.php index f8a336be0..2a5e199fd 100644 --- a/Zotlabs/Module/Email_resend.php +++ b/Zotlabs/Module/Email_resend.php @@ -2,45 +2,43 @@ namespace Zotlabs\Module; +use Zotlabs\Web\Controller; -class Email_resend extends \Zotlabs\Web\Controller { +class Email_resend extends Controller +{ - function post() { + public function post() + { - if($_POST['token']) { - if(! account_approve(trim($_POST['token']))) { - notice(t('Token verification failed.')); - } - } - - } + if ($_POST['token']) { + if (!account_approve(trim($_POST['token']))) { + notice(t('Token verification failed.')); + } + } + } - function get() { + public function get() + { - if(argc() > 1) { - $result = false; - $email = hex2bin(argv(1)); + if (argc() > 1) { + $result = false; + $email = hex2bin(argv(1)); - if($email) { - $result = verify_email_address( [ 'resend' => true, 'email' => $email ] ); - } + if ($email) { + $result = verify_email_address(['resend' => true, 'email' => $email]); + } - if($result) { - notice(t('Email verification resent')); - } - else { - notice(t('Unable to resend email verification message.')); - } + if ($result) { + notice(t('Email verification resent')); + } else { + notice(t('Unable to resend email verification message.')); + } - goaway(z_root() . '/email_validation/' . bin2hex($email)); - - } - - // @todo - one can provide a form here to resend the mail - // after directing to here if a succesful login was attempted from an unverified address. - - - } + goaway(z_root() . '/email_validation/' . bin2hex($email)); + } + // @todo - one can provide a form here to resend the mail + // after directing to here if a succesful login was attempted from an unverified address. + } } diff --git a/Zotlabs/Module/Email_validation.php b/Zotlabs/Module/Email_validation.php index c1ba9a01a..90f68cbca 100644 --- a/Zotlabs/Module/Email_validation.php +++ b/Zotlabs/Module/Email_validation.php @@ -2,47 +2,49 @@ namespace Zotlabs\Module; +use Zotlabs\Web\Controller; -class Email_validation extends \Zotlabs\Web\Controller { +class Email_validation extends Controller +{ - function post() { + public function post() + { - $success = false; - if($_POST['token']) { - // This will redirect internally on success unless the channel is auto_created - if(account_approve(trim(basename($_POST['token'])))) { - $success = true; - if(get_config('system','auto_channel_create')) { - $next_page = get_config('system', 'workflow_channel_next', 'profiles'); - } - if($next_page) { - goaway(z_root() . '/' . $next_page); - } - } - } - if(! $success) { - notice( t('Token verification failed.') . EOL); - } - } + $success = false; + if ($_POST['token']) { + // This will redirect internally on success unless the channel is auto_created + if (account_approve(trim(basename($_POST['token'])))) { + $success = true; + if (get_config('system', 'auto_channel_create')) { + $next_page = get_config('system', 'workflow_channel_next', 'profiles'); + } + if ($next_page) { + goaway(z_root() . '/' . $next_page); + } + } + } + if (!$success) { + notice(t('Token verification failed.') . EOL); + } + } - function get() { + public function get() + { - if(argc() > 1) { - $email = hex2bin(argv(1)); - } + if (argc() > 1) { + $email = hex2bin(argv(1)); + } - $o = replace_macros(get_markup_template('email_validation.tpl'), [ - '$title' => t('Email Verification Required'), - '$desc' => sprintf( t('A verification token was sent to your email address [%s]. Enter that token here to complete the account verification step. Please allow a few minutes for delivery, and check your spam folder if you do not see the message.'),$email), - '$resend' => t('Resend Email'), - '$email' => bin2hex($email), - '$submit' => t('Submit'), - '$token' => [ 'token', t('Validation token'),'','' ], - ]); - - return $o; + $o = replace_macros(get_markup_template('email_validation.tpl'), [ + '$title' => t('Email Verification Required'), + '$desc' => sprintf(t('A verification token was sent to your email address [%s]. Enter that token here to complete the account verification step. Please allow a few minutes for delivery, and check your spam folder if you do not see the message.'), $email), + '$resend' => t('Resend Email'), + '$email' => bin2hex($email), + '$submit' => t('Submit'), + '$token' => ['token', t('Validation token'), '', ''], + ]); - } - -} \ No newline at end of file + return $o; + } +} diff --git a/Zotlabs/Module/Embed.php b/Zotlabs/Module/Embed.php index d470719a0..1cd6e296b 100644 --- a/Zotlabs/Module/Embed.php +++ b/Zotlabs/Module/Embed.php @@ -9,14 +9,16 @@ namespace Zotlabs\Module; use Zotlabs\Web\Controller; -class Embed extends Controller { +class Embed extends Controller +{ - function init() { - $post_id = ((argc() > 1) ? intval(argv(1)) : 0); - - if ($post_id && local_channel()) { - echo '[share=' . $post_id . '][/share]'; - } - killme(); - } + public function init() + { + $post_id = ((argc() > 1) ? intval(argv(1)) : 0); + + if ($post_id && local_channel()) { + echo '[share=' . $post_id . '][/share]'; + } + killme(); + } } diff --git a/Zotlabs/Module/Embedphotos.php b/Zotlabs/Module/Embedphotos.php index cd4db4aa9..1a26532d9 100644 --- a/Zotlabs/Module/Embedphotos.php +++ b/Zotlabs/Module/Embedphotos.php @@ -8,240 +8,237 @@ use Zotlabs\Web\Controller; require_once('include/attach.php'); require_once('include/photos.php'); -class Embedphotos extends Controller { +class Embedphotos extends Controller +{ - /** - * - * This is the POST destination for the embedphotos button - * - */ - function post() { - - // The admin tools for setting a site logo and cover photo set the channel_id explicitly - // to the 'sys' channel and use stored resources for that channel. - // Legacy behaviour uses the local logged in channel. - - if (argc() > 2 && is_site_admin() && intval(argv(2))) { - $channel_id = argv(2); - } - else { - $channel_id = local_channel(); - } - if (argc() > 1 && argv(1) === 'album') { - // API: /embedphotos/album - $name = (x($_POST,'name') ? $_POST['name'] : null ); - if (! $name) { - json_return_and_die(array('errormsg' => 'Error retrieving album', 'status' => false)); - } - $album = $this->embedphotos_widget_album(array('channel_id' => $channel_id, 'album' => $name)); - json_return_and_die(array('status' => true, 'content' => $album)); - } - if (argc() > 1 && argv(1) === 'albumlist') { - // API: /embedphotos/albumlist - $album_list = $this->embedphotos_album_list($channel_id); - json_return_and_die(array('status' => true, 'albumlist' => $album_list)); - } - if (argc() > 1 && argv(1) === 'photolink') { - // API: /embedphotos/photolink - $href = (x($_POST,'href') ? $_POST['href'] : null ); - if (! $href) { - json_return_and_die(array('errormsg' => 'Error retrieving link ' . $href, 'status' => false)); - } - $resource_id = array_pop(explode("/", $href)); + /** + * + * This is the POST destination for the embedphotos button + * + */ + public function post() + { - $x = self::photolink($resource_id, $channel_id); - if ($x) { - json_return_and_die(array('status' => true, 'photolink' => $x, 'resource_id' => $resource_id)); - } - json_return_and_die(array('errormsg' => 'Error retrieving resource ' . $resource_id, 'status' => false)); + // The admin tools for setting a site logo and cover photo set the channel_id explicitly + // to the 'sys' channel and use stored resources for that channel. + // Legacy behaviour uses the local logged in channel. - } - } + if (argc() > 2 && is_site_admin() && intval(argv(2))) { + $channel_id = argv(2); + } else { + $channel_id = local_channel(); + } + if (argc() > 1 && argv(1) === 'album') { + // API: /embedphotos/album + $name = (x($_POST, 'name') ? $_POST['name'] : null); + if (!$name) { + json_return_and_die(array('errormsg' => 'Error retrieving album', 'status' => false)); + } + $album = $this->embedphotos_widget_album(array('channel_id' => $channel_id, 'album' => $name)); + json_return_and_die(array('status' => true, 'content' => $album)); + } + if (argc() > 1 && argv(1) === 'albumlist') { + // API: /embedphotos/albumlist + $album_list = $this->embedphotos_album_list($channel_id); + json_return_and_die(array('status' => true, 'albumlist' => $album_list)); + } + if (argc() > 1 && argv(1) === 'photolink') { + // API: /embedphotos/photolink + $href = (x($_POST, 'href') ? $_POST['href'] : null); + if (!$href) { + json_return_and_die(array('errormsg' => 'Error retrieving link ' . $href, 'status' => false)); + } + $resource_id = array_pop(explode("/", $href)); + + $x = self::photolink($resource_id, $channel_id); + if ($x) { + json_return_and_die(array('status' => true, 'photolink' => $x, 'resource_id' => $resource_id)); + } + json_return_and_die(array('errormsg' => 'Error retrieving resource ' . $resource_id, 'status' => false)); + } + } - protected static function photolink($resource, $channel_id = 0) { - if (intval($channel_id)) { - $channel = channelx_by_n($channel_id); - } - else { - $channel = App::get_channel(); - } - - $output = EMPTY_STR; - if ($channel) { - $resolution = ((feature_enabled($channel['channel_id'],'large_photos')) ? 1 : 2); - $r = q("select mimetype, height, width, title from photo where resource_id = '%s' and $resolution = %d and uid = %d limit 1", - dbesc($resource), - intval($resolution), - intval($channel['channel_id']) - ); - if (! $r) { - return $output; - } - - if ($r[0]['mimetype'] === 'image/jpeg') { - $ext = '.jpg'; - } - elseif ($r[0]['mimetype'] === 'image/png') { - $ext = '.png'; - } - elseif ($r[0]['mimetype'] === 'image/gif') { - $ext = '.gif'; - } - else { - $ext = EMPTY_STR; - } + protected static function photolink($resource, $channel_id = 0) + { + if (intval($channel_id)) { + $channel = channelx_by_n($channel_id); + } else { + $channel = App::get_channel(); + } - $alt = $r[0]['title']; - if (! $alt) { - $a = q("select filename from attach where hash = '%s' and uid = %d limit 1", - dbesc($resource), - intval($channel['channel_id']) - ); - if ($a) { - $alt = $a[0]['filename']; - } - else { - $alt = t('Image/photo'); - } - } - $alt = ' alt="' . $alt . '"'; + $output = EMPTY_STR; + if ($channel) { + $resolution = ((feature_enabled($channel['channel_id'], 'large_photos')) ? 1 : 2); + $r = q( + "select mimetype, height, width, title from photo where resource_id = '%s' and $resolution = %d and uid = %d limit 1", + dbesc($resource), + intval($resolution), + intval($channel['channel_id']) + ); + if (!$r) { + return $output; + } - $output = '[zrl=' . z_root() . '/photos/' . $channel['channel_address'] . '/image/' . $resource . ']' - . '[zmg width="' . $r[0]['width'] . '" height="' . $r[0]['height'] . '"' . $alt . ']' - . z_root() . '/photo/' . $resource . '-' . $resolution . $ext . '[/zmg][/zrl]'; + if ($r[0]['mimetype'] === 'image/jpeg') { + $ext = '.jpg'; + } elseif ($r[0]['mimetype'] === 'image/png') { + $ext = '.png'; + } elseif ($r[0]['mimetype'] === 'image/gif') { + $ext = '.gif'; + } else { + $ext = EMPTY_STR; + } - return $output; - } - } + $alt = $r[0]['title']; + if (!$alt) { + $a = q( + "select filename from attach where hash = '%s' and uid = %d limit 1", + dbesc($resource), + intval($channel['channel_id']) + ); + if ($a) { + $alt = $a[0]['filename']; + } else { + $alt = t('Image/photo'); + } + } + $alt = ' alt="' . $alt . '"'; + + $output = '[zrl=' . z_root() . '/photos/' . $channel['channel_address'] . '/image/' . $resource . ']' + . '[zmg width="' . $r[0]['width'] . '" height="' . $r[0]['height'] . '"' . $alt . ']' + . z_root() . '/photo/' . $resource . '-' . $resolution . $ext . '[/zmg][/zrl]'; + + return $output; + } + } + /** + * Copied from include/widgets.php::widget_album() with a modification to get the profile_uid from + * the input array as in widget_item() + * + * @param array $args + * @return string with HTML + */ + public function embedphotos_widget_album($args) + { + $channel_id = 0; + if (array_key_exists('channel_id', $args)) { + $channel_id = $args['channel_id']; + $channel = channelx_by_n($channel_id); + } - /** - * Copied from include/widgets.php::widget_album() with a modification to get the profile_uid from - * the input array as in widget_item() - * - * @param array $args - * @return string with HTML - */ + if (!$channel_id) { + return ''; + } - function embedphotos_widget_album($args) { + $owner_uid = $channel_id; + require_once('include/security.php'); + $sql_extra = permissions_sql($channel_id); - $channel_id = 0; - if (array_key_exists('channel_id', $args)) { - $channel_id = $args['channel_id']; - $channel = channelx_by_n($channel_id); - } - - if (! $channel_id) { - return ''; - } + if (!perm_is_allowed($channel_id, get_observer_hash(), 'view_storage')) { + return ''; + } - $owner_uid = $channel_id; - require_once('include/security.php'); - $sql_extra = permissions_sql($channel_id); + if ($args['album']) { + $album = (($args['album'] === '/') ? '' : $args['album']); + } - if (! perm_is_allowed($channel_id,get_observer_hash(),'view_storage')) - return ''; + if ($args['title']) { + $title = $args['title']; + } - if ($args['album']) { - $album = (($args['album'] === '/') ? '' : $args['album']); - } - - if ($args['title']) { - $title = $args['title']; - } + /** + * This may return incorrect permissions if you have multiple directories of the same name. + * It is a limitation of the photo table using a name for a photo album instead of a folder hash + */ + if ($album) { + $x = q( + "select hash from attach where filename = '%s' and uid = %d limit 1", + dbesc($album), + intval($owner_uid) + ); + if ($x) { + $y = attach_can_view_folder($owner_uid, get_observer_hash(), $x[0]['hash']); + if (!$y) { + return ''; + } + } + } - /** - * This may return incorrect permissions if you have multiple directories of the same name. - * It is a limitation of the photo table using a name for a photo album instead of a folder hash - */ - if ($album) { - $x = q("select hash from attach where filename = '%s' and uid = %d limit 1", - dbesc($album), - intval($owner_uid) - ); - if ($x) { - $y = attach_can_view_folder($owner_uid,get_observer_hash(),$x[0]['hash']); - if (! $y) { - return ''; - } - } - } + $order = 'DESC'; - $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 + $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 ) $sql_extra GROUP BY resource_id) ph ON (p.resource_id = ph.resource_id AND p.imgscale = ph.imgscale) ORDER BY created $order", - intval($owner_uid), - dbesc($album), - intval(PHOTO_NORMAL), - intval(PHOTO_PROFILE) - ); + intval($owner_uid), + dbesc($album), + intval(PHOTO_NORMAL), + intval(PHOTO_PROFILE) + ); - $photos = []; - if ($r) { - $twist = 'rotright'; - foreach ($r as $rr) { - if ($twist == 'rotright') { - $twist = 'rotleft'; - } - else { - $twist = 'rotright'; - } + $photos = []; + if ($r) { + $twist = 'rotright'; + foreach ($r as $rr) { + if ($twist == 'rotright') { + $twist = 'rotleft'; + } else { + $twist = 'rotright'; + } - $ext = $phototypes[$rr['mimetype']]; + $ext = $phototypes[$rr['mimetype']]; - $imgalt_e = $rr['filename']; - $desc_e = $rr['description']; + $imgalt_e = $rr['filename']; + $desc_e = $rr['description']; - $imagelink = (z_root() . '/photos/' . $channel['channel_address'] . '/image/' . $rr['resource_id'] - . (($_GET['order'] === 'posted') ? '?f=&order=posted' : '')); + $imagelink = (z_root() . '/photos/' . $channel['channel_address'] . '/image/' . $rr['resource_id'] + . (($_GET['order'] === 'posted') ? '?f=&order=posted' : '')); - $photos[] = [ - 'id' => $rr['id'], - 'twist' => ' ' . $twist . rand(2,4), - 'link' => $imagelink, - 'title' => t('View Photo'), - 'src' => z_root() . '/photo/' . $rr['resource_id'] . '-' . $rr['imgscale'] . '.' .$ext, - 'alt' => $imgalt_e, - 'desc' => $desc_e, - 'ext' => $ext, - 'hash' => $rr['resource_id'], - 'unknown' => t('Unknown') - ]; - } - } + $photos[] = [ + 'id' => $rr['id'], + 'twist' => ' ' . $twist . rand(2, 4), + 'link' => $imagelink, + 'title' => t('View Photo'), + 'src' => z_root() . '/photo/' . $rr['resource_id'] . '-' . $rr['imgscale'] . '.' . $ext, + 'alt' => $imgalt_e, + 'desc' => $desc_e, + 'ext' => $ext, + 'hash' => $rr['resource_id'], + 'unknown' => t('Unknown') + ]; + } + } - $o .= replace_macros(get_markup_template('photo_album.tpl'), [ - '$photos' => $photos, - '$album' => (($title) ? $title : $album), - '$album_id' => rand(), - '$album_edit' => array(t('Edit Album'), $album_edit), - '$can_post' => false, - '$upload' => [ t('Upload'), z_root() . '/photos/' . $channel['channel_address'] . '/upload/' . bin2hex($album) ], - '$order' => false, - '$upload_form' => $upload_form, - '$no_fullscreen_btn' => true - ]); + $o .= replace_macros(get_markup_template('photo_album.tpl'), [ + '$photos' => $photos, + '$album' => (($title) ? $title : $album), + '$album_id' => rand(), + '$album_edit' => array(t('Edit Album'), $album_edit), + '$can_post' => false, + '$upload' => [t('Upload'), z_root() . '/photos/' . $channel['channel_address'] . '/upload/' . bin2hex($album)], + '$order' => false, + '$upload_form' => $upload_form, + '$no_fullscreen_btn' => true + ]); - return $o; - } - - function embedphotos_album_list($channel_id) { - $channel = channelx_by_n($channel_id); - $p = photos_albums_list($channel,App::get_observer()); - if ($p['success']) { - return $p['albums']; - } - else { - return null; - } - } + return $o; + } + public function embedphotos_album_list($channel_id) + { + $channel = channelx_by_n($channel_id); + $p = photos_albums_list($channel, App::get_observer()); + if ($p['success']) { + return $p['albums']; + } else { + return null; + } + } } diff --git a/Zotlabs/Module/Event.php b/Zotlabs/Module/Event.php index 9b9769cdf..abfec6fc7 100644 --- a/Zotlabs/Module/Event.php +++ b/Zotlabs/Module/Event.php @@ -1,4 +1,5 @@ MAX_EVENT_REPEAT_COUNT) { - $count = MAX_EVENT_REPEAT_COUNT; - } + $start_text = escape_tags($_REQUEST['start_text']); + $finish_text = escape_tags($_REQUEST['finish_text']); - require_once('include/text.php'); - linkify_tags($desc, local_channel()); - linkify_tags($location, local_channel()); - - //$action = ($event_hash == '') ? 'new' : "event/" . $event_hash; - - //@fixme: this url gives a wsod if there is a linebreak detected in one of the variables ($desc or $location) - //$onerror_url = z_root() . "/events/" . $action . "?summary=$summary&description=$desc&location=$location&start=$start_text&finish=$finish_text&adjust=$adjust&nofinish=$nofinish&type=$type"; - $onerror_url = z_root() . "/events"; - - if(strcmp($finish,$start) < 0 && !$nofinish) { - notice( t('Event can not end before it has started.') . EOL); - if(intval($_REQUEST['preview'])) { - echo( t('Unable to generate preview.')); - killme(); - } - goaway($onerror_url); - } - - if((! $summary) || (! $start)) { - notice( t('Event title and start time are required.') . EOL); - if(intval($_REQUEST['preview'])) { - echo( t('Unable to generate preview.')); - killme(); - } - goaway($onerror_url); - } - - // $share = ((intval($_POST['distr'])) ? intval($_POST['distr']) : 0); + $adjust = intval($_POST['adjust']); + $nofinish = intval($_POST['nofinish']); - $share = 1; + $timezone = ((x($_POST, 'timezone_select')) ? notags(trim($_POST['timezone_select'])) : ''); - - $acl = new AccessControl(false); - - if($event_id) { - $x = q("select * from event where id = %d and uid = %d limit 1", - intval($event_id), - intval(local_channel()) - ); - if(! $x) { - notice( t('Event not found.') . EOL); - if(intval($_REQUEST['preview'])) { - echo( t('Unable to generate preview.')); - killme(); - } - return; - } - - $acl->set($x[0]); - - $created = $x[0]['created']; - $edited = datetime_convert(); - - if($x[0]['allow_cid'] === '<' . $channel['channel_hash'] . '>' - && $x[0]['allow_gid'] === '' && $x[0]['deny_cid'] === '' && $x[0]['deny_gid'] === '') { - $share = false; - } - else { - $share = true; - } - } - else { - $created = $edited = datetime_convert(); - if($share) { - $acl->set_from_array($_POST); - } - else { - $acl->set(array('allow_cid' => '<' . $channel['channel_hash'] . '>', 'allow_gid' => '', 'deny_cid' => '', 'deny_gid' => '')); - } - } - - $post_tags = []; + $tz = (($timezone) ? $timezone : date_default_timezone_get()); - $ac = $acl->get(); - - if(strlen($categories)) { - $cats = explode(',',$categories); - foreach($cats as $cat) { - $post_tags[] = array( - 'uid' => $profile_uid, - 'ttype' => TERM_CATEGORY, - 'otype' => TERM_OBJ_POST, - 'term' => trim($cat), - 'url' => $channel['xchan_url'] . '?f=&cat=' . urlencode(trim($cat)) - ); - } - } - - $datarray = []; - $datarray['dtstart'] = $start; - $datarray['dtend'] = $finish; - $datarray['summary'] = $summary; - $datarray['description'] = $desc; - $datarray['location'] = $location; - $datarray['etype'] = $type; - $datarray['adjust'] = $adjust; - $datarray['nofinish'] = $nofinish; - $datarray['uid'] = local_channel(); - $datarray['account'] = get_account_id(); - $datarray['event_xchan'] = $channel['channel_hash']; - $datarray['allow_cid'] = $ac['allow_cid']; - $datarray['allow_gid'] = $ac['allow_gid']; - $datarray['deny_cid'] = $ac['deny_cid']; - $datarray['deny_gid'] = $ac['deny_gid']; - $datarray['private'] = (($acl->is_private()) ? 1 : 0); - $datarray['id'] = $event_id; - $datarray['created'] = $created; - $datarray['edited'] = $edited; - - if(intval($_REQUEST['preview'])) { - $html = format_event_html($datarray); - echo $html; - killme(); - } - - $event = event_store_event($datarray); - - if($post_tags) - $datarray['term'] = $post_tags; - - $item_id = event_store_item($datarray,$event); - - if($item_id) { - $r = q("select * from item where id = %d", - intval($item_id) - ); - if($r) { - xchan_query($r); - $sync_item = fetch_post_tags($r); - $z = q("select * from event where event_hash = '%s' and uid = %d limit 1", - dbesc($r[0]['resource_id']), - intval($channel['channel_id']) - ); - if($z) { - Libsync::build_sync_packet($channel['channel_id'], [ 'event_item' => [ encode_item($sync_item[0],true)],'event' => $z ]); - } - } - } - - if($share) - Run::Summon( [ 'Notifier', 'event', $item_id ] ); - - } - - - - function get() { - - if(argc() > 2 && argv(1) == 'ical') { - $event_id = argv(2); - - require_once('include/security.php'); - $sql_extra = permissions_sql(local_channel()); - - $r = q("select * from event where event_hash = '%s' $sql_extra limit 1", - dbesc($event_id) - ); - if($r) { - header('Content-type: text/calendar'); - header('Content-Disposition: attachment; filename="' . t('event') . '-' . $event_id . '.ics"' ); - echo ical_wrapper($r); - killme(); - } - else { - notice( t('Event not found.') . EOL ); - return; - } - } - - if(! local_channel()) { - notice( t('Permission denied.') . EOL); - return; - } - - $channel = App::get_channel(); + $categories = escape_tags(trim($_POST['category'])); - nav_set_selected('Events'); - - if((argc() > 2) && (argv(1) === 'ignore') && intval(argv(2))) { - $r = q("update event set dismissed = 1 where id = %d and uid = %d", - intval(argv(2)), - intval(local_channel()) - ); - } - - if((argc() > 2) && (argv(1) === 'unignore') && intval(argv(2))) { - $r = q("update event set dismissed = 0 where id = %d and uid = %d", - intval(argv(2)), - intval(local_channel()) - ); - } - - $first_day = intval(get_pconfig(local_channel(),'system','cal_first_day',0)); - - $htpl = get_markup_template('event_head.tpl'); - App::$page['htmlhead'] .= replace_macros($htpl,array( - '$baseurl' => z_root(), - '$module_url' => '/events', - '$modparams' => 1, - '$lang' => \App::$language, - '$first_day' => $first_day - )); - - $o = ''; - - $mode = 'view'; - $y = 0; - $m = 0; - $ignored = ((x($_REQUEST,'ignored')) ? " and dismissed = " . intval($_REQUEST['ignored']) . " " : ''); - - - // logger('args: ' . print_r(App::$argv,true)); - - - - if(argc() > 1) { - if(argc() > 2 && argv(1) === 'add') { - $mode = 'add'; - $item_id = intval(argv(2)); - } - if(argc() > 2 && argv(1) === 'drop') { - $mode = 'drop'; - $event_id = argv(2); - } - if(argc() > 2 && intval(argv(1)) && intval(argv(2))) { - $mode = 'view'; - $y = intval(argv(1)); - $m = intval(argv(2)); - } - if(argc() <= 2) { - $mode = 'view'; - $event_id = argv(1); - } - } - - if($mode === 'add') { - event_addtocal($item_id,local_channel()); - killme(); - } - - if($mode == 'view') { - - /* edit/create form */ - if($event_id) { - $r = q("SELECT * FROM event WHERE event_hash = '%s' AND uid = %d LIMIT 1", - dbesc($event_id), - intval(local_channel()) - ); - if(count($r)) - $orig_event = $r[0]; - } - - $channel = \App::get_channel(); - - // Passed parameters overrides anything found in the DB - if(!x($orig_event)) - $orig_event = []; - - // In case of an error the browser is redirected back here, with these parameters filled in with the previous values - /* - if(x($_REQUEST,'nofinish')) $orig_event['nofinish'] = $_REQUEST['nofinish']; - if(x($_REQUEST,'adjust')) $orig_event['adjust'] = $_REQUEST['adjust']; - if(x($_REQUEST,'summary')) $orig_event['summary'] = $_REQUEST['summary']; - if(x($_REQUEST,'description')) $orig_event['description'] = $_REQUEST['description']; - if(x($_REQUEST,'location')) $orig_event['location'] = $_REQUEST['location']; - if(x($_REQUEST,'start')) $orig_event['dtstart'] = $_REQUEST['start']; - if(x($_REQUEST,'finish')) $orig_event['dtend'] = $_REQUEST['finish']; - if(x($_REQUEST,'type')) $orig_event['etype'] = $_REQUEST['type']; - */ - - $n_checked = ((x($orig_event) && $orig_event['nofinish']) ? ' checked="checked" ' : ''); - $a_checked = ((x($orig_event) && $orig_event['adjust']) ? ' checked="checked" ' : ''); - $t_orig = ((x($orig_event)) ? $orig_event['summary'] : ''); - $d_orig = ((x($orig_event)) ? $orig_event['description'] : ''); - $l_orig = ((x($orig_event)) ? $orig_event['location'] : ''); - $eid = ((x($orig_event)) ? $orig_event['id'] : 0); - $event_xchan = ((x($orig_event)) ? $orig_event['event_xchan'] : $channel['channel_hash']); - $mid = ((x($orig_event)) ? $orig_event['mid'] : ''); - - if(! x($orig_event)) { - $sh_checked = ''; - $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" ' ); - } + // only allow editing your own events. - if($orig_event['event_xchan']) - $sh_checked .= ' disabled="disabled" '; - - $sdt = ((x($orig_event)) ? $orig_event['dtstart'] : 'now'); - - $fdt = ((x($orig_event)) ? $orig_event['dtend'] : '+1 hour'); - - $tz = date_default_timezone_get(); - if(x($orig_event)) - $tz = (($orig_event['adjust']) ? date_default_timezone_get() : 'UTC'); - - $syear = datetime_convert('UTC', $tz, $sdt, 'Y'); - $smonth = datetime_convert('UTC', $tz, $sdt, 'm'); - $sday = datetime_convert('UTC', $tz, $sdt, 'd'); - $shour = datetime_convert('UTC', $tz, $sdt, 'H'); - $sminute = datetime_convert('UTC', $tz, $sdt, 'i'); - - $stext = datetime_convert('UTC',$tz,$sdt); - $stext = substr($stext,0,14) . "00:00"; - - $fyear = datetime_convert('UTC', $tz, $fdt, 'Y'); - $fmonth = datetime_convert('UTC', $tz, $fdt, 'm'); - $fday = datetime_convert('UTC', $tz, $fdt, 'd'); - $fhour = datetime_convert('UTC', $tz, $fdt, 'H'); - $fminute = datetime_convert('UTC', $tz, $fdt, 'i'); - - $ftext = datetime_convert('UTC',$tz,$fdt); - $ftext = substr($ftext,0,14) . "00:00"; - - $type = ((x($orig_event)) ? $orig_event['etype'] : 'event'); - - $f = get_config('system','event_input_format'); - if(! $f) - $f = 'ymd'; - - $catsenabled = Apps::system_app_installed(local_channel(),'Categories'); - - $category = ''; - - if($catsenabled && x($orig_event)){ - $itm = q("select * from item where resource_type = 'event' and resource_id = '%s' and uid = %d limit 1", - dbesc($orig_event['event_hash']), - intval(local_channel()) - ); - $itm = fetch_post_tags($itm); - if($itm) { - $cats = get_terms_oftype($itm[0]['term'], TERM_CATEGORY); - foreach ($cats as $cat) { - if(strlen($category)) - $category .= ', '; - $category .= $cat['term']; - } - } - } - - require_once('include/acl_selectors.php'); - - $acl = new AccessControl($channel); - $perm_defaults = $acl->get(); + if (($xchan) && ($xchan !== get_observer_hash())) { + return; + } - $permissions = ((x($orig_event)) ? $orig_event : $perm_defaults); + if ($start_text) { + $start = $start_text; + } else { + $start = sprintf('%d-%d-%d %d:%d:0', $startyear, $startmonth, $startday, $starthour, $startminute); + } - $freq_options = [ - 'DAILY' => t('day(s)'), - 'WEEKLY' => t('week(s)'), - 'MONTHLY' => t('month(s)'), - 'YEARLY' => t('year(s)') - ]; - - $tpl = get_markup_template('event_form.tpl'); - - $form = replace_macros($tpl,array( - '$post' => z_root() . '/events', - '$eid' => $eid, - '$type' => $type, - '$xchan' => $event_xchan, - '$mid' => $mid, - '$event_hash' => $event_id, - '$summary' => array('summary', (($event_id) ? t('Edit event title') : t('Event title')), $t_orig, t('Required'), '*'), - '$catsenabled' => $catsenabled, - '$placeholdercategory' => t('Categories (comma-separated list)'), - '$c_text' => (($event_id) ? t('Edit Category') : t('Category')), - '$category' => $category, - '$required' => '*', - '$s_dsel' => datetimesel($f,new \DateTime(),\DateTime::createFromFormat('Y',$syear+5),\DateTime::createFromFormat('Y-m-d H:i',"$syear-$smonth-$sday $shour:$sminute"), (($event_id) ? t('Edit start date and time') : t('Start date and time')), 'start_text',true,true,'','',true,$first_day), - '$n_text' => t('Finish date and time are not known or not relevant'), - '$n_checked' => $n_checked, - '$f_dsel' => datetimesel($f,new \DateTime(),\DateTime::createFromFormat('Y',$fyear+5),\DateTime::createFromFormat('Y-m-d H:i',"$fyear-$fmonth-$fday $fhour:$fminute"), (($event_id) ? t('Edit finish date and time') : t('Finish date and time')),'finish_text',true,true,'start_text','',false,$first_day), - '$nofinish' => array('nofinish', t('Finish date and time are not known or not relevant'), $n_checked, '', array(t('No'),t('Yes')), 'onclick="enableDisableFinishDate();"'), - '$adjust' => array('adjust', t('Adjust for viewer timezone'), $a_checked, t('Important for events that happen in a particular place. Not practical for global holidays.'), array(t('No'),t('Yes'))), - '$a_text' => t('Adjust for viewer timezone'), - '$d_text' => (($event_id) ? t('Edit Description') : t('Description')), - '$d_orig' => $d_orig, - '$l_text' => (($event_id) ? t('Edit Location') : t('Location')), - '$l_orig' => $l_orig, - '$t_orig' => $t_orig, - '$preview' => t('Preview'), - '$perms_label' => t('Permission settings'), - // populating the acl dialog was a permission description from view_stream because Cal.php, which - // displays events, says "since we don't currently have an event permission - use the stream permission" - '$acl' => (($orig_event['event_xchan']) ? '' : populate_acl(((x($orig_event)) ? $orig_event : $perm_defaults), false, \Zotlabs\Lib\PermissionDescription::fromGlobalPermission('view_stream'))), + if ($finish_text) { + $finish = $finish_text; + } else { + $finish = sprintf('%d-%d-%d %d:%d:0', $finishyear, $finishmonth, $finishday, $finishhour, $finishminute); + } - '$allow_cid' => acl2json($permissions['allow_cid']), - '$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()), + if ($nofinish) { + $finish = NULL_DATE; + } - '$lockstate' => (($acl->is_private()) ? 'lock' : 'unlock'), - '$submit' => t('Submit'), - '$advanced' => t('Advanced Options'), + if ($adjust) { + $start = datetime_convert($tz, 'UTC', $start); + if (!$nofinish) { + $finish = datetime_convert($tz, 'UTC', $finish); + } + } else { + $start = datetime_convert('UTC', 'UTC', $start); + if (!$nofinish) { + $finish = datetime_convert('UTC', 'UTC', $finish); + } + } - '$repeat' => [ 'repeat' , t('Event repeat'), false, '', [ t('No'), t('Yes') ] ], - '$freq' => [ 'freq' , t('Repeat frequency') , '', '', $freq_options ], - '$interval' => [ 'interval', t('Repeat every'), 1 , '' ], - '$count' => [ 'count', t('Number of total repeats'), 10, '' ], - '$until' => '', - '$byday' => '', - - )); - /* end edit/create form */ - - $thisyear = datetime_convert('UTC',date_default_timezone_get(),'now','Y'); - $thismonth = datetime_convert('UTC',date_default_timezone_get(),'now','m'); - if(! $y) - $y = intval($thisyear); - if(! $m) - $m = intval($thismonth); - - $export = false; - if(argc() === 4 && argv(3) === 'export') - $export = true; - - // Put some limits on dates. The PHP date functions don't seem to do so well before 1900. - // An upper limit was chosen to keep search engines from exploring links millions of years in the future. - - if($y < 1901) - $y = 1900; - if($y > 2099) - $y = 2100; - - $nextyear = $y; - $nextmonth = $m + 1; - if($nextmonth > 12) { - $nextmonth = 1; - $nextyear ++; - } - - $prevyear = $y; - if($m > 1) - $prevmonth = $m - 1; - else { - $prevmonth = 12; - $prevyear --; - } - - $dim = get_dim($y,$m); - $start = sprintf('%d-%d-%d %d:%d:%d',$y,$m,1,0,0,0); - $finish = sprintf('%d-%d-%d %d:%d:%d',$y,$m,$dim,23,59,59); - - - if (argv(1) === 'json'){ - if (x($_GET,'start')) $start = $_GET['start']; - if (x($_GET,'end')) $finish = $_GET['end']; - } - - $start = datetime_convert('UTC','UTC',$start); - $finish = datetime_convert('UTC','UTC',$finish); - - $adjust_start = datetime_convert('UTC', date_default_timezone_get(), $start); - $adjust_finish = datetime_convert('UTC', date_default_timezone_get(), $finish); - - if (x($_GET,'id')){ - $r = q("SELECT event.*, item.plink, item.item_flags, item.author_xchan, item.owner_xchan + // Don't allow the event to finish before it begins. + // It won't hurt anything, but somebody will file a bug report + // and we'll waste a bunch of time responding to it. Time that + // could've been spent doing something else. + + + $summary = escape_tags(trim($_POST['summary'])); + $desc = escape_tags(trim($_POST['desc'])); + $location = escape_tags(trim($_POST['location'])); + $type = escape_tags(trim($_POST['type'])); + + $repeat = ((array_key_exists('repeat', $_REQUEST) && intval($_REQUEST['repeat'])) ? 1 : 0); + $freq = ((array_key_exists('freq', $_REQUEST) && $_REQUEST['freq']) ? escape_tags(trim($_REQUEST['freq'])) : EMPTY_STR); + $interval = ((array_key_exists('interval', $_REQUEST) && intval($_REQUEST['interval'])) ? 1 : 0); + $count = ((array_key_exists('count', $_REQUEST) && intval($_REQUEST['count'])) ? intval($_REQUEST['count']) : 0); + $until = ((array_key_exists('until', $_REQUEST) && $_REQUEST['until']) ? datetime_convert(date_default_timezone_get(), 'UTC', $_REQUEST['until']) : NULL_DATE); + $byday = []; + + if ((!$freq) || (!in_array($freq, ['DAILY', 'WEEKLY', 'MONTHLY', 'YEARLY']))) { + $repeat = 0; + } + if ($count < 0) { + $count = 0; + } + if ($count > MAX_EVENT_REPEAT_COUNT) { + $count = MAX_EVENT_REPEAT_COUNT; + } + + require_once('include/text.php'); + linkify_tags($desc, local_channel()); + linkify_tags($location, local_channel()); + + //$action = ($event_hash == '') ? 'new' : "event/" . $event_hash; + + //@fixme: this url gives a wsod if there is a linebreak detected in one of the variables ($desc or $location) + //$onerror_url = z_root() . "/events/" . $action . "?summary=$summary&description=$desc&location=$location&start=$start_text&finish=$finish_text&adjust=$adjust&nofinish=$nofinish&type=$type"; + $onerror_url = z_root() . "/events"; + + if (strcmp($finish, $start) < 0 && !$nofinish) { + notice(t('Event can not end before it has started.') . EOL); + if (intval($_REQUEST['preview'])) { + echo(t('Unable to generate preview.')); + killme(); + } + goaway($onerror_url); + } + + if ((!$summary) || (!$start)) { + notice(t('Event title and start time are required.') . EOL); + if (intval($_REQUEST['preview'])) { + echo(t('Unable to generate preview.')); + killme(); + } + goaway($onerror_url); + } + + // $share = ((intval($_POST['distr'])) ? intval($_POST['distr']) : 0); + + $share = 1; + + + $acl = new AccessControl(false); + + if ($event_id) { + $x = q( + "select * from event where id = %d and uid = %d limit 1", + intval($event_id), + intval(local_channel()) + ); + if (!$x) { + notice(t('Event not found.') . EOL); + if (intval($_REQUEST['preview'])) { + echo(t('Unable to generate preview.')); + killme(); + } + return; + } + + $acl->set($x[0]); + + $created = $x[0]['created']; + $edited = datetime_convert(); + + if ( + $x[0]['allow_cid'] === '<' . $channel['channel_hash'] . '>' + && $x[0]['allow_gid'] === '' && $x[0]['deny_cid'] === '' && $x[0]['deny_gid'] === '' + ) { + $share = false; + } else { + $share = true; + } + } else { + $created = $edited = datetime_convert(); + if ($share) { + $acl->set_from_array($_POST); + } else { + $acl->set(array('allow_cid' => '<' . $channel['channel_hash'] . '>', 'allow_gid' => '', 'deny_cid' => '', 'deny_gid' => '')); + } + } + + $post_tags = []; + + $ac = $acl->get(); + + if (strlen($categories)) { + $cats = explode(',', $categories); + foreach ($cats as $cat) { + $post_tags[] = array( + 'uid' => $profile_uid, + 'ttype' => TERM_CATEGORY, + 'otype' => TERM_OBJ_POST, + 'term' => trim($cat), + 'url' => $channel['xchan_url'] . '?f=&cat=' . urlencode(trim($cat)) + ); + } + } + + $datarray = []; + $datarray['dtstart'] = $start; + $datarray['dtend'] = $finish; + $datarray['summary'] = $summary; + $datarray['description'] = $desc; + $datarray['location'] = $location; + $datarray['etype'] = $type; + $datarray['adjust'] = $adjust; + $datarray['nofinish'] = $nofinish; + $datarray['uid'] = local_channel(); + $datarray['account'] = get_account_id(); + $datarray['event_xchan'] = $channel['channel_hash']; + $datarray['allow_cid'] = $ac['allow_cid']; + $datarray['allow_gid'] = $ac['allow_gid']; + $datarray['deny_cid'] = $ac['deny_cid']; + $datarray['deny_gid'] = $ac['deny_gid']; + $datarray['private'] = (($acl->is_private()) ? 1 : 0); + $datarray['id'] = $event_id; + $datarray['created'] = $created; + $datarray['edited'] = $edited; + + if (intval($_REQUEST['preview'])) { + $html = format_event_html($datarray); + echo $html; + killme(); + } + + $event = event_store_event($datarray); + + if ($post_tags) { + $datarray['term'] = $post_tags; + } + + $item_id = event_store_item($datarray, $event); + + if ($item_id) { + $r = q( + "select * from item where id = %d", + intval($item_id) + ); + if ($r) { + xchan_query($r); + $sync_item = fetch_post_tags($r); + $z = q( + "select * from event where event_hash = '%s' and uid = %d limit 1", + dbesc($r[0]['resource_id']), + intval($channel['channel_id']) + ); + if ($z) { + Libsync::build_sync_packet($channel['channel_id'], ['event_item' => [encode_item($sync_item[0], true)], 'event' => $z]); + } + } + } + + if ($share) { + Run::Summon(['Notifier', 'event', $item_id]); + } + } + + + public function get() + { + + if (argc() > 2 && argv(1) == 'ical') { + $event_id = argv(2); + + require_once('include/security.php'); + $sql_extra = permissions_sql(local_channel()); + + $r = q( + "select * from event where event_hash = '%s' $sql_extra limit 1", + dbesc($event_id) + ); + if ($r) { + header('Content-type: text/calendar'); + header('Content-Disposition: attachment; filename="' . t('event') . '-' . $event_id . '.ics"'); + echo ical_wrapper($r); + killme(); + } else { + notice(t('Event not found.') . EOL); + return; + } + } + + if (!local_channel()) { + notice(t('Permission denied.') . EOL); + return; + } + + $channel = App::get_channel(); + + nav_set_selected('Events'); + + if ((argc() > 2) && (argv(1) === 'ignore') && intval(argv(2))) { + $r = q( + "update event set dismissed = 1 where id = %d and uid = %d", + intval(argv(2)), + intval(local_channel()) + ); + } + + if ((argc() > 2) && (argv(1) === 'unignore') && intval(argv(2))) { + $r = q( + "update event set dismissed = 0 where id = %d and uid = %d", + intval(argv(2)), + intval(local_channel()) + ); + } + + $first_day = intval(get_pconfig(local_channel(), 'system', 'cal_first_day', 0)); + + $htpl = get_markup_template('event_head.tpl'); + App::$page['htmlhead'] .= replace_macros($htpl, array( + '$baseurl' => z_root(), + '$module_url' => '/events', + '$modparams' => 1, + '$lang' => App::$language, + '$first_day' => $first_day + )); + + $o = ''; + + $mode = 'view'; + $y = 0; + $m = 0; + $ignored = ((x($_REQUEST, 'ignored')) ? " and dismissed = " . intval($_REQUEST['ignored']) . " " : ''); + + + // logger('args: ' . print_r(App::$argv,true)); + + + if (argc() > 1) { + if (argc() > 2 && argv(1) === 'add') { + $mode = 'add'; + $item_id = intval(argv(2)); + } + if (argc() > 2 && argv(1) === 'drop') { + $mode = 'drop'; + $event_id = argv(2); + } + if (argc() > 2 && intval(argv(1)) && intval(argv(2))) { + $mode = 'view'; + $y = intval(argv(1)); + $m = intval(argv(2)); + } + if (argc() <= 2) { + $mode = 'view'; + $event_id = argv(1); + } + } + + if ($mode === 'add') { + event_addtocal($item_id, local_channel()); + killme(); + } + + if ($mode == 'view') { + /* edit/create form */ + if ($event_id) { + $r = q( + "SELECT * FROM event WHERE event_hash = '%s' AND uid = %d LIMIT 1", + dbesc($event_id), + intval(local_channel()) + ); + if (count($r)) { + $orig_event = $r[0]; + } + } + + $channel = App::get_channel(); + + // Passed parameters overrides anything found in the DB + if (!x($orig_event)) { + $orig_event = []; + } + + // In case of an error the browser is redirected back here, with these parameters filled in with the previous values + /* + if(x($_REQUEST,'nofinish')) $orig_event['nofinish'] = $_REQUEST['nofinish']; + if(x($_REQUEST,'adjust')) $orig_event['adjust'] = $_REQUEST['adjust']; + if(x($_REQUEST,'summary')) $orig_event['summary'] = $_REQUEST['summary']; + if(x($_REQUEST,'description')) $orig_event['description'] = $_REQUEST['description']; + if(x($_REQUEST,'location')) $orig_event['location'] = $_REQUEST['location']; + if(x($_REQUEST,'start')) $orig_event['dtstart'] = $_REQUEST['start']; + if(x($_REQUEST,'finish')) $orig_event['dtend'] = $_REQUEST['finish']; + if(x($_REQUEST,'type')) $orig_event['etype'] = $_REQUEST['type']; + */ + + $n_checked = ((x($orig_event) && $orig_event['nofinish']) ? ' checked="checked" ' : ''); + $a_checked = ((x($orig_event) && $orig_event['adjust']) ? ' checked="checked" ' : ''); + $t_orig = ((x($orig_event)) ? $orig_event['summary'] : ''); + $d_orig = ((x($orig_event)) ? $orig_event['description'] : ''); + $l_orig = ((x($orig_event)) ? $orig_event['location'] : ''); + $eid = ((x($orig_event)) ? $orig_event['id'] : 0); + $event_xchan = ((x($orig_event)) ? $orig_event['event_xchan'] : $channel['channel_hash']); + $mid = ((x($orig_event)) ? $orig_event['mid'] : ''); + + if (!x($orig_event)) { + $sh_checked = ''; + $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" '; + } + + $sdt = ((x($orig_event)) ? $orig_event['dtstart'] : 'now'); + + $fdt = ((x($orig_event)) ? $orig_event['dtend'] : '+1 hour'); + + $tz = date_default_timezone_get(); + if (x($orig_event)) { + $tz = (($orig_event['adjust']) ? date_default_timezone_get() : 'UTC'); + } + + $syear = datetime_convert('UTC', $tz, $sdt, 'Y'); + $smonth = datetime_convert('UTC', $tz, $sdt, 'm'); + $sday = datetime_convert('UTC', $tz, $sdt, 'd'); + $shour = datetime_convert('UTC', $tz, $sdt, 'H'); + $sminute = datetime_convert('UTC', $tz, $sdt, 'i'); + + $stext = datetime_convert('UTC', $tz, $sdt); + $stext = substr($stext, 0, 14) . "00:00"; + + $fyear = datetime_convert('UTC', $tz, $fdt, 'Y'); + $fmonth = datetime_convert('UTC', $tz, $fdt, 'm'); + $fday = datetime_convert('UTC', $tz, $fdt, 'd'); + $fhour = datetime_convert('UTC', $tz, $fdt, 'H'); + $fminute = datetime_convert('UTC', $tz, $fdt, 'i'); + + $ftext = datetime_convert('UTC', $tz, $fdt); + $ftext = substr($ftext, 0, 14) . "00:00"; + + $type = ((x($orig_event)) ? $orig_event['etype'] : 'event'); + + $f = get_config('system', 'event_input_format'); + if (!$f) { + $f = 'ymd'; + } + + $catsenabled = Apps::system_app_installed(local_channel(), 'Categories'); + + $category = ''; + + if ($catsenabled && x($orig_event)) { + $itm = q( + "select * from item where resource_type = 'event' and resource_id = '%s' and uid = %d limit 1", + dbesc($orig_event['event_hash']), + intval(local_channel()) + ); + $itm = fetch_post_tags($itm); + if ($itm) { + $cats = get_terms_oftype($itm[0]['term'], TERM_CATEGORY); + foreach ($cats as $cat) { + if (strlen($category)) { + $category .= ', '; + } + $category .= $cat['term']; + } + } + } + + require_once('include/acl_selectors.php'); + + $acl = new AccessControl($channel); + $perm_defaults = $acl->get(); + + $permissions = ((x($orig_event)) ? $orig_event : $perm_defaults); + + $freq_options = [ + 'DAILY' => t('day(s)'), + 'WEEKLY' => t('week(s)'), + 'MONTHLY' => t('month(s)'), + 'YEARLY' => t('year(s)') + ]; + + + $tpl = get_markup_template('event_form.tpl'); + + $form = replace_macros($tpl, array( + '$post' => z_root() . '/events', + '$eid' => $eid, + '$type' => $type, + '$xchan' => $event_xchan, + '$mid' => $mid, + '$event_hash' => $event_id, + '$summary' => array('summary', (($event_id) ? t('Edit event title') : t('Event title')), $t_orig, t('Required'), '*'), + '$catsenabled' => $catsenabled, + '$placeholdercategory' => t('Categories (comma-separated list)'), + '$c_text' => (($event_id) ? t('Edit Category') : t('Category')), + '$category' => $category, + '$required' => '*', + '$s_dsel' => datetimesel($f, new DateTime(), DateTime::createFromFormat('Y', $syear + 5), DateTime::createFromFormat('Y-m-d H:i', "$syear-$smonth-$sday $shour:$sminute"), (($event_id) ? t('Edit start date and time') : t('Start date and time')), 'start_text', true, true, '', '', true, $first_day), + '$n_text' => t('Finish date and time are not known or not relevant'), + '$n_checked' => $n_checked, + '$f_dsel' => datetimesel($f, new DateTime(), DateTime::createFromFormat('Y', $fyear + 5), DateTime::createFromFormat('Y-m-d H:i', "$fyear-$fmonth-$fday $fhour:$fminute"), (($event_id) ? t('Edit finish date and time') : t('Finish date and time')), 'finish_text', true, true, 'start_text', '', false, $first_day), + '$nofinish' => array('nofinish', t('Finish date and time are not known or not relevant'), $n_checked, '', array(t('No'), t('Yes')), 'onclick="enableDisableFinishDate();"'), + '$adjust' => array('adjust', t('Adjust for viewer timezone'), $a_checked, t('Important for events that happen in a particular place. Not practical for global holidays.'), array(t('No'), t('Yes'))), + '$a_text' => t('Adjust for viewer timezone'), + '$d_text' => (($event_id) ? t('Edit Description') : t('Description')), + '$d_orig' => $d_orig, + '$l_text' => (($event_id) ? t('Edit Location') : t('Location')), + '$l_orig' => $l_orig, + '$t_orig' => $t_orig, + '$preview' => t('Preview'), + '$perms_label' => t('Permission settings'), + // populating the acl dialog was a permission description from view_stream because Cal.php, which + // displays events, says "since we don't currently have an event permission - use the stream permission" + '$acl' => (($orig_event['event_xchan']) ? '' : populate_acl(((x($orig_event)) ? $orig_event : $perm_defaults), false, PermissionDescription::fromGlobalPermission('view_stream'))), + + '$allow_cid' => acl2json($permissions['allow_cid']), + '$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'), + + '$submit' => t('Submit'), + '$advanced' => t('Advanced Options'), + + '$repeat' => ['repeat', t('Event repeat'), false, '', [t('No'), t('Yes')]], + '$freq' => ['freq', t('Repeat frequency'), '', '', $freq_options], + '$interval' => ['interval', t('Repeat every'), 1, ''], + '$count' => ['count', t('Number of total repeats'), 10, ''], + '$until' => '', + '$byday' => '', + + )); + /* end edit/create form */ + + $thisyear = datetime_convert('UTC', date_default_timezone_get(), 'now', 'Y'); + $thismonth = datetime_convert('UTC', date_default_timezone_get(), 'now', 'm'); + if (!$y) { + $y = intval($thisyear); + } + if (!$m) { + $m = intval($thismonth); + } + + $export = false; + if (argc() === 4 && argv(3) === 'export') { + $export = true; + } + + // Put some limits on dates. The PHP date functions don't seem to do so well before 1900. + // An upper limit was chosen to keep search engines from exploring links millions of years in the future. + + if ($y < 1901) { + $y = 1900; + } + if ($y > 2099) { + $y = 2100; + } + + $nextyear = $y; + $nextmonth = $m + 1; + if ($nextmonth > 12) { + $nextmonth = 1; + $nextyear++; + } + + $prevyear = $y; + if ($m > 1) { + $prevmonth = $m - 1; + } else { + $prevmonth = 12; + $prevyear--; + } + + $dim = get_dim($y, $m); + $start = sprintf('%d-%d-%d %d:%d:%d', $y, $m, 1, 0, 0, 0); + $finish = sprintf('%d-%d-%d %d:%d:%d', $y, $m, $dim, 23, 59, 59); + + + if (argv(1) === 'json') { + if (x($_GET, 'start')) { + $start = $_GET['start']; + } + if (x($_GET, 'end')) { + $finish = $_GET['end']; + } + } + + $start = datetime_convert('UTC', 'UTC', $start); + $finish = datetime_convert('UTC', 'UTC', $finish); + + $adjust_start = datetime_convert('UTC', date_default_timezone_get(), $start); + $adjust_finish = datetime_convert('UTC', date_default_timezone_get(), $finish); + + if (x($_GET, 'id')) { + $r = q( + "SELECT event.*, item.plink, item.item_flags, item.author_xchan, item.owner_xchan from event left join item on resource_id = event_hash where resource_type = 'event' and event.uid = %d and event.id = %d limit 1", - intval(local_channel()), - intval($_GET['id']) - ); - } elseif($export) { - $r = q("SELECT * from event where uid = %d + intval(local_channel()), + intval($_GET['id']) + ); + } elseif ($export) { + $r = q( + "SELECT * from event where uid = %d AND (( adjust = 0 AND ( dtend >= '%s' or nofinish = 1 ) AND dtstart <= '%s' ) OR ( adjust = 1 AND ( dtend >= '%s' or nofinish = 1 ) AND dtstart <= '%s' )) ", - intval(local_channel()), - dbesc($start), - dbesc($finish), - dbesc($adjust_start), - dbesc($adjust_finish) - ); - } - else { - // fixed an issue with "nofinish" events not showing up in the calendar. - // There's still an issue if the finish date crosses the end of month. - // Noting this for now - it will need to be fixed here and in Friendica. - // Ultimately the finish date shouldn't be involved in the query. + intval(local_channel()), + dbesc($start), + dbesc($finish), + dbesc($adjust_start), + dbesc($adjust_finish) + ); + } else { + // fixed an issue with "nofinish" events not showing up in the calendar. + // There's still an issue if the finish date crosses the end of month. + // Noting this for now - it will need to be fixed here and in Friendica. + // Ultimately the finish date shouldn't be involved in the query. - $r = q("SELECT event.*, item.plink, item.item_flags, item.author_xchan, item.owner_xchan + $r = q( + "SELECT event.*, item.plink, item.item_flags, item.author_xchan, item.owner_xchan from event left join item on event_hash = resource_id where resource_type = 'event' and event.uid = %d and event.uid = item.uid $ignored AND (( adjust = 0 AND ( dtend >= '%s' or nofinish = 1 ) AND dtstart <= '%s' ) OR ( adjust = 1 AND ( dtend >= '%s' or nofinish = 1 ) AND dtstart <= '%s' )) ", - intval(local_channel()), - dbesc($start), - dbesc($finish), - dbesc($adjust_start), - dbesc($adjust_finish) - ); - } - - $links = []; - - if($r && ! $export) { - xchan_query($r); - $r = fetch_post_tags($r,true); - - $r = sort_by_date($r); - } - - if($r) { - foreach($r as $rr) { - $j = (($rr['adjust']) ? datetime_convert('UTC',date_default_timezone_get(),$rr['dtstart'], 'j') : datetime_convert('UTC','UTC',$rr['dtstart'],'j')); - if(! x($links,$j)) - $links[$j] = z_root() . '/' . \App::$cmd . '#link-' . $j; - } - } - - $events=[]; - - $last_date = ''; - $fmt = t('l, F j'); - - if($r) { - - foreach($r as $rr) { - - $j = (($rr['adjust']) ? datetime_convert('UTC',date_default_timezone_get(),$rr['dtstart'], 'j') : datetime_convert('UTC','UTC',$rr['dtstart'],'j')); - $d = (($rr['adjust']) ? datetime_convert('UTC',date_default_timezone_get(),$rr['dtstart'], $fmt) : datetime_convert('UTC','UTC',$rr['dtstart'],$fmt)); - $d = day_translate($d); - - $start = (($rr['adjust']) ? datetime_convert('UTC',date_default_timezone_get(),$rr['dtstart'], 'c') : datetime_convert('UTC','UTC',$rr['dtstart'],'c')); - if ($rr['nofinish']){ - $end = null; - } else { - $end = (($rr['adjust']) ? datetime_convert('UTC',date_default_timezone_get(),$rr['dtend'], 'c') : datetime_convert('UTC','UTC',$rr['dtend'],'c')); + intval(local_channel()), + dbesc($start), + dbesc($finish), + dbesc($adjust_start), + dbesc($adjust_finish) + ); + } - // give a fake end to birthdays so they get crammed into a - // single day on the calendar + $links = []; - if($rr['etype'] === 'birthday') - $end = null; - } - - - $is_first = ($d !== $last_date); - - $last_date = $d; - - $edit = ((local_channel() && $rr['author_xchan'] == get_observer_hash()) ? array(z_root().'/events/'.$rr['event_hash'].'?expandform=1',t('Edit event'),'','') : false); - - $drop = array(z_root().'/events/drop/'.$rr['event_hash'],t('Delete event'),'',''); - - $title = strip_tags(html_entity_decode(zidify_links(bbcode($rr['summary'])),ENT_QUOTES,'UTF-8')); - if(! $title) { - list($title, $_trash) = explode("$rr['id'], - 'hash' => $rr['event_hash'], - 'start'=> $start, - 'end' => $end, - 'drop' => $drop, - 'allDay' => false, - 'title' => $title, - - 'j' => $j, - 'd' => $d, - 'edit' => $edit, - 'is_first'=>$is_first, - 'item'=>$rr, - 'html'=>$html, - 'plink' => array($rr['plink'],t('Link to Source'),'',''), - ); - - - } - } - - if($export) { - header('Content-type: text/calendar'); - header('Content-Disposition: attachment; filename="' . t('calendar') . '-' . $channel['channel_address'] . '.ics"' ); - echo ical_wrapper($r); - killme(); - } - - if (\App::$argv[1] === 'json'){ - echo json_encode($events); killme(); - } - - // links: array('href', 'text', 'extra css classes', 'title') - if (x($_GET,'id')){ - $tpl = get_markup_template("event.tpl"); - } - else { - $tpl = get_markup_template("events-js.tpl"); - } - - $o = replace_macros($tpl, array( - '$baseurl' => z_root(), - '$new_event' => array(z_root().'/events',(($event_id) ? t('Edit Event') : t('Create Event')),'',''), - '$previus' => array(z_root()."/events/$prevyear/$prevmonth",t('Previous'),'',''), - '$next' => array(z_root()."/events/$nextyear/$nextmonth",t('Next'),'',''), - '$export' => array(z_root()."/events/$y/$m/export",t('Export'),'',''), - '$calendar' => cal($y,$m,$links, ' eventcal'), - '$events' => $events, - '$view_label' => t('View'), - '$month' => t('Month'), - '$week' => t('Week'), - '$day' => t('Day'), - '$prev' => t('Previous'), - '$next' => t('Next'), - '$today' => t('Today'), - '$form' => $form, - '$expandform' => ((x($_GET,'expandform')) ? true : false), - )); - - if (x($_GET,'id')){ echo $o; killme(); } - - return $o; - } - - if($mode === 'drop' && $event_id) { - $r = q("SELECT * FROM event WHERE event_hash = '%s' AND uid = %d LIMIT 1", - dbesc($event_id), - intval(local_channel()) - ); + if ($r && !$export) { + xchan_query($r); + $r = fetch_post_tags($r, true); - $sync_event = $r[0]; - - if($r) { - $r = q("delete from event where event_hash = '%s' and uid = %d", - dbesc($event_id), - intval(local_channel()) - ); - if ($r) { - $i = q("select * from item where resource_type = 'event' and resource_id = '%s' and uid = %d", - dbesc($event_id), - intval(local_channel()) - ); + $r = sort_by_date($r); + } - if ($i) { + if ($r) { + foreach ($r as $rr) { + $j = (($rr['adjust']) ? datetime_convert('UTC', date_default_timezone_get(), $rr['dtstart'], 'j') : datetime_convert('UTC', 'UTC', $rr['dtstart'], 'j')); + if (!x($links, $j)) { + $links[$j] = z_root() . '/' . App::$cmd . '#link-' . $j; + } + } + } - $can_delete = false; - $local_delete = true; + $events = []; - $ob_hash = get_observer_hash(); - if($ob_hash && ($ob_hash === $i[0]['author_xchan'] || $ob_hash === $i[0]['owner_xchan'] || $ob_hash === $i[0]['source_xchan'])) { - $can_delete = true; - } + $last_date = ''; + $fmt = t('l, F j'); - // The site admin can delete any post/item on the site. - // If the item originated on this site+channel the deletion will propagate downstream. - // Otherwise just the local copy is removed. + if ($r) { + foreach ($r as $rr) { + $j = (($rr['adjust']) ? datetime_convert('UTC', date_default_timezone_get(), $rr['dtstart'], 'j') : datetime_convert('UTC', 'UTC', $rr['dtstart'], 'j')); + $d = (($rr['adjust']) ? datetime_convert('UTC', date_default_timezone_get(), $rr['dtstart'], $fmt) : datetime_convert('UTC', 'UTC', $rr['dtstart'], $fmt)); + $d = day_translate($d); - if(is_site_admin()) { - $local_delete = true; - if(intval($i[0]['item_origin'])) - $can_delete = true; - } + $start = (($rr['adjust']) ? datetime_convert('UTC', date_default_timezone_get(), $rr['dtstart'], 'c') : datetime_convert('UTC', 'UTC', $rr['dtstart'], 'c')); + if ($rr['nofinish']) { + $end = null; + } else { + $end = (($rr['adjust']) ? datetime_convert('UTC', date_default_timezone_get(), $rr['dtend'], 'c') : datetime_convert('UTC', 'UTC', $rr['dtend'], 'c')); - if($can_delete || $local_delete) { - - // if this is a different page type or it's just a local delete - // but not by the item author or owner, do a simple deletion + // give a fake end to birthdays so they get crammed into a + // single day on the calendar - $complex = false; + if ($rr['etype'] === 'birthday') { + $end = null; + } + } - if(intval($i[0]['item_type']) || ($local_delete && (! $can_delete))) { - drop_item($i[0]['id']); - } - else { - // complex deletion that needs to propagate and be performed in phases - drop_item($i[0]['id'],true,DROPITEM_PHASE1); - $complex = true; - } - $ii = q("select * from item where id = %d", - intval($i[0]['id']) - ); - if($ii) { - xchan_query($ii); - $sync_item = fetch_post_tags($ii); - Libsync::build_sync_packet($i[0]['uid'],array('item' => array(encode_item($sync_item[0],true)))); - } + $is_first = ($d !== $last_date); - if($complex) { - tag_deliver($i[0]['uid'],$i[0]['id']); - } - } - } + $last_date = $d; - $r = q("update item set resource_type = '', resource_id = '' where resource_type = 'event' and resource_id = '%s' and uid = %d", - dbesc($event_id), - intval(local_channel()) - ); - $sync_event['event_deleted'] = 1; - Libsync::build_sync_packet(0,array('event' => array($sync_event))); - - info( t('Event removed') . EOL); - } - else { - notice( t('Failed to remove event' ) . EOL); - } - goaway(z_root() . '/events'); - } - } - - } - + $edit = ((local_channel() && $rr['author_xchan'] == get_observer_hash()) ? array(z_root() . '/events/' . $rr['event_hash'] . '?expandform=1', t('Edit event'), '', '') : false); + + $drop = array(z_root() . '/events/drop/' . $rr['event_hash'], t('Delete event'), '', ''); + + $title = strip_tags(html_entity_decode(zidify_links(bbcode($rr['summary'])), ENT_QUOTES, 'UTF-8')); + if (!$title) { + list($title, $_trash) = explode(" $rr['id'], + 'hash' => $rr['event_hash'], + 'start' => $start, + 'end' => $end, + 'drop' => $drop, + 'allDay' => false, + 'title' => $title, + + 'j' => $j, + 'd' => $d, + 'edit' => $edit, + 'is_first' => $is_first, + 'item' => $rr, + 'html' => $html, + 'plink' => array($rr['plink'], t('Link to Source'), '', ''), + ); + } + } + + if ($export) { + header('Content-type: text/calendar'); + header('Content-Disposition: attachment; filename="' . t('calendar') . '-' . $channel['channel_address'] . '.ics"'); + echo ical_wrapper($r); + killme(); + } + + if (App::$argv[1] === 'json') { + echo json_encode($events); + killme(); + } + + // links: array('href', 'text', 'extra css classes', 'title') + if (x($_GET, 'id')) { + $tpl = get_markup_template("event.tpl"); + } else { + $tpl = get_markup_template("events-js.tpl"); + } + + $o = replace_macros($tpl, array( + '$baseurl' => z_root(), + '$new_event' => array(z_root() . '/events', (($event_id) ? t('Edit Event') : t('Create Event')), '', ''), + '$previus' => array(z_root() . "/events/$prevyear/$prevmonth", t('Previous'), '', ''), + '$next' => array(z_root() . "/events/$nextyear/$nextmonth", t('Next'), '', ''), + '$export' => array(z_root() . "/events/$y/$m/export", t('Export'), '', ''), + '$calendar' => cal($y, $m, $links, ' eventcal'), + '$events' => $events, + '$view_label' => t('View'), + '$month' => t('Month'), + '$week' => t('Week'), + '$day' => t('Day'), + '$prev' => t('Previous'), + '$next' => t('Next'), + '$today' => t('Today'), + '$form' => $form, + '$expandform' => ((x($_GET, 'expandform')) ? true : false), + )); + + if (x($_GET, 'id')) { + echo $o; + killme(); + } + + return $o; + } + + if ($mode === 'drop' && $event_id) { + $r = q( + "SELECT * FROM event WHERE event_hash = '%s' AND uid = %d LIMIT 1", + dbesc($event_id), + intval(local_channel()) + ); + + $sync_event = $r[0]; + + if ($r) { + $r = q( + "delete from event where event_hash = '%s' and uid = %d", + dbesc($event_id), + intval(local_channel()) + ); + if ($r) { + $i = q( + "select * from item where resource_type = 'event' and resource_id = '%s' and uid = %d", + dbesc($event_id), + intval(local_channel()) + ); + + if ($i) { + $can_delete = false; + $local_delete = true; + + $ob_hash = get_observer_hash(); + if ($ob_hash && ($ob_hash === $i[0]['author_xchan'] || $ob_hash === $i[0]['owner_xchan'] || $ob_hash === $i[0]['source_xchan'])) { + $can_delete = true; + } + + // The site admin can delete any post/item on the site. + // If the item originated on this site+channel the deletion will propagate downstream. + // Otherwise just the local copy is removed. + + if (is_site_admin()) { + $local_delete = true; + if (intval($i[0]['item_origin'])) { + $can_delete = true; + } + } + + if ($can_delete || $local_delete) { + // if this is a different page type or it's just a local delete + // but not by the item author or owner, do a simple deletion + + $complex = false; + + if (intval($i[0]['item_type']) || ($local_delete && (!$can_delete))) { + drop_item($i[0]['id']); + } else { + // complex deletion that needs to propagate and be performed in phases + drop_item($i[0]['id'], true, DROPITEM_PHASE1); + $complex = true; + } + + $ii = q( + "select * from item where id = %d", + intval($i[0]['id']) + ); + if ($ii) { + xchan_query($ii); + $sync_item = fetch_post_tags($ii); + Libsync::build_sync_packet($i[0]['uid'], array('item' => array(encode_item($sync_item[0], true)))); + } + + if ($complex) { + tag_deliver($i[0]['uid'], $i[0]['id']); + } + } + } + + $r = q( + "update item set resource_type = '', resource_id = '' where resource_type = 'event' and resource_id = '%s' and uid = %d", + dbesc($event_id), + intval(local_channel()) + ); + $sync_event['event_deleted'] = 1; + Libsync::build_sync_packet(0, array('event' => array($sync_event))); + + info(t('Event removed') . EOL); + } else { + notice(t('Failed to remove event') . EOL); + } + goaway(z_root() . '/events'); + } + } + } } diff --git a/Zotlabs/Module/Expire.php b/Zotlabs/Module/Expire.php index 81c932430..1123d3f63 100644 --- a/Zotlabs/Module/Expire.php +++ b/Zotlabs/Module/Expire.php @@ -6,48 +6,47 @@ use Zotlabs\Lib\Apps; use Zotlabs\Lib\Libsync; use Zotlabs\Web\Controller; -class Expire extends Controller { +class Expire extends Controller +{ - function post() { + public function post() + { - if (! ( local_channel() && Apps::system_app_installed(local_channel(),'Expire Posts'))) { - return; - } + if (!(local_channel() && Apps::system_app_installed(local_channel(), 'Expire Posts'))) { + return; + } - if ($_POST['expire-submit']) { - $expire = intval($_POST['selfexpiredays']); - if ($expire < 0) { - $expire = 0; - } - set_pconfig(local_channel(),'system','selfexpiredays',$expire); - info( t('Expiration settings updated.') . EOL); - } - - Libsync::build_sync_packet(); - } + if ($_POST['expire-submit']) { + $expire = intval($_POST['selfexpiredays']); + if ($expire < 0) { + $expire = 0; + } + set_pconfig(local_channel(), 'system', 'selfexpiredays', $expire); + info(t('Expiration settings updated.') . EOL); + } + + Libsync::build_sync_packet(); + } - - function get() { + public function get() + { $desc = t('This app allows you to set an optional expiration date/time for your own posts, after which they will be deleted. This must be at least fifteen minutes into the future. You may also choose to automatically delete all your posts after a set number of days'); $text = ''; - if (! ( local_channel() && Apps::system_app_installed(local_channel(),'Expire Posts'))) { + if (!(local_channel() && Apps::system_app_installed(local_channel(), 'Expire Posts'))) { return $text; } - $setting_fields .= replace_macros(get_markup_template('field_input.tpl'), [ - '$field' => array('selfexpiredays', t('Expire and delete all my posts after this many days'), intval(get_pconfig(local_channel(),'system','selfexpiredays',0)), t('Leave at 0 if you wish to manually control expiration of specific posts.')) - ]); - - return replace_macros(get_markup_template('generic_app_settings.tpl'), [ - '$addon' => array('expire', t('Automatic Expiration Settings'), '', t('Submit')), - '$content' => $setting_fields - ]); - - } - + $setting_fields .= replace_macros(get_markup_template('field_input.tpl'), [ + '$field' => array('selfexpiredays', t('Expire and delete all my posts after this many days'), intval(get_pconfig(local_channel(), 'system', 'selfexpiredays', 0)), t('Leave at 0 if you wish to manually control expiration of specific posts.')) + ]); + return replace_macros(get_markup_template('generic_app_settings.tpl'), [ + '$addon' => array('expire', t('Automatic Expiration Settings'), '', t('Submit')), + '$content' => $setting_fields + ]); + } } diff --git a/Zotlabs/Module/Fastping.php b/Zotlabs/Module/Fastping.php index 5eea05c56..63fc2e739 100644 --- a/Zotlabs/Module/Fastping.php +++ b/Zotlabs/Module/Fastping.php @@ -15,59 +15,58 @@ require_once('include/bbcode.php'); * Called from the client at regular intervals to check for updates from the server * */ +class Fastping extends Controller +{ -class Fastping extends Controller { + /** + * @brief do several updates when pinged. + * + * This function does several tasks. Whenever called it checks for new messages, + * introductions, notifications, etc. and returns a json with the results. + * + * @result JSON + */ - /** - * @brief do several updates when pinged. - * - * This function does several tasks. Whenever called it checks for new messages, - * introductions, notifications, etc. and returns a json with the results. - * - * @result JSON - */ + public function init() + { - function init() { + $result['notice'] = []; + $result['info'] = []; - $result['notice'] = []; - $result['info'] = []; + $vnotify = (-1); - $vnotify = (-1); + $result['invalid'] = ((isset($_GET['uid']) && intval($_GET['uid'])) && (intval($_GET['uid']) != local_channel()) ? 1 : 0); - $result['invalid'] = ((isset($_GET['uid']) && intval($_GET['uid'])) && (intval($_GET['uid']) != local_channel()) ? 1 : 0); + if (local_channel()) { + $vnotify = get_pconfig(local_channel(), 'system', 'vnotify', (-1)); + } - if (local_channel()) { - $vnotify = get_pconfig(local_channel(),'system','vnotify',(-1)); - } + /** + * Send all system messages (alerts) to the browser. + * Some are marked as informational and some represent + * errors or serious notifications. These typically + * will popup on the current page (no matter what page it is) + */ - /** - * Send all system messages (alerts) to the browser. - * Some are marked as informational and some represent - * errors or serious notifications. These typically - * will popup on the current page (no matter what page it is) - */ - - if (x($_SESSION, 'sysmsg')) { - foreach ($_SESSION['sysmsg'] as $m) { - $result['notice'][] = array('message' => $m); - } - unset($_SESSION['sysmsg']); - } - if (x($_SESSION, 'sysmsg_info')) { - foreach ($_SESSION['sysmsg_info'] as $m) { - $result['info'][] = array('message' => $m); - } - unset($_SESSION['sysmsg_info']); - } - if (! ($vnotify & VNOTIFY_INFO)) { - $result['info'] = []; - } - if (! ($vnotify & VNOTIFY_ALERT)) { - $result['notice'] = []; - } - - json_return_and_die($result); - - } + if (x($_SESSION, 'sysmsg')) { + foreach ($_SESSION['sysmsg'] as $m) { + $result['notice'][] = array('message' => $m); + } + unset($_SESSION['sysmsg']); + } + if (x($_SESSION, 'sysmsg_info')) { + foreach ($_SESSION['sysmsg_info'] as $m) { + $result['info'][] = array('message' => $m); + } + unset($_SESSION['sysmsg_info']); + } + if (!($vnotify & VNOTIFY_INFO)) { + $result['info'] = []; + } + if (!($vnotify & VNOTIFY_ALERT)) { + $result['notice'] = []; + } + json_return_and_die($result); + } } diff --git a/Zotlabs/Module/Fbrowser.php b/Zotlabs/Module/Fbrowser.php index 75afd705d..396e63fce 100644 --- a/Zotlabs/Module/Fbrowser.php +++ b/Zotlabs/Module/Fbrowser.php @@ -1,133 +1,142 @@ + * @package Friendica\modules + * @subpackage FileBrowser + * @author Fabio Comuni */ require_once('include/photo_factory.php'); -class Fbrowser extends \Zotlabs\Web\Controller { +class Fbrowser extends Controller +{ - function get(){ - - if (!local_channel()) - killme(); - - if (\App::$argc==1) - killme(); - - //echo "
        "; var_dump(\App::$argv); killme();	
        -		
        -		switch(\App::$argv[1]){
        -			case "image":
        -				$path = array( array(z_root()."/fbrowser/image/", t("Photos")));
        -				$albums = false;
        -				$sql_extra = "";
        -				$sql_extra2 = " ORDER BY created DESC LIMIT 0, 10";
        -				
        -				if (\App::$argc==2){
        -					$albums = q("SELECT distinct(album) AS album FROM photo WHERE uid = %d ",
        -						intval(local_channel())
        -					);
        -					// anon functions only from 5.3.0... meglio tardi che mai..
        -					$albums = array_map( "self::folder1" , $albums);
        -					
        -				}
        -				
        -				$album = "";
        -				if (\App::$argc==3){
        -					$album = hex2bin(\App::$argv[2]);
        -					$sql_extra = sprintf("AND album = '%s' ",dbesc($album));
        -					$sql_extra2 = "";
        -					$path[]=array(z_root() . "/fbrowser/image/" . \App::$argv[2] . "/", $album);
        -				}
        -					
        -				$r = q("SELECT resource_id, id, filename, type, min(imgscale) AS hiq,max(imgscale) AS loq, description  
        +    public function get()
        +    {
        +
        +        if (!local_channel()) {
        +            killme();
        +        }
        +
        +        if (App::$argc == 1) {
        +            killme();
        +        }
        +
        +        //echo "
        "; var_dump(\App::$argv); killme();
        +
        +        switch (App::$argv[1]) {
        +            case "image":
        +                $path = array(array(z_root() . "/fbrowser/image/", t("Photos")));
        +                $albums = false;
        +                $sql_extra = "";
        +                $sql_extra2 = " ORDER BY created DESC LIMIT 0, 10";
        +
        +                if (App::$argc == 2) {
        +                    $albums = q(
        +                        "SELECT distinct(album) AS album FROM photo WHERE uid = %d ",
        +                        intval(local_channel())
        +                    );
        +                    // anon functions only from 5.3.0... meglio tardi che mai..
        +                    $albums = array_map("self::folder1", $albums);
        +                }
        +
        +                $album = "";
        +                if (App::$argc == 3) {
        +                    $album = hex2bin(App::$argv[2]);
        +                    $sql_extra = sprintf("AND album = '%s' ", dbesc($album));
        +                    $sql_extra2 = "";
        +                    $path[] = array(z_root() . "/fbrowser/image/" . App::$argv[2] . "/", $album);
        +                }
        +
        +                $r = q(
        +                    "SELECT resource_id, id, filename, type, min(imgscale) AS hiq,max(imgscale) AS loq, description  
         						FROM photo WHERE uid = %d $sql_extra
         						GROUP BY resource_id $sql_extra2",
        -					intval(local_channel())					
        -				);
        -				
        -				$files = array_map("self::files1", $r);
        -				
        -				$tpl = get_markup_template("filebrowser.tpl");
        -				echo replace_macros($tpl, array(
        -					'$type' => 'image',
        -					'$baseurl' => z_root(),
        -					'$path' => $path,
        -					'$folders' => $albums,
        -					'$files' =>$files,
        -					'$cancel' => t('Cancel'),
        -				));
        -					
        -					
        -				break;
        -			case "file":
        -				if (\App::$argc==2){
        -					$files = q("SELECT id, filename, filetype FROM attach WHERE uid = %d ",
        -						intval(local_channel())
        -					);
        -					
        -					$files = array_map("self::files2", $files);
        -					//echo "
        "; var_dump($files); killme();
        -				
        -								
        -					$tpl = get_markup_template("filebrowser.tpl");
        -					echo replace_macros($tpl, array(
        -						'$type' => 'file',
        -						'$baseurl' => z_root(),
        -						'$path' => array( array(z_root()."/fbrowser/image/", t("Files")) ),
        -						'$folders' => false,
        -						'$files' =>$files,
        -						'$cancel' => t('Cancel'),
        -					));
        -					
        -				}
        -			
        -				break;
        -		}
        -		
        -	
        -		killme();
        -		
        -	}
        +                    intval(local_channel())
        +                );
         
        -	private static function folder1($el){
        -		return array(bin2hex($el['album']),$el['album']);
        -	}	
        +                $files = array_map("self::files1", $r);
        +
        +                $tpl = get_markup_template("filebrowser.tpl");
        +                echo replace_macros($tpl, array(
        +                    '$type' => 'image',
        +                    '$baseurl' => z_root(),
        +                    '$path' => $path,
        +                    '$folders' => $albums,
        +                    '$files' => $files,
        +                    '$cancel' => t('Cancel'),
        +                ));
         
         
        -	private static function files1($rr){ 
        +                break;
        +            case "file":
        +                if (App::$argc == 2) {
        +                    $files = q(
        +                        "SELECT id, filename, filetype FROM attach WHERE uid = %d ",
        +                        intval(local_channel())
        +                    );
         
        -		$ph = photo_factory('');
        -		$types = $ph->supportedTypes();
        -		$ext = $types[$rr['type']];
        -	
        -		$filename_e = $rr['filename'];
        -			
        -		return array( 
        -			z_root() . '/photo/' . $rr['resource_id'] . '-' . $rr['hiq'] . '.' .$ext, 
        -			$filename_e, 
        -			z_root() . '/photo/' . $rr['resource_id'] . '-' . $rr['loq'] . '.'. $ext
        -		);
        -	}
        +                    $files = array_map("self::files2", $files);
        +                    //echo "
        "; var_dump($files); killme();
         
        -	private static function files2($rr){
        -		list($m1,$m2) = explode("/",$rr['filetype']);
        -		$filetype = ( (file_exists("images/icons/$m1.png"))?$m1:"zip");
        -	
        -		if(\App::get_template_engine() === 'internal') {
        -			$filename_e = template_escape($rr['filename']);
        -		}
        -		else {
        -			$filename_e = $rr['filename'];
        -		}
        -	
        -		return array( z_root() . '/attach/' . $rr['id'], $filename_e, z_root() . '/images/icons/16/' . $filetype . '.png'); 
        -	}
         
        -	
        +                    $tpl = get_markup_template("filebrowser.tpl");
        +                    echo replace_macros($tpl, array(
        +                        '$type' => 'file',
        +                        '$baseurl' => z_root(),
        +                        '$path' => array(array(z_root() . "/fbrowser/image/", t("Files"))),
        +                        '$folders' => false,
        +                        '$files' => $files,
        +                        '$cancel' => t('Cancel'),
        +                    ));
        +                }
        +
        +                break;
        +        }
        +
        +
        +        killme();
        +    }
        +
        +    private static function folder1($el)
        +    {
        +        return array(bin2hex($el['album']), $el['album']);
        +    }
        +
        +
        +    private static function files1($rr)
        +    {
        +
        +        $ph = photo_factory('');
        +        $types = $ph->supportedTypes();
        +        $ext = $types[$rr['type']];
        +
        +        $filename_e = $rr['filename'];
        +
        +        return array(
        +            z_root() . '/photo/' . $rr['resource_id'] . '-' . $rr['hiq'] . '.' . $ext,
        +            $filename_e,
        +            z_root() . '/photo/' . $rr['resource_id'] . '-' . $rr['loq'] . '.' . $ext
        +        );
        +    }
        +
        +    private static function files2($rr)
        +    {
        +        list($m1, $m2) = explode("/", $rr['filetype']);
        +        $filetype = ((file_exists("images/icons/$m1.png")) ? $m1 : "zip");
        +
        +        if (App::get_template_engine() === 'internal') {
        +            $filename_e = template_escape($rr['filename']);
        +        } else {
        +            $filename_e = $rr['filename'];
        +        }
        +
        +        return array(z_root() . '/attach/' . $rr['id'], $filename_e, z_root() . '/images/icons/16/' . $filetype . '.png');
        +    }
         }
        diff --git a/Zotlabs/Module/Fedi_id.php b/Zotlabs/Module/Fedi_id.php
        index d3ee3ff96..cb4328070 100644
        --- a/Zotlabs/Module/Fedi_id.php
        +++ b/Zotlabs/Module/Fedi_id.php
        @@ -4,50 +4,53 @@ namespace Zotlabs\Module;
         
         use Zotlabs\Web\Controller;
         
        +class Fedi_id extends Controller
        +{
         
        -class Fedi_id extends Controller {
        +    public function post()
        +    {
        +        $channel = channelx_by_n(argv(1));
        +        if (!$channel) {
        +            return;
        +        }
        +        if ($_REQUEST['address']) {
        +            $x = discover_by_webbie(trim($_REQUEST['address']));
        +            if ($x) {
        +                $ab = q(
        +                    "select * from abook where abook_xchan = '%s' and abook_channel = %d",
        +                    dbesc($x),
        +                    intval($channel['channel_id'])
        +                );
        +                if ($ab) {
        +                    notice(t('You are already connected with this channel.'));
        +                    goaway(channel_url($channel));
        +                }
        +                $r = q(
        +                    "select * from xchan where xchan_hash = '%s'",
        +                    dbesc($x)
        +                );
        +                if ($r && $r[0]['xchan_follow']) {
        +                    goaway(sprintf($r[0]['xchan_follow'], urlencode(channel_reddress($channel))));
        +                }
        +            }
         
        -	function post() {
        -		$channel = channelx_by_n(argv(1));
        -		if (! $channel) {
        -			return;
        -		}
        -		if ($_REQUEST['address']) {
        -			$x = discover_by_webbie(trim($_REQUEST['address']));
        -			if ($x) {
        -				$ab = q("select * from abook where abook_xchan = '%s' and abook_channel = %d",
        -					dbesc($x),
        -					intval($channel['channel_id'])
        -				);
        -				if ($ab) {
        -					notice( t('You are already connected with this channel.') );
        -					goaway(channel_url($channel));
        -				}
        -				$r = q("select * from xchan where xchan_hash = '%s'",
        -					dbesc($x)
        -				);
        -				if ($r && $r[0]['xchan_follow']) {
        -					goaway(sprintf($r[0]['xchan_follow'],urlencode(channel_reddress($channel))));
        -				}				
        -			}
        +            notice(t('Unknown or unreachable identifier'));
        +            return;
        +        }
        +    }
         
        -			notice( t('Unknown or unreachable identifier') );
        -			return;
        -		}
        -	}
        +    public function get()
        +    {
         
        -	function get() {
        -
        -		return replace_macros(get_markup_template('fedi_id.tpl'),
        -			[
        -				'$title'   => t('Home instance'),
        -				'$address' => [ 'address', t('Enter your channel address or fediverse ID (e.g. channel@example.com)'), '', t('If you do not have a fediverse ID, please use your browser \'back\' button to return to the previous page') ],
        -				'$action'  => 'fedi_id/' . argv(1),
        -				'$method'  => 'post',
        -				'$submit'  => t('Connect')
        -			]
        -		);	
        -
        -	}
        -
        -}
        \ No newline at end of file
        +        return replace_macros(
        +            get_markup_template('fedi_id.tpl'),
        +            [
        +                '$title' => t('Home instance'),
        +                '$address' => ['address', t('Enter your channel address or fediverse ID (e.g. channel@example.com)'), '', t('If you do not have a fediverse ID, please use your browser \'back\' button to return to the previous page')],
        +                '$action' => 'fedi_id/' . argv(1),
        +                '$method' => 'post',
        +                '$submit' => t('Connect')
        +            ]
        +        );
        +    }
        +}
        diff --git a/Zotlabs/Module/Feed.php b/Zotlabs/Module/Feed.php
        index ace1109a8..69347ba69 100644
        --- a/Zotlabs/Module/Feed.php
        +++ b/Zotlabs/Module/Feed.php
        @@ -1,49 +1,49 @@
          1) {
        +        $params['begin'] = ((x($_REQUEST, 'date_begin')) ? $_REQUEST['date_begin'] : NULL_DATE);
        +        $params['end'] = ((x($_REQUEST, 'date_end')) ? $_REQUEST['date_end'] : '');
        +        $params['type'] = ((stristr(argv(0), 'json')) ? 'json' : 'xml');
        +        $params['pages'] = ((x($_REQUEST, 'pages')) ? intval($_REQUEST['pages']) : 0);
        +        $params['top'] = ((x($_REQUEST, 'top')) ? intval($_REQUEST['top']) : 0);
        +        $params['start'] = ((x($_REQUEST, 'start')) ? intval($_REQUEST['start']) : 0);
        +        $params['records'] = ((x($_REQUEST, 'records')) ? intval($_REQUEST['records']) : 40);
        +        $params['direction'] = ((x($_REQUEST, 'direction')) ? dbesc($_REQUEST['direction']) : 'desc');
        +        $params['cat'] = ((x($_REQUEST, 'cat')) ? escape_tags($_REQUEST['cat']) : '');
        +        $params['compat'] = ((x($_REQUEST, 'compat')) ? intval($_REQUEST['compat']) : 0);
         
        -			if (observer_prohibited(true)) {
        -				killme();
        -			}
        +        if (!in_array($params['direction'], ['asc', 'desc'])) {
        +            $params['direction'] = 'desc';
        +        }
         
        -			$channel = channelx_by_nick(argv(1));
        -			if (! $channel) {
        -				killme();
        -			}	
        -	 
        -			logger('public feed request from ' . $_SERVER['REMOTE_ADDR'] . ' for ' . $channel['channel_address']);
        -	
        -			echo get_public_feed($channel,$params);
        -	
        -			killme();
        -		}
        -	
        -	}
        -	
        +        if (argc() > 1) {
        +            if (observer_prohibited(true)) {
        +                killme();
        +            }
        +
        +            $channel = channelx_by_nick(argv(1));
        +            if (!$channel) {
        +                killme();
        +            }
        +
        +            logger('public feed request from ' . $_SERVER['REMOTE_ADDR'] . ' for ' . $channel['channel_address']);
        +
        +            echo get_public_feed($channel, $params);
        +
        +            killme();
        +        }
        +    }
         }
        diff --git a/Zotlabs/Module/File_upload.php b/Zotlabs/Module/File_upload.php
        index 7e38397c2..7e10c9c52 100644
        --- a/Zotlabs/Module/File_upload.php
        +++ b/Zotlabs/Module/File_upload.php
        @@ -1,110 +1,106 @@
          array($sync)));
        -				}
        -				goaway(z_root() . '/cloud/' . $channel['channel_address'] . '/' . $r['data']['display_path']);
        +        if ($channel['channel_id'] != local_channel()) {
        +            $_REQUEST['contact_allow'] = expand_acl($channel['channel_allow_cid']);
        +            $_REQUEST['group_allow'] = expand_acl($channel['channel_allow_gid']);
        +            $_REQUEST['contact_deny'] = expand_acl($channel['channel_deny_cid']);
        +            $_REQUEST['group_deny'] = expand_acl($channel['channel_deny_gid']);
        +        }
         
        -			}
        -		}
        -		else {
        +        $_REQUEST['allow_cid'] = perms2str($_REQUEST['contact_allow']);
        +        $_REQUEST['allow_gid'] = perms2str($_REQUEST['group_allow']);
        +        $_REQUEST['deny_cid'] = perms2str($_REQUEST['contact_deny']);
        +        $_REQUEST['deny_gid'] = perms2str($_REQUEST['group_deny']);
         
        -			$matches = [];
        -			$partial = false;
        +        if ($_REQUEST['filename']) {
        +            $r = attach_mkdir($channel, get_observer_hash(), $_REQUEST);
        +            if ($r['success']) {
        +                $hash = $r['data']['hash'];
        +
        +                $sync = attach_export_data($channel, $hash);
        +                if ($sync) {
        +                    Libsync::build_sync_packet($channel['channel_id'], array('file' => array($sync)));
        +                }
        +                goaway(z_root() . '/cloud/' . $channel['channel_address'] . '/' . $r['data']['display_path']);
        +            }
        +        } else {
        +            $matches = [];
        +            $partial = false;
         
         
        +            if (array_key_exists('HTTP_CONTENT_RANGE', $_SERVER)) {
        +                $pm = preg_match('/bytes (\d*)\-(\d*)\/(\d*)/', $_SERVER['HTTP_CONTENT_RANGE'], $matches);
        +                if ($pm) {
        +                    logger('Content-Range: ' . print_r($matches, true));
        +                    $partial = true;
        +                }
        +            }
         
        -			if(array_key_exists('HTTP_CONTENT_RANGE',$_SERVER)) {
        -				$pm = preg_match('/bytes (\d*)\-(\d*)\/(\d*)/',$_SERVER['HTTP_CONTENT_RANGE'],$matches);
        -				if($pm) {
        -					logger('Content-Range: ' . print_r($matches,true));
        -					$partial = true;
        -				}
        -			}
        +            if ($partial) {
        +                $x = save_chunk($channel, $matches[1], $matches[2], $matches[3]);
         
        -			if($partial) {
        -				$x = save_chunk($channel,$matches[1],$matches[2],$matches[3]);
        +                if ($x['partial']) {
        +                    header('Range: bytes=0-' . (($x['length']) ? $x['length'] - 1 : 0));
        +                    json_return_and_die($x);
        +                } else {
        +                    header('Range: bytes=0-' . (($x['size']) ? $x['size'] - 1 : 0));
         
        -				if($x['partial']) {
        -					header('Range: bytes=0-' . (($x['length']) ? $x['length'] - 1 : 0));
        -					json_return_and_die($x);
        -				}
        -				else {
        -					header('Range: bytes=0-' . (($x['size']) ? $x['size'] - 1 : 0));
        +                    $_FILES['userfile'] = [
        +                        'name' => $x['name'],
        +                        'type' => $x['type'],
        +                        'tmp_name' => $x['tmp_name'],
        +                        'error' => $x['error'],
        +                        'size' => $x['size']
        +                    ];
        +                }
        +            } else {
        +                if (!array_key_exists('userfile', $_FILES)) {
        +                    $_FILES['userfile'] = [
        +                        'name' => $_FILES['files']['name'],
        +                        'type' => $_FILES['files']['type'],
        +                        'tmp_name' => $_FILES['files']['tmp_name'],
        +                        'error' => $_FILES['files']['error'],
        +                        'size' => $_FILES['files']['size']
        +                    ];
        +                }
        +            }
         
        -					$_FILES['userfile'] = [
        -						'name'     => $x['name'],
        -						'type'     => $x['type'],
        -						'tmp_name' => $x['tmp_name'],
        -						'error'    => $x['error'],
        -						'size'     => $x['size']
        -					];
        -				}
        -			}
        -			else {	
        -				if(! array_key_exists('userfile',$_FILES)) {
        -					$_FILES['userfile'] = [
        -						'name'     => $_FILES['files']['name'],
        -						'type'     => $_FILES['files']['type'],
        -						'tmp_name' => $_FILES['files']['tmp_name'],
        -						'error'    => $_FILES['files']['error'],
        -						'size'     => $_FILES['files']['size']
        -					];
        -				}
        -			}
        -
        -			$r = attach_store($channel, get_observer_hash(), '', $_REQUEST);
        -			if($r['success']) {
        -				$sync = attach_export_data($channel,$r['data']['hash']);
        -				if($sync)
        -					Libsync::build_sync_packet($channel['channel_id'],array('file' => array($sync)));
        -
        -			}
        -		}
        -		goaway(z_root() . '/' . $_REQUEST['return_url']);
        -	
        -	}
        -	
        +            $r = attach_store($channel, get_observer_hash(), '', $_REQUEST);
        +            if ($r['success']) {
        +                $sync = attach_export_data($channel, $r['data']['hash']);
        +                if ($sync) {
        +                    Libsync::build_sync_packet($channel['channel_id'], array('file' => array($sync)));
        +                }
        +            }
        +        }
        +        goaway(z_root() . '/' . $_REQUEST['return_url']);
        +    }
         }
        diff --git a/Zotlabs/Module/Filer.php b/Zotlabs/Module/Filer.php
        index f73981b6b..6596dcd8a 100644
        --- a/Zotlabs/Module/Filer.php
        +++ b/Zotlabs/Module/Filer.php
        @@ -1,61 +1,69 @@
          1) ? intval(\App::$argv[1]) : 0);
        -	
        -		logger('filer: tag ' . $term . ' item ' . $item_id);
        -	
        -		if($item_id && strlen($term)){
        -			// file item
        -			store_item_tag(local_channel(),$item_id,TERM_OBJ_POST,TERM_FILE,$term,'');
        -	
        -			// protect the entire conversation from periodic expiration
        -	
        -			$r = q("select parent from item where id = %d and uid = %d limit 1",
        -				intval($item_id),
        -				intval(local_channel())
        -			);
        -			if($r) {
        -				$x = q("update item set item_retained = 1 where id = %d and uid = %d",
        -					intval($r[0]['parent']),
        -					intval(local_channel())
        -				);
        -			}
        -		} 
        -		else {
        -			$filetags = [];
        -			$r = q("select distinct(term) from term where uid = %d and ttype = %d order by term asc",
        -				intval(local_channel()),
        -				intval(TERM_FILE)
        -			);
        -			if(count($r)) {
        -				foreach($r as $rr)
        -					$filetags[] = $rr['term'];
        -			}
        -			$tpl = get_markup_template("filer_dialog.tpl");
        -			$o = replace_macros($tpl, array(
        -				'$field' => array('term', t('Enter a folder name'), '', '', $filetags, 'placeholder="' . t('or select an existing folder (doubleclick)') . '"'),
        -				'$submit' => t('Save'),
        -				'$title' => t('Save to Folder'),
        -				'$cancel' => t('Cancel')
        -			));
        -			
        -			echo $o;
        -		}
        -		killme();
        -	}
        -	
        +    public function get()
        +    {
        +
        +        if (!local_channel()) {
        +            killme();
        +        }
        +
        +        $term = unxmlify(trim($_GET['term']));
        +        $item_id = ((App::$argc > 1) ? intval(App::$argv[1]) : 0);
        +
        +        logger('filer: tag ' . $term . ' item ' . $item_id);
        +
        +        if ($item_id && strlen($term)) {
        +            // file item
        +            store_item_tag(local_channel(), $item_id, TERM_OBJ_POST, TERM_FILE, $term, '');
        +
        +            // protect the entire conversation from periodic expiration
        +
        +            $r = q(
        +                "select parent from item where id = %d and uid = %d limit 1",
        +                intval($item_id),
        +                intval(local_channel())
        +            );
        +            if ($r) {
        +                $x = q(
        +                    "update item set item_retained = 1 where id = %d and uid = %d",
        +                    intval($r[0]['parent']),
        +                    intval(local_channel())
        +                );
        +            }
        +        } else {
        +            $filetags = [];
        +            $r = q(
        +                "select distinct(term) from term where uid = %d and ttype = %d order by term asc",
        +                intval(local_channel()),
        +                intval(TERM_FILE)
        +            );
        +            if (count($r)) {
        +                foreach ($r as $rr) {
        +                    $filetags[] = $rr['term'];
        +                }
        +            }
        +            $tpl = get_markup_template("filer_dialog.tpl");
        +            $o = replace_macros($tpl, array(
        +                '$field' => array('term', t('Enter a folder name'), '', '', $filetags, 'placeholder="' . t('or select an existing folder (doubleclick)') . '"'),
        +                '$submit' => t('Save'),
        +                '$title' => t('Save to Folder'),
        +                '$cancel' => t('Cancel')
        +            ));
        +
        +            echo $o;
        +        }
        +        killme();
        +    }
         }
        diff --git a/Zotlabs/Module/Filerm.php b/Zotlabs/Module/Filerm.php
        index cbf6a118d..5d2178cff 100644
        --- a/Zotlabs/Module/Filerm.php
        +++ b/Zotlabs/Module/Filerm.php
        @@ -1,39 +1,46 @@
          1) ? intval(\App::$argv[1]) : 0);
        -	
        -		logger('filerm: tag ' . $term . ' item ' . $item_id);
        -	
        -		if($item_id && strlen($term)) {
        -			$r = q("delete from term where uid = %d and ttype = %d and oid = %d and term = '%s'",
        -				intval(local_channel()),
        -				intval(($category) ? TERM_CATEGORY : TERM_FILE),
        -				intval($item_id),
        -				dbesc($term)
        -			);
        -		}
        -	
        -		if(x($_SESSION,'return_url'))
        -			goaway(z_root() . '/' . $_SESSION['return_url']);
        -		
        -		killme();
        -	}
        -	
        +    public function get()
        +    {
        +
        +        if (!local_channel()) {
        +            killme();
        +        }
        +
        +        $term = trim($_GET['term']);
        +        $cat = trim($_GET['cat']);
        +
        +        $category = (($cat) ? true : false);
        +        if ($category) {
        +            $term = $cat;
        +        }
        +
        +        $item_id = ((App::$argc > 1) ? intval(App::$argv[1]) : 0);
        +
        +        logger('filerm: tag ' . $term . ' item ' . $item_id);
        +
        +        if ($item_id && strlen($term)) {
        +            $r = q(
        +                "delete from term where uid = %d and ttype = %d and oid = %d and term = '%s'",
        +                intval(local_channel()),
        +                intval(($category) ? TERM_CATEGORY : TERM_FILE),
        +                intval($item_id),
        +                dbesc($term)
        +            );
        +        }
        +
        +        if (x($_SESSION, 'return_url')) {
        +            goaway(z_root() . '/' . $_SESSION['return_url']);
        +        }
        +
        +        killme();
        +    }
         }
        diff --git a/Zotlabs/Module/Filestorage.php b/Zotlabs/Module/Filestorage.php
        index 6539add67..2acef08c3 100644
        --- a/Zotlabs/Module/Filestorage.php
        +++ b/Zotlabs/Module/Filestorage.php
        @@ -1,6 +1,6 @@
         set_from_array($_POST);
        -		$x = $acl->get();
        +            if ($m) {
        +                // we should always have $newdir, but only call attach_move()
        +                // if it is being changed *or* a new filename is set, and
        +                // account for the fact $newdir can legally be an empty sring
        +                // to indicate the cloud root directory
         
        -		$url = get_cloud_url($channel_id, $channel['channel_address'], $resource);
        +                if ($newdir !== false && $newdir !== $m[0]['folder']) {
        +                    $changed = true;
        +                }
        +                if ($newname) {
        +                    $changed = true;
        +                }
        +                if ($changed) {
        +                    attach_move($channel_id, $resource, $newdir, $newname);
        +                }
        +            }
        +        }
         
        -		// get the object before permissions change so we can catch eventual former allowed members
        -		$object = get_file_activity_object($channel_id, $resource, $url);
        +        $acl = new AccessControl($channel);
        +        $acl->set_from_array($_POST);
        +        $x = $acl->get();
         
        -		attach_change_permissions($channel_id, $resource, $x['allow_cid'], $x['allow_gid'], $x['deny_cid'], $x['deny_gid'], $recurse, true);
        +        $url = get_cloud_url($channel_id, $channel['channel_address'], $resource);
         
        -		$sync = attach_export_data($channel,$resource,false);
        -		if ($sync) {
        -			Libsync::build_sync_packet($channel_id,array('file' => array($sync)));
        -		}
        +        // get the object before permissions change so we can catch eventual former allowed members
        +        $object = get_file_activity_object($channel_id, $resource, $url);
         
        -//		file_activity($channel_id, $object, $x['allow_cid'], $x['allow_gid'], $x['deny_cid'], $x['deny_gid'], 'post', $notify);
        +        attach_change_permissions($channel_id, $resource, $x['allow_cid'], $x['allow_gid'], $x['deny_cid'], $x['deny_gid'], $recurse, true);
         
        -		goaway(dirname($url));
        -	}
        +        $sync = attach_export_data($channel, $resource, false);
        +        if ($sync) {
        +            Libsync::build_sync_packet($channel_id, array('file' => array($sync)));
        +        }
         
        -	function get() {
        +//      file_activity($channel_id, $object, $x['allow_cid'], $x['allow_gid'], $x['deny_cid'], $x['deny_gid'], 'post', $notify);
         
        -		if (argc() > 1) {
        -			$channel = channelx_by_nick(argv(1));
        -		}
        -		if (! $channel) {
        -			notice( t('Channel unavailable.') . EOL);
        -			App::$error = 404;
        -			return;
        -		}			
        +        goaway(dirname($url));
        +    }
         
        -		$owner = intval($channel['channel_id']);
        -		$observer = App::get_observer();
        -		
        -		$ob_hash = (($observer) ? $observer['xchan_hash'] : '');
        +    public function get()
        +    {
         
        -		$perms = get_all_perms($owner, $ob_hash);
        +        if (argc() > 1) {
        +            $channel = channelx_by_nick(argv(1));
        +        }
        +        if (!$channel) {
        +            notice(t('Channel unavailable.') . EOL);
        +            App::$error = 404;
        +            return;
        +        }
         
        -		if (! ($perms['view_storage'] || is_site_admin())){
        -			notice( t('Permission denied.') . EOL);
        -			return;
        -		}
        +        $owner = intval($channel['channel_id']);
        +        $observer = App::get_observer();
        +
        +        $ob_hash = (($observer) ? $observer['xchan_hash'] : '');
        +
        +        $perms = get_all_perms($owner, $ob_hash);
        +
        +        if (!($perms['view_storage'] || is_site_admin())) {
        +            notice(t('Permission denied.') . EOL);
        +            return;
        +        }
         
         
        -		if (argc() > 3 && argv(3) === 'delete') {
        +        if (argc() > 3 && argv(3) === 'delete') {
        +            if (argc() > 4 && argv(4) === 'json') {
        +                $json_return = true;
        +            }
         
        -			if (argc() > 4 && argv(4) === 'json') {
        -				$json_return = true;
        -			}
        +            $admin_delete = false;
         
        -			$admin_delete = false;
        +            if (!$perms['write_storage']) {
        +                if (is_site_admin()) {
        +                    $admin_delete = true;
        +                } else {
        +                    notice(t('Permission denied.') . EOL);
        +                    if ($json_return) {
        +                        json_return_and_die(['success' => false]);
        +                    }
        +                    return;
        +                }
        +            }
         
        -			if (! $perms['write_storage']) {
        -				if (is_site_admin()) {
        -					$admin_delete = true;
        -				}
        -				else {
        -					notice( t('Permission denied.') . EOL);
        -					if ($json_return) {
        -						json_return_and_die([ 'success' => false ]);
        -					}
        -					return;
        -				}
        -			}
        +            $file = intval(argv(2));
        +            $r = q(
        +                "SELECT hash, creator FROM attach WHERE id = %d AND uid = %d LIMIT 1",
        +                dbesc($file),
        +                intval($owner)
        +            );
        +            if (!$r) {
        +                notice(t('File not found.') . EOL);
         
        -			$file = intval(argv(2));
        -			$r = q("SELECT hash, creator FROM attach WHERE id = %d AND uid = %d LIMIT 1",
        -				dbesc($file),
        -				intval($owner)
        -			);
        -			if (! $r) {
        -				notice( t('File not found.') . EOL);
        +                if ($json_return) {
        +                    json_return_and_die(['success' => false]);
        +                }
         
        -				if ($json_return) {
        -					json_return_and_die([ 'success' => false ]);
        -				}
        +                goaway(z_root() . '/cloud/' . $which);
        +            }
         
        -				goaway(z_root() . '/cloud/' . $which);
        -			}
        +            $f = array_shift($r);
         
        -			$f = array_shift($r);
        +            if (intval(local_channel()) !== $owner) {
        +                if ($f['creator'] && $f['creator'] !== $ob_hash) {
        +                    notice(t('Permission denied.') . EOL);
         
        -			if (intval(local_channel()) !== $owner) {
        -				if ($f['creator'] && $f['creator'] !== $ob_hash) {
        -					notice( t('Permission denied.') . EOL);
        +                    if ($json_return) {
        +                        json_return_and_die(['success' => false]);
        +                    }
        +                    goaway(z_root() . '/cloud/' . $which);
        +                }
        +            }
         
        -					if ($json_return) {
        -						json_return_and_die([ 'success' => false ]);
        -					}
        -					goaway(z_root() . '/cloud/' . $which);
        -				}
        -			}
        +            $url = get_cloud_url($channel['channel_id'], $channel['channel_address'], $f['hash']);
         
        -			$url = get_cloud_url($channel['channel_id'], $channel['channel_address'], $f['hash']);
        +            attach_delete($owner, $f['hash']);
         
        -			attach_delete($owner, $f['hash']);
        +            if (!$admin_delete) {
        +                $sync = attach_export_data($channel, $f['hash'], true);
        +                if ($sync) {
        +                    Libsync::build_sync_packet($channel['channel_id'], ['file' => [$sync]]);
        +                }
        +            }
         
        -			if (! $admin_delete) {
        -				$sync = attach_export_data($channel, $f['hash'], true);
        -				if ($sync) {
        -					Libsync::build_sync_packet($channel['channel_id'], [ 'file' => [ $sync ] ]);
        -				}
        -			}
        +            if ($json_return) {
        +                json_return_and_die(['success' => true]);
        +            }
         
        -			if ($json_return) {
        -				json_return_and_die([ 'success' => true ]);
        -			}
        -
        -			goaway(dirname($url));
        -		}
        +            goaway(dirname($url));
        +        }
         
         
        -		// Since we have ACL'd files in the wild, but don't have ACL here yet, we
        -		// need to return for anyone other than the owner, despite the perms check for now.
        +        // Since we have ACL'd files in the wild, but don't have ACL here yet, we
        +        // need to return for anyone other than the owner, despite the perms check for now.
         
        -		$is_owner = (((local_channel()) && ($owner  == local_channel())) ? true : false);
        -		if (! ($is_owner || is_site_admin())) {
        -			notice( t('Permission denied.') . EOL );
        -			return;
        -		}
        +        $is_owner = (((local_channel()) && ($owner == local_channel())) ? true : false);
        +        if (!($is_owner || is_site_admin())) {
        +            notice(t('Permission denied.') . EOL);
        +            return;
        +        }
         
         
        -		if (argc() > 3 && argv(3) === 'edit') {
        -			require_once('include/acl_selectors.php');
        -			if (! $perms['write_storage']) {
        -				notice( t('Permission denied.') . EOL);
        -				return;
        -			}
        -			
        -			$file = intval(argv(2));
        +        if (argc() > 3 && argv(3) === 'edit') {
        +            require_once('include/acl_selectors.php');
        +            if (!$perms['write_storage']) {
        +                notice(t('Permission denied.') . EOL);
        +                return;
        +            }
         
        -			$r = q("select id, uid, folder, filename, revision, flags, is_dir, os_storage, hash, allow_cid, allow_gid, deny_cid, deny_gid from attach where id = %d and uid = %d limit 1",
        -				intval($file),
        -				intval($owner)
        -			);
        +            $file = intval(argv(2));
         
        -			$f = array_shift($r);
        -			
        -			$channel = App::get_channel();
        +            $r = q(
        +                "select id, uid, folder, filename, revision, flags, is_dir, os_storage, hash, allow_cid, allow_gid, deny_cid, deny_gid from attach where id = %d and uid = %d limit 1",
        +                intval($file),
        +                intval($owner)
        +            );
         
        -			$cloudpath = get_cloudpath($f);
        +            $f = array_shift($r);
         
        -			$aclselect_e = populate_acl($f, false, \Zotlabs\Lib\PermissionDescription::fromGlobalPermission('view_storage'));
        -			$is_a_dir = (intval($f['is_dir']) ? true : false);
        +            $channel = App::get_channel();
         
        -			$lockstate = (($f['allow_cid'] || $f['allow_gid'] || $f['deny_cid'] || $f['deny_gid']) ? 'lock' : 'unlock');
        +            $cloudpath = get_cloudpath($f);
         
        -			// Encode path that is used for link so it's a valid URL
        -			// Keep slashes as slashes, otherwise mod_rewrite doesn't work correctly
        -			$encoded_path = str_replace('%2F', '/', rawurlencode($cloudpath));
        -			$folder_list = attach_folder_select_list($channel['channel_id']);
        +            $aclselect_e = populate_acl($f, false, PermissionDescription::fromGlobalPermission('view_storage'));
        +            $is_a_dir = (intval($f['is_dir']) ? true : false);
         
        -			$o = replace_macros(get_markup_template('attach_edit.tpl'), [
        -				'$header' => t('Edit file permissions'),
        -				'$file' => $f,
        -				'$cloudpath' => z_root() . '/' . $encoded_path,
        -				'$uid' => $channel['channel_id'],
        -				'$channelnick' => $channel['channel_address'],
        -				'$permissions' => t('Permissions'),
        -				'$aclselect' => $aclselect_e,
        -				'$allow_cid' => acl2json($f['allow_cid']),
        -				'$allow_gid' => acl2json($f['allow_gid']),
        -				'$deny_cid' => acl2json($f['deny_cid']),
        -				'$deny_gid' => acl2json($f['deny_gid']),
        -				'$lockstate' => $lockstate,
        -				'$newname' => [ 'newname', t('Change filename to') , '', t('Leave blank to keep the existing filename') ],
        -				'$newdir'  => [ 'newdir', t('Move to directory'), $f['folder'], '', $folder_list ],
        -				'$permset' => t('Set/edit permissions'),
        -				'$recurse' => [ 'recurse', t('Include all files and sub folders'), 0, '', [ t('No'), t('Yes') ] ],
        -				'$backlink' => t('Return to file list'),
        -				'$isadir' => $is_a_dir,
        -				'$cpdesc' => t('Copy/paste this code to attach file to a post'),
        -				'$cpldesc' => t('Copy/paste this URL to link file from a web page'),
        -				'$submit' => t('Submit'),
        -				'$attach_btn_title' => t('Share this file'),
        -				'$link_btn_title' => t('Show URL to this file'),
        -				'$notify' => [ 'notify_edit', t('Show in your contacts shared folder'), 0, '', [ t('No'), t('Yes') ] ],
        -			]);
        +            $lockstate = (($f['allow_cid'] || $f['allow_gid'] || $f['deny_cid'] || $f['deny_gid']) ? 'lock' : 'unlock');
         
        -			echo $o;
        -			killme();
        -		}
        -		goaway(z_root() . '/cloud/' . $which);
        -	}
        +            // Encode path that is used for link so it's a valid URL
        +            // Keep slashes as slashes, otherwise mod_rewrite doesn't work correctly
        +            $encoded_path = str_replace('%2F', '/', rawurlencode($cloudpath));
        +            $folder_list = attach_folder_select_list($channel['channel_id']);
        +
        +            $o = replace_macros(get_markup_template('attach_edit.tpl'), [
        +                '$header' => t('Edit file permissions'),
        +                '$file' => $f,
        +                '$cloudpath' => z_root() . '/' . $encoded_path,
        +                '$uid' => $channel['channel_id'],
        +                '$channelnick' => $channel['channel_address'],
        +                '$permissions' => t('Permissions'),
        +                '$aclselect' => $aclselect_e,
        +                '$allow_cid' => acl2json($f['allow_cid']),
        +                '$allow_gid' => acl2json($f['allow_gid']),
        +                '$deny_cid' => acl2json($f['deny_cid']),
        +                '$deny_gid' => acl2json($f['deny_gid']),
        +                '$lockstate' => $lockstate,
        +                '$newname' => ['newname', t('Change filename to'), '', t('Leave blank to keep the existing filename')],
        +                '$newdir' => ['newdir', t('Move to directory'), $f['folder'], '', $folder_list],
        +                '$permset' => t('Set/edit permissions'),
        +                '$recurse' => ['recurse', t('Include all files and sub folders'), 0, '', [t('No'), t('Yes')]],
        +                '$backlink' => t('Return to file list'),
        +                '$isadir' => $is_a_dir,
        +                '$cpdesc' => t('Copy/paste this code to attach file to a post'),
        +                '$cpldesc' => t('Copy/paste this URL to link file from a web page'),
        +                '$submit' => t('Submit'),
        +                '$attach_btn_title' => t('Share this file'),
        +                '$link_btn_title' => t('Show URL to this file'),
        +                '$notify' => ['notify_edit', t('Show in your contacts shared folder'), 0, '', [t('No'), t('Yes')]],
        +            ]);
        +
        +            echo $o;
        +            killme();
        +        }
        +        goaway(z_root() . '/cloud/' . $which);
        +    }
         }
        diff --git a/Zotlabs/Module/Finger.php b/Zotlabs/Module/Finger.php
        index b16ce7d88..bdfeff2cb 100644
        --- a/Zotlabs/Module/Finger.php
        +++ b/Zotlabs/Module/Finger.php
        @@ -12,25 +12,25 @@ namespace Zotlabs\Module;
         use Zotlabs\Web\Controller;
         use Zotlabs\Lib\Webfinger;
         
        -class Finger extends Controller {
        +class Finger extends Controller
        +{
         
        -	function get() {
        +    public function get()
        +    {
         
        -		$o = replace_macros(get_markup_template('finger.tpl'), [
        -			'$page_title' => t('Webfinger Diagnostic'),
        -			'$resource'   => [ 'resource', t('Lookup address or URL') , $_GET['resource'], EMPTY_STR ],
        -			'$submit'     => t('Submit')
        -		]);
        -		
        -		if($_GET['resource']) {
        +        $o = replace_macros(get_markup_template('finger.tpl'), [
        +            '$page_title' => t('Webfinger Diagnostic'),
        +            '$resource' => ['resource', t('Lookup address or URL'), $_GET['resource'], EMPTY_STR],
        +            '$submit' => t('Submit')
        +        ]);
         
        -			$resource = trim(escape_tags($_GET['resource']));
        +        if ($_GET['resource']) {
        +            $resource = trim(escape_tags($_GET['resource']));
         
        -			$result = Webfinger::exec($resource);
        -			
        -			$o .= '
        ' . str_replace("\n",'
        ',print_array($result)) . '
        '; - } - return $o; - } - + $result = Webfinger::exec($resource); + + $o .= '
        ' . str_replace("\n", '
        ', print_array($result)) . '
        '; + } + return $o; + } } diff --git a/Zotlabs/Module/Follow.php b/Zotlabs/Module/Follow.php index c667eca82..a97d50b92 100644 --- a/Zotlabs/Module/Follow.php +++ b/Zotlabs/Module/Follow.php @@ -1,4 +1,5 @@ = 2) { - $abook_id = intval(argv(1)); - if(! $abook_id) - return; - - $r = q("select * from abook left join xchan on abook_xchan = xchan_hash where abook_id = %d", - intval($abook_id) - ); - if (! $r) { - return; - } - - $chan = channelx_by_n($r[0]['abook_channel']); - - if (! $chan) { - http_status_exit(404, 'Not found'); - } - - $actor = Activity::encode_person($chan,true,true); - if (! $actor) { - http_status_exit(404, 'Not found'); - } - - // Pleroma requires a unique follow id for every follow and follow response - // instead of our method of re-using the abook_id. This causes issues if they unfollow - // and re-follow so md5 their follow id and slap it on the end so they don't simply discard our - // subsequent accept/reject actions. - - $orig_follow = get_abconfig($chan['channel_id'],$r[0]['xchan_hash'],'activitypub','their_follow_id'); - $orig_follow_type = get_abconfig($chan['channel_id'],$r[0]['xchan_hash'],'activitypub','their_follow_type'); - - as_return_and_die([ - 'id' => z_root() . '/follow/' . $r[0]['abook_id'] . (($orig_follow) ? '/' . md5($orig_follow) : EMPTY_STR), - 'type' => (($orig_follow_type) ? $orig_follow_type : 'Follow'), - 'actor' => $actor, - 'object' => $r[0]['xchan_url'] - ], $chan); - - } + public function init() + { + if (ActivityStreams::is_as_request() && argc() >= 2) { + $abook_id = intval(argv(1)); + if (!$abook_id) { + return; + } - $uid = local_channel(); + $r = q( + "select * from abook left join xchan on abook_xchan = xchan_hash where abook_id = %d", + intval($abook_id) + ); + if (!$r) { + return; + } - if (! $uid) { - return; - } + $chan = channelx_by_n($r[0]['abook_channel']); - $url = notags(trim(punify($_REQUEST['url']))); - $return_url = $_SESSION['return_url']; - $confirm = intval($_REQUEST['confirm']); - $interactive = (($_REQUEST['interactive']) ? intval($_REQUEST['interactive']) : 1); - $channel = App::get_channel(); + if (!$chan) { + http_status_exit(404, 'Not found'); + } - if ((strpos($url,'http') === 0) || strpos($url,'bear:') === 0 || strpos($url,'x-zot:') === 0) { - $n = Activity::fetch($url); - if ($n && isset($n['type']) && ! ActivityStreams::is_an_actor($n['type'])) { - // set client flag to convert objects to implied activities - $a = new ActivityStreams($n,null,true); - if ($a->type === 'Announce' && is_array($a->obj) - && array_key_exists('object',$a->obj) && array_key_exists('actor',$a->obj)) { - // This is a relayed/forwarded Activity (as opposed to a shared/boosted object) - // Reparse the encapsulated Activity and use that instead - logger('relayed activity',LOGGER_DEBUG); - $a = new ActivityStreams($a->obj,null,true); - } + $actor = Activity::encode_person($chan, true, true); + if (!$actor) { + http_status_exit(404, 'Not found'); + } - if ($a->is_valid()) { + // Pleroma requires a unique follow id for every follow and follow response + // instead of our method of re-using the abook_id. This causes issues if they unfollow + // and re-follow so md5 their follow id and slap it on the end so they don't simply discard our + // subsequent accept/reject actions. - if (is_array($a->actor) && array_key_exists('id',$a->actor)) { - Activity::actor_store($a->actor['id'],$a->actor); - } + $orig_follow = get_abconfig($chan['channel_id'], $r[0]['xchan_hash'], 'activitypub', 'their_follow_id'); + $orig_follow_type = get_abconfig($chan['channel_id'], $r[0]['xchan_hash'], 'activitypub', 'their_follow_type'); - // ActivityPub sourced items are cacheable - $item = Activity::decode_note($a,true); - - if ($item) { - Activity::store($channel,get_observer_hash(),$a,$item,true); - - $r = q("select * from item where mid = '%s' and uid = %d", - dbesc($item['mid']), - intval($uid) - ); - if ($r) { - if ($interactive) { - goaway(z_root() . '/display/' . gen_link_id($item['mid'])); - } - else { - $result['success'] = true; - json_return_and_die($result); - } - } - } - } - } - } + as_return_and_die([ + 'id' => z_root() . '/follow/' . $r[0]['abook_id'] . (($orig_follow) ? '/' . md5($orig_follow) : EMPTY_STR), + 'type' => (($orig_follow_type) ? $orig_follow_type : 'Follow'), + 'actor' => $actor, + 'object' => $r[0]['xchan_url'] + ], $chan); + } - - $result = Connect::connect($channel,$url); - - if ($result['success'] == false) { - - if ($result['message']) { - notice($result['message']); - } - if ($interactive) { - goaway($return_url); - } - else { - json_return_and_die($result); - } - } - - info( t('Connection added.') . EOL); - - $clone = []; - foreach ($result['abook'] as $k => $v) { - if (strpos($k,'abook_') === 0) { - $clone[$k] = $v; - } - } - unset($clone['abook_id']); - unset($clone['abook_account']); - unset($clone['abook_channel']); - - $abconfig = load_abconfig($channel['channel_id'],$clone['abook_xchan']); - if ($abconfig) { - $clone['abconfig'] = $abconfig; - } - Libsync::build_sync_packet(0, [ 'abook' => [ $clone ] ], true); - - $can_view_stream = their_perms_contains($channel['channel_id'],$clone['abook_xchan'],'view_stream'); - - // If we can view their stream, pull in some posts - - if (($can_view_stream) || ($result['abook']['xchan_network'] === 'rss')) { - Run::Summon([ 'Onepoll', $result['abook']['abook_id'] ]); - } - - if ($interactive) { - goaway(z_root() . '/connedit/' . $result['abook']['abook_id'] . '?follow=1'); - } - else { - json_return_and_die([ 'success' => true ]); - } - - } - - function get() { - if (! local_channel()) { - return login(); - } - } + + $uid = local_channel(); + + if (!$uid) { + return; + } + + $url = notags(trim(punify($_REQUEST['url']))); + $return_url = $_SESSION['return_url']; + $confirm = intval($_REQUEST['confirm']); + $interactive = (($_REQUEST['interactive']) ? intval($_REQUEST['interactive']) : 1); + $channel = App::get_channel(); + + if ((strpos($url, 'http') === 0) || strpos($url, 'bear:') === 0 || strpos($url, 'x-zot:') === 0) { + $n = Activity::fetch($url); + if ($n && isset($n['type']) && !ActivityStreams::is_an_actor($n['type'])) { + // set client flag to convert objects to implied activities + $a = new ActivityStreams($n, null, true); + if ( + $a->type === 'Announce' && is_array($a->obj) + && array_key_exists('object', $a->obj) && array_key_exists('actor', $a->obj) + ) { + // This is a relayed/forwarded Activity (as opposed to a shared/boosted object) + // Reparse the encapsulated Activity and use that instead + logger('relayed activity', LOGGER_DEBUG); + $a = new ActivityStreams($a->obj, null, true); + } + + if ($a->is_valid()) { + if (is_array($a->actor) && array_key_exists('id', $a->actor)) { + Activity::actor_store($a->actor['id'], $a->actor); + } + + // ActivityPub sourced items are cacheable + $item = Activity::decode_note($a, true); + + if ($item) { + Activity::store($channel, get_observer_hash(), $a, $item, true); + + $r = q( + "select * from item where mid = '%s' and uid = %d", + dbesc($item['mid']), + intval($uid) + ); + if ($r) { + if ($interactive) { + goaway(z_root() . '/display/' . gen_link_id($item['mid'])); + } else { + $result['success'] = true; + json_return_and_die($result); + } + } + } + } + } + } + + + $result = Connect::connect($channel, $url); + + if ($result['success'] == false) { + if ($result['message']) { + notice($result['message']); + } + if ($interactive) { + goaway($return_url); + } else { + json_return_and_die($result); + } + } + + info(t('Connection added.') . EOL); + + $clone = []; + foreach ($result['abook'] as $k => $v) { + if (strpos($k, 'abook_') === 0) { + $clone[$k] = $v; + } + } + unset($clone['abook_id']); + unset($clone['abook_account']); + unset($clone['abook_channel']); + + $abconfig = load_abconfig($channel['channel_id'], $clone['abook_xchan']); + if ($abconfig) { + $clone['abconfig'] = $abconfig; + } + Libsync::build_sync_packet(0, ['abook' => [$clone]], true); + + $can_view_stream = their_perms_contains($channel['channel_id'], $clone['abook_xchan'], 'view_stream'); + + // If we can view their stream, pull in some posts + + if (($can_view_stream) || ($result['abook']['xchan_network'] === 'rss')) { + Run::Summon(['Onepoll', $result['abook']['abook_id']]); + } + + if ($interactive) { + goaway(z_root() . '/connedit/' . $result['abook']['abook_id'] . '?follow=1'); + } else { + json_return_and_die(['success' => true]); + } + } + + public function get() + { + if (!local_channel()) { + return login(); + } + } } diff --git a/Zotlabs/Module/Followers.php b/Zotlabs/Module/Followers.php index e24d73558..19485452d 100644 --- a/Zotlabs/Module/Followers.php +++ b/Zotlabs/Module/Followers.php @@ -10,69 +10,70 @@ use Zotlabs\Web\HTTPSig; use Zotlabs\Web\Controller; use Zotlabs\Lib\Libprofile; -class Followers extends Controller { +class Followers extends Controller +{ - function init() { + public function init() + { - if (observer_prohibited(true)) { - http_status_exit(403, 'Forbidden'); - } + if (observer_prohibited(true)) { + http_status_exit(403, 'Forbidden'); + } - if (argc() < 2) { - http_status_exit(404, 'Not found'); - } + if (argc() < 2) { + http_status_exit(404, 'Not found'); + } - $channel = channelx_by_nick(argv(1)); - if (! $channel) { - http_status_exit(404, 'Not found'); - } + $channel = channelx_by_nick(argv(1)); + if (!$channel) { + http_status_exit(404, 'Not found'); + } -// if (intval($channel['channel_system'])) { -// http_status_exit(403,'Permission denied'); -// } +// if (intval($channel['channel_system'])) { +// http_status_exit(403,'Permission denied'); +// } - Libprofile::load(argv(1)); + Libprofile::load(argv(1)); - $observer_hash = get_observer_hash(); + $observer_hash = get_observer_hash(); - if (((! (is_array(App::$profile) && count(App::$profile))) || (App::$profile['hide_friends']))) { - http_status_exit(403, 'Forbidden'); - } + if (((!(is_array(App::$profile) && count(App::$profile))) || (App::$profile['hide_friends']))) { + http_status_exit(403, 'Forbidden'); + } - if (! perm_is_allowed($channel['channel_id'],$observer_hash,'view_contacts')) { - http_status_exit(403, 'Forbidden'); - } + if (!perm_is_allowed($channel['channel_id'], $observer_hash, 'view_contacts')) { + http_status_exit(403, 'Forbidden'); + } - $t = q("select count(xchan_hash) as total from xchan left join abconfig on abconfig.xchan = xchan_hash left join abook on abook_xchan = xchan_hash where abook_channel = %d and abconfig.chan = %d and abconfig.cat = 'system' and abconfig.k = 'their_perms' and abconfig.v like '%%send_stream%%' and xchan_hash != '%s' and xchan_orphan = 0 and xchan_deleted = 0 and abook_hidden = 0 and abook_pending = 0 and abook_self = 0 ", - intval($channel['channel_id']), - intval($channel['channel_id']), - dbesc($channel['channel_hash']) - ); - if ($t) { - App::set_pager_total($t[0]['total']); - App::set_pager_itemspage(100); - } + $t = q( + "select count(xchan_hash) as total from xchan left join abconfig on abconfig.xchan = xchan_hash left join abook on abook_xchan = xchan_hash where abook_channel = %d and abconfig.chan = %d and abconfig.cat = 'system' and abconfig.k = 'their_perms' and abconfig.v like '%%send_stream%%' and xchan_hash != '%s' and xchan_orphan = 0 and xchan_deleted = 0 and abook_hidden = 0 and abook_pending = 0 and abook_self = 0 ", + intval($channel['channel_id']), + intval($channel['channel_id']), + dbesc($channel['channel_hash']) + ); + if ($t) { + App::set_pager_total($t[0]['total']); + App::set_pager_itemspage(100); + } - if(App::$pager['unset'] && intval($t[0]['total']) > 100) { - $ret = Activity::paged_collection_init($t[0]['total'],App::$query_string); - } - else { - $pager_sql = sprintf(" LIMIT %d OFFSET %d ", intval(App::$pager['itemspage']), intval(App::$pager['start'])); + if (App::$pager['unset'] && intval($t[0]['total']) > 100) { + $ret = Activity::paged_collection_init($t[0]['total'], App::$query_string); + } else { + $pager_sql = sprintf(" LIMIT %d OFFSET %d ", intval(App::$pager['itemspage']), intval(App::$pager['start'])); - $r = q("select * from xchan left join abconfig on abconfig.xchan = xchan_hash left join abook on abook_xchan = xchan_hash where abook_channel = %d and abconfig.chan = %d and abconfig.cat = 'system' and abconfig.k = 'their_perms' and abconfig.v like '%%send_stream%%' and xchan_hash != '%s' and xchan_orphan = 0 and xchan_deleted = 0 and abook_hidden = 0 and abook_pending = 0 and abook_self = 0 $pager_sql", - intval($channel['channel_id']), - intval($channel['channel_id']), - dbesc($channel['channel_hash']) - ); + $r = q( + "select * from xchan left join abconfig on abconfig.xchan = xchan_hash left join abook on abook_xchan = xchan_hash where abook_channel = %d and abconfig.chan = %d and abconfig.cat = 'system' and abconfig.k = 'their_perms' and abconfig.v like '%%send_stream%%' and xchan_hash != '%s' and xchan_orphan = 0 and xchan_deleted = 0 and abook_hidden = 0 and abook_pending = 0 and abook_self = 0 $pager_sql", + intval($channel['channel_id']), + intval($channel['channel_id']), + dbesc($channel['channel_hash']) + ); - $ret = Activity::encode_follow_collection($r, App::$query_string, 'OrderedCollection', $t[0]['total']); - } - - if (ActivityStreams::is_as_request()) { - as_return_and_die($ret,$channel); - } - - } + $ret = Activity::encode_follow_collection($r, App::$query_string, 'OrderedCollection', $t[0]['total']); + } + if (ActivityStreams::is_as_request()) { + as_return_and_die($ret, $channel); + } + } } diff --git a/Zotlabs/Module/Following.php b/Zotlabs/Module/Following.php index 1ecd3ceb9..ec6171add 100644 --- a/Zotlabs/Module/Following.php +++ b/Zotlabs/Module/Following.php @@ -1,4 +1,5 @@ 100) { - $ret = Activity::paged_collection_init($t[0]['total'],App::$query_string); - } - else { - $pager_sql = sprintf(" LIMIT %d OFFSET %d ", intval(App::$pager['itemspage']), intval(App::$pager['start'])); + if (App::$pager['unset'] && $t[0]['total'] > 100) { + $ret = Activity::paged_collection_init($t[0]['total'], App::$query_string); + } else { + $pager_sql = sprintf(" LIMIT %d OFFSET %d ", intval(App::$pager['itemspage']), intval(App::$pager['start'])); - $r = q("select * from xchan left join abconfig on abconfig.xchan = xchan_hash left join abook on abook_xchan = xchan_hash where abook_channel = %d and abconfig.chan = %d and abconfig.cat = 'system' and abconfig.k = 'my_perms' and abconfig.v like '%%send_stream%%' and xchan_hash != '%s' and xchan_orphan = 0 and xchan_deleted = 0 and abook_hidden = 0 and abook_pending = 0 and abook_self = 0 $pager_sql", - intval($channel['channel_id']), - intval($channel['channel_id']), - dbesc($channel['channel_hash']) - ); + $r = q( + "select * from xchan left join abconfig on abconfig.xchan = xchan_hash left join abook on abook_xchan = xchan_hash where abook_channel = %d and abconfig.chan = %d and abconfig.cat = 'system' and abconfig.k = 'my_perms' and abconfig.v like '%%send_stream%%' and xchan_hash != '%s' and xchan_orphan = 0 and xchan_deleted = 0 and abook_hidden = 0 and abook_pending = 0 and abook_self = 0 $pager_sql", + intval($channel['channel_id']), + intval($channel['channel_id']), + dbesc($channel['channel_hash']) + ); - $ret = Activity::encode_follow_collection($r, App::$query_string, 'OrderedCollection', $t[0]['total']); - } - - if (ActivityStreams::is_as_request()) { - as_return_and_die($ret,$channel); - } - } + $ret = Activity::encode_follow_collection($r, App::$query_string, 'OrderedCollection', $t[0]['total']); + } + if (ActivityStreams::is_as_request()) { + as_return_and_die($ret, $channel); + } + } } diff --git a/Zotlabs/Module/Future.php b/Zotlabs/Module/Future.php index 215e5434d..1cf4b28bf 100644 --- a/Zotlabs/Module/Future.php +++ b/Zotlabs/Module/Future.php @@ -6,16 +6,16 @@ use Zotlabs\Lib\Apps; use Zotlabs\Lib\Libsync; use Zotlabs\Web\Controller; -class Future extends Controller { +class Future extends Controller +{ - function get() { + public function get() + { $desc = t('This app allows you to set an optional publish date/time for posts, which may be in the future. This must be at least ten minutes into the future to initiate delayed publishing. The posts will be published automatically after that time has passed. Once installed, a new button will appear in the post editor to set the date/time.'); $text = ''; - return $text; - - } - + return $text; + } } diff --git a/Zotlabs/Module/Getfile.php b/Zotlabs/Module/Getfile.php index e1541bff7..a2cd7c40f 100644 --- a/Zotlabs/Module/Getfile.php +++ b/Zotlabs/Module/Getfile.php @@ -1,4 +1,5 @@ 1) { + $verify_hash = argv(1); + if ($verify_hash !== $resource) { + logger('resource mismatch'); + killme(); + } + } - if(argc() > 1) { - $verify_hash = argv(1); - if($verify_hash !== $resource) { - logger('resource mismatch'); - killme(); - } - } + if (!$hash) { + logger('no sender hash'); + killme(); + } - if(! $hash) { - logger('no sender hash'); - killme(); - } + foreach (['REDIRECT_REMOTE_USER', 'HTTP_AUTHORIZATION'] as $head) { + if (array_key_exists($head, $_SERVER) && substr(trim($_SERVER[$head]), 0, 9) === 'Signature') { + if ($head !== 'HTTP_AUTHORIZATION') { + $_SERVER['HTTP_AUTHORIZATION'] = $_SERVER[$head]; + continue; + } - foreach([ 'REDIRECT_REMOTE_USER', 'HTTP_AUTHORIZATION' ] as $head) { - if(array_key_exists($head,$_SERVER) && substr(trim($_SERVER[$head]),0,9) === 'Signature') { - if($head !== 'HTTP_AUTHORIZATION') { - $_SERVER['HTTP_AUTHORIZATION'] = $_SERVER[$head]; - continue; - } + $verified = HTTPSig::verify(''); + if ($verified && $verified['header_signed'] && $verified['header_valid']) { + $r = q( + "select hubloc_hash from hubloc where hubloc_id_url = '%s' or hubloc_addr = '%s' limit 1", + dbesc($verified['signer']), + dbesc(str_replace('acct:', '', $verified['signer'])) + ); + if ($r && $r[0]['hubloc_hash'] === $hash) { + $header_verified = true; + } + } + } + } - $verified = HTTPSig::verify(''); - if($verified && $verified['header_signed'] && $verified['header_valid']) { - $r = q("select hubloc_hash from hubloc where hubloc_id_url = '%s' or hubloc_addr = '%s' limit 1", - dbesc($verified['signer']), - dbesc(str_replace('acct:','',$verified['signer'])) - ); - if($r && $r[0]['hubloc_hash'] === $hash) { - $header_verified = true; - } - } - } - } + if (!$header_verified) { + http_status_exit(403, 'Permission denied'); + } - if(! $header_verified) { - http_status_exit(403,'Permission denied'); - } + $channel = channelx_by_hash($hash); - $channel = channelx_by_hash($hash); + if (!$channel) { + logger('error: missing info'); + killme(); + } - if(! $channel) { - logger('error: missing info'); - killme(); - } - - if($resolution > 0) { - $r = q("select * from photo where resource_id = '%s' and uid = %d and imgscale = %d limit 1", - dbesc($resource), - intval($channel['channel_id']), - intval($resolution) - ); - if($r) { - header('Content-type: ' . $r[0]['mimetype']); + if ($resolution > 0) { + $r = q( + "select * from photo where resource_id = '%s' and uid = %d and imgscale = %d limit 1", + dbesc($resource), + intval($channel['channel_id']), + intval($resolution) + ); + if ($r) { + header('Content-type: ' . $r[0]['mimetype']); - if(intval($r[0]['os_storage'])) { - $fname = dbunescbin($r[0]['content']); - if(strpos($fname,'store') !== false) - $istream = fopen($fname,'rb'); - else - $istream = fopen('store/' . $channel['channel_address'] . '/' . $fname,'rb'); - $ostream = fopen('php://output','wb'); - if($istream && $ostream) { - pipe_streams($istream,$ostream); - fclose($istream); - fclose($ostream); - } - } - else { - echo dbunescbin($r[0]['content']); - } - } - killme(); - } + if (intval($r[0]['os_storage'])) { + $fname = dbunescbin($r[0]['content']); + if (strpos($fname, 'store') !== false) { + $istream = fopen($fname, 'rb'); + } else { + $istream = fopen('store/' . $channel['channel_address'] . '/' . $fname, 'rb'); + } + $ostream = fopen('php://output', 'wb'); + if ($istream && $ostream) { + pipe_streams($istream, $ostream); + fclose($istream); + fclose($ostream); + } + } else { + echo dbunescbin($r[0]['content']); + } + } + killme(); + } - $r = attach_by_hash($resource,$channel['channel_hash'],$revision); - - if(! $r['success']) { - logger('attach_by_hash failed: ' . $r['message']); - notice( $r['message'] . EOL); - return; - } - - header('Content-type: ' . $r['data']['filetype']); - header('Content-Disposition: attachment; filename="' . $r['data']['filename'] . '"'); - if(intval($r['data']['os_storage'])) { - $fname = dbunescbin($r['data']['content']); - if(strpos($fname,'store') !== false) - $istream = fopen($fname,'rb'); - else - $istream = fopen('store/' . $channel['channel_address'] . '/' . $fname,'rb'); - $ostream = fopen('php://output','wb'); - if($istream && $ostream) { - pipe_streams($istream,$ostream); - fclose($istream); - fclose($ostream); - } - } - else { - echo dbunescbin($r['data']['content']); - } - killme(); - } + $r = attach_by_hash($resource, $channel['channel_hash'], $revision); + + if (!$r['success']) { + logger('attach_by_hash failed: ' . $r['message']); + notice($r['message'] . EOL); + return; + } + + header('Content-type: ' . $r['data']['filetype']); + header('Content-Disposition: attachment; filename="' . $r['data']['filename'] . '"'); + if (intval($r['data']['os_storage'])) { + $fname = dbunescbin($r['data']['content']); + if (strpos($fname, 'store') !== false) { + $istream = fopen($fname, 'rb'); + } else { + $istream = fopen('store/' . $channel['channel_address'] . '/' . $fname, 'rb'); + } + $ostream = fopen('php://output', 'wb'); + if ($istream && $ostream) { + pipe_streams($istream, $ostream); + fclose($istream); + fclose($ostream); + } + } else { + echo dbunescbin($r['data']['content']); + } + killme(); + } } diff --git a/Zotlabs/Module/Hashtags.php b/Zotlabs/Module/Hashtags.php index f31e86ab7..9b8ee35f4 100644 --- a/Zotlabs/Module/Hashtags.php +++ b/Zotlabs/Module/Hashtags.php @@ -9,25 +9,29 @@ namespace Zotlabs\Module; use Zotlabs\Web\Controller; -class Hashtags extends Controller { +class Hashtags extends Controller +{ - function init() { - $result = []; + public function init() + { + $result = []; - $t = escape_tags($_REQUEST['t']); - if(! $t) - json_return_and_die($result); + $t = escape_tags($_REQUEST['t']); + if (!$t) { + json_return_and_die($result); + } - $r = q("select distinct(term) from term where term like '%s' and ttype = %d order by term", - dbesc($t . '%'), - intval(TERM_HASHTAG) - ); - if($r) { - foreach($r as $rv) { - $result[] = [ 'text' => $rv['term'] ]; - } - } + $r = q( + "select distinct(term) from term where term like '%s' and ttype = %d order by term", + dbesc($t . '%'), + intval(TERM_HASHTAG) + ); + if ($r) { + foreach ($r as $rv) { + $result[] = ['text' => $rv['term']]; + } + } - json_return_and_die($result); - } -} \ No newline at end of file + json_return_and_die($result); + } +} diff --git a/Zotlabs/Module/Hcard.php b/Zotlabs/Module/Hcard.php index a2ffc15b4..14f078021 100644 --- a/Zotlabs/Module/Hcard.php +++ b/Zotlabs/Module/Hcard.php @@ -1,76 +1,77 @@ 1) - $which = argv(1); - else { - notice( t('Requested profile is not available.') . EOL ); - \App::$error = 404; - return; - } - - logger('hcard_request: ' . $which, LOGGER_DEBUG); + public function init() + { - $profile = ''; - $channel = \App::get_channel(); - - if((local_channel()) && (argc() > 2) && (argv(2) === 'view')) { - $which = $channel['channel_address']; - $profile = argv(1); - $r = q("select profile_guid from profile where id = %d and uid = %d limit 1", - intval($profile), - intval(local_channel()) - ); - if(! $r) - $profile = ''; - $profile = $r[0]['profile_guid']; - } - - head_add_link( [ - 'rel' => 'alternate', - 'type' => 'application/atom+xml', - 'title' => t('Posts and comments'), - 'href' => z_root() . '/feed/' . $which - ]); + if (argc() > 1) { + $which = argv(1); + } else { + notice(t('Requested profile is not available.') . EOL); + App::$error = 404; + return; + } - head_add_link( [ - 'rel' => 'alternate', - 'type' => 'application/atom+xml', - 'title' => t('Only posts'), - 'href' => z_root() . '/feed/' . $which . '?f=&top=1' - ]); + logger('hcard_request: ' . $which, LOGGER_DEBUG); - - if(! $profile) { - $x = q("select channel_id as profile_uid from channel where channel_address = '%s' limit 1", - dbesc(argv(1)) - ); - if($x) { - \App::$profile = $x[0]; - } - } - - Libprofile::load($which,$profile); - - - } - - - function get() { + $profile = ''; + $channel = App::get_channel(); - $x = new \Zotlabs\Widget\Profile(); - return $x->widget([]); - - } - - - + if ((local_channel()) && (argc() > 2) && (argv(2) === 'view')) { + $which = $channel['channel_address']; + $profile = argv(1); + $r = q( + "select profile_guid from profile where id = %d and uid = %d limit 1", + intval($profile), + intval(local_channel()) + ); + if (!$r) { + $profile = ''; + } + $profile = $r[0]['profile_guid']; + } + + head_add_link([ + 'rel' => 'alternate', + 'type' => 'application/atom+xml', + 'title' => t('Posts and comments'), + 'href' => z_root() . '/feed/' . $which + ]); + + head_add_link([ + 'rel' => 'alternate', + 'type' => 'application/atom+xml', + 'title' => t('Only posts'), + 'href' => z_root() . '/feed/' . $which . '?f=&top=1' + ]); + + + if (!$profile) { + $x = q( + "select channel_id as profile_uid from channel where channel_address = '%s' limit 1", + dbesc(argv(1)) + ); + if ($x) { + App::$profile = $x[0]; + } + } + + Libprofile::load($which, $profile); + } + + + public function get() + { + + $x = new \Zotlabs\Widget\Profile(); + return $x->widget([]); + } } diff --git a/Zotlabs/Module/Help.php b/Zotlabs/Module/Help.php index 27c0c928f..151d70c64 100644 --- a/Zotlabs/Module/Help.php +++ b/Zotlabs/Module/Help.php @@ -1,4 +1,5 @@ '; - $o .= '
        '; - $o .= '

        ' . t('Documentation Search') . ' - ' . htmlspecialchars($_REQUEST['search']) . '

        '; - $o .= '
        '; - $o .= '
        '; + if ($_REQUEST['search']) { + $o .= '
        '; + $o .= '
        '; + $o .= '

        ' . t('Documentation Search') . ' - ' . htmlspecialchars($_REQUEST['search']) . '

        '; + $o .= '
        '; + $o .= '
        '; - $r = search_doc_files($_REQUEST['search']); - if($r) { - $o .= '
          '; - foreach($r as $rr) { - $dirname = dirname($rr['v']); - $fname = basename($rr['v']); - $fname = substr($fname, 0, strrpos($fname, '.')); - $path = trim(substr($dirname, 4), '/'); + $r = search_doc_files($_REQUEST['search']); + if ($r) { + $o .= '
            '; + foreach ($r as $rr) { + $dirname = dirname($rr['v']); + $fname = basename($rr['v']); + $fname = substr($fname, 0, strrpos($fname, '.')); + $path = trim(substr($dirname, 4), '/'); - $o .= '
          • ' . ucwords(str_replace('_',' ',notags($fname))) . '
            ' - . '' . 'help/' . (($path) ? $path . '/' : '') . $fname . '
            ' - . '...' . str_replace('$Projectname', System::get_platform_name(), $rr['text']) . '...

          • '; - } - $o .= '
          '; - $o .= '
        '; - $o .= '
        '; - } + $o .= '
      • ' . ucwords(str_replace('_', ' ', notags($fname))) . '
        ' + . '' . 'help/' . (($path) ? $path . '/' : '') . $fname . '
        ' + . '...' . str_replace('$Projectname', System::get_platform_name(), $rr['text']) . '...

      • '; + } + $o .= '
      '; + $o .= ''; + $o .= ''; + } - 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)); - - // dump the picture and stop the script - readfile($realpath); - killme(); - } - - if (argc() === 1) { - $files = self::listdir('doc'); - - if ($files) { - foreach ($files as $file) { - if ((! strpos($file,'/site/')) && file_exists(str_replace('doc/','doc/site/',$file))) { - continue; - } - if (strpos($file,'README')) { - continue; - } - if (preg_match('/\/(..|..\-..)\//',$file,$matches)) { - $language = $matches[1]; - } - else { - $language = t('Unknown language'); - } - if ($language === substr(App::$language,0,2)) { - $language = ''; - } - - $link = str_replace( [ 'doc/', '.mc' ], [ 'help/', '' ], $file); - if (strpos($link,'/global/') !== false || strpos($link,'/media/') !== false) { - continue; - } - $content .= '' . (($language) ? " [$language]" : '') . EOL; - } - } - } - else { - $content = get_help_content(); - } - - - return replace_macros(get_markup_template('help.tpl'), array( - '$title' => t('$Projectname Documentation'), - '$tocHeading' => t('Contents'), - '$content' => $content, - '$heading' => $heading, - '$language' => $language - )); - } - - static function listdir($path) { - $results = []; - $handle = opendir($path); - if (! $handle) { - return $results; - } - while (false !== ($file = readdir($handle))) { - if ($file === '.' || $file === '..') { - continue; - } - if (is_dir($path . '/' . $file)) { - $results = array_merge($results, self::listdir($path . '/' . $file)); - } - else { - $results[] = $path . '/' . $file; - } - } - closedir($handle); - return $results; - } + 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)); + + // dump the picture and stop the script + readfile($realpath); + killme(); + } + + if (argc() === 1) { + $files = self::listdir('doc'); + + if ($files) { + foreach ($files as $file) { + if ((!strpos($file, '/site/')) && file_exists(str_replace('doc/', 'doc/site/', $file))) { + continue; + } + if (strpos($file, 'README')) { + continue; + } + if (preg_match('/\/(..|..\-..)\//', $file, $matches)) { + $language = $matches[1]; + } else { + $language = t('Unknown language'); + } + if ($language === substr(App::$language, 0, 2)) { + $language = ''; + } + + $link = str_replace(['doc/', '.mc'], ['help/', ''], $file); + if (strpos($link, '/global/') !== false || strpos($link, '/media/') !== false) { + continue; + } + $content .= '' . (($language) ? " [$language]" : '') . EOL; + } + } + } else { + $content = get_help_content(); + } + + + return replace_macros(get_markup_template('help.tpl'), array( + '$title' => t('$Projectname Documentation'), + '$tocHeading' => t('Contents'), + '$content' => $content, + '$heading' => $heading, + '$language' => $language + )); + } + + public static function listdir($path) + { + $results = []; + $handle = opendir($path); + if (!$handle) { + return $results; + } + while (false !== ($file = readdir($handle))) { + if ($file === '.' || $file === '..') { + continue; + } + if (is_dir($path . '/' . $file)) { + $results = array_merge($results, self::listdir($path . '/' . $file)); + } else { + $results[] = $path . '/' . $file; + } + } + closedir($handle); + return $results; + } } diff --git a/Zotlabs/Module/Home.php b/Zotlabs/Module/Home.php index dd78352b5..e41ff8311 100644 --- a/Zotlabs/Module/Home.php +++ b/Zotlabs/Module/Home.php @@ -1,4 +1,5 @@ [ - ACTIVITYSTREAMS_JSONLD_REV, - 'https://w3id.org/security/v1', - Activity::ap_schema() - ]], Activity::encode_site() ); + if (ActivityStreams::is_as_request()) { + $x = array_merge(['@context' => [ + ACTIVITYSTREAMS_JSONLD_REV, + 'https://w3id.org/security/v1', + Activity::ap_schema() + ]], Activity::encode_site()); - $headers = []; - $headers['Content-Type'] = 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"' ; - $x['signature'] = LDSignatures::sign($x,[ 'channel_address' => z_root(), 'channel_prvkey' => get_config('system','prvkey') ]); - $ret = json_encode($x, JSON_UNESCAPED_SLASHES); - logger('data: ' . jindent($ret), LOGGER_DATA); - $headers['Date'] = datetime_convert('UTC','UTC', 'now', 'D, d M Y H:i:s \\G\\M\\T'); - $headers['Digest'] = HTTPSig::generate_digest_header($ret); - $headers['(request-target)'] = strtolower($_SERVER['REQUEST_METHOD']) . ' ' . $_SERVER['REQUEST_URI']; + $headers = []; + $headers['Content-Type'] = 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"'; + $x['signature'] = LDSignatures::sign($x, ['channel_address' => z_root(), 'channel_prvkey' => get_config('system', 'prvkey')]); + $ret = json_encode($x, JSON_UNESCAPED_SLASHES); + logger('data: ' . jindent($ret), LOGGER_DATA); + $headers['Date'] = datetime_convert('UTC', 'UTC', 'now', 'D, d M Y H:i:s \\G\\M\\T'); + $headers['Digest'] = HTTPSig::generate_digest_header($ret); + $headers['(request-target)'] = strtolower($_SERVER['REQUEST_METHOD']) . ' ' . $_SERVER['REQUEST_URI']; - $h = HTTPSig::create_sig($headers,get_config('system','prvkey'),z_root()); - HTTPSig::set_headers($h); + $h = HTTPSig::create_sig($headers, get_config('system', 'prvkey'), z_root()); + HTTPSig::set_headers($h); - echo $ret; - killme(); - } + echo $ret; + killme(); + } - if (Libzot::is_zot_request()) { + if (Libzot::is_zot_request()) { + $channel = get_sys_channel(); + $sigdata = HTTPSig::verify(file_get_contents('php://input'), EMPTY_STR, 'zot6'); - $channel = get_sys_channel(); - $sigdata = HTTPSig::verify(file_get_contents('php://input'), EMPTY_STR, 'zot6'); + if ($sigdata && $sigdata['signer'] && $sigdata['header_valid']) { + $data = json_encode(Libzot::zotinfo(['guid_hash' => $channel['channel_hash'], 'target_url' => $sigdata['signer']])); + $s = q( + "select site_crypto, hubloc_sitekey from site left join hubloc on hubloc_url = site_url where hubloc_id_url = '%s' and hubloc_network in ('zot6','nomad') limit 1", + dbesc($sigdata['signer']) + ); - if($sigdata && $sigdata['signer'] && $sigdata['header_valid']) { - $data = json_encode(Libzot::zotinfo([ 'guid_hash' => $channel['channel_hash'], 'target_url' => $sigdata['signer'] ])); - $s = q("select site_crypto, hubloc_sitekey from site left join hubloc on hubloc_url = site_url where hubloc_id_url = '%s' and hubloc_network in ('nomad','zot6') limit 1", - dbesc($sigdata['signer']) - ); + if ($s && $s[0]['hubloc_sitekey'] && $s[0]['site_crypto']) { + $data = json_encode(Crypto::encapsulate($data, $s[0]['hubloc_sitekey'], Libzot::best_algorithm($s[0]['site_crypto']))); + } + } else { + $data = json_encode(Libzot::zotinfo(['guid_hash' => $channel['channel_hash']])); + } - if($s && $s[0]['hubloc_sitekey'] && $s[0]['site_crypto']) { - $data = json_encode(Crypto::encapsulate($data,$s[0]['hubloc_sitekey'],Libzot::best_algorithm($s[0]['site_crypto']))); - } - } - else { - $data = json_encode(Libzot::zotinfo([ 'guid_hash' => $channel['channel_hash'] ])); - } - - $headers = [ - 'Content-Type' => 'application/x-nomad+json', - 'Digest' => HTTPSig::generate_digest_header($data), - '(request-target)' => strtolower($_SERVER['REQUEST_METHOD']) . ' ' . $_SERVER['REQUEST_URI'] - ]; - $h = HTTPSig::create_sig($headers,get_config('system','prvkey'),z_root()); - HTTPSig::set_headers($h); - echo $data; - killme(); - } + $headers = [ + 'Content-Type' => 'application/x-nomad+json', + 'Digest' => HTTPSig::generate_digest_header($data), + '(request-target)' => strtolower($_SERVER['REQUEST_METHOD']) . ' ' . $_SERVER['REQUEST_URI'] + ]; + $h = HTTPSig::create_sig($headers, get_config('system', 'prvkey'), z_root()); + HTTPSig::set_headers($h); + echo $data; + killme(); + } - $splash = ((argc() > 1 && argv(1) === 'splash') ? true : false); - - $channel = App::get_channel(); - if (local_channel() && $channel && $channel['xchan_url'] && ! $splash) { - $dest = $channel['channel_startpage']; - if (! $dest) { - $dest = get_pconfig(local_channel(),'system','startpage'); - } - if (! $dest) { - $dest = get_config('system','startpage'); - } - if (! $dest) { - $dest = z_root() . '/stream'; - } - goaway($dest); - } + $splash = ((argc() > 1 && argv(1) === 'splash') ? true : false); - if (remote_channel() && (! $splash) && $_SESSION['atoken']) { - $r = q("select * from atoken where atoken_id = %d", - intval($_SESSION['atoken']) - ); - if ($r) { - $x = channelx_by_n($r[0]['atoken_uid']); - if ($x) { - goaway(z_root() . '/channel/' . $x['channel_address']); - } - } - } + $channel = App::get_channel(); + if (local_channel() && $channel && $channel['xchan_url'] && !$splash) { + $dest = $channel['channel_startpage']; + if (!$dest) { + $dest = get_pconfig(local_channel(), 'system', 'startpage'); + } + if (!$dest) { + $dest = get_config('system', 'startpage'); + } + if (!$dest) { + $dest = z_root() . '/stream'; + } + goaway($dest); + } - - if (get_account_id() && ! $splash) { - goaway(z_root() . '/new_channel'); - } - - } - - - function get() { - - $o = EMPTY_STR; - - if (x($_SESSION,'theme')) { - unset($_SESSION['theme']); - } - if (x($_SESSION,'mobile_theme')) { - unset($_SESSION['mobile_theme']); - } - - $splash = ((argc() > 1 && argv(1) === 'splash') ? true : false); - - call_hooks('home_content',$o); - if ($o) { - return $o; - } - - $frontpage = get_config('system','frontpage'); - if ($frontpage) { - if (strpos($frontpage,'include:') !== false) { - $file = trim(str_replace('include:' , '', $frontpage)); - if (file_exists($file)) { - App::$page['template'] = 'full'; - App::$page['title'] = t('$Projectname'); - $o .= file_get_contents($file); - return $o; - } - } - if (strpos($frontpage,'http') !== 0) { - $frontpage = z_root() . '/' . $frontpage; - } - if (intval(get_config('system','mirror_frontpage'))) { - $o = '' . t('$Projectname') . ''; - echo $o; - killme(); - } - goaway($frontpage); - } - - - $sitename = get_config('system','sitename'); - if ($sitename) { - $o .= '

      ' . sprintf( t('Welcome to %s') ,$sitename) . '

      '; - } - - $loginbox = get_config('system','login_on_homepage'); - if (intval($loginbox) || $loginbox === false) { - $o .= login(true); - } - - return $o; - } + if (remote_channel() && (!$splash) && $_SESSION['atoken']) { + $r = q( + "select * from atoken where atoken_id = %d", + intval($_SESSION['atoken']) + ); + if ($r) { + $x = channelx_by_n($r[0]['atoken_uid']); + if ($x) { + goaway(z_root() . '/channel/' . $x['channel_address']); + } + } + } + + + if (get_account_id() && !$splash) { + goaway(z_root() . '/new_channel'); + } + } + + + public function get() + { + + $o = EMPTY_STR; + + if (x($_SESSION, 'theme')) { + unset($_SESSION['theme']); + } + if (x($_SESSION, 'mobile_theme')) { + unset($_SESSION['mobile_theme']); + } + + $splash = ((argc() > 1 && argv(1) === 'splash') ? true : false); + + call_hooks('home_content', $o); + if ($o) { + return $o; + } + + $frontpage = get_config('system', 'frontpage'); + if ($frontpage) { + if (strpos($frontpage, 'include:') !== false) { + $file = trim(str_replace('include:', '', $frontpage)); + if (file_exists($file)) { + App::$page['template'] = 'full'; + App::$page['title'] = t('$Projectname'); + $o .= file_get_contents($file); + return $o; + } + } + if (strpos($frontpage, 'http') !== 0) { + $frontpage = z_root() . '/' . $frontpage; + } + if (intval(get_config('system', 'mirror_frontpage'))) { + $o = '' . t('$Projectname') . ''; + echo $o; + killme(); + } + goaway($frontpage); + } + + + $sitename = get_config('system', 'sitename'); + if ($sitename) { + $o .= '

      ' . sprintf(t('Welcome to %s'), $sitename) . '

      '; + } + + $loginbox = get_config('system', 'login_on_homepage'); + if (intval($loginbox) || $loginbox === false) { + $o .= login(true); + } + + return $o; + } } diff --git a/Zotlabs/Module/Hostxrd.php b/Zotlabs/Module/Hostxrd.php index c1d5cf0e9..2aa569c64 100644 --- a/Zotlabs/Module/Hostxrd.php +++ b/Zotlabs/Module/Hostxrd.php @@ -1,25 +1,29 @@ \App::get_hostname(), - '$zroot' => z_root() - )); - $arr = array('xrd' => $x); - call_hooks('hostxrd',$arr); - - echo $arr['xrd']; - killme(); - } - + public function init() + { + session_write_close(); + header('Access-Control-Allow-Origin: *'); + header("Content-type: application/xrd+xml"); + logger('hostxrd', LOGGER_DEBUG); + + $tpl = get_markup_template('xrd_host.tpl'); + $x = replace_macros(get_markup_template('xrd_host.tpl'), array( + '$zhost' => App::get_hostname(), + '$zroot' => z_root() + )); + $arr = array('xrd' => $x); + call_hooks('hostxrd', $arr); + + echo $arr['xrd']; + killme(); + } } diff --git a/Zotlabs/Module/Hq.php b/Zotlabs/Module/Hq.php index b6fd52f4c..0e715a93f 100644 --- a/Zotlabs/Module/Hq.php +++ b/Zotlabs/Module/Hq.php @@ -1,4 +1,5 @@ loading) { + $_SESSION['loadtime_hq'] = datetime_convert(); + } - if($this->loading) - $_SESSION['loadtime_hq'] = datetime_convert(); - - if(argc() > 1 && argv(1) !== 'load') { - $item_hash = argv(1); - } - - if($_REQUEST['mid']) - $item_hash = $_REQUEST['mid']; + if (argc() > 1 && argv(1) !== 'load') { + $item_hash = argv(1); + } - $item_normal = item_normal(); - $item_normal_update = item_normal_update(); + if ($_REQUEST['mid']) { + $item_hash = $_REQUEST['mid']; + } - if(! $item_hash) { - $r = q("SELECT mid FROM item + $item_normal = item_normal(); + $item_normal_update = item_normal_update(); + + if (!$item_hash) { + $r = q( + "SELECT mid FROM item WHERE uid = %d $item_normal AND mid = parent_mid ORDER BY created DESC LIMIT 1", - intval(local_channel()) - ); + intval(local_channel()) + ); - if($r[0]['mid']) { - $item_hash = gen_link_id($r[0]['mid']); - } - } + if ($r[0]['mid']) { + $item_hash = gen_link_id($r[0]['mid']); + } + } - if($item_hash) { + if ($item_hash) { + $item_hash = unpack_link_id($item_hash); - $item_hash = unpack_link_id($item_hash); + $target_item = null; - $target_item = null; + $r = q( + "select id, uid, mid, parent_mid, thr_parent, verb, item_type, item_deleted, item_blocked from item where mid like '%s' limit 1", + dbesc($item_hash . '%') + ); - $r = q("select id, uid, mid, parent_mid, thr_parent, verb, item_type, item_deleted, item_blocked from item where mid like '%s' limit 1", - dbesc($item_hash . '%') - ); - - if($r) { - $target_item = $r[0]; - } + if ($r) { + $target_item = $r[0]; + } - //if the item is to be moderated redirect to /moderate - if($target_item['item_blocked'] == ITEM_MODERATED) { - goaway(z_root() . '/moderate/' . $target_item['id']); - } - - $static = ((array_key_exists('static',$_REQUEST)) ? intval($_REQUEST['static']) : 0); + //if the item is to be moderated redirect to /moderate + if ($target_item['item_blocked'] == ITEM_MODERATED) { + goaway(z_root() . '/moderate/' . $target_item['id']); + } - $simple_update = (($this->updating) ? " AND item_unseen = 1 " : ''); - - if($this->updating && $_SESSION['loadtime_hq']) - $simple_update = " AND item.changed > '" . datetime_convert('UTC','UTC',$_SESSION['loadtime_hq']) . "' "; - - if($static && $simple_update) - $simple_update .= " and item_thread_top = 0 and author_xchan = '" . protect_sprintf(get_observer_hash()) . "' "; + $static = ((array_key_exists('static', $_REQUEST)) ? intval($_REQUEST['static']) : 0); - $sys = get_sys_channel(); - $sql_extra = item_permissions_sql($sys['channel_id']); + $simple_update = (($this->updating) ? " AND item_unseen = 1 " : ''); - $sys_item = false; + if ($this->updating && $_SESSION['loadtime_hq']) { + $simple_update = " AND item.changed > '" . datetime_convert('UTC', 'UTC', $_SESSION['loadtime_hq']) . "' "; + } - } - - if(! $this->updating) { - $channel = App::get_channel(); + if ($static && $simple_update) { + $simple_update .= " and item_thread_top = 0 and author_xchan = '" . protect_sprintf(get_observer_hash()) . "' "; + } - $channel_acl = [ - 'allow_cid' => $channel['channel_allow_cid'], - 'allow_gid' => $channel['channel_allow_gid'], - 'deny_cid' => $channel['channel_deny_cid'], - 'deny_gid' => $channel['channel_deny_gid'] - ]; + $sys = get_sys_channel(); + $sql_extra = item_permissions_sql($sys['channel_id']); - $x = [ - 'is_owner' => true, - 'allow_location' => ((intval(get_pconfig($channel['channel_id'],'system','use_browser_location'))) ? '1' : ''), - 'default_location' => $channel['channel_location'], - 'nickname' => $channel['channel_address'], - 'lockstate' => (($group || $cid || $channel['channel_allow_cid'] || $channel['channel_allow_gid'] || $channel['channel_deny_cid'] || $channel['channel_deny_gid']) ? 'lock' : 'unlock'), - 'acl' => populate_acl($channel_acl,true, PermissionDescription::fromGlobalPermission('view_stream'), get_post_aclDialogDescription(), 'acl_dialog_post'), - 'permissions' => $channel_acl, - 'bang' => '', - 'visitor' => true, - 'profile_uid' => local_channel(), - 'return_path' => 'hq', - 'expanded' => true, - 'editor_autocomplete' => true, - 'bbco_autocomplete' => 'bbcode', - 'bbcode' => true, - 'jotnets' => true, - 'reset' => t('Reset form') - ]; + $sys_item = false; + } - $o = replace_macros(get_markup_template("hq.tpl"), - [ - '$no_messages' => (($target_item) ? false : true), - '$no_messages_label' => [ t('Welcome to $Projectname!'), t('You have got no unseen posts...') ], - '$editor' => status_editor($x) - ] - ); + if (!$this->updating) { + $channel = App::get_channel(); - } + $channel_acl = [ + 'allow_cid' => $channel['channel_allow_cid'], + 'allow_gid' => $channel['channel_allow_gid'], + 'deny_cid' => $channel['channel_deny_cid'], + 'deny_gid' => $channel['channel_deny_gid'] + ]; - if(! $this->updating && ! $this->loading) { + $x = [ + 'is_owner' => true, + 'allow_location' => ((intval(get_pconfig($channel['channel_id'], 'system', 'use_browser_location'))) ? '1' : ''), + 'default_location' => $channel['channel_location'], + 'nickname' => $channel['channel_address'], + 'lockstate' => (($group || $cid || $channel['channel_allow_cid'] || $channel['channel_allow_gid'] || $channel['channel_deny_cid'] || $channel['channel_deny_gid']) ? 'lock' : 'unlock'), + 'acl' => populate_acl($channel_acl, true, PermissionDescription::fromGlobalPermission('view_stream'), get_post_aclDialogDescription(), 'acl_dialog_post'), + 'permissions' => $channel_acl, + 'bang' => '', + 'visitor' => true, + 'profile_uid' => local_channel(), + 'return_path' => 'hq', + 'expanded' => true, + 'editor_autocomplete' => true, + 'bbco_autocomplete' => 'bbcode', + 'bbcode' => true, + 'jotnets' => true, + 'reset' => t('Reset form') + ]; - nav_set_selected('HQ'); + $o = replace_macros( + get_markup_template("hq.tpl"), + [ + '$no_messages' => (($target_item) ? false : true), + '$no_messages_label' => [t('Welcome to $Projectname!'), t('You have got no unseen posts...')], + '$editor' => status_editor($x) + ] + ); + } - $static = ((local_channel()) ? channel_manual_conv_update(local_channel()) : 1); + if (!$this->updating && !$this->loading) { + nav_set_selected('HQ'); - if($target_item) { - // if the target item is not a post (eg a like) we want to address its thread parent - $mid = ((($target_item['verb'] == ACTIVITY_LIKE) || ($target_item['verb'] == ACTIVITY_DISLIKE)) ? $target_item['thr_parent'] : $target_item['mid']); + $static = ((local_channel()) ? channel_manual_conv_update(local_channel()) : 1); - // if we got a decoded hash we must encode it again before handing to javascript - $mid = gen_link_id($mid); - } - else { - $mid = ''; - } + if ($target_item) { + // if the target item is not a post (eg a like) we want to address its thread parent + $mid = ((($target_item['verb'] == ACTIVITY_LIKE) || ($target_item['verb'] == ACTIVITY_DISLIKE)) ? $target_item['thr_parent'] : $target_item['mid']); - $o .= '
      ' . "\r\n"; - $o .= "\r\n"; - - App::$page['htmlhead'] .= replace_macros(get_markup_template("build_query.tpl"),[ - '$baseurl' => z_root(), - '$pgtype' => 'hq', - '$uid' => local_channel(), - '$gid' => '0', - '$cid' => '0', - '$cmin' => '0', - '$cmax' => '99', - '$star' => '0', - '$liked' => '0', - '$conv' => '0', - '$spam' => '0', - '$fh' => '0', - '$dm' => '0', - '$nouveau' => '0', - '$wall' => '0', - '$draft' => '0', - '$static' => $static, - '$page' => 1, - '$list' => ((x($_REQUEST,'list')) ? intval($_REQUEST['list']) : 0), - '$search' => '', - '$xchan' => '', - '$order' => '', - '$file' => '', - '$cats' => '', - '$tags' => '', - '$dend' => '', - '$dbegin' => '', - '$verb' => '', - '$net' => '', - '$mid' => (($mid) ? urlencode($mid) : '') - ]); - } + // if we got a decoded hash we must encode it again before handing to javascript + $mid = gen_link_id($mid); + } else { + $mid = ''; + } - $updateable = false; + $o .= '
      ' . "\r\n"; + $o .= "\r\n"; - if($this->loading && $target_item) { - $r = null; + App::$page['htmlhead'] .= replace_macros(get_markup_template("build_query.tpl"), [ + '$baseurl' => z_root(), + '$pgtype' => 'hq', + '$uid' => local_channel(), + '$gid' => '0', + '$cid' => '0', + '$cmin' => '0', + '$cmax' => '99', + '$star' => '0', + '$liked' => '0', + '$conv' => '0', + '$spam' => '0', + '$fh' => '0', + '$dm' => '0', + '$nouveau' => '0', + '$wall' => '0', + '$draft' => '0', + '$static' => $static, + '$page' => 1, + '$list' => ((x($_REQUEST, 'list')) ? intval($_REQUEST['list']) : 0), + '$search' => '', + '$xchan' => '', + '$order' => '', + '$file' => '', + '$cats' => '', + '$tags' => '', + '$dend' => '', + '$dbegin' => '', + '$verb' => '', + '$net' => '', + '$mid' => (($mid) ? urlencode($mid) : '') + ]); + } - $r = q("SELECT item.id AS item_id FROM item + $updateable = false; + + if ($this->loading && $target_item) { + $r = null; + + $r = q( + "SELECT item.id AS item_id FROM item WHERE uid = %d AND mid = '%s' $item_normal LIMIT 1", - intval(local_channel()), - dbesc($target_item['parent_mid']) - ); + intval(local_channel()), + dbesc($target_item['parent_mid']) + ); - if($r) { - $updateable = true; - } + if ($r) { + $updateable = true; + } - if(!$r) { - $sys_item = true; + if (!$r) { + $sys_item = true; - $r = q("SELECT item.id AS item_id FROM item + $r = q( + "SELECT item.id AS item_id FROM item LEFT JOIN abook ON item.author_xchan = abook.abook_xchan WHERE mid = '%s' AND item.uid = %d $item_normal AND (abook.abook_blocked = 0 or abook.abook_flags is null) $sql_extra LIMIT 1", - dbesc($target_item['parent_mid']), - intval($sys['channel_id']) - ); - } - } - elseif($this->updating && $target_item) { - $r = null; + dbesc($target_item['parent_mid']), + intval($sys['channel_id']) + ); + } + } elseif ($this->updating && $target_item) { + $r = null; - $r = q("SELECT item.parent AS item_id FROM item + $r = q( + "SELECT item.parent AS item_id FROM item WHERE uid = %d AND parent_mid = '%s' $item_normal_update $simple_update LIMIT 1", - intval(local_channel()), - dbesc($target_item['parent_mid']) - ); + intval(local_channel()), + dbesc($target_item['parent_mid']) + ); - if($r) { - $updateable = true; - } + if ($r) { + $updateable = true; + } - if(!$r) { - $sys_item = true; + if (!$r) { + $sys_item = true; - $r = q("SELECT item.parent AS item_id FROM item + $r = q( + "SELECT item.parent AS item_id FROM item LEFT JOIN abook ON item.author_xchan = abook.abook_xchan WHERE mid = '%s' AND item.uid = %d $item_normal_update $simple_update AND (abook.abook_blocked = 0 or abook.abook_flags is null) $sql_extra LIMIT 1", - dbesc($target_item['parent_mid']), - intval($sys['channel_id']) - ); - } + dbesc($target_item['parent_mid']), + intval($sys['channel_id']) + ); + } - $_SESSION['loadtime_hq'] = datetime_convert(); - } - else { - $r = []; - } - - if($r) { - $items = q("SELECT item.*, item.id AS item_id + $_SESSION['loadtime_hq'] = datetime_convert(); + } else { + $r = []; + } + + if ($r) { + $items = q( + "SELECT item.*, item.id AS item_id FROM item WHERE parent = '%s' $item_normal ", - dbesc($r[0]['item_id']) - ); - - xchan_query($items,true,(($sys_item) ? local_channel() : 0)); - $items = fetch_post_tags($items,true); - $items = conv_sort($items,'created'); - } - else { - $items = []; - } + dbesc($r[0]['item_id']) + ); - $o .= conversation($items, 'hq', $this->updating, 'client'); + xchan_query($items, true, (($sys_item) ? local_channel() : 0)); + $items = fetch_post_tags($items, true); + $items = conv_sort($items, 'created'); + } else { + $items = []; + } - if($updateable) { - $x = q("UPDATE item SET item_unseen = 0 WHERE item_unseen = 1 AND uid = %d AND parent = %d ", - intval(local_channel()), - intval($r[0]['item_id']) - ); - } + $o .= conversation($items, 'hq', $this->updating, 'client'); - $o .= '
      '; + if ($updateable) { + $x = q( + "UPDATE item SET item_unseen = 0 WHERE item_unseen = 1 AND uid = %d AND parent = %d ", + intval(local_channel()), + intval($r[0]['item_id']) + ); + } - return $o; - - } + $o .= '
      '; + return $o; + } } diff --git a/Zotlabs/Module/Id.php b/Zotlabs/Module/Id.php index 1bac99eeb..41e2ada25 100644 --- a/Zotlabs/Module/Id.php +++ b/Zotlabs/Module/Id.php @@ -7,8 +7,8 @@ namespace Zotlabs\Module; * Controller for responding to x-zot: protocol requests * x-zot:_jkfRG85nJ-714zn-LW_VbTFW8jSjGAhAydOcJzHxqHkvEHWG2E0RbA_pbch-h4R63RG1YJZifaNzgccoLa3MQ/453c1678-1a79-4af7-ab65-6b012f6cab77 - * - */ + * + */ use Zotlabs\Lib\Libsync; use Zotlabs\Lib\Activity; @@ -27,94 +27,92 @@ require_once('include/bbcode.php'); require_once('include/security.php'); -class Id extends Controller { +class Id extends Controller +{ - function init() { + public function init() + { - if (Libzot::is_zot_request()) { + if (Libzot::is_zot_request()) { + $conversation = false; - $conversation = false; + $request_portable_id = argv(1); + if (argc() > 2) { + $item_id = argv(2); + } - $request_portable_id = argv(1); - if(argc() > 2) { - $item_id = argv(2); - } + $portable_id = EMPTY_STR; - $portable_id = EMPTY_STR; + $sigdata = HTTPSig::verify(EMPTY_STR); + if ($sigdata['portable_id'] && $sigdata['header_valid']) { + $portable_id = $sigdata['portable_id']; + } - $sigdata = HTTPSig::verify(EMPTY_STR); - if ($sigdata['portable_id'] && $sigdata['header_valid']) { - $portable_id = $sigdata['portable_id']; - } + $chan = channelx_by_hash($request_portable_id); - $chan = channelx_by_hash($request_portable_id); - - if ($chan) { - $channel_id = $chan['channel_id']; - if (! $item_id) { - $handler = new Channel(); - App::$argc = 2; - App::$argv[0] = 'channel'; - App::$argv[1] = $chan['channel_address']; - $handler->init(); - } - } - else { - http_status_exit(404, 'Not found'); - } + if ($chan) { + $channel_id = $chan['channel_id']; + if (!$item_id) { + $handler = new Channel(); + App::$argc = 2; + App::$argv[0] = 'channel'; + App::$argv[1] = $chan['channel_address']; + $handler->init(); + } + } else { + http_status_exit(404, 'Not found'); + } - $item_normal = " and item.item_hidden = 0 and item.item_type = 0 and item.item_unpublished = 0 and item.item_delayed = 0 and item.item_blocked = 0 "; + $item_normal = " and item.item_hidden = 0 and item.item_type = 0 and item.item_unpublished = 0 and item.item_delayed = 0 and item.item_blocked = 0 "; - $sql_extra = item_permissions_sql(0); + $sql_extra = item_permissions_sql(0); - $r = q("select * from item where uuid = '%s' $item_normal $sql_extra and uid = %d limit 1", - dbesc($item_id), - intval($channel_id) - ); - if(! $r) { + $r = q( + "select * from item where uuid = '%s' $item_normal $sql_extra and uid = %d limit 1", + dbesc($item_id), + intval($channel_id) + ); + if (!$r) { + $r = q( + "select * from item where uuid = '%s' $item_normal and uid = %d limit 1", + dbesc($item_id), + intval($channel_id) + ); + if ($r) { + http_status_exit(403, 'Forbidden'); + } + http_status_exit(404, 'Not found'); + } - $r = q("select * from item where uuid = '%s' $item_normal and uid = %d limit 1", - dbesc($item_id), - intval($channel_id) - ); - if($r) { - http_status_exit(403, 'Forbidden'); - } - http_status_exit(404, 'Not found'); - } + if (!perm_is_allowed($chan['channel_id'], get_observer_hash(), 'view_stream')) { + http_status_exit(403, 'Forbidden'); + } - if(! perm_is_allowed($chan['channel_id'],get_observer_hash(),'view_stream')) - http_status_exit(403, 'Forbidden'); + xchan_query($r, true); + $items = fetch_post_tags($r, true); - xchan_query($r,true); - $items = fetch_post_tags($r,true); + $i = Activity::encode_item($items[0], (get_config('system', 'activitypub', ACTIVITYPUB_ENABLED) ? true : false)); - $i = Activity::encode_item($items[0],( get_config('system','activitypub', ACTIVITYPUB_ENABLED) ? true : false )); + if (!$i) { + http_status_exit(404, 'Not found'); + } - if(! $i) - http_status_exit(404, 'Not found'); - - $x = array_merge(['@context' => [ - ACTIVITYSTREAMS_JSONLD_REV, - 'https://w3id.org/security/v1', - Activity::ap_schema() - ]], $i); - - $headers = []; - $headers['Content-Type'] = 'application/x-nomad+json' ; - $ret = json_encode($x, JSON_UNESCAPED_SLASHES); - $headers['Digest'] = HTTPSig::generate_digest_header($ret); - $headers['(request-target)'] = strtolower($_SERVER['REQUEST_METHOD']) . ' ' . $_SERVER['REQUEST_URI']; - $h = HTTPSig::create_sig($headers,$chan['channel_prvkey'],channel_url($chan)); - HTTPSig::set_headers($h); - echo $ret; - killme(); - - } - - } + $x = array_merge(['@context' => [ + ACTIVITYSTREAMS_JSONLD_REV, + 'https://w3id.org/security/v1', + Activity::ap_schema() + ]], $i); + $headers = []; + $headers['Content-Type'] = 'application/x-nomad+json'; + $ret = json_encode($x, JSON_UNESCAPED_SLASHES); + $headers['Digest'] = HTTPSig::generate_digest_header($ret); + $headers['(request-target)'] = strtolower($_SERVER['REQUEST_METHOD']) . ' ' . $_SERVER['REQUEST_URI']; + $h = HTTPSig::create_sig($headers, $chan['channel_prvkey'], channel_url($chan)); + HTTPSig::set_headers($h); + echo $ret; + killme(); + } + } } - - diff --git a/Zotlabs/Module/Impel.php b/Zotlabs/Module/Impel.php index 48d7b49e9..d82417dd0 100644 --- a/Zotlabs/Module/Impel.php +++ b/Zotlabs/Module/Impel.php @@ -1,197 +1,215 @@ false); - - if(! local_channel()) - json_return_and_die($ret); - - logger('impel: ' . print_r($_REQUEST,true), LOGGER_DATA); - - $elm = $_REQUEST['element']; - $x = base64url_decode($elm); - if(! $x) - json_return_and_die($ret); - - $j = json_decode($x,true); - if(! $j) - json_return_and_die($ret); - - // logger('element: ' . print_r($j,true)); + public function init() + { - $channel = \App::get_channel(); - - $arr = []; - $is_menu = false; - - // a portable menu has its links rewritten with the local baseurl - $portable_menu = false; - - switch($j['type']) { - case 'webpage': - $arr['item_type'] = ITEM_TYPE_WEBPAGE; - $namespace = 'WEBPAGE'; - $installed_type = t('webpage'); - break; - case 'block': - $arr['item_type'] = ITEM_TYPE_BLOCK; - $namespace = 'BUILDBLOCK'; - $installed_type = t('block'); - break; - case 'layout': - $arr['item_type'] = ITEM_TYPE_PDL; - $namespace = 'PDL'; - $installed_type = t('layout'); - break; - case 'portable-menu': - $portable_menu = true; - // fall through - case 'menu': - $is_menu = true; - $installed_type = t('menu'); - break; - default: - logger('mod_impel: unrecognised element type' . print_r($j,true)); - break; - } - - if($is_menu) { - $m = []; - $m['menu_channel_id'] = local_channel(); - $m['menu_name'] = $j['pagetitle']; - $m['menu_desc'] = $j['desc']; - if($j['created']) - $m['menu_created'] = datetime_convert($j['created']); - if($j['edited']) - $m['menu_edited'] = datetime_convert($j['edited']); - - $m['menu_flags'] = 0; - if($j['flags']) { - if(in_array('bookmark',$j['flags'])) - $m['menu_flags'] |= MENU_BOOKMARK; - if(in_array('system',$j['flags'])) - $m['menu_flags'] |= MENU_SYSTEM; - - } - - $menu_id = menu_create($m); - - if($menu_id) { - if(is_array($j['items'])) { - foreach($j['items'] as $it) { - $mitem = []; - - $mitem['mitem_link'] = str_replace('[channelurl]',z_root() . '/channel/' . $channel['channel_address'],$it['link']); - $mitem['mitem_link'] = str_replace('[pageurl]',z_root() . '/page/' . $channel['channel_address'],$it['link']); - $mitem['mitem_link'] = str_replace('[cloudurl]',z_root() . '/cloud/' . $channel['channel_address'],$it['link']); - $mitem['mitem_link'] = str_replace('[baseurl]',z_root(),$it['link']); + $ret = array('success' => false); - $mitem['mitem_desc'] = escape_tags($it['desc']); - $mitem['mitem_order'] = intval($it['order']); - if(is_array($it['flags'])) { - $mitem['mitem_flags'] = 0; - if(in_array('zid',$it['flags'])) - $mitem['mitem_flags'] |= MENU_ITEM_ZID; - if(in_array('new-window',$it['flags'])) - $mitem['mitem_flags'] |= MENU_ITEM_NEWWIN; - if(in_array('chatroom',$it['flags'])) - $mitem['mitem_flags'] |= MENU_ITEM_CHATROOM; - } - menu_add_item($menu_id,local_channel(),$mitem); - } - if($j['edited']) { - $x = q("update menu set menu_edited = '%s' where menu_id = %d and menu_channel_id = %d", - dbesc(datetime_convert('UTC','UTC',$j['edited'])), - intval($menu_id), - intval(local_channel()) - ); - } - } - $ret['success'] = true; - } - $x = $ret; - } - else { - $arr['uid'] = local_channel(); - $arr['aid'] = $channel['channel_account_id']; - $arr['title'] = $j['title']; - $arr['body'] = $j['body']; - $arr['term'] = $j['term']; - $arr['layout_mid'] = $j['layout_mid']; - $arr['created'] = datetime_convert('UTC','UTC', $j['created']); - $arr['edited'] = datetime_convert('UTC','UTC',$j['edited']); - $arr['owner_xchan'] = get_observer_hash(); - $arr['author_xchan'] = (($j['author_xchan']) ? $j['author_xchan'] : get_observer_hash()); - $arr['mimetype'] = (($j['mimetype']) ? $j['mimetype'] : 'text/x-multicode'); - - if(! $j['mid']) { - $j['uuid'] = new_uuid(); - $j['mid'] = z_root() . '/item/' . $j['uuid']; - } - - $arr['uuid'] = $j['uuid']; - $arr['mid'] = $arr['parent_mid'] = $j['mid']; - - - if($j['pagetitle']) { - $pagetitle = strtolower(\URLify::transliterate($j['pagetitle'])); - } - - // Verify ability to use html or php!!! - - $execflag = ((intval($channel['channel_id']) == intval(local_channel()) && ($channel['channel_pageflags'] & PAGE_ALLOWCODE)) ? true : false); + if (!local_channel()) { + json_return_and_die($ret); + } - $i = q("select id, edited, item_deleted from item where mid = '%s' and uid = %d limit 1", - dbesc($arr['mid']), - intval(local_channel()) - ); + logger('impel: ' . print_r($_REQUEST, true), LOGGER_DATA); - \Zotlabs\Lib\IConfig::Set($arr,'system',$namespace,(($pagetitle) ? $pagetitle : substr($arr['mid'],0,16)),true); - - if($i) { - $arr['id'] = $i[0]['id']; - // don't update if it has the same timestamp as the original - if($arr['edited'] > $i[0]['edited']) - $x = item_store_update($arr,$execflag); - } - else { - if(($i) && (intval($i[0]['item_deleted']))) { - // was partially deleted already, finish it off - q("delete from item where mid = '%s' and uid = %d", - dbesc($arr['mid']), - intval(local_channel()) - ); - } - else - $x = item_store($arr,$execflag); - } - - if($x && $x['success']) { - $item_id = $x['item_id']; - } - } - - if($x['success']) { - $ret['success'] = true; - info( sprintf( t('%s element installed'), $installed_type)); - } - else { - notice( sprintf( t('%s element installation failed'), $installed_type)); - } - - //??? should perhaps return ret? + $elm = $_REQUEST['element']; + $x = base64url_decode($elm); + if (!$x) { + json_return_and_die($ret); + } - json_return_and_die(true); - - } - + $j = json_decode($x, true); + if (!$j) { + json_return_and_die($ret); + } + + // logger('element: ' . print_r($j,true)); + + $channel = App::get_channel(); + + $arr = []; + $is_menu = false; + + // a portable menu has its links rewritten with the local baseurl + $portable_menu = false; + + switch ($j['type']) { + case 'webpage': + $arr['item_type'] = ITEM_TYPE_WEBPAGE; + $namespace = 'WEBPAGE'; + $installed_type = t('webpage'); + break; + case 'block': + $arr['item_type'] = ITEM_TYPE_BLOCK; + $namespace = 'BUILDBLOCK'; + $installed_type = t('block'); + break; + case 'layout': + $arr['item_type'] = ITEM_TYPE_PDL; + $namespace = 'PDL'; + $installed_type = t('layout'); + break; + case 'portable-menu': + $portable_menu = true; + // fall through + case 'menu': + $is_menu = true; + $installed_type = t('menu'); + break; + default: + logger('mod_impel: unrecognised element type' . print_r($j, true)); + break; + } + + if ($is_menu) { + $m = []; + $m['menu_channel_id'] = local_channel(); + $m['menu_name'] = $j['pagetitle']; + $m['menu_desc'] = $j['desc']; + if ($j['created']) { + $m['menu_created'] = datetime_convert($j['created']); + } + if ($j['edited']) { + $m['menu_edited'] = datetime_convert($j['edited']); + } + + $m['menu_flags'] = 0; + if ($j['flags']) { + if (in_array('bookmark', $j['flags'])) { + $m['menu_flags'] |= MENU_BOOKMARK; + } + if (in_array('system', $j['flags'])) { + $m['menu_flags'] |= MENU_SYSTEM; + } + } + + $menu_id = menu_create($m); + + if ($menu_id) { + if (is_array($j['items'])) { + foreach ($j['items'] as $it) { + $mitem = []; + + $mitem['mitem_link'] = str_replace('[channelurl]', z_root() . '/channel/' . $channel['channel_address'], $it['link']); + $mitem['mitem_link'] = str_replace('[pageurl]', z_root() . '/page/' . $channel['channel_address'], $it['link']); + $mitem['mitem_link'] = str_replace('[cloudurl]', z_root() . '/cloud/' . $channel['channel_address'], $it['link']); + $mitem['mitem_link'] = str_replace('[baseurl]', z_root(), $it['link']); + + $mitem['mitem_desc'] = escape_tags($it['desc']); + $mitem['mitem_order'] = intval($it['order']); + if (is_array($it['flags'])) { + $mitem['mitem_flags'] = 0; + if (in_array('zid', $it['flags'])) { + $mitem['mitem_flags'] |= MENU_ITEM_ZID; + } + if (in_array('new-window', $it['flags'])) { + $mitem['mitem_flags'] |= MENU_ITEM_NEWWIN; + } + if (in_array('chatroom', $it['flags'])) { + $mitem['mitem_flags'] |= MENU_ITEM_CHATROOM; + } + } + menu_add_item($menu_id, local_channel(), $mitem); + } + if ($j['edited']) { + $x = q( + "update menu set menu_edited = '%s' where menu_id = %d and menu_channel_id = %d", + dbesc(datetime_convert('UTC', 'UTC', $j['edited'])), + intval($menu_id), + intval(local_channel()) + ); + } + } + $ret['success'] = true; + } + $x = $ret; + } else { + $arr['uid'] = local_channel(); + $arr['aid'] = $channel['channel_account_id']; + $arr['title'] = $j['title']; + $arr['body'] = $j['body']; + $arr['term'] = $j['term']; + $arr['layout_mid'] = $j['layout_mid']; + $arr['created'] = datetime_convert('UTC', 'UTC', $j['created']); + $arr['edited'] = datetime_convert('UTC', 'UTC', $j['edited']); + $arr['owner_xchan'] = get_observer_hash(); + $arr['author_xchan'] = (($j['author_xchan']) ? $j['author_xchan'] : get_observer_hash()); + $arr['mimetype'] = (($j['mimetype']) ? $j['mimetype'] : 'text/x-multicode'); + + if (!$j['mid']) { + $j['uuid'] = new_uuid(); + $j['mid'] = z_root() . '/item/' . $j['uuid']; + } + + $arr['uuid'] = $j['uuid']; + $arr['mid'] = $arr['parent_mid'] = $j['mid']; + + + if ($j['pagetitle']) { + $pagetitle = strtolower(URLify::transliterate($j['pagetitle'])); + } + + // Verify ability to use html or php!!! + + $execflag = ((intval($channel['channel_id']) == intval(local_channel()) && ($channel['channel_pageflags'] & PAGE_ALLOWCODE)) ? true : false); + + $i = q( + "select id, edited, item_deleted from item where mid = '%s' and uid = %d limit 1", + dbesc($arr['mid']), + intval(local_channel()) + ); + + IConfig::Set($arr, 'system', $namespace, (($pagetitle) ? $pagetitle : substr($arr['mid'], 0, 16)), true); + + if ($i) { + $arr['id'] = $i[0]['id']; + // don't update if it has the same timestamp as the original + if ($arr['edited'] > $i[0]['edited']) { + $x = item_store_update($arr, $execflag); + } + } else { + if (($i) && (intval($i[0]['item_deleted']))) { + // was partially deleted already, finish it off + q( + "delete from item where mid = '%s' and uid = %d", + dbesc($arr['mid']), + intval(local_channel()) + ); + } else { + $x = item_store($arr, $execflag); + } + } + + if ($x && $x['success']) { + $item_id = $x['item_id']; + } + } + + if ($x['success']) { + $ret['success'] = true; + info(sprintf(t('%s element installed'), $installed_type)); + } else { + notice(sprintf(t('%s element installation failed'), $installed_type)); + } + + //??? should perhaps return ret? + + json_return_and_die(true); + } } diff --git a/Zotlabs/Module/Import.php b/Zotlabs/Module/Import.php index b1c38724f..0e3dc379c 100644 --- a/Zotlabs/Module/Import.php +++ b/Zotlabs/Module/Import.php @@ -9,10 +9,8 @@ use Zotlabs\Web\HTTPSig; use Zotlabs\Lib\Libzot; use Zotlabs\Lib\Connect; use Zotlabs\Daemon\Run; - use Zotlabs\Import\Friendica; - require_once('include/import.php'); require_once('include/photo_factory.php'); @@ -23,691 +21,686 @@ require_once('include/photo_factory.php'); * Import a channel, either by direct file upload or via * connection to another server. */ - -class Import extends Controller { +class Import extends Controller +{ - /** - * @brief Import channel into account. - * - * @param int $account_id - */ - function import_account($account_id) { + /** + * @brief Import channel into account. + * + * @param int $account_id + */ + public function import_account($account_id) + { - if (! $account_id) { - logger('No account ID supplied'); - return; - } + if (!$account_id) { + logger('No account ID supplied'); + return; + } - $max_friends = account_service_class_fetch($account_id,'total_channels'); - $max_feeds = account_service_class_fetch($account_id,'total_feeds'); - $data = null; - $seize = ((x($_REQUEST,'make_primary')) ? intval($_REQUEST['make_primary']) : 0); - $import_posts = ((x($_REQUEST,'import_posts')) ? intval($_REQUEST['import_posts']) : 0); - $moving = false; // intval($_REQUEST['moving']); - $src = $_FILES['filename']['tmp_name']; - $filename = basename($_FILES['filename']['name']); - $filesize = intval($_FILES['filename']['size']); - $filetype = $_FILES['filename']['type']; - $newname = trim(strtolower($_REQUEST['newname'])); + $max_friends = account_service_class_fetch($account_id, 'total_channels'); + $max_feeds = account_service_class_fetch($account_id, 'total_feeds'); + $data = null; + $seize = ((x($_REQUEST, 'make_primary')) ? intval($_REQUEST['make_primary']) : 0); + $import_posts = ((x($_REQUEST, 'import_posts')) ? intval($_REQUEST['import_posts']) : 0); + $moving = false; // intval($_REQUEST['moving']); + $src = $_FILES['filename']['tmp_name']; + $filename = basename($_FILES['filename']['name']); + $filesize = intval($_FILES['filename']['size']); + $filetype = $_FILES['filename']['type']; + $newname = trim(strtolower($_REQUEST['newname'])); - // import channel from file - if ($src) { + // import channel from file + if ($src) { + // This is OS specific and could also fail if your tmpdir isn't very + // large mostly used for Diaspora which exports gzipped files. - // This is OS specific and could also fail if your tmpdir isn't very - // large mostly used for Diaspora which exports gzipped files. + if (strpos($filename, '.gz')) { + @rename($src, $src . '.gz'); + @system('gunzip ' . escapeshellarg($src . '.gz')); + } - if (strpos($filename,'.gz')){ - @rename($src,$src . '.gz'); - @system('gunzip ' . escapeshellarg($src . '.gz')); - } + if ($filesize) { + $data = @file_get_contents($src); + } + unlink($src); + } - if ($filesize) { - $data = @file_get_contents($src); - } - unlink($src); - } + // import channel from another server + if (!$src) { + $old_address = ((x($_REQUEST, 'old_address')) ? $_REQUEST['old_address'] : ''); + if (!$old_address) { + logger('Nothing to import.'); + notice(t('Nothing to import.') . EOL); + return; + } elseif (strpos($old_address, '@')) { + // if you copy the identity address from your profile page, make it work for convenience - WARNING: this is a utf-8 variant and NOT an ASCII ampersand. Please do not edit. + $old_address = str_replace('@', '@', $old_address); + } - // import channel from another server - if (! $src) { - $old_address = ((x($_REQUEST,'old_address')) ? $_REQUEST['old_address'] : ''); - if (! $old_address) { - logger('Nothing to import.'); - notice( t('Nothing to import.') . EOL); - return; - } - elseif (strpos($old_address, '@')) { - // if you copy the identity address from your profile page, make it work for convenience - WARNING: this is a utf-8 variant and NOT an ASCII ampersand. Please do not edit. - $old_address = str_replace('@', '@', $old_address); - } + $email = ((x($_REQUEST, 'email')) ? $_REQUEST['email'] : ''); + $password = ((x($_REQUEST, 'password')) ? $_REQUEST['password'] : ''); - $email = ((x($_REQUEST,'email')) ? $_REQUEST['email'] : ''); - $password = ((x($_REQUEST,'password')) ? $_REQUEST['password'] : ''); + $channelname = substr($old_address, 0, strpos($old_address, '@')); + $servername = substr($old_address, strpos($old_address, '@') + 1); - $channelname = substr($old_address,0,strpos($old_address,'@')); - $servername = substr($old_address,strpos($old_address,'@')+1); + $api_path = probe_api_path($servername); + if (!$api_path) { + notice(t('Unable to download data from old server') . EOL); + return; + } - $api_path = probe_api_path($servername); - if (! $api_path) { - notice( t('Unable to download data from old server') . EOL); - return; - } + $api_path .= 'channel/export/basic?f=&zap_compat=1&channel=' . $channelname; + if ($import_posts) { + $api_path .= '&posts=1'; + } + $binary = false; + $redirects = 0; + $opts = ['http_auth' => $email . ':' . $password]; + $ret = z_fetch_url($api_path, $binary, $redirects, $opts); + if ($ret['success']) { + $data = $ret['body']; + } else { + notice(t('Unable to download data from old server') . EOL); + return; + } + } - $api_path .= 'channel/export/basic?f=&zap_compat=1&channel=' . $channelname; - if ($import_posts) { - $api_path .= '&posts=1'; - } - $binary = false; - $redirects = 0; - $opts = [ 'http_auth' => $email . ':' . $password ]; - $ret = z_fetch_url($api_path, $binary, $redirects, $opts); - if ($ret['success']) { - $data = $ret['body']; - } - else { - notice( t('Unable to download data from old server') . EOL); - return; - } - } + if (!$data) { + logger('Empty import file.'); + notice(t('Imported file is empty.') . EOL); + return; + } - if (! $data) { - logger('Empty import file.'); - notice( t('Imported file is empty.') . EOL); - return; - } + $data = json_decode($data, true); - $data = json_decode($data,true); - - //logger('import: data: ' . print_r($data,true)); - //print_r($data); + //logger('import: data: ' . print_r($data,true)); + //print_r($data); - // handle Friendica export - - if (array_path_exists('user/parent-uid',$data)) { + // handle Friendica export - $settings = [ 'account_id' => $account_id, 'sieze' => 1, 'newname' => $newname ]; - $f = new Friendica($data, $settings); - - return; - } + if (array_path_exists('user/parent-uid', $data)) { + $settings = ['account_id' => $account_id, 'sieze' => 1, 'newname' => $newname]; + $f = new Friendica($data, $settings); - if (! array_key_exists('compatibility',$data)) { - call_hooks('import_foreign_channel_data',$data); - if ($data['handled']) { - return; - } - } + return; + } - $codebase = 'zap'; + if (!array_key_exists('compatibility', $data)) { + call_hooks('import_foreign_channel_data', $data); + if ($data['handled']) { + return; + } + } - if ((! array_path_exists('compatibility/codebase',$data)) || $data['compatibility']['codebase'] !== $codebase) { - notice('Data export format is not compatible with this software'); - return; - } + $codebase = 'zap'; - if ($moving) { - $seize = 1; - } + if ((!array_path_exists('compatibility/codebase', $data)) || $data['compatibility']['codebase'] !== $codebase) { + notice('Data export format is not compatible with this software'); + return; + } - // import channel + if ($moving) { + $seize = 1; + } - $relocate = ((array_key_exists('relocate',$data)) ? $data['relocate'] : null); + // import channel - if (array_key_exists('channel',$data)) { + $relocate = ((array_key_exists('relocate', $data)) ? $data['relocate'] : null); - $max_identities = account_service_class_fetch($account_id,'total_identities'); + if (array_key_exists('channel', $data)) { + $max_identities = account_service_class_fetch($account_id, 'total_identities'); - if ($max_identities !== false) { - $r = q("select channel_id from channel where channel_account_id = %d and channel_removed = 0 ", - intval($account_id) - ); - if ($r && count($r) > $max_identities) { - notice( sprintf( t('Your service plan only allows %d channels.'), $max_identities) . EOL); - return; - } - } + if ($max_identities !== false) { + $r = q( + "select channel_id from channel where channel_account_id = %d and channel_removed = 0 ", + intval($account_id) + ); + if ($r && count($r) > $max_identities) { + notice(sprintf(t('Your service plan only allows %d channels.'), $max_identities) . EOL); + return; + } + } if ($newname) { - $x = false; - - if (get_config('system','unicode_usernames')) { - $x = punify(mb_strtolower($newname)); - } - - if ((! $x) || strlen($x) > 64) { - $x = strtolower(URLify::transliterate($newname)); - } - else { - $x = $newname; - } - $newname = $x; - } - - $channel = import_channel($data['channel'], $account_id, $seize, $newname); - } - else { - $moving = false; - $channel = App::get_channel(); - } - - if (! $channel) { - logger('Channel not found. ' . print_r($channel,true)); - notice( t('No channel. Import failed.') . EOL); - return; - } - - if (is_array($data['config'])) { - import_config($channel,$data['config']); - } - - logger('import step 2'); - - if (array_key_exists('channel',$data)) { - if ($data['photo']) { - import_channel_photo(base64url_decode($data['photo']['data']),$data['photo']['type'],$account_id,$channel['channel_id']); - } - - if (is_array($data['profile'])) { - import_profiles($channel,$data['profile']); - } - } - - logger('import step 3'); - - // import xchans and contact photos - // This *must* be done before importing hublocs - - if (array_key_exists('channel',$data) && $seize) { - - // replace any existing xchan we may have on this site if we're seizing control - - $r = q("delete from xchan where xchan_hash = '%s'", - dbesc($channel['channel_hash']) - ); - - $r = xchan_store_lowlevel( - [ - 'xchan_hash' => $channel['channel_hash'], - 'xchan_guid' => $channel['channel_guid'], - 'xchan_guid_sig' => $channel['channel_guid_sig'], - 'xchan_pubkey' => $channel['channel_pubkey'], - 'xchan_photo_l' => z_root() . "/photo/profile/l/" . $channel['channel_id'], - 'xchan_photo_m' => z_root() . "/photo/profile/m/" . $channel['channel_id'], - 'xchan_photo_s' => z_root() . "/photo/profile/s/" . $channel['channel_id'], - 'xchan_addr' => channel_reddress($channel), - 'xchan_url' => z_root() . '/channel/' . $channel['channel_address'], - 'xchan_connurl' => z_root() . '/poco/' . $channel['channel_address'], - 'xchan_follow' => z_root() . '/follow?f=&url=%s', - 'xchan_name' => $channel['channel_name'], - 'xchan_network' => 'nomad', - 'xchan_updated' => datetime_convert(), - 'xchan_photo_date' => datetime_convert(), - 'xchan_name_date' => datetime_convert() - ] - ); - } - - logger('import step 4'); - - // import xchans - $xchans = $data['xchan']; - if ($xchans) { - foreach ($xchans as $xchan) { - - // Provide backward compatibility for zot11 based projects - - if ($xchan['xchan_network'] === 'nomad' && version_compare(ZOT_REVISION, '10.0') <= 0) { - $xchan['xchan_network'] = 'zot6'; - } - - $hash = Libzot::make_xchan_hash($xchan['xchan_guid'],$xchan['xchan_pubkey']); - - if (in_array($xchan['xchan_network'],['nomad','zot6']) && $hash !== $xchan['xchan_hash']) { - logger('forged xchan: ' . print_r($xchan,true)); - continue; - } - - $r = q("select xchan_hash from xchan where xchan_hash = '%s' limit 1", - dbesc($xchan['xchan_hash']) - ); - if ($r) { - continue; - } - xchan_store_lowlevel($xchan); - - - if ($xchan['xchan_hash'] === $channel['channel_hash']) { - $r = q("update xchan set xchan_updated = '%s', xchan_photo_l = '%s', xchan_photo_m = '%s', xchan_photo_s = '%s' where xchan_hash = '%s'", - dbesc(datetime_convert()), - dbesc(z_root() . '/photo/profile/l/' . $channel['channel_id']), - dbesc(z_root() . '/photo/profile/m/' . $channel['channel_id']), - dbesc(z_root() . '/photo/profile/s/' . $channel['channel_id']), - dbesc($xchan['xchan_hash']) - ); - } - else { - $photos = import_remote_xchan_photo($xchan['xchan_photo_l'],$xchan['xchan_hash']); - if ($photos) { - if ($photos[4]) { - $photodate = NULL_DATE; - } - else { - $photodate = $xchan['xchan_photo_date']; - } - - $r = q("update xchan set xchan_updated = '%s', xchan_photo_l = '%s', xchan_photo_m = '%s', xchan_photo_s = '%s', xchan_photo_mimetype = '%s', xchan_photo_date = '%s' where xchan_hash = '%s'", - dbesc(datetime_convert()), - dbesc($photos[0]), - dbesc($photos[1]), - dbesc($photos[2]), - dbesc($photos[3]), - dbesc($photodate), - dbesc($xchan['xchan_hash']) - ); - } - } - } - - logger('import step 5'); - } - - - logger('import step 6'); - - - if (is_array($data['hubloc'])) { - import_hublocs($channel,$data['hubloc'],$seize,$moving); - } - - logger('import step 7'); - - // create new hubloc for the new channel at this site - - if (array_key_exists('channel',$data)) { - $r = hubloc_store_lowlevel( - [ - 'hubloc_guid' => $channel['channel_guid'], - 'hubloc_guid_sig' => $channel['channel_guid_sig'], - 'hubloc_id_url' => channel_url($channel), - 'hubloc_hash' => $channel['channel_hash'], - 'hubloc_addr' => channel_reddress($channel), - 'hubloc_network' => 'nomad', - 'hubloc_primary' => (($seize) ? 1 : 0), - 'hubloc_url' => z_root(), - 'hubloc_url_sig' => Libzot::sign(z_root(),$channel['channel_prvkey']), - 'hubloc_site_id' => Libzot::make_xchan_hash(z_root(),get_config('system','pubkey')), - 'hubloc_host' => App::get_hostname(), - 'hubloc_callback' => z_root() . '/zot', - 'hubloc_sitekey' => get_config('system','pubkey'), - 'hubloc_updated' => datetime_convert() - ] - ); - - // reset the original primary hubloc if it is being seized - - if ($seize) { - $r = q("update hubloc set hubloc_primary = 0 where hubloc_primary = 1 and hubloc_hash = '%s' and hubloc_url != '%s' ", - dbesc($channel['channel_hash']), - dbesc(z_root()) - ); - } - } - - - $friends = 0; - $feeds = 0; - - // import contacts - $abooks = $data['abook']; - if ($abooks) { - foreach ($abooks as $abook) { - - $abook_copy = $abook; - - $abconfig = null; - if (array_key_exists('abconfig',$abook) && is_array($abook['abconfig']) && count($abook['abconfig'])) { - $abconfig = $abook['abconfig']; - } - - unset($abook['abook_id']); - unset($abook['abook_rating']); - unset($abook['abook_rating_text']); - unset($abook['abconfig']); - unset($abook['abook_their_perms']); - unset($abook['abook_my_perms']); - unset($abook['abook_not_here']); - - $abook['abook_account'] = $account_id; - $abook['abook_channel'] = $channel['channel_id']; - - $reconnect = false; - - if (array_key_exists('abook_instance',$abook) && $abook['abook_instance'] && strpos($abook['abook_instance'],z_root()) === false) { - $abook['abook_not_here'] = 1; - if (! ($abook['abook_pending'] || $abook['abook_blocked'])) { - $reconnect = true; - } - } - - if ($abook['abook_self']) { - $ctype = 0; - $role = get_pconfig($channel['channel_id'],'system','permissions_role'); - if (strpos($role,'collection' !== false)) { - $ctype = 2; - } - elseif (strpos($role,'group') !== false) { - $ctype = 1; - } - if ($ctype) { - q("update xchan set xchan_type = %d where xchan_hash = '%s' ", - intval($ctype), - dbesc($abook['abook_xchan']) - ); - } - } - else { - if ($max_friends !== false && $friends > $max_friends) { - continue; - } - if ($max_feeds !== false && intval($abook['abook_feed']) && ($feeds > $max_feeds)) { - continue; - } - } - - $r = q("select abook_id from abook where abook_xchan = '%s' and abook_channel = %d limit 1", - dbesc($abook['abook_xchan']), - intval($channel['channel_id']) - ); - if($r) { - $columns = db_columns('abook'); - - foreach ($abook as $k => $v) { - if (! in_array($k,$columns)) { - continue; - } - $r = q("UPDATE abook SET " . TQUOT . "%s" . TQUOT . " = '%s' WHERE abook_xchan = '%s' AND abook_channel = %d", - dbesc($k), - dbesc($v), - dbesc($abook['abook_xchan']), - intval($channel['channel_id']) - ); - } - } - else { - abook_store_lowlevel($abook); - - $friends ++; - if (intval($abook['abook_feed'])) { - $feeds ++; - } - } - - if ($abconfig) { - foreach ($abconfig as $abc) { - set_abconfig($channel['channel_id'],$abc['xchan'],$abc['cat'],$abc['k'],$abc['v']); - } - } - if ($reconnect) { - Connect::connect($channel,$abook['abook_xchan']); - } - } - - logger('import step 8'); - } - - // import groups - $groups = $data['group']; - if ($groups) { - $saved = []; - foreach ($groups as $group) { - $saved[$group['hash']] = [ 'old' => $group['id'] ]; - if (array_key_exists('name', $group)) { - $group['gname'] = $group['name']; - unset($group['name']); - } - unset($group['id']); - $group['uid'] = $channel['channel_id']; - - create_table_from_array('pgrp', $group); - } - $r = q("select * from pgrp where uid = %d", - intval($channel['channel_id']) - ); - if ($r) { - foreach ($r as $rr) { - $saved[$rr['hash']]['new'] = $rr['id']; - } - } - } - - // import group members - $group_members = $data['group_member']; - if ($group_members) { - foreach ($group_members as $group_member) { - unset($group_member['id']); - $group_member['uid'] = $channel['channel_id']; - foreach ($saved as $x) { - if ($x['old'] == $group_member['gid']) { - $group_member['gid'] = $x['new']; - } - } - create_table_from_array('pgrp_member', $group_member); - } - } - - logger('import step 9'); - - - if (is_array($data['atoken'])) { - import_atoken($channel,$data['atoken']); - } - if (is_array($data['xign'])) { - import_xign($channel,$data['xign']); - } - if (is_array($data['block'])) { - import_block($channel,$data['block']); - } - if (is_array($data['obj'])) { - import_objs($channel,$data['obj']); - } - if (is_array($data['likes'])) { - import_likes($channel,$data['likes']); - } - if (is_array($data['app'])) { - import_apps($channel,$data['app']); - } - if (is_array($data['sysapp'])) { - import_sysapps($channel,$data['sysapp']); - } - if (is_array($data['chatroom'])) { - import_chatrooms($channel,$data['chatroom']); - } -// if (is_array($data['conv'])) { -// import_conv($channel,$data['conv']); -// } -// if (is_array($data['mail'])) { -// import_mail($channel,$data['mail']); -// } - if (is_array($data['event'])) { - import_events($channel,$data['event']); - } - if (is_array($data['event_item'])) { - import_items($channel,$data['event_item'],false,$relocate); - } -// if (is_array($data['menu'])) { -// import_menus($channel,$data['menu']); -// } -// if (is_array($data['wiki'])) { -// import_items($channel,$data['wiki'],false,$relocate); -// } -// if (is_array($data['webpages'])) { -// import_items($channel,$data['webpages'],false,$relocate); -// } - $addon = array('channel' => $channel,'data' => $data); - call_hooks('import_channel',$addon); - - $saved_notification_flags = notifications_off($channel['channel_id']); - if ($import_posts && array_key_exists('item',$data) && $data['item']) { - import_items($channel,$data['item'],false,$relocate); - } - - if ($api_path && $import_posts) { // we are importing from a server and not a file - - $m = parse_url($api_path); - - $hz_server = $m['scheme'] . '://' . $m['host']; - - $since = datetime_convert(date_default_timezone_get(),date_default_timezone_get(),'0001-01-01 00:00'); - $until = datetime_convert(date_default_timezone_get(),date_default_timezone_get(),'now + 1 day'); - - $poll_interval = get_config('system','poll_interval',3); - $page = 0; - - while (1) { - $headers = [ - 'X-API-Token' => random_string(), - 'X-API-Request' => $hz_server . '/api/z/1.0/item/export_page?f=&zap_compat=1&since=' . urlencode($since) . '&until=' . urlencode($until) . '&page=' . $page , - 'Host' => $m['host'], - '(request-target)' => 'get /api/z/1.0/item/export_page?f=&zap_compat=1&since=' . urlencode($since) . '&until=' . urlencode($until) . '&page=' . $page , - ]; - - $headers = HTTPSig::create_sig($headers,$channel['channel_prvkey'], channel_url($channel),true,'sha512'); - - $x = z_fetch_url($hz_server . '/api/z/1.0/item/export_page?f=&zap_compat=1&since=' . urlencode($since) . '&until=' . urlencode($until) . '&page=' . $page,false,$redirects,[ 'headers' => $headers ]); - - // logger('z_fetch: ' . print_r($x,true)); - - if (! $x['success']) { - logger('no API response'); - break; - } - - $j = json_decode($x['body'],true); - - if (! $j) { - break; - } - - if (! ($j['item'] || count($j['item']))) { - break; - } - - Run::Summon([ 'Content_importer', sprintf('%d',$page), $since, $until, $channel['channel_address'], urlencode($hz_server) ]); - sleep($poll_interval); - - $page ++; - continue; - } - - $headers = [ - 'X-API-Token' => random_string(), - 'X-API-Request' => $hz_server . '/api/z/1.0/files?f=&zap_compat=1&since=' . urlencode($since) . '&until=' . urlencode($until), - 'Host' => $m['host'], - '(request-target)' => 'get /api/z/1.0/files?f=&zap_compat=1&since=' . urlencode($since) . '&until=' . urlencode($until), - ]; - - $headers = HTTPSig::create_sig($headers,$channel['channel_prvkey'], channel_url($channel),true,'sha512'); - - $x = z_fetch_url($hz_server . '/api/z/1.0/files?f=&zap_compat=1&since=' . urlencode($since) . '&until=' . urlencode($until),false,$redirects,[ 'headers' => $headers ]); - - if (! $x['success']) { - logger('no API response'); - return; - } + $x = false; + + if (get_config('system', 'unicode_usernames')) { + $x = punify(mb_strtolower($newname)); + } + + if ((!$x) || strlen($x) > 64) { + $x = strtolower(URLify::transliterate($newname)); + } else { + $x = $newname; + } + $newname = $x; + } + + $channel = import_channel($data['channel'], $account_id, $seize, $newname); + } else { + $moving = false; + $channel = App::get_channel(); + } + + if (!$channel) { + logger('Channel not found. ' . print_r($channel, true)); + notice(t('No channel. Import failed.') . EOL); + return; + } + + if (is_array($data['config'])) { + import_config($channel, $data['config']); + } + + logger('import step 2'); + + if (array_key_exists('channel', $data)) { + if ($data['photo']) { + import_channel_photo(base64url_decode($data['photo']['data']), $data['photo']['type'], $account_id, $channel['channel_id']); + } + + if (is_array($data['profile'])) { + import_profiles($channel, $data['profile']); + } + } + + logger('import step 3'); + + // import xchans and contact photos + // This *must* be done before importing hublocs + + if (array_key_exists('channel', $data) && $seize) { + // replace any existing xchan we may have on this site if we're seizing control + + $r = q( + "delete from xchan where xchan_hash = '%s'", + dbesc($channel['channel_hash']) + ); + + $r = xchan_store_lowlevel( + [ + 'xchan_hash' => $channel['channel_hash'], + 'xchan_guid' => $channel['channel_guid'], + 'xchan_guid_sig' => $channel['channel_guid_sig'], + 'xchan_pubkey' => $channel['channel_pubkey'], + 'xchan_photo_l' => z_root() . "/photo/profile/l/" . $channel['channel_id'], + 'xchan_photo_m' => z_root() . "/photo/profile/m/" . $channel['channel_id'], + 'xchan_photo_s' => z_root() . "/photo/profile/s/" . $channel['channel_id'], + 'xchan_addr' => channel_reddress($channel), + 'xchan_url' => z_root() . '/channel/' . $channel['channel_address'], + 'xchan_connurl' => z_root() . '/poco/' . $channel['channel_address'], + 'xchan_follow' => z_root() . '/follow?f=&url=%s', + 'xchan_name' => $channel['channel_name'], + 'xchan_network' => 'nomad', + 'xchan_updated' => datetime_convert(), + 'xchan_photo_date' => datetime_convert(), + 'xchan_name_date' => datetime_convert() + ] + ); + } + + logger('import step 4'); + + // import xchans + $xchans = $data['xchan']; + if ($xchans) { + foreach ($xchans as $xchan) { + // Provide backward compatibility for zot11 based projects + + if ($xchan['xchan_network'] === 'nomad' && version_compare(ZOT_REVISION, '10.0') <= 0) { + $xchan['xchan_network'] = 'zot6'; + } + + $hash = Libzot::make_xchan_hash($xchan['xchan_guid'], $xchan['xchan_pubkey']); + + if (in_array($xchan['xchan_network'], ['nomad', 'zot6']) && $hash !== $xchan['xchan_hash']) { + logger('forged xchan: ' . print_r($xchan, true)); + continue; + } + + $r = q( + "select xchan_hash from xchan where xchan_hash = '%s' limit 1", + dbesc($xchan['xchan_hash']) + ); + if ($r) { + continue; + } + xchan_store_lowlevel($xchan); + + + if ($xchan['xchan_hash'] === $channel['channel_hash']) { + $r = q( + "update xchan set xchan_updated = '%s', xchan_photo_l = '%s', xchan_photo_m = '%s', xchan_photo_s = '%s' where xchan_hash = '%s'", + dbesc(datetime_convert()), + dbesc(z_root() . '/photo/profile/l/' . $channel['channel_id']), + dbesc(z_root() . '/photo/profile/m/' . $channel['channel_id']), + dbesc(z_root() . '/photo/profile/s/' . $channel['channel_id']), + dbesc($xchan['xchan_hash']) + ); + } else { + $photos = import_remote_xchan_photo($xchan['xchan_photo_l'], $xchan['xchan_hash']); + if ($photos) { + if ($photos[4]) { + $photodate = NULL_DATE; + } else { + $photodate = $xchan['xchan_photo_date']; + } + + $r = q( + "update xchan set xchan_updated = '%s', xchan_photo_l = '%s', xchan_photo_m = '%s', xchan_photo_s = '%s', xchan_photo_mimetype = '%s', xchan_photo_date = '%s' where xchan_hash = '%s'", + dbesc(datetime_convert()), + dbesc($photos[0]), + dbesc($photos[1]), + dbesc($photos[2]), + dbesc($photos[3]), + dbesc($photodate), + dbesc($xchan['xchan_hash']) + ); + } + } + } + + logger('import step 5'); + } + + + logger('import step 6'); + + + if (is_array($data['hubloc'])) { + import_hublocs($channel, $data['hubloc'], $seize, $moving); + } + + logger('import step 7'); + + // create new hubloc for the new channel at this site + + if (array_key_exists('channel', $data)) { + $r = hubloc_store_lowlevel( + [ + 'hubloc_guid' => $channel['channel_guid'], + 'hubloc_guid_sig' => $channel['channel_guid_sig'], + 'hubloc_id_url' => channel_url($channel), + 'hubloc_hash' => $channel['channel_hash'], + 'hubloc_addr' => channel_reddress($channel), + 'hubloc_network' => 'nomad', + 'hubloc_primary' => (($seize) ? 1 : 0), + 'hubloc_url' => z_root(), + 'hubloc_url_sig' => Libzot::sign(z_root(), $channel['channel_prvkey']), + 'hubloc_site_id' => Libzot::make_xchan_hash(z_root(), get_config('system', 'pubkey')), + 'hubloc_host' => App::get_hostname(), + 'hubloc_callback' => z_root() . '/zot', + 'hubloc_sitekey' => get_config('system', 'pubkey'), + 'hubloc_updated' => datetime_convert() + ] + ); + + // reset the original primary hubloc if it is being seized + + if ($seize) { + $r = q( + "update hubloc set hubloc_primary = 0 where hubloc_primary = 1 and hubloc_hash = '%s' and hubloc_url != '%s' ", + dbesc($channel['channel_hash']), + dbesc(z_root()) + ); + } + } + + + $friends = 0; + $feeds = 0; + + // import contacts + $abooks = $data['abook']; + if ($abooks) { + foreach ($abooks as $abook) { + $abook_copy = $abook; + + $abconfig = null; + if (array_key_exists('abconfig', $abook) && is_array($abook['abconfig']) && count($abook['abconfig'])) { + $abconfig = $abook['abconfig']; + } + + unset($abook['abook_id']); + unset($abook['abook_rating']); + unset($abook['abook_rating_text']); + unset($abook['abconfig']); + unset($abook['abook_their_perms']); + unset($abook['abook_my_perms']); + unset($abook['abook_not_here']); + + $abook['abook_account'] = $account_id; + $abook['abook_channel'] = $channel['channel_id']; + + $reconnect = false; + + if (array_key_exists('abook_instance', $abook) && $abook['abook_instance'] && strpos($abook['abook_instance'], z_root()) === false) { + $abook['abook_not_here'] = 1; + if (!($abook['abook_pending'] || $abook['abook_blocked'])) { + $reconnect = true; + } + } + + if ($abook['abook_self']) { + $ctype = 0; + $role = get_pconfig($channel['channel_id'], 'system', 'permissions_role'); + if (strpos($role, 'collection' !== false)) { + $ctype = 2; + } elseif (strpos($role, 'group') !== false) { + $ctype = 1; + } + if ($ctype) { + q( + "update xchan set xchan_type = %d where xchan_hash = '%s' ", + intval($ctype), + dbesc($abook['abook_xchan']) + ); + } + } else { + if ($max_friends !== false && $friends > $max_friends) { + continue; + } + if ($max_feeds !== false && intval($abook['abook_feed']) && ($feeds > $max_feeds)) { + continue; + } + } + + $r = q( + "select abook_id from abook where abook_xchan = '%s' and abook_channel = %d limit 1", + dbesc($abook['abook_xchan']), + intval($channel['channel_id']) + ); + if ($r) { + $columns = db_columns('abook'); + + foreach ($abook as $k => $v) { + if (!in_array($k, $columns)) { + continue; + } + $r = q( + "UPDATE abook SET " . TQUOT . "%s" . TQUOT . " = '%s' WHERE abook_xchan = '%s' AND abook_channel = %d", + dbesc($k), + dbesc($v), + dbesc($abook['abook_xchan']), + intval($channel['channel_id']) + ); + } + } else { + abook_store_lowlevel($abook); + + $friends++; + if (intval($abook['abook_feed'])) { + $feeds++; + } + } + + if ($abconfig) { + foreach ($abconfig as $abc) { + set_abconfig($channel['channel_id'], $abc['xchan'], $abc['cat'], $abc['k'], $abc['v']); + } + } + if ($reconnect) { + Connect::connect($channel, $abook['abook_xchan']); + } + } + + logger('import step 8'); + } + + // import groups + $groups = $data['group']; + if ($groups) { + $saved = []; + foreach ($groups as $group) { + $saved[$group['hash']] = ['old' => $group['id']]; + if (array_key_exists('name', $group)) { + $group['gname'] = $group['name']; + unset($group['name']); + } + unset($group['id']); + $group['uid'] = $channel['channel_id']; + + create_table_from_array('pgrp', $group); + } + $r = q( + "select * from pgrp where uid = %d", + intval($channel['channel_id']) + ); + if ($r) { + foreach ($r as $rr) { + $saved[$rr['hash']]['new'] = $rr['id']; + } + } + } + + // import group members + $group_members = $data['group_member']; + if ($group_members) { + foreach ($group_members as $group_member) { + unset($group_member['id']); + $group_member['uid'] = $channel['channel_id']; + foreach ($saved as $x) { + if ($x['old'] == $group_member['gid']) { + $group_member['gid'] = $x['new']; + } + } + create_table_from_array('pgrp_member', $group_member); + } + } + + logger('import step 9'); + + + if (is_array($data['atoken'])) { + import_atoken($channel, $data['atoken']); + } + if (is_array($data['xign'])) { + import_xign($channel, $data['xign']); + } + if (is_array($data['block'])) { + import_block($channel, $data['block']); + } + if (is_array($data['obj'])) { + import_objs($channel, $data['obj']); + } + if (is_array($data['likes'])) { + import_likes($channel, $data['likes']); + } + if (is_array($data['app'])) { + import_apps($channel, $data['app']); + } + if (is_array($data['sysapp'])) { + import_sysapps($channel, $data['sysapp']); + } + if (is_array($data['chatroom'])) { + import_chatrooms($channel, $data['chatroom']); + } +// if (is_array($data['conv'])) { +// import_conv($channel,$data['conv']); +// } +// if (is_array($data['mail'])) { +// import_mail($channel,$data['mail']); +// } + if (is_array($data['event'])) { + import_events($channel, $data['event']); + } + if (is_array($data['event_item'])) { + import_items($channel, $data['event_item'], false, $relocate); + } +// if (is_array($data['menu'])) { +// import_menus($channel,$data['menu']); +// } +// if (is_array($data['wiki'])) { +// import_items($channel,$data['wiki'],false,$relocate); +// } +// if (is_array($data['webpages'])) { +// import_items($channel,$data['webpages'],false,$relocate); +// } + $addon = array('channel' => $channel, 'data' => $data); + call_hooks('import_channel', $addon); + + $saved_notification_flags = notifications_off($channel['channel_id']); + if ($import_posts && array_key_exists('item', $data) && $data['item']) { + import_items($channel, $data['item'], false, $relocate); + } + + if ($api_path && $import_posts) { // we are importing from a server and not a file + $m = parse_url($api_path); + + $hz_server = $m['scheme'] . '://' . $m['host']; + + $since = datetime_convert(date_default_timezone_get(), date_default_timezone_get(), '0001-01-01 00:00'); + $until = datetime_convert(date_default_timezone_get(), date_default_timezone_get(), 'now + 1 day'); + + $poll_interval = get_config('system', 'poll_interval', 3); + $page = 0; + + while (1) { + $headers = [ + 'X-API-Token' => random_string(), + 'X-API-Request' => $hz_server . '/api/z/1.0/item/export_page?f=&zap_compat=1&since=' . urlencode($since) . '&until=' . urlencode($until) . '&page=' . $page, + 'Host' => $m['host'], + '(request-target)' => 'get /api/z/1.0/item/export_page?f=&zap_compat=1&since=' . urlencode($since) . '&until=' . urlencode($until) . '&page=' . $page, + ]; + + $headers = HTTPSig::create_sig($headers, $channel['channel_prvkey'], channel_url($channel), true, 'sha512'); + + $x = z_fetch_url($hz_server . '/api/z/1.0/item/export_page?f=&zap_compat=1&since=' . urlencode($since) . '&until=' . urlencode($until) . '&page=' . $page, false, $redirects, ['headers' => $headers]); + + // logger('z_fetch: ' . print_r($x,true)); + + if (!$x['success']) { + logger('no API response'); + break; + } + + $j = json_decode($x['body'], true); + + if (!$j) { + break; + } + + if (!($j['item'] || count($j['item']))) { + break; + } + + Run::Summon(['Content_importer', sprintf('%d', $page), $since, $until, $channel['channel_address'], urlencode($hz_server)]); + sleep($poll_interval); + + $page++; + continue; + } + + $headers = [ + 'X-API-Token' => random_string(), + 'X-API-Request' => $hz_server . '/api/z/1.0/files?f=&zap_compat=1&since=' . urlencode($since) . '&until=' . urlencode($until), + 'Host' => $m['host'], + '(request-target)' => 'get /api/z/1.0/files?f=&zap_compat=1&since=' . urlencode($since) . '&until=' . urlencode($until), + ]; + + $headers = HTTPSig::create_sig($headers, $channel['channel_prvkey'], channel_url($channel), true, 'sha512'); + + $x = z_fetch_url($hz_server . '/api/z/1.0/files?f=&zap_compat=1&since=' . urlencode($since) . '&until=' . urlencode($until), false, $redirects, ['headers' => $headers]); + + if (!$x['success']) { + logger('no API response'); + return; + } + + $j = json_decode($x['body'], true); + + if (!$j) { + return; + } + + if (!$j['success']) { + return; + } - $j = json_decode($x['body'],true); + $poll_interval = get_config('system', 'poll_interval', 3); - if (! $j) { - return; - } + if (count($j['results'])) { + $todo = count($j['results']); + logger('total to process: ' . $todo, LOGGER_DEBUG); - if (! $j['success']) { - return; - } + foreach ($j['results'] as $jj) { + Run::Summon(['File_importer', $jj['hash'], $channel['channel_address'], urlencode($hz_server)]); + sleep($poll_interval); + } + } - $poll_interval = get_config('system','poll_interval',3); + notice(t('Files and Posts imported.') . EOL); + } - if(count($j['results'])) { - $todo = count($j['results']); - logger('total to process: ' . $todo,LOGGER_DEBUG); + notifications_on($channel['channel_id'], $saved_notification_flags); - foreach($j['results'] as $jj) { - Run::Summon([ 'File_importer',$jj['hash'], $channel['channel_address'], urlencode($hz_server) ]); - sleep($poll_interval); - } - } - notice(t('Files and Posts imported.') . EOL); + // send out refresh requests + // notify old server that it may no longer be primary. - } + Run::Summon(['Notifier', 'refresh_all', $channel['channel_id']]); - notifications_on($channel['channel_id'],$saved_notification_flags); + // This will indirectly perform a refresh_all *and* update the directory + Run::Summon(['Directory', $channel['channel_id']]); - // send out refresh requests - // notify old server that it may no longer be primary. + notice(t('Import completed.') . EOL); - Run::Summon( [ 'Notifier','refresh_all',$channel['channel_id'] ] ); + change_channel($channel['channel_id']); - // This will indirectly perform a refresh_all *and* update the directory + goaway(z_root() . '/stream'); + } - Run::Summon( [ 'Directory', $channel['channel_id'] ] ); + /** + * @brief Handle POST action on channel import page. + */ - notice( t('Import completed.') . EOL); + public function post() + { + $account_id = get_account_id(); + if (!$account_id) { + return; + } - change_channel($channel['channel_id']); + check_form_security_token_redirectOnErr('/import', 'channel_import'); + $this->import_account($account_id); + } - goaway(z_root() . '/stream' ); - } + /** + * @brief Generate channel import page. + * + * @return string with parsed HTML. + */ - /** - * @brief Handle POST action on channel import page. - */ + public function get() + { - function post() { - $account_id = get_account_id(); - if (! $account_id) { - return; - } + if (!get_account_id()) { + notice(t('You must be logged in to use this feature.') . EOL); + return EMPTY_STR; + } - check_form_security_token_redirectOnErr('/import', 'channel_import'); - $this->import_account($account_id); - } + return replace_macros(get_markup_template('channel_import.tpl'), [ + '$title' => t('Import Channel'), + '$desc' => t('Use this form to import an existing channel from a different server. You may retrieve the channel identity from the old server via the network or provide an export file.'), + '$label_filename' => t('File to Upload'), + '$choice' => t('Or provide the old server details'), + '$old_address' => ['old_address', t('Your old identity address (xyz@example.com)'), '', ''], + '$email' => ['email', t('Your old login email address'), '', ''], + '$password' => ['password', t('Your old login password'), '', ''], + '$import_posts' => ['import_posts', t('Import a few months of posts if possible (limited by available memory)'), false, '', [t('No'), t('Yes')]], - /** - * @brief Generate channel import page. - * - * @return string with parsed HTML. - */ + '$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.'), - function get() { + '$make_primary' => ['make_primary', t('Make this hub my primary location'), false, '', [t('No'), t('Yes')]], + '$moving' => ['moving', t('Move this channel (disable all previous locations)'), false, '', [t('No'), t('Yes')]], + '$newname' => ['newname', t('Use this channel nickname instead of the one provided'), '', t('Leave blank to keep your existing channel nickname. You will be randomly assigned a similar nickname if either name is already allocated on this site.')], - if (! get_account_id()) { - notice( t('You must be logged in to use this feature.') . EOL); - return EMPTY_STR; - } - - return replace_macros(get_markup_template('channel_import.tpl'), [ - '$title' => t('Import Channel'), - '$desc' => t('Use this form to import an existing channel from a different server. You may retrieve the channel identity from the old server via the network or provide an export file.'), - '$label_filename' => t('File to Upload'), - '$choice' => t('Or provide the old server details'), - '$old_address' => [ 'old_address', t('Your old identity address (xyz@example.com)'), '', ''], - '$email' => [ 'email', t('Your old login email address'), '', '' ], - '$password' => [ 'password', t('Your old login password'), '', '' ], - '$import_posts' => [ 'import_posts', t('Import a few months of posts if possible (limited by available memory)'), false, '', [ t('No'), t('Yes') ]], - - '$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.'), - - '$make_primary' => [ 'make_primary', t('Make this hub my primary location'), false, '', [ t('No'), t('Yes') ] ], - '$moving' => [ 'moving', t('Move this channel (disable all previous locations)'), false, '', [ t('No'), t('Yes') ] ], - '$newname' => [ 'newname', t('Use this channel nickname instead of the one provided'), '', t('Leave blank to keep your existing channel nickname. You will be randomly assigned a similar nickname if either name is already allocated on this site.')], - - '$pleasewait' => t('This process may take several minutes to complete and considerably longer if importing a large amount of posts and files. Please submit the form only once and leave this page open until finished.'), - - '$form_security_token' => get_form_security_token('channel_import'), - '$submit' => t('Submit') - ]); - } + '$pleasewait' => t('This process may take several minutes to complete and considerably longer if importing a large amount of posts and files. Please submit the form only once and leave this page open until finished.'), + '$form_security_token' => get_form_security_token('channel_import'), + '$submit' => t('Submit') + ]); + } } diff --git a/Zotlabs/Module/Import_items.php b/Zotlabs/Module/Import_items.php index 225e3d6c6..e489f9fe9 100644 --- a/Zotlabs/Module/Import_items.php +++ b/Zotlabs/Module/Import_items.php @@ -1,6 +1,10 @@ $email . ':' . $password); + $url = $scheme . $servername . $api_path; + $ret = z_fetch_url($url, $binary, $redirects, $opts); + if (!$ret['success']) { + $ret = z_fetch_url('http://' . $servername . $api_path, $binary, $redirects, $opts); + } + if ($ret['success']) { + $data = $ret['body']; + } else { + notice(t('Unable to download data from old server') . EOL); + } + } - $scheme = 'https://'; - $api_path = '/api/red/channel/export/items?f=&zap_compat=1&channel=' . $channelname . '&year=' . intval($year); - $binary = false; - $redirects = 0; - $opts = array('http_auth' => $email . ':' . $password); - $url = $scheme . $servername . $api_path; - $ret = z_fetch_url($url, $binary, $redirects, $opts); - if(! $ret['success']) - $ret = z_fetch_url('http://' . $servername . $api_path, $binary, $redirects, $opts); - if($ret['success']) - $data = $ret['body']; - else - notice( t('Unable to download data from old server') . EOL); - } + if (!$data) { + logger('Empty file.'); + notice(t('Imported file is empty.') . EOL); + return; + } - if(! $data) { - logger('Empty file.'); - notice( t('Imported file is empty.') . EOL); - return; - } + $data = json_decode($data, true); - $data = json_decode($data, true); + //logger('import: data: ' . print_r($data,true)); + //print_r($data); - //logger('import: data: ' . print_r($data,true)); - //print_r($data); + if (!is_array($data)) { + return; + } - if(! is_array($data)) - return; +// if(array_key_exists('compatibility',$data) && array_key_exists('database',$data['compatibility'])) { +// $v1 = substr($data['compatibility']['database'],-4); +// $v2 = substr(DB_UPDATE_VERSION,-4); +// if($v2 > $v1) { +// $t = sprintf( t('Warning: Database versions differ by %1$d updates.'), $v2 - $v1 ); +// notice($t . EOL); +// } +// } -// if(array_key_exists('compatibility',$data) && array_key_exists('database',$data['compatibility'])) { -// $v1 = substr($data['compatibility']['database'],-4); -// $v2 = substr(DB_UPDATE_VERSION,-4); -// if($v2 > $v1) { -// $t = sprintf( t('Warning: Database versions differ by %1$d updates.'), $v2 - $v1 ); -// notice($t . EOL); -// } -// } + $codebase = 'zap'; - $codebase = 'zap'; + if ((!array_path_exists('compatibility/codebase', $data)) || $data['compatibility']['codebase'] !== $codebase) { + notice(t('Data export format is not compatible with this software')); + return; + } - if ((! array_path_exists('compatibility/codebase',$data)) || $data['compatibility']['codebase'] !== $codebase) { - notice(t('Data export format is not compatible with this software')); - return; - } + $channel = App::get_channel(); - $channel = \App::get_channel(); + if (array_key_exists('item', $data) && $data['item']) { + import_items($channel, $data['item'], false, ((array_key_exists('relocate', $data)) ? $data['relocate'] : null)); + } - if(array_key_exists('item',$data) && $data['item']) { - import_items($channel,$data['item'],false,((array_key_exists('relocate',$data)) ? $data['relocate'] : null)); - } - - info( t('Import completed') . EOL); - } + info(t('Import completed') . EOL); + } - /** - * @brief Generate item import page. - * - * @return string with parsed HTML. - */ - function get() { + /** + * @brief Generate item import page. + * + * @return string with parsed HTML. + */ + public function get() + { - if(! local_channel()) { - notice( t('Permission denied') . EOL); - return login(); - } + if (!local_channel()) { + notice(t('Permission denied') . EOL); + return login(); + } - $o = replace_macros(get_markup_template('item_import.tpl'), array( - '$title' => t('Import Items'), - '$desc' => t('Use this form to import existing posts and content from an export file.'), - '$label_filename' => t('File to Upload'), - '$form_security_token' => get_form_security_token('import_items'), - '$submit' => t('Submit') - )); - - return $o; - } + $o = replace_macros(get_markup_template('item_import.tpl'), array( + '$title' => t('Import Items'), + '$desc' => t('Use this form to import existing posts and content from an export file.'), + '$label_filename' => t('File to Upload'), + '$form_security_token' => get_form_security_token('import_items'), + '$submit' => t('Submit') + )); + return $o; + } } diff --git a/Zotlabs/Module/Inbox.php b/Zotlabs/Module/Inbox.php index 89f08412e..3a5e70782 100644 --- a/Zotlabs/Module/Inbox.php +++ b/Zotlabs/Module/Inbox.php @@ -1,4 +1,5 @@ is_valid() && $AS->type === 'Announce' && is_array($AS->obj) - && array_key_exists('object',$AS->obj) && array_key_exists('actor',$AS->obj)) { - // This is a relayed/forwarded Activity (as opposed to a shared/boosted object) - // Reparse the encapsulated Activity and use that instead - logger('relayed activity',LOGGER_DEBUG); - $AS = new ActivityStreams($AS->obj); - } + // By convention, fediverse server-to-server communications require a valid HTTP Signature + // which includes a signed digest header. - // logger('debug: ' . $AS->debug()); + // This check may need to move elsewhere or be modified in order to fully implement ActivityPub C2S. - if (! $AS->is_valid()) { - if ($AS->deleted) { - // process mastodon user deletion activities, but only if we can validate the signature - if ($hsig['header_valid'] && $hsig['content_valid'] && $hsig['portable_id']) { - logger('removing deleted actor'); - remove_all_xchan_resources($hsig['portable_id']); - } - else { - logger('ignoring deleted actor', LOGGER_DEBUG, LOG_INFO); - } - } - return; - } - + if (!($hsig['header_signed'] && $hsig['header_valid'] && $hsig['content_signed'] && $hsig['content_valid'])) { + http_status_exit(403, 'Permission denied'); + } - if (is_array($AS->actor) && array_key_exists('id',$AS->actor)) { - Activity::actor_store($AS->actor['id'],$AS->actor); - } + $AS = new ActivityStreams($data); + if ( + $AS->is_valid() && $AS->type === 'Announce' && is_array($AS->obj) + && array_key_exists('object', $AS->obj) && array_key_exists('actor', $AS->obj) + ) { + // This is a relayed/forwarded Activity (as opposed to a shared/boosted object) + // Reparse the encapsulated Activity and use that instead + logger('relayed activity', LOGGER_DEBUG); + $AS = new ActivityStreams($AS->obj); + } - if (is_array($AS->obj) && ActivityStreams::is_an_actor($AS->obj['type'])) { - Activity::actor_store($AS->obj['id'],$AS->obj); - } + // logger('debug: ' . $AS->debug()); - if (is_array($AS->obj) && is_array($AS->obj['actor']) && array_key_exists('id',$AS->obj['actor']) && $AS->obj['actor']['id'] !== $AS->actor['id']) { - Activity::actor_store($AS->obj['actor']['id'],$AS->obj['actor']); - if (! check_channelallowed($AS->obj['actor']['id'])) { - http_status_exit(403,'Permission denied'); - } - } - - // Validate that the channel that sent us this activity has authority to do so. - // Require a valid HTTPSignature with a signed Digest header. - - // Only permit relayed activities if the activity is signed with LDSigs - // AND the signature is valid AND the signer is the actor. - - if ($hsig['header_valid'] && $hsig['content_valid'] && $hsig['portable_id']) { - - // if the sender has the ability to send messages over zot/nomad, ignore messages sent via activitypub - // as observer aware features and client side markup will be unavailable - - $test = Activity::get_actor_hublocs($hsig['portable_id'],'all,not_deleted'); - if ($test) { - foreach ($test as $t) { - if ($t['hubloc_network'] === 'zot6') { - http_status_exit(409,'Conflict'); - } - } - } - - // fetch the portable_id for the actor, which may or may not be the sender - - $v = Activity::get_actor_hublocs($AS->actor['id'],'activitypub,not_deleted'); - - if ($v && $v[0]['hubloc_hash'] !== $hsig['portable_id']) { - // The sender is not actually the activity actor, so verify the LD signature. - // litepub activities (with no LD signature) will always have a matching actor and sender - - if ($AS->signer && is_array($AS->signer) && $AS->signer['id'] !== $AS->actor['id']) { - // the activity wasn't signed by the activity actor - return; - } - if (! $AS->sigok) { - // The activity signature isn't valid. - return; - } - - } - - if ($v) { - // The sender has been validated and stored - $observer_hash = $hsig['portable_id']; - } - - } - - if (! $observer_hash) { - return; - } - - // verify that this site has permitted communication with the sender. - - $m = parse_url($observer_hash); - - if ($m && $m['scheme'] && $m['host']) { - if (! check_siteallowed($m['scheme'] . '://' . $m['host'])) { - http_status_exit(403,'Permission denied'); - } - // this site obviously isn't dead because they are trying to communicate with us. - $test = q("update site set site_dead = 0 where site_dead = 1 and site_url = '%s' ", - dbesc($m['scheme'] . '://' . $m['host']) - ); - } - if (! check_channelallowed($observer_hash)) { - http_status_exit(403,'Permission denied'); - } - - // update the hubloc_connected timestamp, ignore failures - - $test = q("update hubloc set hubloc_connected = '%s' where hubloc_hash = '%s' and hubloc_network = 'activitypub'", - dbesc(datetime_convert()), - dbesc($observer_hash) - ); + if (!$AS->is_valid()) { + if ($AS->deleted) { + // process mastodon user deletion activities, but only if we can validate the signature + if ($hsig['header_valid'] && $hsig['content_valid'] && $hsig['portable_id']) { + logger('removing deleted actor'); + remove_all_xchan_resources($hsig['portable_id']); + } else { + logger('ignoring deleted actor', LOGGER_DEBUG, LOG_INFO); + } + } + return; + } - // Now figure out who the recipients are + if (is_array($AS->actor) && array_key_exists('id', $AS->actor)) { + Activity::actor_store($AS->actor['id'], $AS->actor); + } - if ($is_public) { + if (is_array($AS->obj) && ActivityStreams::is_an_actor($AS->obj['type'])) { + Activity::actor_store($AS->obj['id'], $AS->obj); + } - if (in_array($AS->type, [ 'Follow', 'Join' ]) && is_array($AS->obj) && ActivityStreams::is_an_actor($AS->obj['type'])) { - $channels = q("SELECT * from channel where channel_address = '%s' and channel_removed = 0 ", - dbesc(basename($AS->obj['id'])) - ); - } - else { - // deliver to anybody following $AS->actor + if (is_array($AS->obj) && is_array($AS->obj['actor']) && array_key_exists('id', $AS->obj['actor']) && $AS->obj['actor']['id'] !== $AS->actor['id']) { + Activity::actor_store($AS->obj['actor']['id'], $AS->obj['actor']); + if (!check_channelallowed($AS->obj['actor']['id'])) { + http_status_exit(403, 'Permission denied'); + } + } - $channels = q("SELECT * from channel where channel_id in ( SELECT abook_channel from abook left join xchan on abook_xchan = xchan_hash WHERE xchan_network = 'activitypub' and xchan_hash = '%s' ) and channel_removed = 0 ", - dbesc($observer_hash) - ); - if (! $channels) { - $channels = []; - } + // Validate that the channel that sent us this activity has authority to do so. + // Require a valid HTTPSignature with a signed Digest header. - $parent = $AS->parent_id; - if ($parent) { - // this is a comment - deliver to everybody who owns the parent - $owners = q("SELECT * from channel where channel_id in ( SELECT uid from item where mid = '%s' ) ", - dbesc($parent) - ); - if ($owners) { - $channels = array_merge($channels,$owners); - } - } - } + // Only permit relayed activities if the activity is signed with LDSigs + // AND the signature is valid AND the signer is the actor. - if ($channels === false) { - $channels = []; - } + if ($hsig['header_valid'] && $hsig['content_valid'] && $hsig['portable_id']) { + // if the sender has the ability to send messages over zot/nomad, ignore messages sent via activitypub + // as observer aware features and client side markup will be unavailable - if (in_array(ACTIVITY_PUBLIC_INBOX,$AS->recips) || in_array('Public',$AS->recips) || in_array('as:Public',$AS->recips)) { + $test = Activity::get_actor_hublocs($hsig['portable_id'], 'all,not_deleted'); + if ($test) { + foreach ($test as $t) { + if ($t['hubloc_network'] === 'zot6') { + http_status_exit(409, 'Conflict'); + } + } + } - // look for channels with send_stream = PERMS_PUBLIC (accept posts from anybody on the internet) + // fetch the portable_id for the actor, which may or may not be the sender - $r = q("select * from channel where channel_id in (select uid from pconfig where cat = 'perm_limits' and k = 'send_stream' and v = '1' ) and channel_removed = 0 "); - if ($r) { - $channels = array_merge($channels,$r); - } + $v = Activity::get_actor_hublocs($AS->actor['id'], 'activitypub,not_deleted'); - // look for channels that are following hashtags. These will be checked in tgroup_check() - - $r = q("select * from channel where channel_id in (select uid from pconfig where cat = 'system' and k = 'followed_tags' and v != '' ) and channel_removed = 0 "); - if ($r) { - $channels = array_merge($channels,$r); - } + if ($v && $v[0]['hubloc_hash'] !== $hsig['portable_id']) { + // The sender is not actually the activity actor, so verify the LD signature. + // litepub activities (with no LD signature) will always have a matching actor and sender + + if ($AS->signer && is_array($AS->signer) && $AS->signer['id'] !== $AS->actor['id']) { + // the activity wasn't signed by the activity actor + return; + } + if (!$AS->sigok) { + // The activity signature isn't valid. + return; + } + } + + if ($v) { + // The sender has been validated and stored + $observer_hash = $hsig['portable_id']; + } + } + + if (!$observer_hash) { + return; + } + + // verify that this site has permitted communication with the sender. + + $m = parse_url($observer_hash); + + if ($m && $m['scheme'] && $m['host']) { + if (!check_siteallowed($m['scheme'] . '://' . $m['host'])) { + http_status_exit(403, 'Permission denied'); + } + // this site obviously isn't dead because they are trying to communicate with us. + $test = q( + "update site set site_dead = 0 where site_dead = 1 and site_url = '%s' ", + dbesc($m['scheme'] . '://' . $m['host']) + ); + } + if (!check_channelallowed($observer_hash)) { + http_status_exit(403, 'Permission denied'); + } + + // update the hubloc_connected timestamp, ignore failures + + $test = q( + "update hubloc set hubloc_connected = '%s' where hubloc_hash = '%s' and hubloc_network = 'activitypub'", + dbesc(datetime_convert()), + dbesc($observer_hash) + ); - if (! $sys_disabled) { - $channels[] = get_sys_channel(); - } + // Now figure out who the recipients are - } + if ($is_public) { + if (in_array($AS->type, ['Follow', 'Join']) && is_array($AS->obj) && ActivityStreams::is_an_actor($AS->obj['type'])) { + $channels = q( + "SELECT * from channel where channel_address = '%s' and channel_removed = 0 ", + dbesc(basename($AS->obj['id'])) + ); + } else { + // deliver to anybody following $AS->actor - } + $channels = q( + "SELECT * from channel where channel_id in ( SELECT abook_channel from abook left join xchan on abook_xchan = xchan_hash WHERE xchan_network = 'activitypub' and xchan_hash = '%s' ) and channel_removed = 0 ", + dbesc($observer_hash) + ); + if (!$channels) { + $channels = []; + } - // $channels represents all "potential" recipients. If they are not in this array, they will not receive the activity. - // If they are in this array, we will decide whether or not to deliver on a case-by-case basis. - - if (! $channels) { - logger('no deliveries on this site'); - return; - } + $parent = $AS->parent_id; + if ($parent) { + // this is a comment - deliver to everybody who owns the parent + $owners = q( + "SELECT * from channel where channel_id in ( SELECT uid from item where mid = '%s' ) ", + dbesc($parent) + ); + if ($owners) { + $channels = array_merge($channels, $owners); + } + } + } - // Bto and Bcc will only be present in a C2S transaction and should not be stored. - - $saved_recips = []; - foreach ( [ 'to', 'cc', 'audience' ] as $x ) { - if (array_key_exists($x,$AS->data)) { - $saved_recips[$x] = $AS->data[$x]; - } - } - $AS->set_recips($saved_recips); + if ($channels === false) { + $channels = []; + } + + if (in_array(ACTIVITY_PUBLIC_INBOX, $AS->recips) || in_array('Public', $AS->recips) || in_array('as:Public', $AS->recips)) { + // look for channels with send_stream = PERMS_PUBLIC (accept posts from anybody on the internet) + + $r = q("select * from channel where channel_id in (select uid from pconfig where cat = 'perm_limits' and k = 'send_stream' and v = '1' ) and channel_removed = 0 "); + if ($r) { + $channels = array_merge($channels, $r); + } + + // look for channels that are following hashtags. These will be checked in tgroup_check() + + $r = q("select * from channel where channel_id in (select uid from pconfig where cat = 'system' and k = 'followed_tags' and v != '' ) and channel_removed = 0 "); + if ($r) { + $channels = array_merge($channels, $r); + } - foreach ($channels as $channel) { + if (!$sys_disabled) { + $channels[] = get_sys_channel(); + } + } + } - // Even though activitypub may be enabled for the site, check if the channel has specifically disabled it - if (! PConfig::Get($channel['channel_id'],'system','activitypub',Config::Get('system','activitypub',ACTIVITYPUB_ENABLED))) { - continue; - } - - logger('inbox_channel: ' . $channel['channel_address'],LOGGER_DEBUG); + // $channels represents all "potential" recipients. If they are not in this array, they will not receive the activity. + // If they are in this array, we will decide whether or not to deliver on a case-by-case basis. - switch ($AS->type) { - case 'Follow': - if (is_array($AS->obj) && array_key_exists('type', $AS->obj) && ActivityStreams::is_an_actor($AS->obj['type'])) { - // do follow activity - Activity::follow($channel,$AS); - } - break; - case 'Invite': - if (is_array($AS->obj) && array_key_exists('type', $AS->obj) && $AS->obj['type'] === 'Group') { - // do follow activity - Activity::follow($channel,$AS); - } - break; - case 'Join': - if (is_array($AS->obj) && array_key_exists('type', $AS->obj) && $AS->obj['type'] === 'Group') { - // do follow activity - Activity::follow($channel,$AS); - } - break; - case 'Accept': - // Activitypub for wordpress sends lowercase 'follow' on accept. - // https://github.com/pfefferle/wordpress-activitypub/issues/97 - // Mobilizon sends Accept/"Member" (not in vocabulary) in response to Join/Group - if (is_array($AS->obj) && array_key_exists('type', $AS->obj) && in_array($AS->obj['type'], ['Follow','follow', 'Member'])) { - // do follow activity - Activity::follow($channel,$AS); - } - break; + if (!$channels) { + logger('no deliveries on this site'); + return; + } - case 'Reject': + // Bto and Bcc will only be present in a C2S transaction and should not be stored. - default: - break; + $saved_recips = []; + foreach (['to', 'cc', 'audience'] as $x) { + if (array_key_exists($x, $AS->data)) { + $saved_recips[$x] = $AS->data[$x]; + } + } + $AS->set_recips($saved_recips); - } - // These activities require permissions + foreach ($channels as $channel) { + // Even though activitypub may be enabled for the site, check if the channel has specifically disabled it + if (!PConfig::Get($channel['channel_id'], 'system', 'activitypub', Config::Get('system', 'activitypub', ACTIVITYPUB_ENABLED))) { + continue; + } - $item = null; + logger('inbox_channel: ' . $channel['channel_address'], LOGGER_DEBUG); - switch ($AS->type) { - case 'Update': - if (is_array($AS->obj) && array_key_exists('type',$AS->obj) && ActivityStreams::is_an_actor($AS->obj['type'])) { - Activity::actor_store($AS->obj['id'],$AS->obj, true /* force cache refresh */); - break; - } - case 'Accept': - if (is_array($AS->obj) && array_key_exists('type',$AS->obj) && (ActivityStreams::is_an_actor($AS->obj['type']) || $AS->obj['type'] === 'Member')) { - break; - } - case 'Create': - case 'Like': - case 'Dislike': - case 'Announce': - case 'Reject': - case 'TentativeAccept': - case 'TentativeReject': - case 'Add': - case 'Arrive': - case 'Block': - case 'Flag': - case 'Ignore': - case 'Invite': - case 'Listen': - case 'Move': - case 'Offer': - case 'Question': - case 'Read': - case 'Travel': - case 'View': - case 'emojiReaction': - case 'EmojiReaction': - case 'EmojiReact': - // These require a resolvable object structure - if (is_array($AS->obj)) { - // The boolean flag enables html cache of the item - $item = Activity::decode_note($AS,true); - } - else { - logger('unresolved object: ' . print_r($AS->obj,true)); - } - break; - case 'Undo': - if ($AS->obj && is_array($AS->obj) && array_key_exists('type', $AS->obj) && $AS->obj['type'] === 'Follow') { - // do unfollow activity - Activity::unfollow($channel,$AS); - break; - } - case 'Leave': - if ($AS->obj && is_array($AS->obj) && array_key_exists('type', $AS->obj) && $AS->obj['type'] === 'Group') { - // do unfollow activity - Activity::unfollow($channel,$AS); - break; - } - case 'Tombstone': - case 'Delete': - Activity::drop($channel,$observer_hash,$AS); - break; + switch ($AS->type) { + case 'Follow': + if (is_array($AS->obj) && array_key_exists('type', $AS->obj) && ActivityStreams::is_an_actor($AS->obj['type'])) { + // do follow activity + Activity::follow($channel, $AS); + } + break; + case 'Invite': + if (is_array($AS->obj) && array_key_exists('type', $AS->obj) && $AS->obj['type'] === 'Group') { + // do follow activity + Activity::follow($channel, $AS); + } + break; + case 'Join': + if (is_array($AS->obj) && array_key_exists('type', $AS->obj) && $AS->obj['type'] === 'Group') { + // do follow activity + Activity::follow($channel, $AS); + } + break; + case 'Accept': + // Activitypub for wordpress sends lowercase 'follow' on accept. + // https://github.com/pfefferle/wordpress-activitypub/issues/97 + // Mobilizon sends Accept/"Member" (not in vocabulary) in response to Join/Group + if (is_array($AS->obj) && array_key_exists('type', $AS->obj) && in_array($AS->obj['type'], ['Follow', 'follow', 'Member'])) { + // do follow activity + Activity::follow($channel, $AS); + } + break; - case 'Move': - if($observer_hash && $observer_hash === $AS->actor - && is_array($AS->obj) && array_key_exists('type', $AS->obj) && ActivityStream::is_an_actor($AS->obj['type']) - && is_array($AS->tgt) && array_key_exists('type', $AS->tgt) && ActivityStream::is_an_actor($AS->tgt['type'])) { - ActivityPub::move($AS->obj,$AS->tgt); - } - break; - case 'Add': - case 'Remove': + case 'Reject': + default: + break; + } - // for writeable collections as target, it's best to provide an array and include both the type and the id in the target element. - // If it's just a string id, we'll try to fetch the collection when we receive it and that's wasteful since we don't actually need - // the contents. - if (is_array($AS->obj) && isset($AS->tgt)) { - // The boolean flag enables html cache of the item - $item = Activity::decode_note($AS,true); - break; - } - default: - break; + // These activities require permissions - } + $item = null; - if ($item) { - logger('parsed_item: ' . print_r($item,true),LOGGER_DATA); - Activity::store($channel,$observer_hash,$AS,$item); - } + switch ($AS->type) { + case 'Update': + if (is_array($AS->obj) && array_key_exists('type', $AS->obj) && ActivityStreams::is_an_actor($AS->obj['type'])) { + Activity::actor_store($AS->obj['id'], $AS->obj, true /* force cache refresh */); + break; + } + case 'Accept': + if (is_array($AS->obj) && array_key_exists('type', $AS->obj) && (ActivityStreams::is_an_actor($AS->obj['type']) || $AS->obj['type'] === 'Member')) { + break; + } + case 'Create': + case 'Like': + case 'Dislike': + case 'Announce': + case 'Reject': + case 'TentativeAccept': + case 'TentativeReject': + case 'Add': + case 'Arrive': + case 'Block': + case 'Flag': + case 'Ignore': + case 'Invite': + case 'Listen': + case 'Move': + case 'Offer': + case 'Question': + case 'Read': + case 'Travel': + case 'View': + case 'emojiReaction': + case 'EmojiReaction': + case 'EmojiReact': + // These require a resolvable object structure + if (is_array($AS->obj)) { + // The boolean flag enables html cache of the item + $item = Activity::decode_note($AS, true); + } else { + logger('unresolved object: ' . print_r($AS->obj, true)); + } + break; + case 'Undo': + if ($AS->obj && is_array($AS->obj) && array_key_exists('type', $AS->obj) && $AS->obj['type'] === 'Follow') { + // do unfollow activity + Activity::unfollow($channel, $AS); + break; + } + case 'Leave': + if ($AS->obj && is_array($AS->obj) && array_key_exists('type', $AS->obj) && $AS->obj['type'] === 'Group') { + // do unfollow activity + Activity::unfollow($channel, $AS); + break; + } + case 'Tombstone': + case 'Delete': + Activity::drop($channel, $observer_hash, $AS); + break; - } + case 'Move': + if ( + $observer_hash && $observer_hash === $AS->actor + && is_array($AS->obj) && array_key_exists('type', $AS->obj) && ActivityStream::is_an_actor($AS->obj['type']) + && is_array($AS->tgt) && array_key_exists('type', $AS->tgt) && ActivityStream::is_an_actor($AS->tgt['type']) + ) { + ActivityPub::move($AS->obj, $AS->tgt); + } + break; + case 'Add': + case 'Remove': + // for writeable collections as target, it's best to provide an array and include both the type and the id in the target element. + // If it's just a string id, we'll try to fetch the collection when we receive it and that's wasteful since we don't actually need + // the contents. + if (is_array($AS->obj) && isset($AS->tgt)) { + // The boolean flag enables html cache of the item + $item = Activity::decode_note($AS, true); + break; + } + default: + break; + } - http_status_exit(200,'OK'); - } + if ($item) { + logger('parsed_item: ' . print_r($item, true), LOGGER_DATA); + Activity::store($channel, $observer_hash, $AS, $item); + } + } - function get() { - - } + http_status_exit(200, 'OK'); + } + public function get() + { + } } - - - diff --git a/Zotlabs/Module/Inspect.php b/Zotlabs/Module/Inspect.php index e8081496e..dfa8d02fa 100644 --- a/Zotlabs/Module/Inspect.php +++ b/Zotlabs/Module/Inspect.php @@ -6,88 +6,86 @@ use App; use Zotlabs\Web\Controller; use Zotlabs\Lib\Activity; -class Inspect extends Controller { +class Inspect extends Controller +{ - function get() { - - $output = EMPTY_STR; + public function get() + { - if (! is_site_admin()) { - notice( t('Permission denied.') . EOL); - return $output; - } - - $sys = get_sys_channel(); + $output = EMPTY_STR; - if (argc() > 2) { - $item_type = argv(1); - $item_id = argv(2); - } - elseif (argc() > 1) { - $item_type = 'item'; - $item_id = argv(1); - } - - if (! $item_id) { - App::$error = 404; - notice( t('Item not found.') . EOL); - } - - if ($item_type === 'item') { - $r = q("select * from item where uuid = '%s' or id = %d ", - dbesc($item_id), - intval($item_id) - ); - - if ($r) { - xchan_query($r); - $items = fetch_post_tags($r,true); - } + if (!is_site_admin()) { + notice(t('Permission denied.') . EOL); + return $output; + } - if(! $items) { - return $output; - } + $sys = get_sys_channel(); - foreach ($items as $item) { - if ($item['obj']) { - $item['obj'] = json_decode($item['obj'],true); - } - if ($item['target']) { - $item['target'] = json_decode($item['target'],true); - } - if ($item['attach']) { - $item['attach'] = json_decode($item['attach'],true); - } + if (argc() > 2) { + $item_type = argv(1); + $item_id = argv(2); + } elseif (argc() > 1) { + $item_type = 'item'; + $item_id = argv(1); + } - $output .= '
      ' . print_array($item) . '
      ' . EOL . EOL; + if (!$item_id) { + App::$error = 404; + notice(t('Item not found.') . EOL); + } - $output .= '
      ' . escape_tags(json_encode(Activity::encode_activity($item,true), JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES)) . '
      ' . EOL . EOL; + if ($item_type === 'item') { + $r = q( + "select * from item where uuid = '%s' or id = %d ", + dbesc($item_id), + intval($item_id) + ); - $output .= '
      ' . escape_tags(json_encode(json_decode(get_iconfig($item['id'],'activitypub','rawmsg'),true), JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES)) . '
      ' . EOL . EOL; - - } + if ($r) { + xchan_query($r); + $items = fetch_post_tags($r, true); + } - } + if (!$items) { + return $output; + } - if ($item_type === 'xchan') { - $items = q("select * from xchan left join hubloc on xchan_hash = hubloc_hash where hubloc_hash = '%s' or hubloc_addr = '%s' ", - dbesc($item_id), - dbesc($item_id) - ); - - if(! $items) { - return $output; - } + foreach ($items as $item) { + if ($item['obj']) { + $item['obj'] = json_decode($item['obj'], true); + } + if ($item['target']) { + $item['target'] = json_decode($item['target'], true); + } + if ($item['attach']) { + $item['attach'] = json_decode($item['attach'], true); + } - foreach ($items as $item) { - $output .= '
      ' . print_array($item) . '
      ' . EOL . EOL; - } - } + $output .= '
      ' . print_array($item) . '
      ' . EOL . EOL; + + $output .= '
      ' . escape_tags(json_encode(Activity::encode_activity($item, true), JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)) . '
      ' . EOL . EOL; + + $output .= '
      ' . escape_tags(json_encode(json_decode(get_iconfig($item['id'], 'activitypub', 'rawmsg'), true), JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)) . '
      ' . EOL . EOL; + } + } + + if ($item_type === 'xchan') { + $items = q( + "select * from xchan left join hubloc on xchan_hash = hubloc_hash where hubloc_hash = '%s' or hubloc_addr = '%s' ", + dbesc($item_id), + dbesc($item_id) + ); + + if (!$items) { + return $output; + } + + foreach ($items as $item) { + $output .= '
      ' . print_array($item) . '
      ' . EOL . EOL; + } + } - - return $output; - } - - + return $output; + } } diff --git a/Zotlabs/Module/Invite.php b/Zotlabs/Module/Invite.php index 38f85fe5a..f400e1ebd 100644 --- a/Zotlabs/Module/Invite.php +++ b/Zotlabs/Module/Invite.php @@ -1,4 +1,5 @@ $max_invites) { + notice(t('Total invitation limit exceeded.') . EOL); + return; + } -class Invite extends Controller { + $recips = ((x($_POST, 'recipients')) ? explode("\n", $_POST['recipients']) : []); + $message = ((x($_POST, 'message')) ? notags(trim($_POST['message'])) : ''); - function post() { - - if (! local_channel()) { - return; - } + $total = 0; - if(! Apps::system_app_installed(local_channel(), 'Invite')) { - return; - } - - check_form_security_token_redirectOnErr('/', 'send_invite'); - - $max_invites = intval(get_config('system','max_invites'), 20); - - $current_invites = intval(get_pconfig(local_channel(),'system','sent_invites')); - if($current_invites > $max_invites) { - notice( t('Total invitation limit exceeded.') . EOL); - return; - }; - - - $recips = ((x($_POST,'recipients')) ? explode("\n",$_POST['recipients']) : []); - $message = ((x($_POST,'message')) ? notags(trim($_POST['message'])) : ''); - - $total = 0; - - if (get_config('system','invitation_only')) { - $invonly = true; - $x = get_pconfig(local_channel(),'system','invites_remaining'); - if ((! $x) && (! is_site_admin())) - return; - } - - foreach ($recips as $recip) { - - $recip = trim($recip); - if(! $recip) - continue; - - if(! validate_email($recip)) { - notice( sprintf( t('%s : Not a valid email address.'), $recip) . EOL); - continue; - } - - else - $nmessage = $message; - - $account = App::get_account(); - - $res = z_mail( - [ - 'toEmail' => $recip, - 'fromName' => ' ', - 'fromEmail' => $account['account_email'], - 'messageSubject' => t('Please join us on $Projectname'), - 'textVersion' => $nmessage, - ] - ); - - if($res) { - $total ++; - $current_invites ++; - set_pconfig(local_channel(),'system','sent_invites',$current_invites); - if($current_invites > $max_invites) { - notice( t('Invitation limit exceeded. Please contact your site administrator.') . EOL); - return; - } - } - else { - notice( sprintf( t('%s : Message delivery failed.'), $recip) . EOL); - } - - } - notice( sprintf( tt("%d message sent.", "%d messages sent.", $total) , $total) . EOL); - return; - } - - - function get() { - - if(! local_channel()) { - notice( t('Permission denied.') . EOL); - return; - } + if (get_config('system', 'invitation_only')) { + $invonly = true; + $x = get_pconfig(local_channel(), 'system', 'invites_remaining'); + if ((!$x) && (!is_site_admin())) { + return; + } + } - if(! Apps::system_app_installed(local_channel(), 'Invite')) { - //Do not display any associated widgets at this point - App::$pdl = ''; + foreach ($recips as $recip) { + $recip = trim($recip); + if (!$recip) { + continue; + } - $o = 'Invite App (Not Installed):
      '; - $o .= t('Send email invitations to join this network'); - return $o; - } + if (!validate_email($recip)) { + notice(sprintf(t('%s : Not a valid email address.'), $recip) . EOL); + continue; + } else { + $nmessage = $message; + } - nav_set_selected('Invite'); - - $tpl = get_markup_template('invite.tpl'); - $invonly = false; - - if(get_config('system','invitation_only')) { - $invonly = true; - $x = get_pconfig(local_channel(),'system','invites_remaining'); - if((! $x) && (! is_site_admin())) { - notice( t('You have no more invitations available') . EOL); - return ''; - } - } - - if($invonly && ($x || is_site_admin())) { - $invite_code = autoname(8) . rand(1000,9999); - $nmessage = str_replace('$invite_code',$invite_code,$message); - - $r = q("INSERT INTO register (hash,created,uid,password,lang) VALUES ('%s', '%s',0,'','') ", - dbesc($invite_code), - dbesc(datetime_convert()) - ); - - if(! is_site_admin()) { - $x --; - if($x >= 0) - set_pconfig(local_channel(),'system','invites_remaining',$x); - else - return; - } - } - - $ob = App::get_observer(); - if(! $ob) - return $o; - - $channel = App::get_channel(); - - $o = replace_macros($tpl, array( - '$form_security_token' => get_form_security_token("send_invite"), - '$invite' => t('Send invitations'), - '$addr_text' => t('Enter email addresses, one per line:'), - '$msg_text' => t('Your message:'), - '$default_message' => t('Please join my community on $Projectname.') . "\r\n" . "\r\n" - . $linktxt - . (($invonly) ? "\r\n" . "\r\n" . t('You will need to supply this invitation code:') . " " . $invite_code . "\r\n" . "\r\n" : '') - . t('1. Register at any $Projectname location (they are all inter-connected)') - . "\r\n" . "\r\n" . z_root() . '/register' - . "\r\n" . "\r\n" . t('2. Enter my $Projectname network address into the site searchbar.') - . "\r\n" . "\r\n" . $ob['xchan_addr'] . ' (' . t('or visit') . " " . z_root() . '/channel/' . $channel['channel_address'] . ')' - . "\r\n" . "\r\n" - . t('3. Click [Connect]') - . "\r\n" . "\r\n" , - '$submit' => t('Submit') - )); - - return $o; - } - + $account = App::get_account(); + + $res = z_mail( + [ + 'toEmail' => $recip, + 'fromName' => ' ', + 'fromEmail' => $account['account_email'], + 'messageSubject' => t('Please join us on $Projectname'), + 'textVersion' => $nmessage, + ] + ); + + if ($res) { + $total++; + $current_invites++; + set_pconfig(local_channel(), 'system', 'sent_invites', $current_invites); + if ($current_invites > $max_invites) { + notice(t('Invitation limit exceeded. Please contact your site administrator.') . EOL); + return; + } + } else { + notice(sprintf(t('%s : Message delivery failed.'), $recip) . EOL); + } + } + notice(sprintf(tt("%d message sent.", "%d messages sent.", $total), $total) . EOL); + return; + } + + + public function get() + { + + if (!local_channel()) { + notice(t('Permission denied.') . EOL); + return; + } + + if (!Apps::system_app_installed(local_channel(), 'Invite')) { + //Do not display any associated widgets at this point + App::$pdl = ''; + + $o = 'Invite App (Not Installed):
      '; + $o .= t('Send email invitations to join this network'); + return $o; + } + + nav_set_selected('Invite'); + + $tpl = get_markup_template('invite.tpl'); + $invonly = false; + + if (get_config('system', 'invitation_only')) { + $invonly = true; + $x = get_pconfig(local_channel(), 'system', 'invites_remaining'); + if ((!$x) && (!is_site_admin())) { + notice(t('You have no more invitations available') . EOL); + return ''; + } + } + + if ($invonly && ($x || is_site_admin())) { + $invite_code = autoname(8) . rand(1000, 9999); + $nmessage = str_replace('$invite_code', $invite_code, $message); + + $r = q( + "INSERT INTO register (hash,created,uid,password,lang) VALUES ('%s', '%s',0,'','') ", + dbesc($invite_code), + dbesc(datetime_convert()) + ); + + if (!is_site_admin()) { + $x--; + if ($x >= 0) { + set_pconfig(local_channel(), 'system', 'invites_remaining', $x); + } else { + return; + } + } + } + + $ob = App::get_observer(); + if (!$ob) { + return $o; + } + + $channel = App::get_channel(); + + $o = replace_macros($tpl, array( + '$form_security_token' => get_form_security_token("send_invite"), + '$invite' => t('Send invitations'), + '$addr_text' => t('Enter email addresses, one per line:'), + '$msg_text' => t('Your message:'), + '$default_message' => t('Please join my community on $Projectname.') . "\r\n" . "\r\n" + . $linktxt + . (($invonly) ? "\r\n" . "\r\n" . t('You will need to supply this invitation code:') . " " . $invite_code . "\r\n" . "\r\n" : '') + . t('1. Register at any $Projectname location (they are all inter-connected)') + . "\r\n" . "\r\n" . z_root() . '/register' + . "\r\n" . "\r\n" . t('2. Enter my $Projectname network address into the site searchbar.') + . "\r\n" . "\r\n" . $ob['xchan_addr'] . ' (' . t('or visit') . " " . z_root() . '/channel/' . $channel['channel_address'] . ')' + . "\r\n" . "\r\n" + . t('3. Click [Connect]') + . "\r\n" . "\r\n", + '$submit' => t('Submit') + )); + + return $o; + } } diff --git a/Zotlabs/Module/Item.php b/Zotlabs/Module/Item.php index 527421f65..8ca5ddcd1 100644 --- a/Zotlabs/Module/Item.php +++ b/Zotlabs/Module/Item.php @@ -8,16 +8,16 @@ namespace Zotlabs\Module; * acts as a permalink for local content. * * Otherwise this is the POST destination for most all locally posted - * text stuff. This function handles status, wall-to-wall status, - * local comments, and remote coments that are posted on this site + * text stuff. This function handles status, wall-to-wall status, + * local comments, and remote coments that are posted on this site * (as opposed to being delivered in a feed). - * Also processed here are posts and comments coming through the API. - * All of these become an "item" which is our basic unit of + * Also processed here are posts and comments coming through the API. + * All of these become an "item" which is our basic unit of * information. - * Posts that originate externally or do not fall into the above - * posting categories go through item_store() instead of this function. + * Posts that originate externally or do not fall into the above + * posting categories go through item_store() instead of this function. * - */ + */ use Zotlabs\Lib\Libsync; use Zotlabs\Lib\Activity; @@ -46,1905 +46,1942 @@ require_once('include/security.php'); use Zotlabs\Lib as Zlib; -class Item extends Controller { - - public $return_404 = false; - - function init() { - - - if (ActivityStreams::is_as_request()) { - $item_uuid = argv(1); - if (! $item_uuid) { - http_status_exit(404, 'Not found'); - } - $portable_id = EMPTY_STR; - - $item_normal = " and item.item_hidden = 0 and item.item_type = 0 and item.item_unpublished = 0 and item.item_delayed = 0 and item.item_blocked = 0 "; - - $i = null; - - // do we have the item (at all)? - // add preferential bias to item owners (item_wall = 1) - - $r = q("select * from item where (mid = '%s' or uuid = '%s') $item_normal order by item_wall desc limit 1", - dbesc(z_root() . '/item/' . $item_uuid), - dbesc($item_uuid) - ); - - if (! $r) { - http_status_exit(404,'Not found'); - } - - // process an authenticated fetch - - - $sigdata = HTTPSig::verify(EMPTY_STR); - if ($sigdata['portable_id'] && $sigdata['header_valid']) { - $portable_id = $sigdata['portable_id']; - if (! check_channelallowed($portable_id)) { - http_status_exit(403, 'Permission denied'); - } - if (! check_siteallowed($sigdata['signer'])) { - http_status_exit(403, 'Permission denied'); - } - observer_auth($portable_id); - - $i = q("select id as item_id from item where mid = '%s' $item_normal and owner_xchan = '%s' limit 1 ", - dbesc($r[0]['parent_mid']), - dbesc($portable_id) - ); - } - elseif (Config::get('system','require_authenticated_fetch',false)) { - http_status_exit(403,'Permission denied'); - } - - // if we don't have a parent id belonging to the signer see if we can obtain one as a visitor that we have permission to access - // with a bias towards those items owned by channels on this site (item_wall = 1) - - $sql_extra = item_permissions_sql(0); - - if (! $i) { - $i = q("select id as item_id from item where mid = '%s' $item_normal $sql_extra order by item_wall desc limit 1", - dbesc($r[0]['parent_mid']) - ); - } - - $bear = Activity::token_from_request(); - if ($bear) { - logger('bear: ' . $bear, LOGGER_DEBUG); - if (! $i) { - $t = q("select * from iconfig where cat = 'ocap' and k = 'relay' and v = '%s'", - dbesc($bear) - ); - if ($t) { - $i = q("select id as item_id from item where uuid = '%s' and id = %d $item_normal limit 1", - dbesc($item_uuid), - intval($t[0]['iid']) - ); - } - } - } - - if (! $i) { - http_status_exit(403,'Forbidden'); - } - - // If we get to this point we have determined we can access the original in $r (fetched much further above), so use it. - - xchan_query($r,true); - $items = fetch_post_tags($r,false); - - $chan = channelx_by_n($items[0]['uid']); - - if (! $chan) { - http_status_exit(404, 'Not found'); - } - - if (! perm_is_allowed($chan['channel_id'],get_observer_hash(),'view_stream')) { - http_status_exit(403, 'Forbidden'); - } - - $i = Activity::encode_item($items[0],true); - - if (! $i) { - http_status_exit(404, 'Not found'); - } - - - if ($portable_id && (! intval($items[0]['item_private']))) { - $c = q("select abook_id from abook where abook_channel = %d and abook_xchan = '%s'", - intval($items[0]['uid']), - dbesc($portable_id) - ); - if (! $c) { - ThreadListener::store(z_root() . '/item/' . $item_uuid,$portable_id); - } - } - - as_return_and_die($i,$chan); - } - - if (Libzot::is_zot_request()) { - - $item_uuid = argv(1); - - if (! $item_uuid) { - http_status_exit(404, 'Not found'); - } - - $portable_id = EMPTY_STR; - - $item_normal = " and item.item_hidden = 0 and item.item_type = 0 and item.item_unpublished = 0 and item.item_delayed = 0 and item.item_blocked = 0 and not verb in ( 'Follow', 'Ignore' ) "; - - $i = null; - - // do we have the item (at all)? - - $r = q("select * from item where (mid = '%s' or uuid = '%s') $item_normal limit 1", - dbesc(z_root() . '/item/' . $item_uuid), - dbesc($item_uuid) - ); - - if (! $r) { - http_status_exit(404,'Not found'); - } - - // process an authenticated fetch - - $sigdata = HTTPSig::verify(($_SERVER['REQUEST_METHOD'] === 'POST') ? file_get_contents('php://input') : EMPTY_STR); - if ($sigdata['portable_id'] && $sigdata['header_valid']) { - $portable_id = $sigdata['portable_id']; - if (! check_channelallowed($portable_id)) { - http_status_exit(403, 'Permission denied'); - } - if (! check_siteallowed($sigdata['signer'])) { - http_status_exit(403, 'Permission denied'); - } - observer_auth($portable_id); - - $i = q("select id as item_id from item where mid = '%s' $item_normal and owner_xchan = '%s' limit 1", - dbesc($r[0]['parent_mid']), - dbesc($portable_id) - ); - } - elseif (Config::get('system','require_authenticated_fetch',false)) { - http_status_exit(403,'Permission denied'); - } - - // if we don't have a parent id belonging to the signer see if we can obtain one as a visitor that we have permission to access - // with a bias towards those items owned by channels on this site (item_wall = 1) - - $sql_extra = item_permissions_sql(0); - - if (! $i) { - $i = q("select id as item_id from item where mid = '%s' $item_normal $sql_extra order by item_wall desc limit 1", - dbesc($r[0]['parent_mid']) - ); - } - - $bear = Activity::token_from_request(); - if ($bear) { - logger('bear: ' . $bear, LOGGER_DEBUG); - if (! $i) { - $t = q("select * from iconfig where cat = 'ocap' and k = 'relay' and v = '%s'", - dbesc($bear) - ); - if ($t) { - $i = q("select id as item_id from item where uuid = '%s' and id = %d $item_normal limit 1", - dbesc($item_uuid), - intval($t[0]['iid']) - ); - } - } - } - - if (! $i) { - http_status_exit(403,'Forbidden'); - } - - $parents_str = ids_to_querystr($i,'item_id'); - - $items = q("SELECT item.*, item.id AS item_id FROM item WHERE item.parent IN ( %s ) $item_normal order by item.id asc", - dbesc($parents_str) - ); - - if (! $items) { - http_status_exit(404, 'Not found'); - } - - xchan_query($items,true); - $items = fetch_post_tags($items,true); - - if (! $items) { - http_status_exit(404, 'Not found'); - } - $chan = channelx_by_n($items[0]['uid']); - - if (! $chan) { - http_status_exit(404, 'Not found'); - } - - if (! perm_is_allowed($chan['channel_id'],get_observer_hash(),'view_stream')) { - http_status_exit(403, 'Forbidden'); - } - - $i = Activity::encode_item_collection($items,'conversation/' . $item_uuid,'OrderedCollection',true, count($items)); - if ($portable_id && (! intval($items[0]['item_private']))) { - ThreadListener::store(z_root() . '/item/' . $item_uuid,$portable_id); - } - - if (! $i) { - http_status_exit(404, 'Not found'); - } - $x = array_merge(['@context' => [ - ACTIVITYSTREAMS_JSONLD_REV, - 'https://w3id.org/security/v1', - Activity::ap_schema() - ]], $i); - - $headers = []; - $headers['Content-Type'] = 'application/x-nomad+json' ; - $x['signature'] = LDSignatures::sign($x,$chan); - $ret = json_encode($x, JSON_UNESCAPED_SLASHES); - $headers['Digest'] = HTTPSig::generate_digest_header($ret); - $headers['(request-target)'] = strtolower($_SERVER['REQUEST_METHOD']) . ' ' . $_SERVER['REQUEST_URI']; - $h = HTTPSig::create_sig($headers,$chan['channel_prvkey'],channel_url($chan)); - HTTPSig::set_headers($h); - echo $ret; - killme(); - - } - - // if it isn't a drop command and isn't a post method and wasn't handled already, - // the default action is a browser request for a persistent uri and this should return - // the text/html page of the item. - - if (argc() > 1 && argv(1) !== 'drop') { - $x = q("select uid, item_wall, llink, mid from item where mid = '%s' or mid = '%s' or uuid = '%s'", - dbesc(z_root() . '/item/' . argv(1)), - dbesc(z_root() . '/activity/' . argv(1)), - dbesc(argv(1)) - ); - if ($x) { - foreach ($x as $xv) { - if (intval($xv['item_wall'])) { - $c = channelx_by_n($xv['uid']); - if ($c) { - goaway($c['xchan_url'] . '?mid=' . gen_link_id($xv['mid'])); - } - } - } - goaway($x[0]['llink']); - } - - // save this state and catch it in the get() function - $this->return_404 = true; - } - } - - function post() { - - if ((! local_channel()) && (! remote_channel()) && (! isset($_REQUEST['anonname']))) { - return; - } - - // drop an array of items. - - if (isset($_REQUEST['dropitems'])) { - $arr_drop = explode(',',$_REQUEST['dropitems']); - drop_items($arr_drop); - $json = array('success' => 1); - echo json_encode($json); - killme(); - } - - - $uid = local_channel(); - $channel = null; - $observer = null; - $token = EMPTY_STR; - $datarray = []; - $item_starred = false; - $item_uplink = false; - $item_notshown = false; - $item_nsfw = false; - $item_relay = false; - $item_mentionsme = false; - $item_verified = false; - $item_retained = false; - $item_rss = false; - $item_deleted = false; - $item_hidden = false; - $item_delayed = false; - $item_pending_remove = false; - $item_blocked = false; - - $post_tags = false; - $pub_copy = false; - - - - - /** - * Is this a reply to something? - */ - - $parent = ((isset($_REQUEST['parent'])) ? intval($_REQUEST['parent']) : 0); - $parent_mid = ((isset($_REQUEST['parent_mid'])) ? trim($_REQUEST['parent_mid']) : ''); - - $hidden_mentions = ((isset($_REQUEST['hidden_mentions'])) ? trim($_REQUEST['hidden_mentions']) : ''); - - - /** - * Who is viewing this page and posting this thing - */ - - $remote_xchan = ((isset($_REQUEST['remote_xchan'])) ? trim($_REQUEST['remote_xchan']) : false); - $remote_observer = xchan_match( ['xchan_hash' => $remote_xchan ] ); - - if (! $remote_observer) { - $remote_xchan = $remote_observer = false; - } - - // This is the local channel representing who the posted item will belong to. - - $profile_uid = ((isset($_REQUEST['profile_uid'])) ? intval($_REQUEST['profile_uid']) : 0); - - // *If* you are logged in as the site admin you are allowed to create top-level items for the sys channel. - // This would typically be a webpage or webpage element. - // Comments and replies are excluded because further below we also check for sys channel ownership and - // will make a copy of the parent that you can interact with in your own stream - - $sys = get_sys_channel(); - if ($sys && $profile_uid && ($sys['channel_id'] == $profile_uid) && is_site_admin() && ! $parent) { - $uid = intval($sys['channel_id']); - $channel = $sys; - $observer = $sys; - } - - call_hooks('post_local_start', $_REQUEST); - - // logger('postvars ' . print_r($_REQUEST,true), LOGGER_DATA); - - $api_source = ((isset($_REQUEST['api_source']) && $_REQUEST['api_source']) ? true : false); - - $nocomment = 0; - if (isset($_REQUEST['comments_enabled'])) { - $nocomment = 1 - intval($_REQUEST['comments_enabled']); - } - - // this is in days, convert to absolute time - $channel_comments_closed = get_pconfig($profile_uid,'system','close_comments'); - if (intval($channel_comments_closed)) { - $channel_comments_closed = datetime_convert(date_Default_timezone_get(),'UTC', 'now + ' . intval($channel_comments_closed) . ' days'); - } - else { - $channel_comments_closed = NULL_DATE; - } - - $comments_closed = ((isset($_REQUEST['comments_closed']) && $_REQUEST['comments_closed']) ? datetime_convert(date_default_timezone_get(),'UTC',$_REQUEST['comments_closed']) : $channel_comments_closed); - - $is_poll = ((trim($_REQUEST['poll_answers'][0]) != '' && trim($_REQUEST['poll_answers'][1]) != '') ? true : false); - - // 'origin' (if non-zero) indicates that this network is where the message originated, - // for the purpose of relaying comments to other conversation members. - // If using the API from a device (leaf node) you must set origin to 1 (default) or leave unset. - // If the API is used from another network with its own distribution - // and deliveries, you may wish to set origin to 0 or false and allow the other - // network to relay comments. - - // If you are unsure, it is prudent (and important) to leave it unset. - - $origin = (($api_source && array_key_exists('origin',$_REQUEST)) ? intval($_REQUEST['origin']) : 1); - - // To represent message-ids on other networks - this will create an iconfig record - - $namespace = (($api_source && array_key_exists('namespace',$_REQUEST)) ? strip_tags($_REQUEST['namespace']) : ''); - $remote_id = (($api_source && array_key_exists('remote_id',$_REQUEST)) ? strip_tags($_REQUEST['remote_id']) : ''); - - $owner_hash = null; - - $message_id = ((x($_REQUEST,'message_id') && $api_source) ? strip_tags($_REQUEST['message_id']) : ''); - $created = ((x($_REQUEST,'created')) ? datetime_convert(date_default_timezone_get(),'UTC',$_REQUEST['created']) : datetime_convert()); - - // Because somebody will probably try this and create a mess - - if ($created <= NULL_DATE) { - $created = datetime_convert(); - } - - $post_id = ((x($_REQUEST,'post_id')) ? intval($_REQUEST['post_id']) : 0); - - $app = ((x($_REQUEST,'source')) ? strip_tags($_REQUEST['source']) : ''); - $return_path = ((x($_REQUEST,'return')) ? $_REQUEST['return'] : ''); - $preview = ((x($_REQUEST,'preview')) ? intval($_REQUEST['preview']) : 0); - $categories = ((x($_REQUEST,'category')) ? escape_tags($_REQUEST['category']) : ''); - $webpage = ((x($_REQUEST,'webpage')) ? intval($_REQUEST['webpage']) : 0); - $item_obscured = ((x($_REQUEST,'obscured')) ? intval($_REQUEST['obscured']) : 0); - $pagetitle = ((x($_REQUEST,'pagetitle')) ? escape_tags(urlencode($_REQUEST['pagetitle'])) : ''); - $layout_mid = ((x($_REQUEST,'layout_mid')) ? escape_tags($_REQUEST['layout_mid']): ''); - $plink = ((x($_REQUEST,'permalink')) ? escape_tags($_REQUEST['permalink']) : ''); - $obj_type = ((x($_REQUEST,'obj_type')) ? escape_tags($_REQUEST['obj_type']) : ACTIVITY_OBJ_NOTE); - - - $item_unpublished = ((isset($_REQUEST['draft'])) ? intval($_REQUEST['draft']) : 0); - - // allow API to bulk load a bunch of imported items without sending out a bunch of posts. - $nopush = ((x($_REQUEST,'nopush')) ? intval($_REQUEST['nopush']) : 0); - - /* - * Check service class limits - */ - if ($uid && !(x($_REQUEST,'parent')) && !(x($_REQUEST,'post_id'))) { - $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(); - } - } - - if ($pagetitle) { - $pagetitle = strtolower(URLify::transliterate($pagetitle)); - } - - $item_flags = $item_restrict = 0; - $expires = NULL_DATE; - - $route = ''; - $parent_item = null; - $parent_contact = null; - $thr_parent = ''; - $parid = 0; - $r = false; - - - // If this is a comment, find the parent and preset some stuff - - if ($parent || $parent_mid) { - - if (! x($_REQUEST,'type')) { - $_REQUEST['type'] = 'net-comment'; - } - if ($obj_type == ACTIVITY_OBJ_NOTE) { - $obj_type = ACTIVITY_OBJ_COMMENT; - } - - // fetch the parent item - - if ($parent) { - $r = q("SELECT * FROM item WHERE id = %d LIMIT 1", - intval($parent) - ); - } - elseif ($parent_mid && $uid) { - // This is coming from an API source, and we are logged in - $r = q("SELECT * FROM item WHERE mid = '%s' AND uid = %d LIMIT 1", - dbesc($parent_mid), - intval($uid) - ); - } - - // if this isn't the real parent of the conversation, find it - if ($r) { - $parid = $r[0]['parent']; - $parent_mid = $r[0]['mid']; - if ($r[0]['id'] != $r[0]['parent']) { - $r = q("SELECT * FROM item WHERE id = parent AND parent = %d LIMIT 1", - intval($parid) - ); - } - - // if interacting with a pubstream item (owned by the sys channel), - // create a copy of the parent in your stream - - // $r may have changed. Check it again before trying to use it. - - if ($r && local_channel() && (! is_sys_channel(local_channel()))) { - $old_id = $r[0]['id']; - $r = [ copy_of_pubitem(App::get_channel(), $r[0]['mid']) ]; - if ($r[0]['id'] !== $old_id) { - // keep track that a copy was made to display a special status notice that is unique to this condition - $pub_copy = true; - } - } - } - - if (! $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(); - } - - xchan_query($r,true); - - $parent_item = $r[0]; - $parent = $r[0]['id']; - - // multi-level threading - preserve the info but re-parent to our single level threading - - $thr_parent = $parent_mid; - - $route = $parent_item['route']; - - } - - if ($parent_item && isset($parent_item['replyto']) && $parent_item['replyto']) { - $replyto = unserialise($parent_item['replyto']); - } - - $moderated = false; - - if (! $observer) { - $observer = App::get_observer(); - if (! $observer) { - // perhaps we're allowing moderated comments from anonymous viewers - $observer = anon_identity_init($_REQUEST); - if ($observer) { - $moderated = true; - $remote_xchan = $remote_observer = $observer; - } - } - } - - if (! $observer) { - notice( t('Permission denied.') . EOL) ; - if ($api_source) { - return ( [ 'success' => false, 'message' => 'permission denied' ] ); - } - if (x($_REQUEST,'return')) { - goaway(z_root() . "/" . $return_path ); - } - killme(); - } - - if ($parent) { - logger('mod_item: item_post parent=' . $parent); - $can_comment = false; - - $can_comment = can_comment_on_post($observer['xchan_hash'],$parent_item); - if (! $can_comment) { - if ((array_key_exists('owner',$parent_item)) && intval($parent_item['owner']['abook_self']) == 1 ) { - $can_comment = perm_is_allowed($profile_uid,$observer['xchan_hash'],'post_comments'); - } - } - - 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(); - } - } - else { - // fixme - $webpage could also be a wiki page or article and require a different permission to be checked. - 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(); - } - } - - // check if this author is being moderated through the 'moderated' (negative) permission - // when posting wall-to-wall - if ($moderated === false && intval($uid) !== intval($profile_uid)) { - $moderated = perm_is_allowed($profile_uid,$observer['xchan_hash'],'moderated'); - } - - // If this is a comment, check the moderated permission of the parent; who may be on another site - $remote_moderated = (($parent) ? their_perms_contains($profile_uid,$parent_item['owner_xchan'],'moderated') : false); - if ($remote_moderated) { - notice( t('Comment may be moderated.') . EOL); - } - - // is this an edited post? - - $orig_post = null; - - if ($namespace && $remote_id) { - // It wasn't an internally generated post - see if we've got an item matching this remote service id - $i = q("select iid from iconfig where cat = 'system' and k = '%s' and v = '%s' limit 1", - dbesc($namespace), - dbesc($remote_id) - ); - if($i) - $post_id = $i[0]['iid']; - } - - $iconfig = null; - - if($post_id) { - $i = q("SELECT * FROM item WHERE uid = %d AND id = %d LIMIT 1", - intval($profile_uid), - intval($post_id) - ); - if(! count($i)) - killme(); - $orig_post = $i[0]; - $iconfig = q("select * from iconfig where iid = %d", - intval($post_id) - ); - } - - - if(! $channel) { - if($uid && $uid == $profile_uid) { - $channel = App::get_channel(); - } - else { - // posting as yourself but not necessarily to a channel you control - $r = q("select * from channel left join account on channel_account_id = account_id where channel_id = %d LIMIT 1", - intval($profile_uid) - ); - if($r) - $channel = $r[0]; - } - } - - - 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(); - } - - $owner_xchan = null; - - $r = q("select * from xchan where xchan_hash = '%s' limit 1", - dbesc($channel['channel_hash']) - ); - if($r && count($r)) { - $owner_xchan = $r[0]; - } - 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(); - } - - $walltowall = false; - $walltowall_comment = false; - - if($remote_xchan && ! $moderated) - $observer = $remote_observer; - - if($observer) { - logger('mod_item: post accepted from ' . $observer['xchan_name'] . ' for ' . $owner_xchan['xchan_name'], LOGGER_DEBUG); - - // wall-to-wall detection. - // For top-level posts, if the author and owner are different it's a wall-to-wall - // For comments, We need to additionally look at the parent and see if it's a wall post that originated locally. - - if($observer['xchan_name'] != $owner_xchan['xchan_name']) { - if(($parent_item) && ($parent_item['item_wall'] && $parent_item['item_origin'])) { - $walltowall_comment = true; - $walltowall = true; - } - if(! $parent) { - $walltowall = true; - } - } - } - - if (! isset($replyto)) { - if (strpos($owner_xchan['xchan_hash'],'http') === 0) { - $replyto = $owner_xchan['xchan_hash']; - } - else { - $replyto = $owner_xchan['xchan_url']; - } - } - - - $acl = new AccessControl($channel); - - $view_policy = PermissionLimits::Get($channel['channel_id'],'view_stream'); - $comment_policy = ((isset($_REQUEST['comments_from']) && intval($_REQUEST['comments_from'])) ? intval($_REQUEST['comments_from']) : PermissionLimits::Get($channel['channel_id'],'post_comments')); - - $public_policy = ((x($_REQUEST,'public_policy')) ? escape_tags($_REQUEST['public_policy']) : map_scope($view_policy,true)); - if($webpage) - $public_policy = ''; - if($public_policy) - $private = 1; - - if($orig_post) { - - $private = 0; - // webpages and unpublished drafts are allowed to change ACLs after the fact. Normal conversation items aren't. - if($webpage || intval($orig_post['item_unpublished'])) { - $acl->set_from_array($_REQUEST); - } - else { - $acl->set($orig_post); - $public_policy = $orig_post['public_policy']; - $private = $orig_post['item_private']; - } - - if($public_policy || $acl->is_private()) { - $private = (($private) ? $private : 1); - } - - $location = $orig_post['location']; - $coord = $orig_post['coord']; - $verb = $orig_post['verb']; - $app = $orig_post['app']; - $title = escape_tags(trim($_REQUEST['title'])); - $summary = trim($_REQUEST['summary']); - $body = trim($_REQUEST['body']); - - $item_flags = $orig_post['item_flags']; - $item_origin = $orig_post['item_origin']; - $item_unseen = $orig_post['item_unseen']; - $item_starred = $orig_post['item_starred']; - $item_uplink = $orig_post['item_uplink']; - $item_wall = $orig_post['item_wall']; - $item_thread_top = $orig_post['item_thread_top']; - $item_notshown = $orig_post['item_notshown']; - $item_nsfw = $orig_post['item_nsfw']; - $item_relay = $orig_post['item_relay']; - $item_mentionsme = $orig_post['item_mentionsme']; - $item_nocomment = $orig_post['item_nocomment']; - $item_obscured = $orig_post['item_obscured']; - $item_verified = $orig_post['item_verified']; - $item_retained = $orig_post['item_retained']; - $item_rss = $orig_post['item_rss']; - $item_deleted = $orig_post['item_deleted']; - $item_type = $orig_post['item_type']; - $item_hidden = $orig_post['item_hidden']; - $item_delayed = $orig_post['item_delayed']; - $item_pending_remove = $orig_post['item_pending_remove']; - $item_blocked = $orig_post['item_blocked']; - - - - $postopts = $orig_post['postopts']; - $created = ((intval($orig_post['item_unpublished'])) ? $created : $orig_post['created']); - $expires = ((intval($orig_post['item_unpublished'])) ? NULL_DATE : $orig_post['expires']); - $mid = $orig_post['mid']; - $parent_mid = $orig_post['parent_mid']; - $plink = $orig_post['plink']; - - } - else { - if(! $walltowall) { - if((array_key_exists('contact_allow',$_REQUEST)) - || (array_key_exists('group_allow',$_REQUEST)) - || (array_key_exists('contact_deny',$_REQUEST)) - || (array_key_exists('group_deny',$_REQUEST))) { - $acl->set_from_array($_REQUEST); - } - elseif(! $api_source) { - - // if no ACL has been defined and we aren't using the API, the form - // didn't send us any parameters. This means there's no ACL or it has - // been reset to the default audience. - // If $api_source is set and there are no ACL parameters, we default - // to the channel permissions which were set in the ACL contructor. - - $acl->set(array('allow_cid' => '', 'allow_gid' => '', 'deny_cid' => '', 'deny_gid' => '')); - } - } - - - $location = ((isset($_REQUEST['location'])) ? notags(trim($_REQUEST['location'])) : EMPTY_STR); - $coord = ((isset($_REQUEST['coord'])) ? notags(trim($_REQUEST['coord'])) : EMPTY_STR); - $verb = ((isset($_REQUEST['verb'])) ? notags(trim($_REQUEST['verb'])) : EMPTY_STR); - $title = ((isset($_REQUEST['title'])) ? escape_tags(trim($_REQUEST['title'])) : EMPTY_STR); - $summary = ((isset($_REQUEST['summary'])) ? trim($_REQUEST['summary']) : EMPTY_STR);; - $body = ((isset($_REQUEST['body'])) ? trim($_REQUEST['body']) : EMPTY_STR); - $body .= ((isset($_REQUEST['attachment'])) ? trim($_REQUEST['attachment']) : EMPTY_STR); - $postopts = ''; - - $allow_empty = ((array_key_exists('allow_empty',$_REQUEST)) ? intval($_REQUEST['allow_empty']) : 0); - - $private = ((isset($private) && $private) ? $private : intval($acl->is_private() || ($public_policy))); - - // If this is a comment, set the permissions from the parent. - - if($parent_item) { - $private = 0; - $acl->set($parent_item); - $private = ((intval($parent_item['item_private']) ? $parent_item['item_private'] : $acl->is_private())); - $public_policy = $parent_item['public_policy']; - $owner_hash = $parent_item['owner_xchan']; - $webpage = $parent_item['item_type']; - $comment_policy = $parent_item['comment_policy']; - $item_nocomment = $parent_item['item_nocomment']; - $comments_closed = $parent_item['comments_closed']; - } - - 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(); - } - } - - - - if(Apps::system_app_installed($profile_uid,'Expire Posts')) { - if(x($_REQUEST,'expire')) { - $expires = datetime_convert(date_default_timezone_get(),'UTC', $_REQUEST['expire']); - if($expires <= datetime_convert()) - $expires = NULL_DATE; - } - } - - - $mimetype = notags(trim($_REQUEST['mimetype'])); - if(! $mimetype) - $mimetype = 'text/x-multicode'; - - - $execflag = ((intval($uid) == intval($profile_uid) - && ($channel['channel_pageflags'] & PAGE_ALLOWCODE)) ? true : false); - - if($preview) { - $summary = z_input_filter($summary,$mimetype,$execflag); - $body = z_input_filter($body,$mimetype,$execflag); - } - - - $arr = [ 'profile_uid' => $profile_uid, 'summary' => $summary, 'content' => $body, 'mimetype' => $mimetype ]; - call_hooks('post_content',$arr); - $summary = $arr['summary']; - $body = $arr['content']; - $mimetype = $arr['mimetype']; - - - $gacl = $acl->get(); - $str_contact_allow = $gacl['allow_cid']; - $str_group_allow = $gacl['allow_gid']; - $str_contact_deny = $gacl['deny_cid']; - $str_group_deny = $gacl['deny_gid']; - - - // if the acl contains a single contact and it's a group, add a mention. This is for compatibility - // with other groups implementations which require a mention to trigger group delivery. - - if (($str_contact_allow) && (! $str_group_allow) && (! $str_contact_deny) && (! $str_group_deny)) { - $cida = expand_acl($str_contact_allow); - if (count($cida) === 1) { - $netgroups = get_forum_channels($profile_uid,1); - if ($netgroups) { - foreach($netgroups as $ng) { - if ($ng['xchan_hash'] == $cida[0]) { - if (! is_array($post_tags)) { - $post_tags = []; - } - $post_tags[] = array( - 'uid' => $profile_uid, - 'ttype' => TERM_MENTION, - 'otype' => TERM_OBJ_POST, - 'term' => $ng['xchan_name'], - 'url' => $ng['xchan_url'] - ); - - $colls = get_xconfig($ng['xchan_hash'],'activitypub','collections'); - if ($colls && is_array($colls) && isset($colls['wall'])) { - $datarray['target'] = [ - 'id' => $colls['wall'], - 'type' => 'Collection', - 'attributedTo' => (in_array($ng['xchan_network'],['nomad','zot6']) ? $ng['xchan_url'] : $ng['xchan_hash']) - ]; - $datarray['tgt_type'] = 'Collection'; - } - } - } - } - } - } - - $groupww = false; - - // if this is a wall-to-wall post to a group, turn it into a direct message - - $role = get_pconfig($profile_uid,'system','permissions_role'); - - $rolesettings = PermissionRoles::role_perms($role); - - $channel_type = isset($rolesettings['channel_type']) ? $rolesettings['channel_type'] : 'normal'; - - $is_group = (($channel_type === 'group') ? true : false); - - if (($is_group) && ($walltowall) && (! $walltowall_comment)) { - $groupww = true; - $str_contact_allow = $owner_xchan['xchan_hash']; - $str_group_allow = ''; - } - - - if(in_array($mimetype, [ 'text/bbcode', 'text/x-multicode' ])) { - - // BBCODE alert: the following functions assume bbcode input - // and will require alternatives for alternative content-types (text/html, text/markdown, text/plain, etc.) - // we may need virtual or template classes to implement the possible alternatives - - if(strpos($body,'[/summary]') !== false) { - $match = ''; - $cnt = preg_match("/\[summary\](.*?)\[\/summary\]/ism",$body,$match); - if($cnt) { - $summary .= $match[1]; - } - $body_content = preg_replace("/^(.*?)\[summary\](.*?)\[\/summary\]/ism", '',$body); - $body = trim($body_content); - } - - $summary = cleanup_bbcode($summary); - $body = cleanup_bbcode($body); - - // Look for tags and linkify them - $summary_tags = linkify_tags($summary, ($uid) ? $uid : $profile_uid); - $body_tags = linkify_tags($body, ($uid) ? $uid : $profile_uid); - $comment_tags = linkify_tags($hidden_mentions, ($uid) ? $uid : $profile_uid); - - foreach ( [ $summary_tags, $body_tags, $comment_tags ] as $results ) { - - if ($results) { - - // Set permissions based on tag replacements - set_linkified_perms($results, $str_contact_allow, $str_group_allow, $profile_uid, $parent_item, $private); - - if (! isset($post_tags)) { - $post_tags = []; - } - foreach ($results as $result) { - $success = $result['success']; - if ($success['replaced']) { - - // suppress duplicate mentions/tags - $already_tagged = false; - foreach ($post_tags as $pt) { - if ($pt['term'] === $success['term'] && $pt['url'] === $success['url'] && intval($pt['ttype']) === intval($success['termtype'])) { - $already_tagged = true; - break; - } - } - if ($already_tagged) { - continue; - } - - $post_tags[] = array( - 'uid' => $profile_uid, - 'ttype' => $success['termtype'], - 'otype' => TERM_OBJ_POST, - 'term' => $success['term'], - 'url' => $success['url'] - ); - - // support #collection syntax to post to a collection - // this is accomplished by adding a pcategory tag for each collection target - // this is checked inside tag_deliver() to create a second delivery chain - - if ($success['termtype'] === TERM_HASHTAG) { - $r = q("select xchan_url from channel left join xchan on xchan_hash = channel_hash where channel_address = '%s' and channel_parent = '%s' and channel_removed = 0", - dbesc($success['term']), - dbesc(get_observer_hash()) - ); - if ($r) { - $post_tags[] = [ - 'uid' => $profile_uid, - 'ttype' => TERM_PCATEGORY, - 'otype' => TERM_OBJ_POST, - 'term' => $success['term'] . '@' . App::get_hostname(), - 'url' => $r[0]['xchan_url'] - ]; - } - } - } - } - } - } - - - /** - * process collections selected manually - */ - - if (array_key_exists('collections',$_REQUEST) && is_array($_REQUEST['collections']) && count($_REQUEST['collections'])) { - foreach ($_REQUEST['collections'] as $clct) { - $r = q("select xchan_url, xchan_hash from xchan left join hubloc on hubloc_hash = xchan_hash where hubloc_addr = '%s' limit 1", - dbesc($clct) - ); - if ($r) { - if (! isset($post_tags)) { - $post_tags = []; - } - $post_tags[] = [ - 'uid' => $profile_uid, - 'ttype' => TERM_PCATEGORY, - 'otype' => TERM_OBJ_POST, - 'term' => $clct, - 'url' => $r[0]['xchan_url'] - ]; - } - } - } - - if(($str_contact_allow) && (! $str_group_allow)) { - // direct message - private between individual channels but not groups - $private = 2; - } - - if ($private) { - - // for edited posts, re-use any existing OCAP token (if found). - // Otherwise generate a new one. - - if ($iconfig) { - foreach ($iconfig as $cfg) { - if ($cfg['cat'] === 'ocap' && $cfg['k'] === 'relay') { - $token = $cfg['v']; - } - } - } - if (! $token) { - $token = new_token(); - } - } - - - /** - * - * When a photo was uploaded into the message using the (profile wall) ajax - * uploader, The permissions are initially set to disallow anybody but the - * owner from seeing it. This is because the permissions may not yet have been - * set for the post. If it's private, the photo permissions should be set - * appropriately. But we didn't know the final permissions on the post until - * now. So now we'll look for links of uploaded photos and attachments that are in the - * post and set them to the same permissions as the post itself. - * - * If the post was end-to-end encrypted we can't find images and attachments in the body, - * use our media_str input instead which only contains these elements - but only do this - * when encrypted content exists because the photo/attachment may have been removed from - * the post and we should keep it private. If it's encrypted we have no way of knowing - * so we'll set the permissions regardless and realise that the media may not be - * referenced in the post. - * - */ - - if(! $preview) { - fix_attached_permissions($profile_uid,((strpos($body,'[/crypt]')) ? $_POST['media_str'] : $body),$str_contact_allow,$str_group_allow,$str_contact_deny,$str_group_deny, $token); - } - - - $attachments = ''; - $match = false; - - if(preg_match_all('/(\[attachment\](.*?)\[\/attachment\])/',$body,$match)) { - $attachments = []; - $i = 0; - foreach($match[2] as $mtch) { - $attach_link = ''; - $hash = substr($mtch,0,strpos($mtch,',')); - $rev = intval(substr($mtch,strpos($mtch,','))); - $r = attach_by_hash_nodata($hash, $observer['xchan_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 = str_replace($match[1][$i],$attach_link,$body); - $i++; - } - } - - - if(preg_match_all('/(\[share=(.*?)\](.*?)\[\/share\])/',$body,$match)) { - // process share by id - - $i = 0; - foreach($match[2] as $mtch) { - $reshare = new \Zotlabs\Lib\Share($mtch); - $body = str_replace($match[1][$i],$reshare->bbcode(),$body); - $i++; - } - } - - } - - // BBCODE end alert - - if(strlen($categories)) { - if (! isset($post_tags)) { - $post_tags = []; - } - - $cats = explode(',',$categories); - foreach($cats as $cat) { - - if($webpage == ITEM_TYPE_CARD) { - $catlink = z_root() . '/cards/' . $channel['channel_address'] . '?f=&cat=' . urlencode(trim($cat)); - } - elseif($webpage == ITEM_TYPE_ARTICLE) { - $catlink = z_root() . '/articles/' . $channel['channel_address'] . '?f=&cat=' . urlencode(trim($cat)); - } - else { - $catlink = $owner_xchan['xchan_url'] . '?f=&cat=' . urlencode(trim($cat)); - } - - $post_tags[] = array( - 'uid' => $profile_uid, - 'ttype' => TERM_CATEGORY, - 'otype' => TERM_OBJ_POST, - 'term' => trim($cat), - 'url' => $catlink - ); - } - } - - if($orig_post) { - // preserve original tags - $t = q("select * from term where oid = %d and otype = %d and uid = %d and ttype in ( %d, %d, %d )", - intval($orig_post['id']), - intval(TERM_OBJ_POST), - intval($profile_uid), - intval(TERM_UNKNOWN), - intval(TERM_FILE), - intval(TERM_COMMUNITYTAG) - ); - if($t) { - if (! isset($post_tags)) { - $post_tags = []; - } - - foreach($t as $t1) { - $post_tags[] = array( - 'uid' => $profile_uid, - 'ttype' => $t1['ttype'], - 'otype' => TERM_OBJ_POST, - 'term' => $t1['term'], - 'url' => $t1['url'], - ); - } - } - } - - - $item_unseen = ((local_channel() != $profile_uid) ? 1 : 0); - $item_wall = ((isset($_REQUEST['type']) && ($_REQUEST['type'] === 'wall' || $_REQUEST['type'] === 'wall-comment')) ? 1 : 0); - $item_origin = (($origin) ? 1 : 0); - $item_nocomment = ((isset($item_nocomment)) ? $item_nocomment : $nocomment); - - - // determine if this is a wall post - - if($parent) { - $item_wall = $parent_item['item_wall']; - } - else { - if(! $webpage) { - $item_wall = 1; - } - } - - - if($moderated) - $item_blocked = ITEM_MODERATED; - - - if(! strlen($verb)) - $verb = ACTIVITY_POST ; - - $notify_type = (($parent) ? 'comment-new' : 'wall-new' ); - - if (! (isset($mid) && $mid)) { - if($message_id) { - $mid = $message_id; - } - else { - $uuid = new_uuid(); - $mid = z_root() . '/item/' . $uuid; - } - } - - - if($is_poll) { - $poll = [ - 'question' => $body, - 'answers' => $_REQUEST['poll_answers'], - 'multiple_answers' => $_REQUEST['poll_multiple_answers'], - 'expire_value' => $_REQUEST['poll_expire_value'], - 'expire_unit' => $_REQUEST['poll_expire_unit'] - ]; - $obj = $this->extract_poll_data($poll, [ 'item_private' => $private, 'allow_cid' => $str_contact_allow, 'allow_gid' => $str_contact_deny ]); - } - else { - $obj = $this->extract_bb_poll_data($body,[ 'item_private' => $private, 'allow_cid' => $str_contact_allow, 'allow_gid' => $str_contact_deny ]); - } - - - if ($obj) { - $obj['url'] = $obj['id'] = $mid; - $obj['attributedTo'] = channel_url($channel); - $datarray['obj'] = $obj; - $obj_type = 'Question'; - if ($obj['endTime']) { - $d = datetime_convert('UTC','UTC', $obj['endTime']); - if ($d > NULL_DATE) { - $comments_closed = $d; - } - } - } - - if(! $parent_mid) { - $parent_mid = $mid; - } - - if($parent_item) - $parent_mid = $parent_item['mid']; - - - - // Fallback so that we alway have a thr_parent - - if(!$thr_parent) - $thr_parent = $mid; - - - $item_thread_top = ((! $parent) ? 1 : 0); - - - // fix permalinks for cards, etc. - - if($webpage == ITEM_TYPE_CARD) { - $plink = z_root() . '/cards/' . $channel['channel_address'] . '/' . (($pagetitle) ? $pagetitle : $uuid); - } - if(($parent_item) && ($parent_item['item_type'] == ITEM_TYPE_CARD)) { - $r = q("select v from iconfig where iconfig.cat = 'system' and iconfig.k = 'CARD' and iconfig.iid = %d limit 1", - intval($parent_item['id']) - ); - if($r) { - $plink = z_root() . '/cards/' . $channel['channel_address'] . '/' . $r[0]['v']; - } - } - - if($webpage == ITEM_TYPE_ARTICLE) { - $plink = z_root() . '/articles/' . $channel['channel_address'] . '/' . (($pagetitle) ? $pagetitle : $uuid); - } - if(($parent_item) && ($parent_item['item_type'] == ITEM_TYPE_ARTICLE)) { - $r = q("select v from iconfig where iconfig.cat = 'system' and iconfig.k = 'ARTICLE' and iconfig.iid = %d limit 1", - intval($parent_item['id']) - ); - if($r) { - $plink = z_root() . '/articles/' . $channel['channel_address'] . '/' . $r[0]['v']; - } - } - - if ((! (isset($plink) && $plink)) && $item_thread_top) { - $plink = z_root() . '/item/' . $uuid; - } - - if (array_path_exists('obj/id',$datarray)) { - $datarray['obj']['id'] = $mid; - } - - $datarray['aid'] = $channel['channel_account_id']; - $datarray['uid'] = $profile_uid; - $datarray['uuid'] = $uuid; - $datarray['owner_xchan'] = (($owner_hash) ? $owner_hash : $owner_xchan['xchan_hash']); - $datarray['author_xchan'] = $observer['xchan_hash']; - $datarray['created'] = $created; - $datarray['edited'] = (($orig_post && (! intval($orig_post['item_unpublished']))) ? datetime_convert() : $created); - $datarray['expires'] = $expires; - $datarray['commented'] = (($orig_post && (! intval($orig_post['item_unpublished']))) ? datetime_convert() : $created); - $datarray['received'] = (($orig_post && (! intval($orig_post['item_unpublished']))) ? datetime_convert() : $created); - $datarray['changed'] = (($orig_post && (! intval($orig_post['item_unpublished']))) ? datetime_convert() : $created); - $datarray['comments_closed'] = $comments_closed; - $datarray['mid'] = $mid; - $datarray['parent_mid'] = $parent_mid; - $datarray['mimetype'] = $mimetype; - $datarray['title'] = $title; - $datarray['summary'] = $summary; - $datarray['body'] = $body; - $datarray['app'] = $app; - $datarray['location'] = $location; - $datarray['coord'] = $coord; - $datarray['verb'] = $verb; - $datarray['obj_type'] = $obj_type; - $datarray['allow_cid'] = $str_contact_allow; - $datarray['allow_gid'] = $str_group_allow; - $datarray['deny_cid'] = $str_contact_deny; - $datarray['deny_gid'] = $str_group_deny; - $datarray['attach'] = $attachments; - $datarray['thr_parent'] = $thr_parent; - $datarray['postopts'] = $postopts; - $datarray['item_unseen'] = intval($item_unseen); - $datarray['item_wall'] = intval($item_wall); - $datarray['item_origin'] = intval($item_origin); - $datarray['item_type'] = $webpage; - $datarray['item_private'] = intval($private); - $datarray['item_thread_top'] = intval($item_thread_top); - $datarray['item_unseen'] = intval($item_unseen); - $datarray['item_starred'] = intval($item_starred); - $datarray['item_uplink'] = intval($item_uplink); - $datarray['item_consensus'] = 0; - $datarray['item_notshown'] = intval($item_notshown); - $datarray['item_nsfw'] = intval($item_nsfw); - $datarray['item_relay'] = intval($item_relay); - $datarray['item_mentionsme'] = intval($item_mentionsme); - $datarray['item_nocomment'] = intval($item_nocomment); - $datarray['item_obscured'] = intval($item_obscured); - $datarray['item_verified'] = intval($item_verified); - $datarray['item_retained'] = intval($item_retained); - $datarray['item_rss'] = intval($item_rss); - $datarray['item_deleted'] = intval($item_deleted); - $datarray['item_hidden'] = intval($item_hidden); - $datarray['item_unpublished'] = intval($item_unpublished); - $datarray['item_delayed'] = intval($item_delayed); - $datarray['item_pending_remove'] = intval($item_pending_remove); - $datarray['item_blocked'] = intval($item_blocked); - $datarray['layout_mid'] = $layout_mid; - $datarray['public_policy'] = $public_policy; - $datarray['comment_policy'] = ((is_numeric($comment_policy)) ? map_scope($comment_policy) : $comment_policy); // only map scope if it is numeric, otherwise use what we have - $datarray['term'] = $post_tags; - $datarray['plink'] = $plink; - $datarray['route'] = $route; - $datarray['replyto'] = $replyto; - - // A specific ACL over-rides public_policy completely - - if(! empty_acl($datarray)) - $datarray['public_policy'] = ''; - - if ($iconfig) { - $datarray['iconfig'] = $iconfig; - } - if ($private) { - IConfig::set($datarray,'ocap','relay',$token); - } - - if(! array_key_exists('obj',$datarray)) { - $copy = $datarray; - $copy['author'] = $observer; - $datarray['obj'] = Activity::encode_item($copy,((get_config('system','activitypub', ACTIVITYPUB_ENABLED)) ? true : false)); - $recips = []; - $i = $datarray['obj']; - if ($i['to']) { - $recips['to'] = $i['to']; - } - if ($i['cc']) { - $recips['cc'] = $i['cc']; - } - IConfig::Set($datarray,'activitypub','recips',$recips); - } - - Activity::rewrite_mentions($datarray); - - // preview mode - prepare the body for display and send it via json - - if($preview) { - require_once('include/conversation.php'); - - $datarray['owner'] = $owner_xchan; - $datarray['author'] = $observer; - $datarray['attach'] = json_encode($datarray['attach']); - $o = conversation(array($datarray),'search',false,'preview'); - // logger('preview: ' . $o, LOGGER_DEBUG); - echo json_encode(array('preview' => $o)); - killme(); - } - - // Let 'post_local' event listeners know if this is an edit. - // We will unset it immediately afterward. - - if ($orig_post) { - $datarray['edit'] = true; - } - - // suppress duplicates, *unless* you're editing an existing post. This could get picked up - // as a duplicate if you're editing it very soon after posting it initially and you edited - // some attribute besides the content, such as title or categories. - - if(PConfig::Get($profile_uid,'system','suppress_duplicates',true) && (! $orig_post)) { - - $z = q("select created from item where uid = %d and created > %s - INTERVAL %s and body = '%s' limit 1", - intval($profile_uid), - db_utcnow(), - db_quoteinterval('2 MINUTE'), - dbesc($body) - ); - - if($z) { - $datarray['cancel'] = 1; - notice( t('Duplicate post suppressed.') . EOL); - logger('Duplicate post. Cancelled.'); - } - } - - call_hooks('post_local',$datarray); - - // This is no longer needed - unset($datarray['edit']); - - if (x($datarray,'cancel')) { - 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']; - json_return_and_die($json); - } - - - if(mb_strlen($datarray['title']) > 191) - $datarray['title'] = mb_substr($datarray['title'],0,191); - - if($webpage) { - IConfig::Set($datarray,'system', webpage_to_namespace($webpage), - (($pagetitle) ? $pagetitle : basename($datarray['mid'])), true); - } - elseif($namespace) { - IConfig::Set($datarray,'system', $namespace, - (($remote_id) ? $remote_id : basename($datarray['mid'])), true); - } - - if (intval($datarray['item_unpublished'])) { - $draft_msg = t('Draft saved. Use Drafts app to continue editing.'); - } - - if($orig_post) { - $datarray['id'] = $post_id; - - $x = item_store_update($datarray,$execflag); - if(! $parent) { - $r = q("select * from item where id = %d", - intval($post_id) - ); - if($r) { - xchan_query($r); - $sync_item = fetch_post_tags($r); - Libsync::build_sync_packet($profile_uid,array('item' => array(encode_item($sync_item[0],true)))); - } - } - if (! $nopush) { - Run::Summon( [ 'Notifier', 'edit_post', $post_id ] ); - } - - if ($api_source) { - return($x); - } - - - if (intval($datarray['item_unpublished'])) { - info($draft_msg); - } - - - if((x($_REQUEST,'return')) && strlen($return_path)) { - logger('return: ' . $return_path); - goaway(z_root() . "/" . $return_path ); - } - killme(); - } - else - $post_id = 0; - - $post = item_store($datarray,$execflag); - - if ($pub_copy) { - info( t('Your comment has been posted.') . EOL); - } - - $post_id = $post['item_id']; - $datarray = $post['item']; - - - - if($post_id) { - logger('mod_item: saved item ' . $post_id); - - if($parent) { - - // prevent conversations which you are involved from being expired - - if(local_channel()) - retain_item($parent); - - // only send comment notification if this is a wall-to-wall comment and not a DM, - // otherwise it will happen during delivery - - if(($datarray['owner_xchan'] != $datarray['author_xchan']) && (intval($parent_item['item_wall'])) && intval($datarray['item_private']) != 2) { - Enotify::submit(array( - 'type' => NOTIFY_COMMENT, - 'from_xchan' => $datarray['author_xchan'], - 'to_xchan' => $datarray['owner_xchan'], - 'item' => $datarray, - 'link' => z_root() . '/display/' . gen_link_id($datarray['mid']), - 'verb' => ACTIVITY_POST, - 'otype' => 'item', - 'parent' => $parent, - 'parent_mid' => $parent_item['mid'] - )); - - } - } - else { - $parent = $post_id; - - if(($datarray['owner_xchan'] != $datarray['author_xchan']) && ($datarray['item_type'] == ITEM_TYPE_POST)) { - Enotify::submit(array( - 'type' => NOTIFY_WALL, - 'from_xchan' => $datarray['author_xchan'], - 'to_xchan' => $datarray['owner_xchan'], - 'item' => $datarray, - 'link' => z_root() . '/display/' . gen_link_id($datarray['mid']), - 'verb' => ACTIVITY_POST, - 'otype' => 'item' - )); - } - - if($uid && $uid == $profile_uid && (is_item_normal($datarray))) { - q("update channel set channel_lastpost = '%s' where channel_id = %d", - dbesc(datetime_convert()), - intval($uid) - ); - } - } - - // photo comments turn the corresponding item visible to the profile wall - // This way we don't see every picture in your new photo album posted to your wall at once. - // They will show up as people comment on them. - - if(intval($parent_item['item_hidden'])) { - $r = q("UPDATE item SET item_hidden = 0 WHERE id = %d", - intval($parent_item['id']) - ); - } - } - else { - logger('mod_item: unable to retrieve post that was just stored.'); - notice( t('System error. Post not saved.') . EOL); - if($return_path) - goaway(z_root() . "/" . $return_path ); - if($api_source) - return ( [ 'success' => false, 'message' => 'system error' ] ); - killme(); - } - - if(($parent) && ($parent != $post_id)) { - // Store the comment signature information in case we need to relay to Diaspora - //$ditem = $datarray; - //$ditem['author'] = $observer; - //store_diaspora_comment_sig($ditem,$channel,$parent_item, $post_id, (($walltowall_comment) ? 1 : 0)); - } - else { - $r = q("select * from item where id = %d", - intval($post_id) - ); - if($r) { - xchan_query($r); - $sync_item = fetch_post_tags($r); - Libsync::build_sync_packet($profile_uid,array('item' => array(encode_item($sync_item[0],true)))); - } - } - - $datarray['id'] = $post_id; - $datarray['llink'] = z_root() . '/display/' . gen_link_id($datarray['mid']); - - call_hooks('post_local_end', $datarray); - - if ($groupww) { - $nopush = false; - } - - if(! $nopush) { - Run::Summon( [ 'Notifier', $notify_type, $post_id ] ); - } - logger('post_complete'); - - if($moderated) { - info(t('Your post/comment is awaiting approval.') . EOL); - } - - // figure out how to return, depending on from whence we came - - if($api_source) - return $post; - - if (intval($datarray['item_unpublished'])) { - info($draft_msg); - } - - if($return_path) { - goaway(z_root() . "/" . $return_path); - } - - $json = array('success' => 1); - if(x($_REQUEST,'jsreload') && strlen($_REQUEST['jsreload'])) - $json['reload'] = z_root() . '/' . $_REQUEST['jsreload']; - - logger('post_json: ' . print_r($json,true), LOGGER_DEBUG); - - echo json_encode($json); - killme(); - // NOTREACHED - } - - - function get() { - - - if ($this->return_404) { - notice( t('Not found') ); - return; - } - - if((! local_channel()) && (! remote_channel())) - return; - - // allow pinned items to be dropped. 'pin-' was prepended to the id of these - // items so that they would have a unique html id even if the pinned item - // was also displayed in a normal conversation on the same web page. - - $drop_id = str_replace('pin-','',argv(2)); - - if((argc() == 3) && (argv(1) === 'drop') && intval($drop_id)) { - - $i = q("select * from item where id = %d limit 1", - intval($drop_id) - ); - - if($i) { - $can_delete = false; - $local_delete = false; - $regular_delete = false; - - if(local_channel() && local_channel() == $i[0]['uid']) { - $local_delete = true; - } - - $ob_hash = get_observer_hash(); - if($ob_hash && ($ob_hash === $i[0]['author_xchan'] || $ob_hash === $i[0]['owner_xchan'] || $ob_hash === $i[0]['source_xchan'])) { - $can_delete = true; - $regular_delete = true; - } - - // The site admin can delete any post/item on the site. - // If the item originated on this site+channel the deletion will propagate downstream. - // Otherwise just the local copy is removed. - - if(is_site_admin()) { - $local_delete = true; - if(intval($i[0]['item_origin'])) - $can_delete = true; - } - - - if(! ($can_delete || $local_delete)) { - notice( t('Permission denied.') . EOL); - return; - } - - if ($i[0]['resource_type'] === 'event') { - // delete and sync the event separately - $r = q("SELECT * FROM event WHERE event_hash = '%s' AND uid = %d LIMIT 1", - dbesc($i[0]['resource_id']), - intval($i[0]['uid']) - ); - if ($r && $regular_delete) { - $sync_event = $r[0]; - q("delete from event WHERE event_hash = '%s' AND uid = %d LIMIT 1", - dbesc($i[0]['resource_id']), - intval($i[0]['uid']) - ); - $sync_event['event_deleted'] = 1; - Libsync::build_sync_packet($i[0]['uid'],array('event' => array($sync_event))); - } - } - - if ($i[0]['resource_type'] === 'photo') { - attach_delete($i[0]['uid'], $i[0]['resource_id'], true ); - $ch = channelx_by_n($i[0]['uid']); - if ($ch && $regular_delete) { - $sync = attach_export_data($ch,$i[0]['resource_id'], true); - if ($sync) { - Libsync::build_sync_packet($i[0]['uid'],array('file' => array($sync))); - } - } - } - - - // if this is a different page type or it's just a local delete - // but not by the item author or owner, do a simple deletion - - $complex = false; - - if(intval($i[0]['item_type']) || ($local_delete && (! $can_delete))) { - drop_item($i[0]['id']); - } - else { - // complex deletion that needs to propagate and be performed in phases - drop_item($i[0]['id'],true,DROPITEM_PHASE1); - $complex = true; - } - - $r = q("select * from item where id = %d", - intval($i[0]['id']) - ); - if($r) { - xchan_query($r); - $sync_item = fetch_post_tags($r); - Libsync::build_sync_packet($i[0]['uid'],array('item' => array(encode_item($sync_item[0],true)))); - } - - if($complex) { - tag_deliver($i[0]['uid'],$i[0]['id']); - } - } - } - } - - - - function item_check_service_class($channel_id,$iswebpage) { - $ret = array('success' => false, 'message' => ''); - - if ($iswebpage) { - $r = q("select count(i.id) as total from item i +class Item extends Controller +{ + + public $return_404 = false; + + public function init() + { + + + if (ActivityStreams::is_as_request()) { + $item_uuid = argv(1); + if (!$item_uuid) { + http_status_exit(404, 'Not found'); + } + $portable_id = EMPTY_STR; + + $item_normal = " and item.item_hidden = 0 and item.item_type = 0 and item.item_unpublished = 0 and item.item_delayed = 0 and item.item_blocked = 0 "; + + $i = null; + + // do we have the item (at all)? + // add preferential bias to item owners (item_wall = 1) + + $r = q( + "select * from item where (mid = '%s' or uuid = '%s') $item_normal order by item_wall desc limit 1", + dbesc(z_root() . '/item/' . $item_uuid), + dbesc($item_uuid) + ); + + if (!$r) { + http_status_exit(404, 'Not found'); + } + + // process an authenticated fetch + + + $sigdata = HTTPSig::verify(EMPTY_STR); + if ($sigdata['portable_id'] && $sigdata['header_valid']) { + $portable_id = $sigdata['portable_id']; + if (!check_channelallowed($portable_id)) { + http_status_exit(403, 'Permission denied'); + } + if (!check_siteallowed($sigdata['signer'])) { + http_status_exit(403, 'Permission denied'); + } + observer_auth($portable_id); + + $i = q( + "select id as item_id from item where mid = '%s' $item_normal and owner_xchan = '%s' limit 1 ", + dbesc($r[0]['parent_mid']), + dbesc($portable_id) + ); + } elseif (Config::get('system', 'require_authenticated_fetch', false)) { + http_status_exit(403, 'Permission denied'); + } + + // if we don't have a parent id belonging to the signer see if we can obtain one as a visitor that we have permission to access + // with a bias towards those items owned by channels on this site (item_wall = 1) + + $sql_extra = item_permissions_sql(0); + + if (!$i) { + $i = q( + "select id as item_id from item where mid = '%s' $item_normal $sql_extra order by item_wall desc limit 1", + dbesc($r[0]['parent_mid']) + ); + } + + $bear = Activity::token_from_request(); + if ($bear) { + logger('bear: ' . $bear, LOGGER_DEBUG); + if (!$i) { + $t = q( + "select * from iconfig where cat = 'ocap' and k = 'relay' and v = '%s'", + dbesc($bear) + ); + if ($t) { + $i = q( + "select id as item_id from item where uuid = '%s' and id = %d $item_normal limit 1", + dbesc($item_uuid), + intval($t[0]['iid']) + ); + } + } + } + + if (!$i) { + http_status_exit(403, 'Forbidden'); + } + + // If we get to this point we have determined we can access the original in $r (fetched much further above), so use it. + + xchan_query($r, true); + $items = fetch_post_tags($r, false); + + $chan = channelx_by_n($items[0]['uid']); + + if (!$chan) { + http_status_exit(404, 'Not found'); + } + + if (!perm_is_allowed($chan['channel_id'], get_observer_hash(), 'view_stream')) { + http_status_exit(403, 'Forbidden'); + } + + $i = Activity::encode_item($items[0], true); + + if (!$i) { + http_status_exit(404, 'Not found'); + } + + + if ($portable_id && (!intval($items[0]['item_private']))) { + $c = q( + "select abook_id from abook where abook_channel = %d and abook_xchan = '%s'", + intval($items[0]['uid']), + dbesc($portable_id) + ); + if (!$c) { + ThreadListener::store(z_root() . '/item/' . $item_uuid, $portable_id); + } + } + + as_return_and_die($i, $chan); + } + + if (Libzot::is_zot_request()) { + $item_uuid = argv(1); + + if (!$item_uuid) { + http_status_exit(404, 'Not found'); + } + + $portable_id = EMPTY_STR; + + $item_normal = " and item.item_hidden = 0 and item.item_type = 0 and item.item_unpublished = 0 and item.item_delayed = 0 and item.item_blocked = 0 and not verb in ( 'Follow', 'Ignore' ) "; + + $i = null; + + // do we have the item (at all)? + + $r = q( + "select * from item where (mid = '%s' or uuid = '%s') $item_normal limit 1", + dbesc(z_root() . '/item/' . $item_uuid), + dbesc($item_uuid) + ); + + if (!$r) { + http_status_exit(404, 'Not found'); + } + + // process an authenticated fetch + + $sigdata = HTTPSig::verify(($_SERVER['REQUEST_METHOD'] === 'POST') ? file_get_contents('php://input') : EMPTY_STR); + if ($sigdata['portable_id'] && $sigdata['header_valid']) { + $portable_id = $sigdata['portable_id']; + if (!check_channelallowed($portable_id)) { + http_status_exit(403, 'Permission denied'); + } + if (!check_siteallowed($sigdata['signer'])) { + http_status_exit(403, 'Permission denied'); + } + observer_auth($portable_id); + + $i = q( + "select id as item_id from item where mid = '%s' $item_normal and owner_xchan = '%s' limit 1", + dbesc($r[0]['parent_mid']), + dbesc($portable_id) + ); + } elseif (Config::get('system', 'require_authenticated_fetch', false)) { + http_status_exit(403, 'Permission denied'); + } + + // if we don't have a parent id belonging to the signer see if we can obtain one as a visitor that we have permission to access + // with a bias towards those items owned by channels on this site (item_wall = 1) + + $sql_extra = item_permissions_sql(0); + + if (!$i) { + $i = q( + "select id as item_id from item where mid = '%s' $item_normal $sql_extra order by item_wall desc limit 1", + dbesc($r[0]['parent_mid']) + ); + } + + $bear = Activity::token_from_request(); + if ($bear) { + logger('bear: ' . $bear, LOGGER_DEBUG); + if (!$i) { + $t = q( + "select * from iconfig where cat = 'ocap' and k = 'relay' and v = '%s'", + dbesc($bear) + ); + if ($t) { + $i = q( + "select id as item_id from item where uuid = '%s' and id = %d $item_normal limit 1", + dbesc($item_uuid), + intval($t[0]['iid']) + ); + } + } + } + + if (!$i) { + http_status_exit(403, 'Forbidden'); + } + + $parents_str = ids_to_querystr($i, 'item_id'); + + $items = q( + "SELECT item.*, item.id AS item_id FROM item WHERE item.parent IN ( %s ) $item_normal order by item.id asc", + dbesc($parents_str) + ); + + if (!$items) { + http_status_exit(404, 'Not found'); + } + + xchan_query($items, true); + $items = fetch_post_tags($items, true); + + if (!$items) { + http_status_exit(404, 'Not found'); + } + $chan = channelx_by_n($items[0]['uid']); + + if (!$chan) { + http_status_exit(404, 'Not found'); + } + + if (!perm_is_allowed($chan['channel_id'], get_observer_hash(), 'view_stream')) { + http_status_exit(403, 'Forbidden'); + } + + $i = Activity::encode_item_collection($items, 'conversation/' . $item_uuid, 'OrderedCollection', true, count($items)); + if ($portable_id && (!intval($items[0]['item_private']))) { + ThreadListener::store(z_root() . '/item/' . $item_uuid, $portable_id); + } + + if (!$i) { + http_status_exit(404, 'Not found'); + } + $x = array_merge(['@context' => [ + ACTIVITYSTREAMS_JSONLD_REV, + 'https://w3id.org/security/v1', + Activity::ap_schema() + ]], $i); + + $headers = []; + $headers['Content-Type'] = 'application/x-nomad+json'; + $x['signature'] = LDSignatures::sign($x, $chan); + $ret = json_encode($x, JSON_UNESCAPED_SLASHES); + $headers['Digest'] = HTTPSig::generate_digest_header($ret); + $headers['(request-target)'] = strtolower($_SERVER['REQUEST_METHOD']) . ' ' . $_SERVER['REQUEST_URI']; + $h = HTTPSig::create_sig($headers, $chan['channel_prvkey'], channel_url($chan)); + HTTPSig::set_headers($h); + echo $ret; + killme(); + } + + // if it isn't a drop command and isn't a post method and wasn't handled already, + // the default action is a browser request for a persistent uri and this should return + // the text/html page of the item. + + if (argc() > 1 && argv(1) !== 'drop') { + $x = q( + "select uid, item_wall, llink, mid from item where mid = '%s' or mid = '%s' or uuid = '%s'", + dbesc(z_root() . '/item/' . argv(1)), + dbesc(z_root() . '/activity/' . argv(1)), + dbesc(argv(1)) + ); + if ($x) { + foreach ($x as $xv) { + if (intval($xv['item_wall'])) { + $c = channelx_by_n($xv['uid']); + if ($c) { + goaway($c['xchan_url'] . '?mid=' . gen_link_id($xv['mid'])); + } + } + } + goaway($x[0]['llink']); + } + + // save this state and catch it in the get() function + $this->return_404 = true; + } + } + + public function post() + { + + if ((!local_channel()) && (!remote_channel()) && (!isset($_REQUEST['anonname']))) { + return; + } + + // drop an array of items. + + if (isset($_REQUEST['dropitems'])) { + $arr_drop = explode(',', $_REQUEST['dropitems']); + drop_items($arr_drop); + $json = array('success' => 1); + echo json_encode($json); + killme(); + } + + + $uid = local_channel(); + $channel = null; + $observer = null; + $token = EMPTY_STR; + $datarray = []; + $item_starred = false; + $item_uplink = false; + $item_notshown = false; + $item_nsfw = false; + $item_relay = false; + $item_mentionsme = false; + $item_verified = false; + $item_retained = false; + $item_rss = false; + $item_deleted = false; + $item_hidden = false; + $item_delayed = false; + $item_pending_remove = false; + $item_blocked = false; + + $post_tags = false; + $pub_copy = false; + + + /** + * Is this a reply to something? + */ + + $parent = ((isset($_REQUEST['parent'])) ? intval($_REQUEST['parent']) : 0); + $parent_mid = ((isset($_REQUEST['parent_mid'])) ? trim($_REQUEST['parent_mid']) : ''); + + $hidden_mentions = ((isset($_REQUEST['hidden_mentions'])) ? trim($_REQUEST['hidden_mentions']) : ''); + + + /** + * Who is viewing this page and posting this thing + */ + + $remote_xchan = ((isset($_REQUEST['remote_xchan'])) ? trim($_REQUEST['remote_xchan']) : false); + $remote_observer = xchan_match(['xchan_hash' => $remote_xchan]); + + if (!$remote_observer) { + $remote_xchan = $remote_observer = false; + } + + // This is the local channel representing who the posted item will belong to. + + $profile_uid = ((isset($_REQUEST['profile_uid'])) ? intval($_REQUEST['profile_uid']) : 0); + + // *If* you are logged in as the site admin you are allowed to create top-level items for the sys channel. + // This would typically be a webpage or webpage element. + // Comments and replies are excluded because further below we also check for sys channel ownership and + // will make a copy of the parent that you can interact with in your own stream + + $sys = get_sys_channel(); + if ($sys && $profile_uid && ($sys['channel_id'] == $profile_uid) && is_site_admin() && !$parent) { + $uid = intval($sys['channel_id']); + $channel = $sys; + $observer = $sys; + } + + call_hooks('post_local_start', $_REQUEST); + + // logger('postvars ' . print_r($_REQUEST,true), LOGGER_DATA); + + $api_source = ((isset($_REQUEST['api_source']) && $_REQUEST['api_source']) ? true : false); + + $nocomment = 0; + if (isset($_REQUEST['comments_enabled'])) { + $nocomment = 1 - intval($_REQUEST['comments_enabled']); + } + + // this is in days, convert to absolute time + $channel_comments_closed = get_pconfig($profile_uid, 'system', 'close_comments'); + if (intval($channel_comments_closed)) { + $channel_comments_closed = datetime_convert(date_Default_timezone_get(), 'UTC', 'now + ' . intval($channel_comments_closed) . ' days'); + } else { + $channel_comments_closed = NULL_DATE; + } + + $comments_closed = ((isset($_REQUEST['comments_closed']) && $_REQUEST['comments_closed']) ? datetime_convert(date_default_timezone_get(), 'UTC', $_REQUEST['comments_closed']) : $channel_comments_closed); + + $is_poll = ((trim($_REQUEST['poll_answers'][0]) != '' && trim($_REQUEST['poll_answers'][1]) != '') ? true : false); + + // 'origin' (if non-zero) indicates that this network is where the message originated, + // for the purpose of relaying comments to other conversation members. + // If using the API from a device (leaf node) you must set origin to 1 (default) or leave unset. + // If the API is used from another network with its own distribution + // and deliveries, you may wish to set origin to 0 or false and allow the other + // network to relay comments. + + // If you are unsure, it is prudent (and important) to leave it unset. + + $origin = (($api_source && array_key_exists('origin', $_REQUEST)) ? intval($_REQUEST['origin']) : 1); + + // To represent message-ids on other networks - this will create an iconfig record + + $namespace = (($api_source && array_key_exists('namespace', $_REQUEST)) ? strip_tags($_REQUEST['namespace']) : ''); + $remote_id = (($api_source && array_key_exists('remote_id', $_REQUEST)) ? strip_tags($_REQUEST['remote_id']) : ''); + + $owner_hash = null; + + $message_id = ((x($_REQUEST, 'message_id') && $api_source) ? strip_tags($_REQUEST['message_id']) : ''); + $created = ((x($_REQUEST, 'created')) ? datetime_convert(date_default_timezone_get(), 'UTC', $_REQUEST['created']) : datetime_convert()); + + // Because somebody will probably try this and create a mess + + if ($created <= NULL_DATE) { + $created = datetime_convert(); + } + + $post_id = ((x($_REQUEST, 'post_id')) ? intval($_REQUEST['post_id']) : 0); + + $app = ((x($_REQUEST, 'source')) ? strip_tags($_REQUEST['source']) : ''); + $return_path = ((x($_REQUEST, 'return')) ? $_REQUEST['return'] : ''); + $preview = ((x($_REQUEST, 'preview')) ? intval($_REQUEST['preview']) : 0); + $categories = ((x($_REQUEST, 'category')) ? escape_tags($_REQUEST['category']) : ''); + $webpage = ((x($_REQUEST, 'webpage')) ? intval($_REQUEST['webpage']) : 0); + $item_obscured = ((x($_REQUEST, 'obscured')) ? intval($_REQUEST['obscured']) : 0); + $pagetitle = ((x($_REQUEST, 'pagetitle')) ? escape_tags(urlencode($_REQUEST['pagetitle'])) : ''); + $layout_mid = ((x($_REQUEST, 'layout_mid')) ? escape_tags($_REQUEST['layout_mid']) : ''); + $plink = ((x($_REQUEST, 'permalink')) ? escape_tags($_REQUEST['permalink']) : ''); + $obj_type = ((x($_REQUEST, 'obj_type')) ? escape_tags($_REQUEST['obj_type']) : ACTIVITY_OBJ_NOTE); + + + $item_unpublished = ((isset($_REQUEST['draft'])) ? intval($_REQUEST['draft']) : 0); + + // allow API to bulk load a bunch of imported items without sending out a bunch of posts. + $nopush = ((x($_REQUEST, 'nopush')) ? intval($_REQUEST['nopush']) : 0); + + /* + * Check service class limits + */ + if ($uid && !(x($_REQUEST, 'parent')) && !(x($_REQUEST, 'post_id'))) { + $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(); + } + } + + if ($pagetitle) { + $pagetitle = strtolower(URLify::transliterate($pagetitle)); + } + + $item_flags = $item_restrict = 0; + $expires = NULL_DATE; + + $route = ''; + $parent_item = null; + $parent_contact = null; + $thr_parent = ''; + $parid = 0; + $r = false; + + + // If this is a comment, find the parent and preset some stuff + + if ($parent || $parent_mid) { + if (!x($_REQUEST, 'type')) { + $_REQUEST['type'] = 'net-comment'; + } + if ($obj_type == ACTIVITY_OBJ_NOTE) { + $obj_type = ACTIVITY_OBJ_COMMENT; + } + + // fetch the parent item + + if ($parent) { + $r = q( + "SELECT * FROM item WHERE id = %d LIMIT 1", + intval($parent) + ); + } elseif ($parent_mid && $uid) { + // This is coming from an API source, and we are logged in + $r = q( + "SELECT * FROM item WHERE mid = '%s' AND uid = %d LIMIT 1", + dbesc($parent_mid), + intval($uid) + ); + } + + // if this isn't the real parent of the conversation, find it + if ($r) { + $parid = $r[0]['parent']; + $parent_mid = $r[0]['mid']; + if ($r[0]['id'] != $r[0]['parent']) { + $r = q( + "SELECT * FROM item WHERE id = parent AND parent = %d LIMIT 1", + intval($parid) + ); + } + + // if interacting with a pubstream item (owned by the sys channel), + // create a copy of the parent in your stream + + // $r may have changed. Check it again before trying to use it. + + if ($r && local_channel() && (!is_sys_channel(local_channel()))) { + $old_id = $r[0]['id']; + $r = [copy_of_pubitem(App::get_channel(), $r[0]['mid'])]; + if ($r[0]['id'] !== $old_id) { + // keep track that a copy was made to display a special status notice that is unique to this condition + $pub_copy = true; + } + } + } + + if (!$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(); + } + + xchan_query($r, true); + + $parent_item = $r[0]; + $parent = $r[0]['id']; + + // multi-level threading - preserve the info but re-parent to our single level threading + + $thr_parent = $parent_mid; + + $route = $parent_item['route']; + } + + if ($parent_item && isset($parent_item['replyto']) && $parent_item['replyto']) { + $replyto = unserialise($parent_item['replyto']); + } + + $moderated = false; + + if (!$observer) { + $observer = App::get_observer(); + if (!$observer) { + // perhaps we're allowing moderated comments from anonymous viewers + $observer = anon_identity_init($_REQUEST); + if ($observer) { + $moderated = true; + $remote_xchan = $remote_observer = $observer; + } + } + } + + if (!$observer) { + notice(t('Permission denied.') . EOL); + if ($api_source) { + return (['success' => false, 'message' => 'permission denied']); + } + if (x($_REQUEST, 'return')) { + goaway(z_root() . "/" . $return_path); + } + killme(); + } + + if ($parent) { + logger('mod_item: item_post parent=' . $parent); + $can_comment = false; + + $can_comment = can_comment_on_post($observer['xchan_hash'], $parent_item); + if (!$can_comment) { + if ((array_key_exists('owner', $parent_item)) && intval($parent_item['owner']['abook_self']) == 1) { + $can_comment = perm_is_allowed($profile_uid, $observer['xchan_hash'], 'post_comments'); + } + } + + 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(); + } + } else { + // fixme - $webpage could also be a wiki page or article and require a different permission to be checked. + 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(); + } + } + + // check if this author is being moderated through the 'moderated' (negative) permission + // when posting wall-to-wall + if ($moderated === false && intval($uid) !== intval($profile_uid)) { + $moderated = perm_is_allowed($profile_uid, $observer['xchan_hash'], 'moderated'); + } + + // If this is a comment, check the moderated permission of the parent; who may be on another site + $remote_moderated = (($parent) ? their_perms_contains($profile_uid, $parent_item['owner_xchan'], 'moderated') : false); + if ($remote_moderated) { + notice(t('Comment may be moderated.') . EOL); + } + + // is this an edited post? + + $orig_post = null; + + if ($namespace && $remote_id) { + // It wasn't an internally generated post - see if we've got an item matching this remote service id + $i = q( + "select iid from iconfig where cat = 'system' and k = '%s' and v = '%s' limit 1", + dbesc($namespace), + dbesc($remote_id) + ); + if ($i) { + $post_id = $i[0]['iid']; + } + } + + $iconfig = null; + + if ($post_id) { + $i = q( + "SELECT * FROM item WHERE uid = %d AND id = %d LIMIT 1", + intval($profile_uid), + intval($post_id) + ); + if (!count($i)) { + killme(); + } + $orig_post = $i[0]; + $iconfig = q( + "select * from iconfig where iid = %d", + intval($post_id) + ); + } + + + if (!$channel) { + if ($uid && $uid == $profile_uid) { + $channel = App::get_channel(); + } else { + // posting as yourself but not necessarily to a channel you control + $r = q( + "select * from channel left join account on channel_account_id = account_id where channel_id = %d LIMIT 1", + intval($profile_uid) + ); + if ($r) { + $channel = $r[0]; + } + } + } + + + 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(); + } + + $owner_xchan = null; + + $r = q( + "select * from xchan where xchan_hash = '%s' limit 1", + dbesc($channel['channel_hash']) + ); + if ($r && count($r)) { + $owner_xchan = $r[0]; + } 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(); + } + + $walltowall = false; + $walltowall_comment = false; + + if ($remote_xchan && !$moderated) { + $observer = $remote_observer; + } + + if ($observer) { + logger('mod_item: post accepted from ' . $observer['xchan_name'] . ' for ' . $owner_xchan['xchan_name'], LOGGER_DEBUG); + + // wall-to-wall detection. + // For top-level posts, if the author and owner are different it's a wall-to-wall + // For comments, We need to additionally look at the parent and see if it's a wall post that originated locally. + + if ($observer['xchan_name'] != $owner_xchan['xchan_name']) { + if (($parent_item) && ($parent_item['item_wall'] && $parent_item['item_origin'])) { + $walltowall_comment = true; + $walltowall = true; + } + if (!$parent) { + $walltowall = true; + } + } + } + + if (!isset($replyto)) { + if (strpos($owner_xchan['xchan_hash'], 'http') === 0) { + $replyto = $owner_xchan['xchan_hash']; + } else { + $replyto = $owner_xchan['xchan_url']; + } + } + + + $acl = new AccessControl($channel); + + $view_policy = PermissionLimits::Get($channel['channel_id'], 'view_stream'); + $comment_policy = ((isset($_REQUEST['comments_from']) && intval($_REQUEST['comments_from'])) ? intval($_REQUEST['comments_from']) : PermissionLimits::Get($channel['channel_id'], 'post_comments')); + + $public_policy = ((x($_REQUEST, 'public_policy')) ? escape_tags($_REQUEST['public_policy']) : map_scope($view_policy, true)); + if ($webpage) { + $public_policy = ''; + } + if ($public_policy) { + $private = 1; + } + + if ($orig_post) { + $private = 0; + // webpages and unpublished drafts are allowed to change ACLs after the fact. Normal conversation items aren't. + if ($webpage || intval($orig_post['item_unpublished'])) { + $acl->set_from_array($_REQUEST); + } else { + $acl->set($orig_post); + $public_policy = $orig_post['public_policy']; + $private = $orig_post['item_private']; + } + + if ($public_policy || $acl->is_private()) { + $private = (($private) ? $private : 1); + } + + $location = $orig_post['location']; + $coord = $orig_post['coord']; + $verb = $orig_post['verb']; + $app = $orig_post['app']; + $title = escape_tags(trim($_REQUEST['title'])); + $summary = trim($_REQUEST['summary']); + $body = trim($_REQUEST['body']); + + $item_flags = $orig_post['item_flags']; + $item_origin = $orig_post['item_origin']; + $item_unseen = $orig_post['item_unseen']; + $item_starred = $orig_post['item_starred']; + $item_uplink = $orig_post['item_uplink']; + $item_wall = $orig_post['item_wall']; + $item_thread_top = $orig_post['item_thread_top']; + $item_notshown = $orig_post['item_notshown']; + $item_nsfw = $orig_post['item_nsfw']; + $item_relay = $orig_post['item_relay']; + $item_mentionsme = $orig_post['item_mentionsme']; + $item_nocomment = $orig_post['item_nocomment']; + $item_obscured = $orig_post['item_obscured']; + $item_verified = $orig_post['item_verified']; + $item_retained = $orig_post['item_retained']; + $item_rss = $orig_post['item_rss']; + $item_deleted = $orig_post['item_deleted']; + $item_type = $orig_post['item_type']; + $item_hidden = $orig_post['item_hidden']; + $item_delayed = $orig_post['item_delayed']; + $item_pending_remove = $orig_post['item_pending_remove']; + $item_blocked = $orig_post['item_blocked']; + + + $postopts = $orig_post['postopts']; + $created = ((intval($orig_post['item_unpublished'])) ? $created : $orig_post['created']); + $expires = ((intval($orig_post['item_unpublished'])) ? NULL_DATE : $orig_post['expires']); + $mid = $orig_post['mid']; + $parent_mid = $orig_post['parent_mid']; + $plink = $orig_post['plink']; + } else { + if (!$walltowall) { + if ( + (array_key_exists('contact_allow', $_REQUEST)) + || (array_key_exists('group_allow', $_REQUEST)) + || (array_key_exists('contact_deny', $_REQUEST)) + || (array_key_exists('group_deny', $_REQUEST)) + ) { + $acl->set_from_array($_REQUEST); + } elseif (!$api_source) { + // if no ACL has been defined and we aren't using the API, the form + // didn't send us any parameters. This means there's no ACL or it has + // been reset to the default audience. + // If $api_source is set and there are no ACL parameters, we default + // to the channel permissions which were set in the ACL contructor. + + $acl->set(array('allow_cid' => '', 'allow_gid' => '', 'deny_cid' => '', 'deny_gid' => '')); + } + } + + + $location = ((isset($_REQUEST['location'])) ? notags(trim($_REQUEST['location'])) : EMPTY_STR); + $coord = ((isset($_REQUEST['coord'])) ? notags(trim($_REQUEST['coord'])) : EMPTY_STR); + $verb = ((isset($_REQUEST['verb'])) ? notags(trim($_REQUEST['verb'])) : EMPTY_STR); + $title = ((isset($_REQUEST['title'])) ? escape_tags(trim($_REQUEST['title'])) : EMPTY_STR); + $summary = ((isset($_REQUEST['summary'])) ? trim($_REQUEST['summary']) : EMPTY_STR); + $body = ((isset($_REQUEST['body'])) ? trim($_REQUEST['body']) : EMPTY_STR); + $body .= ((isset($_REQUEST['attachment'])) ? trim($_REQUEST['attachment']) : EMPTY_STR); + $postopts = ''; + + $allow_empty = ((array_key_exists('allow_empty', $_REQUEST)) ? intval($_REQUEST['allow_empty']) : 0); + + $private = ((isset($private) && $private) ? $private : intval($acl->is_private() || ($public_policy))); + + // If this is a comment, set the permissions from the parent. + + if ($parent_item) { + $private = 0; + $acl->set($parent_item); + $private = ((intval($parent_item['item_private']) ? $parent_item['item_private'] : $acl->is_private())); + $public_policy = $parent_item['public_policy']; + $owner_hash = $parent_item['owner_xchan']; + $webpage = $parent_item['item_type']; + $comment_policy = $parent_item['comment_policy']; + $item_nocomment = $parent_item['item_nocomment']; + $comments_closed = $parent_item['comments_closed']; + } + + 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(); + } + } + + + if (Apps::system_app_installed($profile_uid, 'Expire Posts')) { + if (x($_REQUEST, 'expire')) { + $expires = datetime_convert(date_default_timezone_get(), 'UTC', $_REQUEST['expire']); + if ($expires <= datetime_convert()) { + $expires = NULL_DATE; + } + } + } + + + $mimetype = notags(trim($_REQUEST['mimetype'])); + if (!$mimetype) { + $mimetype = 'text/x-multicode'; + } + + + $execflag = ((intval($uid) == intval($profile_uid) + && ($channel['channel_pageflags'] & PAGE_ALLOWCODE)) ? true : false); + + if ($preview) { + $summary = z_input_filter($summary, $mimetype, $execflag); + $body = z_input_filter($body, $mimetype, $execflag); + } + + + $arr = ['profile_uid' => $profile_uid, 'summary' => $summary, 'content' => $body, 'mimetype' => $mimetype]; + call_hooks('post_content', $arr); + $summary = $arr['summary']; + $body = $arr['content']; + $mimetype = $arr['mimetype']; + + + $gacl = $acl->get(); + $str_contact_allow = $gacl['allow_cid']; + $str_group_allow = $gacl['allow_gid']; + $str_contact_deny = $gacl['deny_cid']; + $str_group_deny = $gacl['deny_gid']; + + + // if the acl contains a single contact and it's a group, add a mention. This is for compatibility + // with other groups implementations which require a mention to trigger group delivery. + + if (($str_contact_allow) && (!$str_group_allow) && (!$str_contact_deny) && (!$str_group_deny)) { + $cida = expand_acl($str_contact_allow); + if (count($cida) === 1) { + $netgroups = get_forum_channels($profile_uid, 1); + if ($netgroups) { + foreach ($netgroups as $ng) { + if ($ng['xchan_hash'] == $cida[0]) { + if (!is_array($post_tags)) { + $post_tags = []; + } + $post_tags[] = array( + 'uid' => $profile_uid, + 'ttype' => TERM_MENTION, + 'otype' => TERM_OBJ_POST, + 'term' => $ng['xchan_name'], + 'url' => $ng['xchan_url'] + ); + + $colls = get_xconfig($ng['xchan_hash'], 'activitypub', 'collections'); + if ($colls && is_array($colls) && isset($colls['wall'])) { + $datarray['target'] = [ + 'id' => $colls['wall'], + 'type' => 'Collection', + 'attributedTo' => ((in_array($ng['xchan_network'], ['zot6', 'nomad'])) ? $ng['xchan_url'] : $ng['xchan_hash']) + ]; + $datarray['tgt_type'] = 'Collection'; + } + } + } + } + } + } + + $groupww = false; + + // if this is a wall-to-wall post to a group, turn it into a direct message + + $role = get_pconfig($profile_uid, 'system', 'permissions_role'); + + $rolesettings = PermissionRoles::role_perms($role); + + $channel_type = isset($rolesettings['channel_type']) ? $rolesettings['channel_type'] : 'normal'; + + $is_group = (($channel_type === 'group') ? true : false); + + if (($is_group) && ($walltowall) && (!$walltowall_comment)) { + $groupww = true; + $str_contact_allow = $owner_xchan['xchan_hash']; + $str_group_allow = ''; + } + + + if (in_array($mimetype, [ 'text/bbcode', 'text/x-multicode' ])) { + // BBCODE alert: the following functions assume bbcode input + // and will require alternatives for alternative content-types (text/html, text/markdown, text/plain, etc.) + // we may need virtual or template classes to implement the possible alternatives + + if (strpos($body, '[/summary]') !== false) { + $match = ''; + $cnt = preg_match("/\[summary\](.*?)\[\/summary\]/ism", $body, $match); + if ($cnt) { + $summary .= $match[1]; + } + $body_content = preg_replace("/^(.*?)\[summary\](.*?)\[\/summary\]/ism", '', $body); + $body = trim($body_content); + } + + $summary = cleanup_bbcode($summary); + $body = cleanup_bbcode($body); + + // Look for tags and linkify them + $summary_tags = linkify_tags($summary, ($uid) ? $uid : $profile_uid); + $body_tags = linkify_tags($body, ($uid) ? $uid : $profile_uid); + $comment_tags = linkify_tags($hidden_mentions, ($uid) ? $uid : $profile_uid); + + foreach ([$summary_tags, $body_tags, $comment_tags] as $results) { + if ($results) { + // Set permissions based on tag replacements + set_linkified_perms($results, $str_contact_allow, $str_group_allow, $profile_uid, $parent_item, $private); + + if (!isset($post_tags)) { + $post_tags = []; + } + foreach ($results as $result) { + $success = $result['success']; + if ($success['replaced']) { + // suppress duplicate mentions/tags + $already_tagged = false; + foreach ($post_tags as $pt) { + if ($pt['term'] === $success['term'] && $pt['url'] === $success['url'] && intval($pt['ttype']) === intval($success['termtype'])) { + $already_tagged = true; + break; + } + } + if ($already_tagged) { + continue; + } + + $post_tags[] = array( + 'uid' => $profile_uid, + 'ttype' => $success['termtype'], + 'otype' => TERM_OBJ_POST, + 'term' => $success['term'], + 'url' => $success['url'] + ); + + // support #collection syntax to post to a collection + // this is accomplished by adding a pcategory tag for each collection target + // this is checked inside tag_deliver() to create a second delivery chain + + if ($success['termtype'] === TERM_HASHTAG) { + $r = q( + "select xchan_url from channel left join xchan on xchan_hash = channel_hash where channel_address = '%s' and channel_parent = '%s' and channel_removed = 0", + dbesc($success['term']), + dbesc(get_observer_hash()) + ); + if ($r) { + $post_tags[] = [ + 'uid' => $profile_uid, + 'ttype' => TERM_PCATEGORY, + 'otype' => TERM_OBJ_POST, + 'term' => $success['term'] . '@' . App::get_hostname(), + 'url' => $r[0]['xchan_url'] + ]; + } + } + } + } + } + } + + + /** + * process collections selected manually + */ + + if (array_key_exists('collections', $_REQUEST) && is_array($_REQUEST['collections']) && count($_REQUEST['collections'])) { + foreach ($_REQUEST['collections'] as $clct) { + $r = q( + "select xchan_url, xchan_hash from xchan left join hubloc on hubloc_hash = xchan_hash where hubloc_addr = '%s' limit 1", + dbesc($clct) + ); + if ($r) { + if (!isset($post_tags)) { + $post_tags = []; + } + $post_tags[] = [ + 'uid' => $profile_uid, + 'ttype' => TERM_PCATEGORY, + 'otype' => TERM_OBJ_POST, + 'term' => $clct, + 'url' => $r[0]['xchan_url'] + ]; + } + } + } + + if (($str_contact_allow) && (!$str_group_allow)) { + // direct message - private between individual channels but not groups + $private = 2; + } + + if ($private) { + // for edited posts, re-use any existing OCAP token (if found). + // Otherwise generate a new one. + + if ($iconfig) { + foreach ($iconfig as $cfg) { + if ($cfg['cat'] === 'ocap' && $cfg['k'] === 'relay') { + $token = $cfg['v']; + } + } + } + if (!$token) { + $token = new_token(); + } + } + + + /** + * + * When a photo was uploaded into the message using the (profile wall) ajax + * uploader, The permissions are initially set to disallow anybody but the + * owner from seeing it. This is because the permissions may not yet have been + * set for the post. If it's private, the photo permissions should be set + * appropriately. But we didn't know the final permissions on the post until + * now. So now we'll look for links of uploaded photos and attachments that are in the + * post and set them to the same permissions as the post itself. + * + * If the post was end-to-end encrypted we can't find images and attachments in the body, + * use our media_str input instead which only contains these elements - but only do this + * when encrypted content exists because the photo/attachment may have been removed from + * the post and we should keep it private. If it's encrypted we have no way of knowing + * so we'll set the permissions regardless and realise that the media may not be + * referenced in the post. + * + */ + + if (!$preview) { + fix_attached_permissions($profile_uid, ((strpos($body, '[/crypt]')) ? $_POST['media_str'] : $body), $str_contact_allow, $str_group_allow, $str_contact_deny, $str_group_deny, $token); + } + + + $attachments = ''; + $match = false; + + if (preg_match_all('/(\[attachment\](.*?)\[\/attachment\])/', $body, $match)) { + $attachments = []; + $i = 0; + foreach ($match[2] as $mtch) { + $attach_link = ''; + $hash = substr($mtch, 0, strpos($mtch, ',')); + $rev = intval(substr($mtch, strpos($mtch, ','))); + $r = attach_by_hash_nodata($hash, $observer['xchan_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 = str_replace($match[1][$i], $attach_link, $body); + $i++; + } + } + + + if (preg_match_all('/(\[share=(.*?)\](.*?)\[\/share\])/', $body, $match)) { + // process share by id + + $i = 0; + foreach ($match[2] as $mtch) { + $reshare = new \Zotlabs\Lib\Share($mtch); + $body = str_replace($match[1][$i], $reshare->bbcode(), $body); + $i++; + } + } + } + + // BBCODE end alert + + if (strlen($categories)) { + if (!isset($post_tags)) { + $post_tags = []; + } + + $cats = explode(',', $categories); + foreach ($cats as $cat) { + if ($webpage == ITEM_TYPE_CARD) { + $catlink = z_root() . '/cards/' . $channel['channel_address'] . '?f=&cat=' . urlencode(trim($cat)); + } elseif ($webpage == ITEM_TYPE_ARTICLE) { + $catlink = z_root() . '/articles/' . $channel['channel_address'] . '?f=&cat=' . urlencode(trim($cat)); + } else { + $catlink = $owner_xchan['xchan_url'] . '?f=&cat=' . urlencode(trim($cat)); + } + + $post_tags[] = array( + 'uid' => $profile_uid, + 'ttype' => TERM_CATEGORY, + 'otype' => TERM_OBJ_POST, + 'term' => trim($cat), + 'url' => $catlink + ); + } + } + + if ($orig_post) { + // preserve original tags + $t = q( + "select * from term where oid = %d and otype = %d and uid = %d and ttype in ( %d, %d, %d )", + intval($orig_post['id']), + intval(TERM_OBJ_POST), + intval($profile_uid), + intval(TERM_UNKNOWN), + intval(TERM_FILE), + intval(TERM_COMMUNITYTAG) + ); + if ($t) { + if (!isset($post_tags)) { + $post_tags = []; + } + + foreach ($t as $t1) { + $post_tags[] = array( + 'uid' => $profile_uid, + 'ttype' => $t1['ttype'], + 'otype' => TERM_OBJ_POST, + 'term' => $t1['term'], + 'url' => $t1['url'], + ); + } + } + } + + + $item_unseen = ((local_channel() != $profile_uid) ? 1 : 0); + $item_wall = ((isset($_REQUEST['type']) && ($_REQUEST['type'] === 'wall' || $_REQUEST['type'] === 'wall-comment')) ? 1 : 0); + $item_origin = (($origin) ? 1 : 0); + $item_nocomment = ((isset($item_nocomment)) ? $item_nocomment : $nocomment); + + + // determine if this is a wall post + + if ($parent) { + $item_wall = $parent_item['item_wall']; + } else { + if (!$webpage) { + $item_wall = 1; + } + } + + + if ($moderated) { + $item_blocked = ITEM_MODERATED; + } + + + if (!strlen($verb)) { + $verb = ACTIVITY_POST; + } + + $notify_type = (($parent) ? 'comment-new' : 'wall-new'); + + if (!(isset($mid) && $mid)) { + if ($message_id) { + $mid = $message_id; + } else { + $uuid = new_uuid(); + $mid = z_root() . '/item/' . $uuid; + } + } + + + if ($is_poll) { + $poll = [ + 'question' => $body, + 'answers' => $_REQUEST['poll_answers'], + 'multiple_answers' => $_REQUEST['poll_multiple_answers'], + 'expire_value' => $_REQUEST['poll_expire_value'], + 'expire_unit' => $_REQUEST['poll_expire_unit'] + ]; + $obj = $this->extract_poll_data($poll, ['item_private' => $private, 'allow_cid' => $str_contact_allow, 'allow_gid' => $str_contact_deny]); + } else { + $obj = $this->extract_bb_poll_data($body, ['item_private' => $private, 'allow_cid' => $str_contact_allow, 'allow_gid' => $str_contact_deny]); + } + + + if ($obj) { + $obj['url'] = $obj['id'] = $mid; + $obj['attributedTo'] = channel_url($channel); + $datarray['obj'] = $obj; + $obj_type = 'Question'; + if ($obj['endTime']) { + $d = datetime_convert('UTC', 'UTC', $obj['endTime']); + if ($d > NULL_DATE) { + $comments_closed = $d; + } + } + } + + if (!$parent_mid) { + $parent_mid = $mid; + } + + if ($parent_item) { + $parent_mid = $parent_item['mid']; + } + + + // Fallback so that we alway have a thr_parent + + if (!$thr_parent) { + $thr_parent = $mid; + } + + + $item_thread_top = ((!$parent) ? 1 : 0); + + + // fix permalinks for cards, etc. + + if ($webpage == ITEM_TYPE_CARD) { + $plink = z_root() . '/cards/' . $channel['channel_address'] . '/' . (($pagetitle) ? $pagetitle : $uuid); + } + if (($parent_item) && ($parent_item['item_type'] == ITEM_TYPE_CARD)) { + $r = q( + "select v from iconfig where iconfig.cat = 'system' and iconfig.k = 'CARD' and iconfig.iid = %d limit 1", + intval($parent_item['id']) + ); + if ($r) { + $plink = z_root() . '/cards/' . $channel['channel_address'] . '/' . $r[0]['v']; + } + } + + if ($webpage == ITEM_TYPE_ARTICLE) { + $plink = z_root() . '/articles/' . $channel['channel_address'] . '/' . (($pagetitle) ? $pagetitle : $uuid); + } + if (($parent_item) && ($parent_item['item_type'] == ITEM_TYPE_ARTICLE)) { + $r = q( + "select v from iconfig where iconfig.cat = 'system' and iconfig.k = 'ARTICLE' and iconfig.iid = %d limit 1", + intval($parent_item['id']) + ); + if ($r) { + $plink = z_root() . '/articles/' . $channel['channel_address'] . '/' . $r[0]['v']; + } + } + + if ((!(isset($plink) && $plink)) && $item_thread_top) { + $plink = z_root() . '/item/' . $uuid; + } + + if (array_path_exists('obj/id', $datarray)) { + $datarray['obj']['id'] = $mid; + } + + $datarray['aid'] = $channel['channel_account_id']; + $datarray['uid'] = $profile_uid; + $datarray['uuid'] = $uuid; + $datarray['owner_xchan'] = (($owner_hash) ? $owner_hash : $owner_xchan['xchan_hash']); + $datarray['author_xchan'] = $observer['xchan_hash']; + $datarray['created'] = $created; + $datarray['edited'] = (($orig_post && (!intval($orig_post['item_unpublished']))) ? datetime_convert() : $created); + $datarray['expires'] = $expires; + $datarray['commented'] = (($orig_post && (!intval($orig_post['item_unpublished']))) ? datetime_convert() : $created); + $datarray['received'] = (($orig_post && (!intval($orig_post['item_unpublished']))) ? datetime_convert() : $created); + $datarray['changed'] = (($orig_post && (!intval($orig_post['item_unpublished']))) ? datetime_convert() : $created); + $datarray['comments_closed'] = $comments_closed; + $datarray['mid'] = $mid; + $datarray['parent_mid'] = $parent_mid; + $datarray['mimetype'] = $mimetype; + $datarray['title'] = $title; + $datarray['summary'] = $summary; + $datarray['body'] = $body; + $datarray['app'] = $app; + $datarray['location'] = $location; + $datarray['coord'] = $coord; + $datarray['verb'] = $verb; + $datarray['obj_type'] = $obj_type; + $datarray['allow_cid'] = $str_contact_allow; + $datarray['allow_gid'] = $str_group_allow; + $datarray['deny_cid'] = $str_contact_deny; + $datarray['deny_gid'] = $str_group_deny; + $datarray['attach'] = $attachments; + $datarray['thr_parent'] = $thr_parent; + $datarray['postopts'] = $postopts; + $datarray['item_unseen'] = intval($item_unseen); + $datarray['item_wall'] = intval($item_wall); + $datarray['item_origin'] = intval($item_origin); + $datarray['item_type'] = $webpage; + $datarray['item_private'] = intval($private); + $datarray['item_thread_top'] = intval($item_thread_top); + $datarray['item_unseen'] = intval($item_unseen); + $datarray['item_starred'] = intval($item_starred); + $datarray['item_uplink'] = intval($item_uplink); + $datarray['item_consensus'] = 0; + $datarray['item_notshown'] = intval($item_notshown); + $datarray['item_nsfw'] = intval($item_nsfw); + $datarray['item_relay'] = intval($item_relay); + $datarray['item_mentionsme'] = intval($item_mentionsme); + $datarray['item_nocomment'] = intval($item_nocomment); + $datarray['item_obscured'] = intval($item_obscured); + $datarray['item_verified'] = intval($item_verified); + $datarray['item_retained'] = intval($item_retained); + $datarray['item_rss'] = intval($item_rss); + $datarray['item_deleted'] = intval($item_deleted); + $datarray['item_hidden'] = intval($item_hidden); + $datarray['item_unpublished'] = intval($item_unpublished); + $datarray['item_delayed'] = intval($item_delayed); + $datarray['item_pending_remove'] = intval($item_pending_remove); + $datarray['item_blocked'] = intval($item_blocked); + $datarray['layout_mid'] = $layout_mid; + $datarray['public_policy'] = $public_policy; + $datarray['comment_policy'] = ((is_numeric($comment_policy)) ? map_scope($comment_policy) : $comment_policy); // only map scope if it is numeric, otherwise use what we have + $datarray['term'] = $post_tags; + $datarray['plink'] = $plink; + $datarray['route'] = $route; + $datarray['replyto'] = $replyto; + + // A specific ACL over-rides public_policy completely + + if (!empty_acl($datarray)) { + $datarray['public_policy'] = ''; + } + + if ($iconfig) { + $datarray['iconfig'] = $iconfig; + } + if ($private) { + IConfig::set($datarray, 'ocap', 'relay', $token); + } + + if (!array_key_exists('obj', $datarray)) { + $copy = $datarray; + $copy['author'] = $observer; + $datarray['obj'] = Activity::encode_item($copy, ((get_config('system', 'activitypub', ACTIVITYPUB_ENABLED)) ? true : false)); + $recips = []; + $i = $datarray['obj']; + if ($i['to']) { + $recips['to'] = $i['to']; + } + if ($i['cc']) { + $recips['cc'] = $i['cc']; + } + IConfig::Set($datarray, 'activitypub', 'recips', $recips); + } + + Activity::rewrite_mentions($datarray); + + // preview mode - prepare the body for display and send it via json + + if ($preview) { + require_once('include/conversation.php'); + + $datarray['owner'] = $owner_xchan; + $datarray['author'] = $observer; + $datarray['attach'] = json_encode($datarray['attach']); + $o = conversation(array($datarray), 'search', false, 'preview'); + // logger('preview: ' . $o, LOGGER_DEBUG); + echo json_encode(array('preview' => $o)); + killme(); + } + + // Let 'post_local' event listeners know if this is an edit. + // We will unset it immediately afterward. + + if ($orig_post) { + $datarray['edit'] = true; + } + + // suppress duplicates, *unless* you're editing an existing post. This could get picked up + // as a duplicate if you're editing it very soon after posting it initially and you edited + // some attribute besides the content, such as title or categories. + + if (PConfig::Get($profile_uid, 'system', 'suppress_duplicates', true) && (!$orig_post)) { + $z = q( + "select created from item where uid = %d and created > %s - INTERVAL %s and body = '%s' limit 1", + intval($profile_uid), + db_utcnow(), + db_quoteinterval('2 MINUTE'), + dbesc($body) + ); + + if ($z) { + $datarray['cancel'] = 1; + notice(t('Duplicate post suppressed.') . EOL); + logger('Duplicate post. Cancelled.'); + } + } + + call_hooks('post_local', $datarray); + + // This is no longer needed + unset($datarray['edit']); + + if (x($datarray, 'cancel')) { + 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']; + json_return_and_die($json); + } + + + if (mb_strlen($datarray['title']) > 191) { + $datarray['title'] = mb_substr($datarray['title'], 0, 191); + } + + if ($webpage) { + IConfig::Set( + $datarray, + 'system', + webpage_to_namespace($webpage), + (($pagetitle) ? $pagetitle : basename($datarray['mid'])), + true + ); + } elseif ($namespace) { + IConfig::Set( + $datarray, + 'system', + $namespace, + (($remote_id) ? $remote_id : basename($datarray['mid'])), + true + ); + } + + if (intval($datarray['item_unpublished'])) { + $draft_msg = t('Draft saved. Use Drafts app to continue editing.'); + } + + if ($orig_post) { + $datarray['id'] = $post_id; + + $x = item_store_update($datarray, $execflag); + if (!$parent) { + $r = q( + "select * from item where id = %d", + intval($post_id) + ); + if ($r) { + xchan_query($r); + $sync_item = fetch_post_tags($r); + Libsync::build_sync_packet($profile_uid, array('item' => array(encode_item($sync_item[0], true)))); + } + } + if (!$nopush) { + Run::Summon(['Notifier', 'edit_post', $post_id]); + } + + if ($api_source) { + return ($x); + } + + + if (intval($datarray['item_unpublished'])) { + info($draft_msg); + } + + + if ((x($_REQUEST, 'return')) && strlen($return_path)) { + logger('return: ' . $return_path); + goaway(z_root() . "/" . $return_path); + } + killme(); + } else { + $post_id = 0; + } + + $post = item_store($datarray, $execflag); + + if ($pub_copy) { + info(t('Your comment has been posted.') . EOL); + } + + $post_id = $post['item_id']; + $datarray = $post['item']; + + + if ($post_id) { + logger('mod_item: saved item ' . $post_id); + + if ($parent) { + // prevent conversations which you are involved from being expired + + if (local_channel()) { + retain_item($parent); + } + + // only send comment notification if this is a wall-to-wall comment and not a DM, + // otherwise it will happen during delivery + + if (($datarray['owner_xchan'] != $datarray['author_xchan']) && (intval($parent_item['item_wall'])) && intval($datarray['item_private']) != 2) { + Enotify::submit(array( + 'type' => NOTIFY_COMMENT, + 'from_xchan' => $datarray['author_xchan'], + 'to_xchan' => $datarray['owner_xchan'], + 'item' => $datarray, + 'link' => z_root() . '/display/' . gen_link_id($datarray['mid']), + 'verb' => ACTIVITY_POST, + 'otype' => 'item', + 'parent' => $parent, + 'parent_mid' => $parent_item['mid'] + )); + } + } else { + $parent = $post_id; + + if (($datarray['owner_xchan'] != $datarray['author_xchan']) && ($datarray['item_type'] == ITEM_TYPE_POST)) { + Enotify::submit(array( + 'type' => NOTIFY_WALL, + 'from_xchan' => $datarray['author_xchan'], + 'to_xchan' => $datarray['owner_xchan'], + 'item' => $datarray, + 'link' => z_root() . '/display/' . gen_link_id($datarray['mid']), + 'verb' => ACTIVITY_POST, + 'otype' => 'item' + )); + } + + if ($uid && $uid == $profile_uid && (is_item_normal($datarray))) { + q( + "update channel set channel_lastpost = '%s' where channel_id = %d", + dbesc(datetime_convert()), + intval($uid) + ); + } + } + + // photo comments turn the corresponding item visible to the profile wall + // This way we don't see every picture in your new photo album posted to your wall at once. + // They will show up as people comment on them. + + if (intval($parent_item['item_hidden'])) { + $r = q( + "UPDATE item SET item_hidden = 0 WHERE id = %d", + intval($parent_item['id']) + ); + } + } else { + logger('mod_item: unable to retrieve post that was just stored.'); + notice(t('System error. Post not saved.') . EOL); + if ($return_path) { + goaway(z_root() . "/" . $return_path); + } + if ($api_source) { + return (['success' => false, 'message' => 'system error']); + } + killme(); + } + + if (($parent) && ($parent != $post_id)) { + // Store the comment signature information in case we need to relay to Diaspora + //$ditem = $datarray; + //$ditem['author'] = $observer; + //store_diaspora_comment_sig($ditem,$channel,$parent_item, $post_id, (($walltowall_comment) ? 1 : 0)); + } else { + $r = q( + "select * from item where id = %d", + intval($post_id) + ); + if ($r) { + xchan_query($r); + $sync_item = fetch_post_tags($r); + Libsync::build_sync_packet($profile_uid, array('item' => array(encode_item($sync_item[0], true)))); + } + } + + $datarray['id'] = $post_id; + $datarray['llink'] = z_root() . '/display/' . gen_link_id($datarray['mid']); + + call_hooks('post_local_end', $datarray); + + if ($groupww) { + $nopush = false; + } + + if (!$nopush) { + Run::Summon(['Notifier', $notify_type, $post_id]); + } + logger('post_complete'); + + if ($moderated) { + info(t('Your post/comment is awaiting approval.') . EOL); + } + + // figure out how to return, depending on from whence we came + + if ($api_source) { + return $post; + } + + if (intval($datarray['item_unpublished'])) { + info($draft_msg); + } + + if ($return_path) { + goaway(z_root() . "/" . $return_path); + } + + $json = array('success' => 1); + if (x($_REQUEST, 'jsreload') && strlen($_REQUEST['jsreload'])) { + $json['reload'] = z_root() . '/' . $_REQUEST['jsreload']; + } + + logger('post_json: ' . print_r($json, true), LOGGER_DEBUG); + + echo json_encode($json); + killme(); + // NOTREACHED + } + + + public function get() + { + + + if ($this->return_404) { + notice(t('Not found')); + return; + } + + if ((!local_channel()) && (!remote_channel())) { + return; + } + + // allow pinned items to be dropped. 'pin-' was prepended to the id of these + // items so that they would have a unique html id even if the pinned item + // was also displayed in a normal conversation on the same web page. + + $drop_id = str_replace('pin-', '', argv(2)); + + if ((argc() == 3) && (argv(1) === 'drop') && intval($drop_id)) { + $i = q( + "select * from item where id = %d limit 1", + intval($drop_id) + ); + + if ($i) { + $can_delete = false; + $local_delete = false; + $regular_delete = false; + + if (local_channel() && local_channel() == $i[0]['uid']) { + $local_delete = true; + } + + $ob_hash = get_observer_hash(); + if ($ob_hash && ($ob_hash === $i[0]['author_xchan'] || $ob_hash === $i[0]['owner_xchan'] || $ob_hash === $i[0]['source_xchan'])) { + $can_delete = true; + $regular_delete = true; + } + + // The site admin can delete any post/item on the site. + // If the item originated on this site+channel the deletion will propagate downstream. + // Otherwise just the local copy is removed. + + if (is_site_admin()) { + $local_delete = true; + if (intval($i[0]['item_origin'])) { + $can_delete = true; + } + } + + + if (!($can_delete || $local_delete)) { + notice(t('Permission denied.') . EOL); + return; + } + + if ($i[0]['resource_type'] === 'event') { + // delete and sync the event separately + $r = q( + "SELECT * FROM event WHERE event_hash = '%s' AND uid = %d LIMIT 1", + dbesc($i[0]['resource_id']), + intval($i[0]['uid']) + ); + if ($r && $regular_delete) { + $sync_event = $r[0]; + q( + "delete from event WHERE event_hash = '%s' AND uid = %d LIMIT 1", + dbesc($i[0]['resource_id']), + intval($i[0]['uid']) + ); + $sync_event['event_deleted'] = 1; + Libsync::build_sync_packet($i[0]['uid'], array('event' => array($sync_event))); + } + } + + if ($i[0]['resource_type'] === 'photo') { + attach_delete($i[0]['uid'], $i[0]['resource_id'], true); + $ch = channelx_by_n($i[0]['uid']); + if ($ch && $regular_delete) { + $sync = attach_export_data($ch, $i[0]['resource_id'], true); + if ($sync) { + Libsync::build_sync_packet($i[0]['uid'], array('file' => array($sync))); + } + } + } + + + // if this is a different page type or it's just a local delete + // but not by the item author or owner, do a simple deletion + + $complex = false; + + if (intval($i[0]['item_type']) || ($local_delete && (!$can_delete))) { + drop_item($i[0]['id']); + } else { + // complex deletion that needs to propagate and be performed in phases + drop_item($i[0]['id'], true, DROPITEM_PHASE1); + $complex = true; + } + + $r = q( + "select * from item where id = %d", + intval($i[0]['id']) + ); + if ($r) { + xchan_query($r); + $sync_item = fetch_post_tags($r); + Libsync::build_sync_packet($i[0]['uid'], array('item' => array(encode_item($sync_item[0], true)))); + } + + if ($complex) { + tag_deliver($i[0]['uid'], $i[0]['id']); + } + } + } + } + + + public function item_check_service_class($channel_id, $iswebpage) + { + $ret = array('success' => false, 'message' => ''); + + if ($iswebpage) { + $r = q( + "select count(i.id) as total from item i right join channel c on (i.author_xchan=c.channel_hash and i.uid=c.channel_id ) and i.parent=i.id and i.item_type = %d and i.item_deleted = 0 and i.uid= %d ", - intval(ITEM_TYPE_WEBPAGE), - intval($channel_id) - ); - } - else { - $r = q("select count(id) as total from item where parent = id and item_wall = 1 and uid = %d " . item_normal(), - intval($channel_id) - ); - } - - if(! $r) { - $ret['message'] = t('Unable to obtain post information from database.'); - return $ret; - } + intval(ITEM_TYPE_WEBPAGE), + intval($channel_id) + ); + } else { + $r = q( + "select count(id) as total from item where parent = id and item_wall = 1 and uid = %d " . item_normal(), + intval($channel_id) + ); + } + + if (!$r) { + $ret['message'] = t('Unable to obtain post information from database.'); + return $ret; + } - if (!$iswebpage) { - $max = engr_units_to_bytes(service_class_fetch($channel_id,'total_items')); - if(! service_class_allows($channel_id,'total_items',$r[0]['total'])) { - $result['message'] .= upgrade_message() . sprintf( t('You have reached your limit of %1$.0f top level posts.'),$max); - return $result; - } - } - else { - $max = engr_units_to_bytes(service_class_fetch($channel_id,'total_pages')); - if(! service_class_allows($channel_id,'total_pages',$r[0]['total'])) { - $result['message'] .= upgrade_message() . sprintf( t('You have reached your limit of %1$.0f webpages.'),$max); - return $result; - } - } - - $ret['success'] = true; - return $ret; - } - - function extract_bb_poll_data(&$body,$item) { + if (!$iswebpage) { + $max = engr_units_to_bytes(service_class_fetch($channel_id, 'total_items')); + if (!service_class_allows($channel_id, 'total_items', $r[0]['total'])) { + $result['message'] .= upgrade_message() . sprintf(t('You have reached your limit of %1$.0f top level posts.'), $max); + return $result; + } + } else { + $max = engr_units_to_bytes(service_class_fetch($channel_id, 'total_pages')); + if (!service_class_allows($channel_id, 'total_pages', $r[0]['total'])) { + $result['message'] .= upgrade_message() . sprintf(t('You have reached your limit of %1$.0f webpages.'), $max); + return $result; + } + } - $multiple = false; + $ret['success'] = true; + return $ret; + } - if (strpos($body,'[/question]') === false && strpos($body,'[/answer]') === false) { - return false; - } - if (strpos($body,'[nobb]') !== false) { - return false; - } + public function extract_bb_poll_data(&$body, $item) + { - $obj = []; - $ptr = []; - $matches = null; - $obj['type'] = 'Question'; + $multiple = false; - if (preg_match_all('/\[answer\](.*?)\[\/answer\]/ism',$body,$matches,PREG_SET_ORDER)) { - foreach ($matches as $match) { - $ptr[] = [ 'name' => $match[1], 'type' => 'Note', 'replies' => [ 'type' => 'Collection', 'totalItems' => 0 ]]; - $body = str_replace('[answer]' . $match[1] . '[/answer]', EMPTY_STR, $body); - } - } + if (strpos($body, '[/question]') === false && strpos($body, '[/answer]') === false) { + return false; + } + if (strpos($body, '[nobb]') !== false) { + return false; + } - $matches = null; + $obj = []; + $ptr = []; + $matches = null; + $obj['type'] = 'Question'; - if (preg_match('/\[question\](.*?)\[\/question\]/ism',$body,$matches)) { - $obj['content'] = bbcode($matches[1]); - $body = str_replace('[question]' . $matches[1] . '[/question]', $matches[1], $body); - $obj['oneOf'] = $ptr; - } + if (preg_match_all('/\[answer\](.*?)\[\/answer\]/ism', $body, $matches, PREG_SET_ORDER)) { + foreach ($matches as $match) { + $ptr[] = ['name' => $match[1], 'type' => 'Note', 'replies' => ['type' => 'Collection', 'totalItems' => 0]]; + $body = str_replace('[answer]' . $match[1] . '[/answer]', EMPTY_STR, $body); + } + } - $matches = null; - - if (preg_match('/\[question=multiple\](.*?)\[\/question\]/ism',$body,$matches)) { - $obj['content'] = bbcode($matches[1]); - $body = str_replace('[question=multiple]' . $matches[1] . '[/question]', $matches[1], $body); - $obj['anyOf'] = $ptr; - } + $matches = null; - $matches = null; - - if (preg_match('/\[ends\](.*?)\[\/ends\]/ism',$body,$matches)) { - $obj['endTime'] = datetime_convert(date_default_timezone_get(),'UTC', $matches[1],ATOM_TIME); - $body = str_replace('[ends]' . $matches[1] . '[/ends]', EMPTY_STR, $body); - } + if (preg_match('/\[question\](.*?)\[\/question\]/ism', $body, $matches)) { + $obj['content'] = bbcode($matches[1]); + $body = str_replace('[question]' . $matches[1] . '[/question]', $matches[1], $body); + $obj['oneOf'] = $ptr; + } - if ($item['item_private']) { - $obj['to'] = Activity::map_acl($item); - } - else { - $obj['to'] = [ ACTIVITY_PUBLIC_INBOX ]; - } - - return $obj; + $matches = null; - } + if (preg_match('/\[question=multiple\](.*?)\[\/question\]/ism', $body, $matches)) { + $obj['content'] = bbcode($matches[1]); + $body = str_replace('[question=multiple]' . $matches[1] . '[/question]', $matches[1], $body); + $obj['anyOf'] = $ptr; + } - function extract_poll_data($poll, $item) { + $matches = null; - $multiple = intval($poll['multiple_answers']); - $expire_value = intval($poll['expire_value']); - $expire_unit = $poll['expire_unit']; - $question = $poll['question']; - $answers = $poll['answers']; + if (preg_match('/\[ends\](.*?)\[\/ends\]/ism', $body, $matches)) { + $obj['endTime'] = datetime_convert(date_default_timezone_get(), 'UTC', $matches[1], ATOM_TIME); + $body = str_replace('[ends]' . $matches[1] . '[/ends]', EMPTY_STR, $body); + } - $obj = []; - $ptr = []; - $obj['type'] = 'Question'; - $obj['content'] = bbcode($question); + if ($item['item_private']) { + $obj['to'] = Activity::map_acl($item); + } else { + $obj['to'] = [ACTIVITY_PUBLIC_INBOX]; + } - foreach($answers as $answer) { - if(trim($answer)) - $ptr[] = [ 'name' => escape_tags($answer), 'type' => 'Note', 'replies' => [ 'type' => 'Collection', 'totalItems' => 0 ]]; - } + return $obj; + } - if($multiple) { - $obj['anyOf'] = $ptr; - } - else { - $obj['oneOf'] = $ptr; - } + public function extract_poll_data($poll, $item) + { - $obj['endTime'] = datetime_convert(date_default_timezone_get(), 'UTC', 'now + ' . $expire_value . ' ' . $expire_unit, ATOM_TIME); + $multiple = intval($poll['multiple_answers']); + $expire_value = intval($poll['expire_value']); + $expire_unit = $poll['expire_unit']; + $question = $poll['question']; + $answers = $poll['answers']; - if ($item['item_private']) { - $obj['to'] = Activity::map_acl($item); - } - else { - $obj['to'] = [ ACTIVITY_PUBLIC_INBOX ]; - } + $obj = []; + $ptr = []; + $obj['type'] = 'Question'; + $obj['content'] = bbcode($question); - return $obj; + foreach ($answers as $answer) { + if (trim($answer)) { + $ptr[] = ['name' => escape_tags($answer), 'type' => 'Note', 'replies' => ['type' => 'Collection', 'totalItems' => 0]]; + } + } - } + if ($multiple) { + $obj['anyOf'] = $ptr; + } else { + $obj['oneOf'] = $ptr; + } + $obj['endTime'] = datetime_convert(date_default_timezone_get(), 'UTC', 'now + ' . $expire_value . ' ' . $expire_unit, ATOM_TIME); + + if ($item['item_private']) { + $obj['to'] = Activity::map_acl($item); + } else { + $obj['to'] = [ACTIVITY_PUBLIC_INBOX]; + } + + return $obj; + } } diff --git a/Zotlabs/Module/Jwks.php b/Zotlabs/Module/Jwks.php index 660b37568..3e0dd544f 100644 --- a/Zotlabs/Module/Jwks.php +++ b/Zotlabs/Module/Jwks.php @@ -5,57 +5,58 @@ namespace Zotlabs\Module; use Zotlabs\Lib\Keyutils; use Zotlabs\Web\Controller; -class Jwks extends Controller { +class Jwks extends Controller +{ - function init() { - - Keyutils::pemtome(get_config('system','pubkey'),$m,$e); + public function init() + { - /** - * RFC7518 - * - 6.3.1.1. "n" (Modulus) Parameter + Keyutils::pemtome(get_config('system', 'pubkey'), $m, $e); - The "n" (modulus) parameter contains the modulus value for the RSA - public key. It is represented as a Base64urlUInt-encoded value. + /** + * RFC7518 + * + * 6.3.1.1. "n" (Modulus) Parameter + * + * The "n" (modulus) parameter contains the modulus value for the RSA + * public key. It is represented as a Base64urlUInt-encoded value. + * + * Note that implementers have found that some cryptographic libraries + * prefix an extra zero-valued octet to the modulus representations they + * return, for instance, returning 257 octets for a 2048-bit key, rather + * than 256. Implementations using such libraries will need to take + * care to omit the extra octet from the base64url-encoded + * representation. + * + */ - Note that implementers have found that some cryptographic libraries - prefix an extra zero-valued octet to the modulus representations they - return, for instance, returning 257 octets for a 2048-bit key, rather - than 256. Implementations using such libraries will need to take - care to omit the extra octet from the base64url-encoded - representation. - * - */ + $l = strlen((string)$m); + if ($l & 1) { + $m = substr((string)$m, 1); + } - $l = strlen((string) $m); - if ($l & 1) { - $m = substr((string) $m,1); - } - - $keys = [ - [ - 'e' => base64url_encode($e), - 'n' => base64url_encode($m), - 'kty' => 'RSA', - 'kid' => '0', - ] - ]; + $keys = [ + [ + 'e' => base64url_encode($e), + 'n' => base64url_encode($m), + 'kty' => 'RSA', + 'kid' => '0', + ] + ]; - $ret = [ - 'keys' => $keys - ]; + $ret = [ + 'keys' => $keys + ]; - if (argc() > 1) { - $entry = intval(argv(1)); - if ($keys[$entry]) { - unset($keys[$entry]['kid']); - json_return_and_die($keys[$entry],'application/jwk+json'); - } - } + if (argc() > 1) { + $entry = intval(argv(1)); + if ($keys[$entry]) { + unset($keys[$entry]['kid']); + json_return_and_die($keys[$entry], 'application/jwk+json'); + } + } - json_return_and_die($ret,'application/jwk-set+json'); - - } -} \ No newline at end of file + json_return_and_die($ret, 'application/jwk-set+json'); + } +} diff --git a/Zotlabs/Module/Lang.php b/Zotlabs/Module/Lang.php index 9858beecd..53f7081af 100644 --- a/Zotlabs/Module/Lang.php +++ b/Zotlabs/Module/Lang.php @@ -1,28 +1,29 @@ Language App (Not Installed):
      '; - $o .= t('Change UI language'); - return $o; - } - } + $o = 'Language App (Not Installed):
      '; + $o .= t('Change UI language'); + return $o; + } + } - nav_set_selected('Language'); - return lang_selector(); - - } - + nav_set_selected('Language'); + return lang_selector(); + } } diff --git a/Zotlabs/Module/Layouts.php b/Zotlabs/Module/Layouts.php index 33c869a6b..e156fcc1e 100644 --- a/Zotlabs/Module/Layouts.php +++ b/Zotlabs/Module/Layouts.php @@ -1,211 +1,217 @@ 1 && argv(1) === 'sys' && is_site_admin()) { - $sys = get_sys_channel(); - if($sys && intval($sys['channel_id'])) { - \App::$is_sys = true; - } - } + if (argc() > 1 && argv(1) === 'sys' && is_site_admin()) { + $sys = get_sys_channel(); + if ($sys && intval($sys['channel_id'])) { + App::$is_sys = true; + } + } - if(argc() > 1) - $which = argv(1); - else - return; + if (argc() > 1) { + $which = argv(1); + } else { + return; + } - Libprofile::load($which); + Libprofile::load($which); + } - } + public function get() + { - function get() { + if (!App::$profile) { + notice(t('Requested profile is not available.') . EOL); + App::$error = 404; + return; + } - if(! \App::$profile) { - notice( t('Requested profile is not available.') . EOL ); - \App::$error = 404; - return; - } + $which = argv(1); - $which = argv(1); + $_SESSION['return_url'] = App::$query_string; - $_SESSION['return_url'] = \App::$query_string; + $uid = local_channel(); + $owner = 0; + $channel = null; + $observer = App::get_observer(); - $uid = local_channel(); - $owner = 0; - $channel = null; - $observer = \App::get_observer(); + $channel = App::get_channel(); - $channel = \App::get_channel(); + if (App::$is_sys && is_site_admin()) { + $sys = get_sys_channel(); + if ($sys && intval($sys['channel_id'])) { + $uid = $owner = intval($sys['channel_id']); + $channel = $sys; + $observer = $sys; + } + } - if(\App::$is_sys && is_site_admin()) { - $sys = get_sys_channel(); - if($sys && intval($sys['channel_id'])) { - $uid = $owner = intval($sys['channel_id']); - $channel = $sys; - $observer = $sys; - } - } + if (!$owner) { + // Figure out who the page owner is. + $r = q( + "select channel_id from channel where channel_address = '%s'", + dbesc($which) + ); + if ($r) { + $owner = intval($r[0]['channel_id']); + } + } - if(! $owner) { - // Figure out who the page owner is. - $r = q("select channel_id from channel where channel_address = '%s'", - dbesc($which) - ); - if($r) { - $owner = intval($r[0]['channel_id']); - } - } + $ob_hash = (($observer) ? $observer['xchan_hash'] : ''); - $ob_hash = (($observer) ? $observer['xchan_hash'] : ''); + $perms = get_all_perms($owner, $ob_hash); - $perms = get_all_perms($owner,$ob_hash); + if (!$perms['write_pages']) { + notice(t('Permission denied.') . EOL); + return; + } - if(! $perms['write_pages']) { - notice( t('Permission denied.') . EOL); - return; - } + // Block design features from visitors - // Block design features from visitors + if ((!$uid) || ($uid != $owner)) { + notice(t('Permission denied.') . EOL); + return; + } - if((! $uid) || ($uid != $owner)) { - notice( t('Permission denied.') . EOL); - return; - } + // Get the observer, check their permissions - // Get the observer, check their permissions + $ob_hash = (($observer) ? $observer['xchan_hash'] : ''); - $ob_hash = (($observer) ? $observer['xchan_hash'] : ''); + $perms = get_all_perms($owner, $ob_hash); - $perms = get_all_perms($owner,$ob_hash); + if (!$perms['write_pages']) { + notice(t('Permission denied.') . EOL); + return; + } - if(! $perms['write_pages']) { - notice( t('Permission denied.') . EOL); - return; - } + // This feature is not exposed in redbasic ui since it is not clear why one would want to + // download a json encoded pdl file - we dont have a possibility to import it. + // Use the buildin share/install feature instead. - // This feature is not exposed in redbasic ui since it is not clear why one would want to - // download a json encoded pdl file - we dont have a possibility to import it. - // Use the buildin share/install feature instead. - - if((argc() > 3) && (argv(2) === 'share') && (argv(3))) { - $r = q("select iconfig.v, iconfig.k, mimetype, title, body from iconfig + if ((argc() > 3) && (argv(2) === 'share') && (argv(3))) { + $r = q( + "select iconfig.v, iconfig.k, mimetype, title, body from iconfig left join item on item.id = iconfig.iid where uid = %d and mid = '%s' and iconfig.cat = 'system' and iconfig.k = 'PDL' order by iconfig.v asc", - intval($owner), - dbesc(argv(3)) - ); - if($r) { - header('Content-type: application/x-hubzilla-layout'); - header('Content-Disposition: attachment; filename="' . $r[0]['sid'] . '.pdl"'); - echo json_encode($r); - killme(); - } - } + intval($owner), + dbesc(argv(3)) + ); + if ($r) { + header('Content-type: application/x-hubzilla-layout'); + header('Content-Disposition: attachment; filename="' . $r[0]['sid'] . '.pdl"'); + echo json_encode($r); + killme(); + } + } - // Create a status editor (for now - we'll need a WYSIWYG eventually) to create pages - // Nickname is set to the observers xchan, and profile_uid to the owners. - // This lets you post pages at other people's channels. + // Create a status editor (for now - we'll need a WYSIWYG eventually) to create pages + // Nickname is set to the observers xchan, and profile_uid to the owners. + // This lets you post pages at other people's channels. - $x = array( - 'webpage' => ITEM_TYPE_PDL, - 'is_owner' => true, - 'nickname' => \App::$profile['channel_address'], - 'showacl' => false, - 'hide_voting' => true, - 'hide_future' => true, - 'hide_expire' => true, - 'hide_location' => true, - 'hide_weblink' => true, - 'hide_attach' => true, - 'hide_preview' => true, - 'disable_comments' => true, - 'ptlabel' => t('Layout Name'), - 'profile_uid' => intval($owner), - 'expanded' => true, - 'placeholdertitle' => t('Layout Description (Optional)'), - 'novoting' => true, - 'bbco_autocomplete' => 'comanche' - ); + $x = array( + 'webpage' => ITEM_TYPE_PDL, + 'is_owner' => true, + 'nickname' => App::$profile['channel_address'], + 'showacl' => false, + 'hide_voting' => true, + 'hide_future' => true, + 'hide_expire' => true, + 'hide_location' => true, + 'hide_weblink' => true, + 'hide_attach' => true, + 'hide_preview' => true, + 'disable_comments' => true, + 'ptlabel' => t('Layout Name'), + 'profile_uid' => intval($owner), + 'expanded' => true, + 'placeholdertitle' => t('Layout Description (Optional)'), + 'novoting' => true, + 'bbco_autocomplete' => 'comanche' + ); - if($_REQUEST['title']) - $x['title'] = $_REQUEST['title']; - if($_REQUEST['body']) - $x['body'] = $_REQUEST['body']; - if($_REQUEST['pagetitle']) - $x['pagetitle'] = $_REQUEST['pagetitle']; + if ($_REQUEST['title']) { + $x['title'] = $_REQUEST['title']; + } + if ($_REQUEST['body']) { + $x['body'] = $_REQUEST['body']; + } + if ($_REQUEST['pagetitle']) { + $x['pagetitle'] = $_REQUEST['pagetitle']; + } - $editor = status_editor($x); + $editor = status_editor($x); - $r = q("select iconfig.iid, iconfig.v, mid, title, body, mimetype, created, edited, item_type from iconfig + $r = q( + "select iconfig.iid, iconfig.v, mid, title, body, mimetype, created, edited, item_type from iconfig left join item on iconfig.iid = item.id where uid = %d and iconfig.cat = 'system' and iconfig.k = 'PDL' and item_type = %d order by item.created desc", - intval($owner), - intval(ITEM_TYPE_PDL) - ); + intval($owner), + intval(ITEM_TYPE_PDL) + ); - $pages = null; + $pages = null; - if($r) { - $pages = []; - foreach($r as $rr) { - $element_arr = array( - 'type' => 'layout', - 'title' => $rr['title'], - 'body' => $rr['body'], - 'created' => $rr['created'], - 'edited' => $rr['edited'], - 'mimetype' => $rr['mimetype'], - 'pagetitle' => urldecode($rr['v']), - 'mid' => $rr['mid'] - ); - $pages[$rr['iid']][] = array( - 'url' => $rr['iid'], - 'title' => urldecode($rr['v']), - 'descr' => $rr['title'], - 'mid' => $rr['mid'], - 'created' => $rr['created'], - 'edited' => $rr['edited'], - 'bb_element' => '[element]' . base64url_encode(json_encode($element_arr)) . '[/element]' - ); - } - } + if ($r) { + $pages = []; + foreach ($r as $rr) { + $element_arr = array( + 'type' => 'layout', + 'title' => $rr['title'], + 'body' => $rr['body'], + 'created' => $rr['created'], + 'edited' => $rr['edited'], + 'mimetype' => $rr['mimetype'], + 'pagetitle' => urldecode($rr['v']), + 'mid' => $rr['mid'] + ); + $pages[$rr['iid']][] = array( + 'url' => $rr['iid'], + 'title' => urldecode($rr['v']), + 'descr' => $rr['title'], + 'mid' => $rr['mid'], + 'created' => $rr['created'], + 'edited' => $rr['edited'], + 'bb_element' => '[element]' . base64url_encode(json_encode($element_arr)) . '[/element]' + ); + } + } - //Build the base URL for edit links - $url = z_root() . '/editlayout/' . $which; + //Build the base URL for edit links + $url = z_root() . '/editlayout/' . $which; - $o .= replace_macros(get_markup_template('layoutlist.tpl'), array( - '$title' => t('Layouts'), - '$create' => t('Create'), - '$help' => '', // array('text' => t('Help'), 'url' => 'help/comanche', 'title' => t('Comanche page description language help')), - '$editor' => $editor, - '$baseurl' => $url, - '$name' => t('Layout Name'), - '$descr' => t('Layout Description'), - '$created' => t('Created'), - '$edited' => t('Edited'), - '$edit' => t('Edit'), - '$share' => t('Share'), - '$download' => t('Download PDL file'), - '$pages' => $pages, - '$channel' => $which, - '$view' => t('View'), - )); - - return $o; - - } + $o .= replace_macros(get_markup_template('layoutlist.tpl'), array( + '$title' => t('Layouts'), + '$create' => t('Create'), + '$help' => '', // array('text' => t('Help'), 'url' => 'help/comanche', 'title' => t('Comanche page description language help')), + '$editor' => $editor, + '$baseurl' => $url, + '$name' => t('Layout Name'), + '$descr' => t('Layout Description'), + '$created' => t('Created'), + '$edited' => t('Edited'), + '$edit' => t('Edit'), + '$share' => t('Share'), + '$download' => t('Download PDL file'), + '$pages' => $pages, + '$channel' => $which, + '$view' => t('View'), + )); + return $o; + } } diff --git a/Zotlabs/Module/Like.php b/Zotlabs/Module/Like.php index 5954040b3..1d2fbf3b9 100644 --- a/Zotlabs/Module/Like.php +++ b/Zotlabs/Module/Like.php @@ -1,4 +1,5 @@ 1 && $test[0] === 'Undo') { - $undo = true; - $activity = $test[1]; - } + if (! $activity) { + return EMPTY_STR; + } - if (! in_array($activity, [ 'Like', 'Dislike', 'Accept', 'Reject', 'TentativeAccept' ])) { - killme(); - } + // Check for negative (undo) condition + // eg: 'Undo/Like' results in $undo conditional and $activity set to 'Like' - $is_rsvp = in_array($activity, [ 'Accept', 'Reject', 'TentativeAccept' ]); + $test = explode('/', $activity); + if (count($test) > 1 && $test[0] === 'Undo') { + $undo = true; + $activity = $test[1]; + } - // Check for when target is something besides messages, where argv(1) is the type of thing - // and argv(2) is an identifier of things of that type - // We currently only recognise 'profile' but other types could be handled - - - if (! $observer) { - killme(); - } + if (! in_array($activity, [ 'Like', 'Dislike', 'Accept', 'Reject', 'TentativeAccept' ])) { + killme(); + } - // this is used to like an item or comment - - $item_id = ((argc() == 2) ? notags(trim(argv(1))) : 0); + $is_rsvp = in_array($activity, [ 'Accept', 'Reject', 'TentativeAccept' ]); - logger('like: undo: ' . (($undo) ? 'true' : 'false')); - logger('like: verb ' . $activity . ' item ' . $item_id, LOGGER_DEBUG); - - // get the item. Allow linked photos (which are normally hidden) to be liked + // Check for when target is something besides messages, where argv(1) is the type of thing + // and argv(2) is an identifier of things of that type + // We currently only recognise 'profile' but other types could be handled - $r = q("SELECT * FROM item WHERE id = %d + + if (! $observer) { + killme(); + } + + // this is used to like an item or comment + + $item_id = ((argc() == 2) ? notags(trim(argv(1))) : 0); + + logger('like: undo: ' . (($undo) ? 'true' : 'false')); + logger('like: verb ' . $activity . ' item ' . $item_id, LOGGER_DEBUG); + + // get the item. Allow linked photos (which are normally hidden) to be liked + + $r = q( + "SELECT * FROM item WHERE id = %d and item_type in (0,6,7) and item_deleted = 0 and item_unpublished = 0 and item_delayed = 0 and item_pending_remove = 0 and item_blocked = 0 LIMIT 1", - intval($item_id) - ); + intval($item_id) + ); - // if interacting with a pubstream item, - // create a copy of the parent in your stream. + // if interacting with a pubstream item, + // create a copy of the parent in your stream. - if ($r) { - if (local_channel() && (! is_sys_channel(local_channel()))) { - $r = [ copy_of_pubitem(App::get_channel(), $r[0]['mid']) ]; - } - } + if ($r) { + if (local_channel() && (! is_sys_channel(local_channel()))) { + $r = [ copy_of_pubitem(App::get_channel(), $r[0]['mid']) ]; + } + } - if(! $item_id || (! $r)) { - logger('like: no item ' . $item_id); - killme(); - } + if (! $item_id || (! $r)) { + logger('like: no item ' . $item_id); + killme(); + } - xchan_query($r,true); + xchan_query($r, true); - $item = array_shift($r); + $item = array_shift($r); - $owner_uid = $item['uid']; - $owner_aid = $item['aid']; + $owner_uid = $item['uid']; + $owner_aid = $item['aid']; $can_comment = false; - if ((array_key_exists('owner',$item)) && intval($item['owner']['abook_self'])) { - $can_comment = perm_is_allowed($item['uid'],$observer['xchan_hash'],'post_comments'); - } - else { - $can_comment = can_comment_on_post($observer['xchan_hash'],$item); - } + if ((array_key_exists('owner', $item)) && intval($item['owner']['abook_self'])) { + $can_comment = perm_is_allowed($item['uid'], $observer['xchan_hash'], 'post_comments'); + } else { + $can_comment = can_comment_on_post($observer['xchan_hash'], $item); + } - if (! $can_comment) { - notice( t('Permission denied') . EOL); - killme(); - } + if (! $can_comment) { + notice(t('Permission denied') . EOL); + killme(); + } - $r = q("select * from xchan where xchan_hash = '%s' limit 1", - dbesc($item['owner_xchan']) - ); + $r = q( + "select * from xchan where xchan_hash = '%s' limit 1", + dbesc($item['owner_xchan']) + ); - if ($r) { - $thread_owner = array_shift($r); - } - else { - killme(); - } - $r = q("select * from xchan where xchan_hash = '%s' limit 1", - dbesc($item['author_xchan']) - ); - if ($r) { - $item_author = array_shift($r); - } - else { - killme(); - } + if ($r) { + $thread_owner = array_shift($r); + } else { + killme(); + } + $r = q( + "select * from xchan where xchan_hash = '%s' limit 1", + dbesc($item['author_xchan']) + ); + if ($r) { + $item_author = array_shift($r); + } else { + killme(); + } - if ($undo) { - $r = q("select * from item where thr_parent = '%s' and verb = '%s' and author_xchan = '%s' and uid = %d and item_deleted = 0 limit 1", - dbesc($item['thr_parent']), - dbesc($activity), - dbesc($observer['xchan_hash']), - intval($owner_uid) - ); + if ($undo) { + $r = q( + "select * from item where thr_parent = '%s' and verb = '%s' and author_xchan = '%s' and uid = %d and item_deleted = 0 limit 1", + dbesc($item['thr_parent']), + dbesc($activity), + dbesc($observer['xchan_hash']), + intval($owner_uid) + ); - xchan_query($r,true); - $r = fetch_post_tags($r,true); - $r[0]['obj'] = json_decode($r[0]['obj'],true); - $object = Activity::encode_activity($r[0],true); - - // do not do either a federated or hard delete on the original reaction - // as we are going to send an Undo to perform this task - // just set item_deleted to update the local conversation + xchan_query($r, true); + $r = fetch_post_tags($r, true); + $r[0]['obj'] = json_decode($r[0]['obj'], true); + $object = Activity::encode_activity($r[0], true); - $retval = q("update item set item_deleted = 1 where id = %d", - intval($r[0]['id']) - ); + // do not do either a federated or hard delete on the original reaction + // as we are going to send an Undo to perform this task + // just set item_deleted to update the local conversation - } - else { - $object = Activity::fetch_item( [ 'id' => $item['mid'] ]); - } + $retval = q( + "update item set item_deleted = 1 where id = %d", + intval($r[0]['id']) + ); + } else { + $object = Activity::fetch_item([ 'id' => $item['mid'] ]); + } - if (! $object) { - killme(); - } + if (! $object) { + killme(); + } - $uuid = new_uuid(); + $uuid = new_uuid(); - // we have everything we need - start building our new item + // we have everything we need - start building our new item - $arr = []; + $arr = []; - $arr['uuid'] = $uuid; + $arr['uuid'] = $uuid; $arr['mid'] = z_root() . (($is_rsvp) ? '/activity/' : '/item/' ) . $uuid; - $post_type = (($item['resource_type'] === 'photo') ? t('photo') : t('status')); - if ($item['obj_type'] === ACTIVITY_OBJ_EVENT) { - $post_type = t('event'); - } - - $objtype = $item['obj_type']; + $post_type = (($item['resource_type'] === 'photo') ? t('photo') : t('status')); + if ($item['obj_type'] === ACTIVITY_OBJ_EVENT) { + $post_type = t('event'); + } - $body = $item['body']; - - - if (! intval($item['item_thread_top'])) { - $post_type = 'comment'; - } - - $arr['item_origin'] = 1; - $arr['item_notshown'] = 1; - $arr['item_type'] = $item['item_type']; - - if (intval($item['item_wall'])) { - $arr['item_wall'] = 1; - } - - // if this was a linked photo and was hidden, unhide it and distribute it. - - if (intval($item['item_hidden'])) { - $r = q("update item set item_hidden = 0 where id = %d", - intval($item['id']) - ); + $objtype = $item['obj_type']; - $r = q("select * from item where id = %d", - intval($item['id']) - ); - if ($r) { - xchan_query($r); - $sync_item = fetch_post_tags($r); - Libsync::build_sync_packet($channel['channel_id'], [ 'item' => [ encode_item($sync_item[0],true) ] ]); - } - - Run::Summon( [ 'Notifier','wall-new',$item['id'] ] ); - } + $body = $item['body']; - if ($undo) { - $arr['body'] = t('Undo a previous action'); - $arr['item_notshown'] = 1; - } - else { - if ($activity === 'Like') { - $bodyverb = t('%1$s likes %2$s\'s %3$s'); - } - if ($activity === 'Dislike') { - $bodyverb = t('%1$s doesn\'t like %2$s\'s %3$s'); - } - if ($activity === 'Accept') { - $bodyverb = t('%1$s is attending %2$s\'s %3$s'); - } - if ($activity === 'Reject') { - $bodyverb = t('%1$s is not attending %2$s\'s %3$s'); - } - if ($activity === 'TentativeAccept') { - $bodyverb = t('%1$s may attend %2$s\'s %3$s'); - } - - if (! isset($bodyverb)) { - killme(); - } + if (! intval($item['item_thread_top'])) { + $post_type = 'comment'; + } - $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/' . gen_link_id($item['mid']) . ']' . $post_type . '[/zrl]'; + $arr['item_origin'] = 1; + $arr['item_notshown'] = 1; + $arr['item_type'] = $item['item_type']; - $arr['body'] = sprintf( $bodyverb, $alink, $ulink, $plink ); + if (intval($item['item_wall'])) { + $arr['item_wall'] = 1; + } - } + // if this was a linked photo and was hidden, unhide it and distribute it. + + if (intval($item['item_hidden'])) { + $r = q( + "update item set item_hidden = 0 where id = %d", + intval($item['id']) + ); + + $r = q( + "select * from item where id = %d", + intval($item['id']) + ); + if ($r) { + xchan_query($r); + $sync_item = fetch_post_tags($r); + Libsync::build_sync_packet($channel['channel_id'], [ 'item' => [ encode_item($sync_item[0], true) ] ]); + } + + Run::Summon([ 'Notifier','wall-new',$item['id'] ]); + } - if (local_channel() && $activity === 'Accept') { - event_addtocal($item['id'],$channel['channel_id']); - } - - $arr['parent'] = $item['id']; - $arr['thr_parent'] = $item['mid']; - $allow_cid = $item['allow_cid']; - $allow_gid = $item['allow_gid']; - $deny_cid = $item['deny_cid']; - $deny_gid = $item['deny_gid']; - $private = $item['private']; - - $arr['aid'] = $owner_aid; - $arr['uid'] = $owner_uid; + if ($undo) { + $arr['body'] = t('Undo a previous action'); + $arr['item_notshown'] = 1; + } else { + if ($activity === 'Like') { + $bodyverb = t('%1$s likes %2$s\'s %3$s'); + } + if ($activity === 'Dislike') { + $bodyverb = t('%1$s doesn\'t like %2$s\'s %3$s'); + } + if ($activity === 'Accept') { + $bodyverb = t('%1$s is attending %2$s\'s %3$s'); + } + if ($activity === 'Reject') { + $bodyverb = t('%1$s is not attending %2$s\'s %3$s'); + } + if ($activity === 'TentativeAccept') { + $bodyverb = t('%1$s may attend %2$s\'s %3$s'); + } - $arr['item_flags'] = $item['item_flags']; - $arr['item_wall'] = $item['item_wall']; - $arr['parent_mid'] = $item['mid']; - $arr['owner_xchan'] = $thread_owner['xchan_hash']; - $arr['author_xchan'] = $observer['xchan_hash']; - - - - $arr['verb'] = (($undo) ? 'Undo' : $activity); - $arr['obj_type'] = (($undo) ? $activity : $objtype); - $arr['obj'] = $object; - - if ($target) { - $arr['tgt_type'] = $tgttype; - $arr['target'] = $target; - } - - $arr['allow_cid'] = $allow_cid; - $arr['allow_gid'] = $allow_gid; - $arr['deny_cid'] = $deny_cid; - $arr['deny_gid'] = $deny_gid; - $arr['item_private'] = $private; - - call_hooks('post_local',$arr); - - $post = item_store($arr); - $post_id = $post['item_id']; + if (! isset($bodyverb)) { + killme(); + } - // save the conversation from expiration + $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/' . gen_link_id($item['mid']) . ']' . $post_type . '[/zrl]'; - if (local_channel() && array_key_exists('item',$post) && (intval($post['item']['id']) != intval($post['item']['parent']))) { - retain_item($post['item']['parent']); - } - - $arr['id'] = $post_id; - - call_hooks('post_local_end', $arr); - - $r = q("select * from item where id = %d", - intval($post_id) - ); - if ($r) { - xchan_query($r); - $sync_item = fetch_post_tags($r); - Libsync::build_sync_packet($channel['channel_id'], [ 'item' => [ encode_item($sync_item[0],true) ] ]); - } + $arr['body'] = sprintf($bodyverb, $alink, $ulink, $plink); + } - Run::Summon( [ 'Notifier', 'like', $post_id ] ); - - killme(); - } - - - + + if (local_channel() && $activity === 'Accept') { + event_addtocal($item['id'], $channel['channel_id']); + } + + $arr['parent'] = $item['id']; + $arr['thr_parent'] = $item['mid']; + $allow_cid = $item['allow_cid']; + $allow_gid = $item['allow_gid']; + $deny_cid = $item['deny_cid']; + $deny_gid = $item['deny_gid']; + $private = $item['private']; + + $arr['aid'] = $owner_aid; + $arr['uid'] = $owner_uid; + + $arr['item_flags'] = $item['item_flags']; + $arr['item_wall'] = $item['item_wall']; + $arr['parent_mid'] = $item['mid']; + $arr['owner_xchan'] = $thread_owner['xchan_hash']; + $arr['author_xchan'] = $observer['xchan_hash']; + + + + $arr['verb'] = (($undo) ? 'Undo' : $activity); + $arr['obj_type'] = (($undo) ? $activity : $objtype); + $arr['obj'] = $object; + + if ($target) { + $arr['tgt_type'] = $tgttype; + $arr['target'] = $target; + } + + $arr['allow_cid'] = $allow_cid; + $arr['allow_gid'] = $allow_gid; + $arr['deny_cid'] = $deny_cid; + $arr['deny_gid'] = $deny_gid; + $arr['item_private'] = $private; + + call_hooks('post_local', $arr); + + $post = item_store($arr); + $post_id = $post['item_id']; + + // save the conversation from expiration + + if (local_channel() && array_key_exists('item', $post) && (intval($post['item']['id']) != intval($post['item']['parent']))) { + retain_item($post['item']['parent']); + } + + $arr['id'] = $post_id; + + call_hooks('post_local_end', $arr); + + $r = q( + "select * from item where id = %d", + intval($post_id) + ); + if ($r) { + xchan_query($r); + $sync_item = fetch_post_tags($r); + Libsync::build_sync_packet($channel['channel_id'], [ 'item' => [ encode_item($sync_item[0], true) ] ]); + } + + Run::Summon([ 'Notifier', 'like', $post_id ]); + + killme(); + } } diff --git a/Zotlabs/Module/Linkinfo.php b/Zotlabs/Module/Linkinfo.php index fc7ac2e8b..9df5300f0 100644 --- a/Zotlabs/Module/Linkinfo.php +++ b/Zotlabs/Module/Linkinfo.php @@ -1,7 +1,10 @@ true, 'nobody' => true ] ); - if ($result['success']) { - $hdrs=[]; - $h = explode("\n",$result['header']); - foreach ($h as $l) { - list($k,$v) = array_map("trim", explode(":", trim($l), 2)); - $hdrs[strtolower($k)] = $v; - } - if (array_key_exists('content-type', $hdrs)) - $type = $hdrs['content-type']; - if ($type) { - if (stripos($type,'image/') !== false) { - $basename = basename($url); - - if ($zrl) - echo $br . '[zmg alt="' . $basename . '"]' . $url . '[/zmg]' . $br; - else - echo $br . '[img alt="' . $basename . '"]' . $url . '[/img]' . $br; - killme(); - } - if ((stripos($type,'video/') !== false) || ($type === 'application/ogg')) { - $thumb = self::get_video_poster($url); - if($thumb) { - if ($zrl) - echo $br . '[zvideo poster=\'' . $thumb . '\']' . $url . '[/zvideo]' . $br; - else - echo $br . '[video poster=\'' . $thumb . '\']' . $url . '[/video]' . $br; - killme(); - } - if ($zrl) - echo $br . '[zvideo]' . $url . '[/zvideo]' . $br; - else - echo $br . '[video]' . $url . '[/video]' . $br; - killme(); - } - if (stripos($type,'audio/') !== false) { - if ($zrl) - echo $br . '[zaudio]' . $url . '[/zaudio]' . $br; - else - echo $br . '[audio]' . $url . '[/audio]' . $br; - killme(); - } - if (strtolower($type) === 'text/calendar') { - $content = z_fetch_url($url,false,0,array('novalidate' => true)); - if ($content['success']) { - $ev = ical_to_ev($content['body']); - if ($ev) { - echo $br . format_event_bbcode($ev[0]) . $br; - killme(); - } - } - } - if (strtolower($type) === 'application/pdf' || strtolower($type) === 'application/x-pdf') { - echo $br . '[embed]' . $url . '[/embed]' . $br; - killme(); - } - } - } - - $template = $br . '[url=%s]%s[/url]%s' . $br; - - $arr = array('url' => $url, 'text' => ''); - - call_hooks('parse_link', $arr); - - if (strlen($arr['text'])) { - echo $arr['text']; - killme(); - } + if (strpos($url, 'geo:') === 0) { + if ($process_embed) { + echo $br . '[map=' . substr($url, 4) . ']' . $br; + } else { + echo $br . '[url]' . $url . '[/url]' . $br; + } + killme(); + } - if ($process_oembed) { - $x = oembed_process($url); - if ($x) { - echo $x; - killme(); - } - } - - if ($process_zotobj) { - $x = Activity::fetch($url, App::get_channel()); - $y = null; - if (is_array($x)) { - if (ActivityStreams::is_an_actor($x['type']) && $x['id']) { - if (check_siteallowed($x['id']) && check_channelallowed($x['id'])) { - $url = $x['url']; - if (is_array($url)) { - $url = $url[0]['href']; - } - $name = (($x['name']) ? $x['name'] . ' (' . $x['preferredUsername'] . ')' : $x['preferredUsername']); + if (strpos($url, 'tel:') === 0 || (is_phone_number($url) !== false)) { + $phone = $url; + if (strpos($url, 'tel:') !== 0) { + $url = 'tel:' . is_phone_number($url); + } + echo $br . '[url=' . $url . ']' . $phone . '[/url]' . $br; + killme(); + } - if (array_path_exists('icon/url',$x)) { - $text = $br . $br . '[zrl=' . $url . '][zmg=300x300]' . $x['icon']['url'] . '[/zmg][/zrl]' ; - } - $text .= $br . $br . '[zrl=' . $url . ']' . $name . '[/zrl]' . $br . $br; - echo $text; - killme(); - } - } - else { - $y = new ActivityStreams($x); - if ($y->is_valid() && $y->type === 'Announce' && is_array($y->obj) - && array_key_exists('object',$y->obj) && array_key_exists('actor',$y->obj)) { - // This is a relayed/forwarded Activity (as opposed to a shared/boosted object) - // Reparse the encapsulated Activity and use that instead - logger('relayed activity',LOGGER_DEBUG); - $y = new ActivityStreams($y->obj); - } - } + $m = parse_url($url); - if ($y && $y->is_valid()) { - $z = Activity::decode_note($y); - $r = q("select hubloc_hash, hubloc_network, hubloc_url from hubloc where hubloc_hash = '%s' OR hubloc_id_url = '%s'", - dbesc(is_array($y->actor) ? $y->actor['id'] : $y->actor), - dbesc(is_array($y->actor) ? $y->actor['id'] : $y->actor) - ); + if (!$m['scheme']) { + if (strpos($url, '@')) { + $xc = discover_by_webbie($url); + if ($xc) { + $x = q( + "select * from xchan where xchan_hash = '%s'", + dbesc($xc) + ); + if ($x) { + $url = $x[0]['xchan_url']; + } + } else { + echo $br . '[url=mailto:' . $url . ']' . $url . '[/url]' . $br; + killme(); + } + } else { + $url = 'http://' . $url; + } + } - if ($r) { - $r = Libzot::zot_record_preferred($r); - if ($z) { - $z['author_xchan'] = $r['hubloc_hash']; - } - } + if ($_GET['title']) { + $title = strip_tags(trim($_GET['title'])); + } - if ($z) { + if ($_GET['description']) { + $text = strip_tags(trim($_GET['description'])); + } - // do not allow somebody to embed a post that was blocked by the site admin - // We *will* let them over-rule any blocks they created themselves - - if (check_siteallowed($r['hubloc_id_url']) && check_channelallowed($z['author_xchan'])) { - $s = new Zlib\Share($z); - echo $s->bbcode(); - killme(); - } - } - } - } - } + if ($_GET['tags']) { + $arr_tags = str_getcsv($_GET['tags']); + if (count($arr_tags)) { + array_walk($arr_tags, 'self::arr_add_hashes'); + $str_tags = $br . implode(' ', $arr_tags) . $br; + } + } + + logger('linkinfo: ' . $url, LOGGER_DEBUG); + + $zrl = is_matrix_url($url); + + if (!$process_embed) { + if ($zrl) { + echo $br . '[zrl]' . $url . '[/zrl]' . $br; + } else { + echo $br . '[url]' . $url . '[/url]' . $br; + } + killme(); + } + + $result = z_fetch_url($url, false, 0, ['novalidate' => true, 'nobody' => true]); + if ($result['success']) { + $hdrs = []; + $h = explode("\n", $result['header']); + foreach ($h as $l) { + list($k, $v) = array_map("trim", explode(":", trim($l), 2)); + $hdrs[strtolower($k)] = $v; + } + if (array_key_exists('content-type', $hdrs)) { + $type = $hdrs['content-type']; + } + if ($type) { + if (stripos($type, 'image/') !== false) { + $basename = basename($url); + + if ($zrl) { + echo $br . '[zmg alt="' . $basename . '"]' . $url . '[/zmg]' . $br; + } else { + echo $br . '[img alt="' . $basename . '"]' . $url . '[/img]' . $br; + } + killme(); + } + if ((stripos($type, 'video/') !== false) || ($type === 'application/ogg')) { + $thumb = self::get_video_poster($url); + if ($thumb) { + if ($zrl) { + echo $br . '[zvideo poster=\'' . $thumb . '\']' . $url . '[/zvideo]' . $br; + } else { + echo $br . '[video poster=\'' . $thumb . '\']' . $url . '[/video]' . $br; + } + killme(); + } + if ($zrl) { + echo $br . '[zvideo]' . $url . '[/zvideo]' . $br; + } else { + echo $br . '[video]' . $url . '[/video]' . $br; + } + killme(); + } + if (stripos($type, 'audio/') !== false) { + if ($zrl) { + echo $br . '[zaudio]' . $url . '[/zaudio]' . $br; + } else { + echo $br . '[audio]' . $url . '[/audio]' . $br; + } + killme(); + } + if (strtolower($type) === 'text/calendar') { + $content = z_fetch_url($url, false, 0, array('novalidate' => true)); + if ($content['success']) { + $ev = ical_to_ev($content['body']); + if ($ev) { + echo $br . format_event_bbcode($ev[0]) . $br; + killme(); + } + } + } + if (strtolower($type) === 'application/pdf' || strtolower($type) === 'application/x-pdf') { + echo $br . '[embed]' . $url . '[/embed]' . $br; + killme(); + } + } + } + + $template = $br . '[url=%s]%s[/url]%s' . $br; + + $arr = array('url' => $url, 'text' => ''); + + call_hooks('parse_link', $arr); + + if (strlen($arr['text'])) { + echo $arr['text']; + killme(); + } + + if ($process_oembed) { + $x = oembed_process($url); + if ($x) { + echo $x; + killme(); + } + } + + if ($process_zotobj) { + $x = Activity::fetch($url, App::get_channel()); + $y = null; + if (is_array($x)) { + if (ActivityStreams::is_an_actor($x['type']) && $x['id']) { + if (check_siteallowed($x['id']) && check_channelallowed($x['id'])) { + $url = $x['url']; + if (is_array($url)) { + $url = $url[0]['href']; + } + $name = (($x['name']) ? $x['name'] . ' (' . $x['preferredUsername'] . ')' : $x['preferredUsername']); + + if (array_path_exists('icon/url', $x)) { + $text = $br . $br . '[zrl=' . $url . '][zmg=300x300]' . $x['icon']['url'] . '[/zmg][/zrl]'; + } + $text .= $br . $br . '[zrl=' . $url . ']' . $name . '[/zrl]' . $br . $br; + echo $text; + killme(); + } + } else { + $y = new ActivityStreams($x); + if ( + $y->is_valid() && $y->type === 'Announce' && is_array($y->obj) + && array_key_exists('object', $y->obj) && array_key_exists('actor', $y->obj) + ) { + // This is a relayed/forwarded Activity (as opposed to a shared/boosted object) + // Reparse the encapsulated Activity and use that instead + logger('relayed activity', LOGGER_DEBUG); + $y = new ActivityStreams($y->obj); + } + } + + if ($y && $y->is_valid()) { + $z = Activity::decode_note($y); + $r = q( + "select hubloc_hash, hubloc_network, hubloc_url from hubloc where hubloc_hash = '%s' OR hubloc_id_url = '%s'", + dbesc(is_array($y->actor) ? $y->actor['id'] : $y->actor), + dbesc(is_array($y->actor) ? $y->actor['id'] : $y->actor) + ); + + if ($r) { + $r = Libzot::zot_record_preferred($r); + if ($z) { + $z['author_xchan'] = $r['hubloc_hash']; + } + } + + if ($z) { + // do not allow somebody to embed a post that was blocked by the site admin + // We *will* let them over-rule any blocks they created themselves + + if (check_siteallowed($r['hubloc_id_url']) && check_channelallowed($z['author_xchan'])) { + $s = new Zlib\Share($z); + echo $s->bbcode(); + killme(); + } + } + } + } + } - if ($url && $title && $text) { - - $text = $br . '[quote]' . trim($text) . '[/quote]' . $br; - - $title = str_replace(array("\r","\n"),array('',''),$title); - - $result = sprintf($template,$url,($title) ? $title : $url,$text) . $str_tags; - - logger('linkinfo (unparsed): returns: ' . $result); - - echo $result; - killme(); - } - - $siteinfo = self::parseurl_getsiteinfo($url); - - // If the site uses this platform, use zrl rather than url so they get zids sent to them by default - - if (is_matrix_url($url)) - $template = str_replace('url','zrl',$template); - - if ($siteinfo["title"] == "") { - echo sprintf($template,$url,$url,'') . $str_tags; - killme(); - } else { - $text = $siteinfo["text"]; - $title = $siteinfo["title"]; - } - - $image = ""; - - if (isset($siteinfo['images']) && is_array($siteinfo['images']) && count($siteinfo["images"])) { - /* Execute below code only if image is present in siteinfo */ - - $total_images = 0; - $max_images = get_config('system','max_bookmark_images'); - if ($max_images === false) - $max_images = 2; - else - $max_images = intval($max_images); - - foreach ($siteinfo["images"] as $imagedata) { - if ($url) { - $image .= sprintf('[url=%s]', $url); - } - $image .= '[img='.$imagedata["width"].'x'.$imagedata["height"].']'.$imagedata["src"].'[/img]'; - if ($url) { - $image .= '[/url]'; - } - $image .= "\n"; - $total_images ++; - if ($max_images && $max_images >= $total_images) - break; - } - } - - if (strlen($text)) { - $text = $br.'[quote]'.trim($text).'[/quote]'.$br ; - } - - if ($image) { - $text = $br.$br.$image.$text; - } - $title = str_replace(array("\r","\n"),array('',''),$title); - - $result = sprintf($template,$url,($title) ? $title : $url,$text) . $str_tags; - - logger('linkinfo: returns: ' . $result, LOGGER_DEBUG); - - echo trim($result); - killme(); - - } - - - public static function deletexnode(&$doc, $node) { - $xpath = new \DomXPath($doc); - $list = $xpath->query("//".$node); - foreach ($list as $child) - $child->parentNode->removeChild($child); - } - - public static function completeurl($url, $scheme) { - $urlarr = parse_url($url); - - if (isset($urlarr["scheme"])) - return($url); - - $schemearr = parse_url($scheme); - - $complete = $schemearr["scheme"]."://".$schemearr["host"]; - - if ($schemearr["port"] != "") - $complete .= ":".$schemearr["port"]; - - if (strpos($urlarr['path'],'/') !== 0) - $complete .= '/'; - - $complete .= $urlarr["path"]; - - if ($urlarr["query"] != "") - $complete .= "?".$urlarr["query"]; - - if ($urlarr["fragment"] != "") - $complete .= "#".$urlarr["fragment"]; - - return($complete); - } + if ($url && $title && $text) { + $text = $br . '[quote]' . trim($text) . '[/quote]' . $br; - public static function get_video_poster($url) { + $title = str_replace(array("\r", "\n"), array('', ''), $title); - if(strpos($url,z_root() . '/cloud/') === false) { - return EMPTY_STR; - } - $m = parse_url($url,PHP_URL_PATH); - if($m) { - // strip leading '/cloud/' - $m = substr($m,7); - } - $nick = substr($m,0,strpos($m,'/')); - $p = substr($m,strpos($m,'/')+1); + $result = sprintf($template, $url, ($title) ? $title : $url, $text) . $str_tags; - // get the channel to check permissions - - $u = channelx_by_nick($nick); + logger('linkinfo (unparsed): returns: ' . $result); - if($u && $p) { + echo $result; + killme(); + } - $sql_extra = permissions_sql(intval($u['channel_id'])); + $siteinfo = self::parseurl_getsiteinfo($url); - $r = q("select hash, content from attach where display_path = '%s' and uid = %d and os_storage = 1 $sql_extra limit 1", - dbesc($p), - intval($u['channel_id']) - ); - if($r) { - $path = dbunescbin($r[0]['content']); - if($path && @file_exists($path . '.thumb')) { - return z_root() . '/poster/' . $nick . '/' . $r[0]['hash']; - } - } - } - return EMPTY_STR; - } + // If the site uses this platform, use zrl rather than url so they get zids sent to them by default - - public static function parseurl_getsiteinfo($url) { - $siteinfo = []; - - - $result = z_fetch_url($url,false,0,array('novalidate' => true)); - if (! $result['success']) - return $siteinfo; - - $header = $result['header']; - $body = $result['body']; - - // Check codepage in HTTP headers or HTML if not exist - $cp = (preg_match('/Content-Type: text\/html; charset=(.+)\r\n/i', $header, $o) ? $o[1] : ''); - if (empty($cp)) - $cp = (preg_match('/meta.+content=["|\']text\/html; charset=([^"|\']+)/i', $body, $o) ? $o[1] : 'AUTO'); + if (is_matrix_url($url)) { + $template = str_replace('url', 'zrl', $template); + } - $body = mb_convert_encoding($body, 'UTF-8', $cp); - $body = mb_convert_encoding($body, 'HTML-ENTITIES', "UTF-8"); - - $doc = new \DOMDocument(); - @$doc->loadHTML($body); - - self::deletexnode($doc, 'style'); - self::deletexnode($doc, 'script'); - self::deletexnode($doc, 'option'); - self::deletexnode($doc, 'h1'); - self::deletexnode($doc, 'h2'); - self::deletexnode($doc, 'h3'); - self::deletexnode($doc, 'h4'); - self::deletexnode($doc, 'h5'); - self::deletexnode($doc, 'h6'); - self::deletexnode($doc, 'ol'); - self::deletexnode($doc, 'ul'); - - $xpath = new \DomXPath($doc); - - //$list = $xpath->query("head/title"); - $list = $xpath->query("//title"); - foreach ($list as $node) - $siteinfo["title"] = html_entity_decode($node->nodeValue, ENT_QUOTES, "UTF-8"); - - //$list = $xpath->query("head/meta[@name]"); - $list = $xpath->query("//meta[@name]"); - foreach ($list as $node) { - $attr = []; - if ($node->attributes->length) - foreach ($node->attributes as $attribute) - $attr[$attribute->name] = $attribute->value; - - $attr["content"] = html_entity_decode($attr["content"], ENT_QUOTES, "UTF-8"); - - switch (strtolower($attr["name"])) { - case "fulltitle": - $siteinfo["title"] = trim($attr["content"]); - break; - case "description": - $siteinfo["text"] = trim($attr["content"]); - break; - case "thumbnail": - $siteinfo["image"] = $attr["content"]; - break; - case "twitter:image": - $siteinfo["image"] = $attr["content"]; - break; - case "twitter:image:src": - $siteinfo["image"] = $attr["content"]; - break; - case "twitter:card": - if (($siteinfo["type"] == "") || ($attr["content"] == "photo")) { - $siteinfo["type"] = $attr["content"]; - } - break; - case "twitter:description": - $siteinfo["text"] = trim($attr["content"]); - break; - case "twitter:title": - $siteinfo["title"] = trim($attr["content"]); - break; - case "dc.title": - $siteinfo["title"] = trim($attr["content"]); - break; - case "dc.description": - $siteinfo["text"] = trim($attr["content"]); - break; - case "keywords": - $keywords = explode(",", $attr["content"]); - break; - case "news_keywords": - $keywords = explode(",", $attr["content"]); - break; - } - } - - //$list = $xpath->query("head/meta[@property]"); - $list = $xpath->query("//meta[@property]"); - foreach ($list as $node) { - $attr = []; - if ($node->attributes->length) - foreach ($node->attributes as $attribute) - $attr[$attribute->name] = $attribute->value; - - $attr["content"] = html_entity_decode($attr["content"], ENT_QUOTES, "UTF-8"); - - switch (strtolower($attr["property"])) { - case "og:image": - $siteinfo["image"] = $attr["content"]; - break; - case "og:title": - $siteinfo["title"] = $attr["content"]; - break; - case "og:description": - $siteinfo["text"] = $attr["content"]; - break; - } - } - - if ($siteinfo["image"] == "") { - $list = $xpath->query("//img[@src]"); - foreach ($list as $node) { - $attr = []; - if ($node->attributes->length) - foreach ($node->attributes as $attribute) - $attr[$attribute->name] = $attribute->value; - - $src = self::completeurl($attr["src"], $url); - $photodata = @getimagesize($src); - - if (($photodata) && ($photodata[0] > 150) and ($photodata[1] > 150)) { - if ($photodata[0] > 300) { - $photodata[1] = round($photodata[1] * (300 / $photodata[0])); - $photodata[0] = 300; - } - if ($photodata[1] > 300) { - $photodata[0] = round($photodata[0] * (300 / $photodata[1])); - $photodata[1] = 300; - } - $siteinfo["images"][] = array("src"=>$src, - "width"=>$photodata[0], - "height"=>$photodata[1]); - } - - } - } else { - $src = self::completeurl($siteinfo["image"], $url); - - unset($siteinfo["image"]); - - $photodata = @getimagesize($src); - - if (($photodata) && ($photodata[0] > 10) and ($photodata[1] > 10)) - $siteinfo["images"][] = array("src"=>$src, - "width"=>$photodata[0], - "height"=>$photodata[1]); - } - - if ($siteinfo["text"] == "") { - $text = ""; - - $list = $xpath->query("//div[@class='article']"); - foreach ($list as $node) - if (strlen($node->nodeValue) > 40) - $text .= " ".trim($node->nodeValue); - - if ($text == "") { - $list = $xpath->query("//div[@class='content']"); - foreach ($list as $node) - if (strlen($node->nodeValue) > 40) - $text .= " ".trim($node->nodeValue); - } - - // If none text was found then take the paragraph content - if ($text == "") { - $list = $xpath->query("//p"); - foreach ($list as $node) - if (strlen($node->nodeValue) > 40) - $text .= " ".trim($node->nodeValue); - } - - if ($text != "") { - $text = trim(str_replace(array("\n", "\r"), array(" ", " "), $text)); - - while (strpos($text, " ")) - $text = trim(str_replace(" ", " ", $text)); - - $siteinfo["text"] = html_entity_decode(substr($text,0,350), ENT_QUOTES, "UTF-8").'...'; - } - } - - return($siteinfo); - } - + if ($siteinfo["title"] == "") { + echo sprintf($template, $url, $url, '') . $str_tags; + killme(); + } else { + $text = $siteinfo["text"]; + $title = $siteinfo["title"]; + } - private static function arr_add_hashes(&$item,$k) { - if (substr($item,0,1) !== '#') { - $item = '#' . $item; - } - } + $image = ""; + if (isset($siteinfo['images']) && is_array($siteinfo['images']) && count($siteinfo["images"])) { + /* Execute below code only if image is present in siteinfo */ + + $total_images = 0; + $max_images = get_config('system', 'max_bookmark_images'); + if ($max_images === false) { + $max_images = 2; + } else { + $max_images = intval($max_images); + } + + foreach ($siteinfo["images"] as $imagedata) { + if ($url) { + $image .= sprintf('[url=%s]', $url); + } + $image .= '[img=' . $imagedata["width"] . 'x' . $imagedata["height"] . ']' . $imagedata["src"] . '[/img]'; + if ($url) { + $image .= '[/url]'; + } + $image .= "\n"; + $total_images++; + if ($max_images && $max_images >= $total_images) { + break; + } + } + } + + if (strlen($text)) { + $text = $br . '[quote]' . trim($text) . '[/quote]' . $br; + } + + if ($image) { + $text = $br . $br . $image . $text; + } + $title = str_replace(array("\r", "\n"), array('', ''), $title); + + $result = sprintf($template, $url, ($title) ? $title : $url, $text) . $str_tags; + + logger('linkinfo: returns: ' . $result, LOGGER_DEBUG); + + echo trim($result); + killme(); + } + + + public static function deletexnode(&$doc, $node) + { + $xpath = new DomXPath($doc); + $list = $xpath->query("//" . $node); + foreach ($list as $child) { + $child->parentNode->removeChild($child); + } + } + + public static function completeurl($url, $scheme) + { + $urlarr = parse_url($url); + + if (isset($urlarr["scheme"])) { + return ($url); + } + + $schemearr = parse_url($scheme); + + $complete = $schemearr["scheme"] . "://" . $schemearr["host"]; + + if ($schemearr["port"] != "") { + $complete .= ":" . $schemearr["port"]; + } + + if (strpos($urlarr['path'], '/') !== 0) { + $complete .= '/'; + } + + $complete .= $urlarr["path"]; + + if ($urlarr["query"] != "") { + $complete .= "?" . $urlarr["query"]; + } + + if ($urlarr["fragment"] != "") { + $complete .= "#" . $urlarr["fragment"]; + } + + return ($complete); + } + + public static function get_video_poster($url) + { + + if (strpos($url, z_root() . '/cloud/') === false) { + return EMPTY_STR; + } + $m = parse_url($url, PHP_URL_PATH); + if ($m) { + // strip leading '/cloud/' + $m = substr($m, 7); + } + $nick = substr($m, 0, strpos($m, '/')); + $p = substr($m, strpos($m, '/') + 1); + + // get the channel to check permissions + + $u = channelx_by_nick($nick); + + if ($u && $p) { + $sql_extra = permissions_sql(intval($u['channel_id'])); + + $r = q( + "select hash, content from attach where display_path = '%s' and uid = %d and os_storage = 1 $sql_extra limit 1", + dbesc($p), + intval($u['channel_id']) + ); + if ($r) { + $path = dbunescbin($r[0]['content']); + if ($path && @file_exists($path . '.thumb')) { + return z_root() . '/poster/' . $nick . '/' . $r[0]['hash']; + } + } + } + return EMPTY_STR; + } + + + public static function parseurl_getsiteinfo($url) + { + $siteinfo = []; + + + $result = z_fetch_url($url, false, 0, array('novalidate' => true)); + if (!$result['success']) { + return $siteinfo; + } + + $header = $result['header']; + $body = $result['body']; + + // Check codepage in HTTP headers or HTML if not exist + $cp = (preg_match('/Content-Type: text\/html; charset=(.+)\r\n/i', $header, $o) ? $o[1] : ''); + if (empty($cp)) { + $cp = (preg_match('/meta.+content=["|\']text\/html; charset=([^"|\']+)/i', $body, $o) ? $o[1] : 'AUTO'); + } + + $body = mb_convert_encoding($body, 'UTF-8', $cp); + $body = mb_convert_encoding($body, 'HTML-ENTITIES', "UTF-8"); + + $doc = new DOMDocument(); + @$doc->loadHTML($body); + + self::deletexnode($doc, 'style'); + self::deletexnode($doc, 'script'); + self::deletexnode($doc, 'option'); + self::deletexnode($doc, 'h1'); + self::deletexnode($doc, 'h2'); + self::deletexnode($doc, 'h3'); + self::deletexnode($doc, 'h4'); + self::deletexnode($doc, 'h5'); + self::deletexnode($doc, 'h6'); + self::deletexnode($doc, 'ol'); + self::deletexnode($doc, 'ul'); + + $xpath = new DomXPath($doc); + + //$list = $xpath->query("head/title"); + $list = $xpath->query("//title"); + foreach ($list as $node) { + $siteinfo["title"] = html_entity_decode($node->nodeValue, ENT_QUOTES, "UTF-8"); + } + + //$list = $xpath->query("head/meta[@name]"); + $list = $xpath->query("//meta[@name]"); + foreach ($list as $node) { + $attr = []; + if ($node->attributes->length) { + foreach ($node->attributes as $attribute) { + $attr[$attribute->name] = $attribute->value; + } + } + + $attr["content"] = html_entity_decode($attr["content"], ENT_QUOTES, "UTF-8"); + + switch (strtolower($attr["name"])) { + case "fulltitle": + $siteinfo["title"] = trim($attr["content"]); + break; + case "description": + $siteinfo["text"] = trim($attr["content"]); + break; + case "thumbnail": + $siteinfo["image"] = $attr["content"]; + break; + case "twitter:image": + $siteinfo["image"] = $attr["content"]; + break; + case "twitter:image:src": + $siteinfo["image"] = $attr["content"]; + break; + case "twitter:card": + if (($siteinfo["type"] == "") || ($attr["content"] == "photo")) { + $siteinfo["type"] = $attr["content"]; + } + break; + case "twitter:description": + $siteinfo["text"] = trim($attr["content"]); + break; + case "twitter:title": + $siteinfo["title"] = trim($attr["content"]); + break; + case "dc.title": + $siteinfo["title"] = trim($attr["content"]); + break; + case "dc.description": + $siteinfo["text"] = trim($attr["content"]); + break; + case "keywords": + $keywords = explode(",", $attr["content"]); + break; + case "news_keywords": + $keywords = explode(",", $attr["content"]); + break; + } + } + + //$list = $xpath->query("head/meta[@property]"); + $list = $xpath->query("//meta[@property]"); + foreach ($list as $node) { + $attr = []; + if ($node->attributes->length) { + foreach ($node->attributes as $attribute) { + $attr[$attribute->name] = $attribute->value; + } + } + + $attr["content"] = html_entity_decode($attr["content"], ENT_QUOTES, "UTF-8"); + + switch (strtolower($attr["property"])) { + case "og:image": + $siteinfo["image"] = $attr["content"]; + break; + case "og:title": + $siteinfo["title"] = $attr["content"]; + break; + case "og:description": + $siteinfo["text"] = $attr["content"]; + break; + } + } + + if ($siteinfo["image"] == "") { + $list = $xpath->query("//img[@src]"); + foreach ($list as $node) { + $attr = []; + if ($node->attributes->length) { + foreach ($node->attributes as $attribute) { + $attr[$attribute->name] = $attribute->value; + } + } + + $src = self::completeurl($attr["src"], $url); + $photodata = @getimagesize($src); + + if (($photodata) && ($photodata[0] > 150) and ($photodata[1] > 150)) { + if ($photodata[0] > 300) { + $photodata[1] = round($photodata[1] * (300 / $photodata[0])); + $photodata[0] = 300; + } + if ($photodata[1] > 300) { + $photodata[0] = round($photodata[0] * (300 / $photodata[1])); + $photodata[1] = 300; + } + $siteinfo["images"][] = array("src" => $src, + "width" => $photodata[0], + "height" => $photodata[1]); + } + } + } else { + $src = self::completeurl($siteinfo["image"], $url); + + unset($siteinfo["image"]); + + $photodata = @getimagesize($src); + + if (($photodata) && ($photodata[0] > 10) and ($photodata[1] > 10)) { + $siteinfo["images"][] = array("src" => $src, + "width" => $photodata[0], + "height" => $photodata[1]); + } + } + + if ($siteinfo["text"] == "") { + $text = ""; + + $list = $xpath->query("//div[@class='article']"); + foreach ($list as $node) { + if (strlen($node->nodeValue) > 40) { + $text .= " " . trim($node->nodeValue); + } + } + + if ($text == "") { + $list = $xpath->query("//div[@class='content']"); + foreach ($list as $node) { + if (strlen($node->nodeValue) > 40) { + $text .= " " . trim($node->nodeValue); + } + } + } + + // If none text was found then take the paragraph content + if ($text == "") { + $list = $xpath->query("//p"); + foreach ($list as $node) { + if (strlen($node->nodeValue) > 40) { + $text .= " " . trim($node->nodeValue); + } + } + } + + if ($text != "") { + $text = trim(str_replace(array("\n", "\r"), array(" ", " "), $text)); + + while (strpos($text, " ")) { + $text = trim(str_replace(" ", " ", $text)); + } + + $siteinfo["text"] = html_entity_decode(substr($text, 0, 350), ENT_QUOTES, "UTF-8") . '...'; + } + } + + return ($siteinfo); + } + + + private static function arr_add_hashes(&$item, $k) + { + if (substr($item, 0, 1) !== '#') { + $item = '#' . $item; + } + } } diff --git a/Zotlabs/Module/Lists.php b/Zotlabs/Module/Lists.php index 599a19abb..02cdf5115 100644 --- a/Zotlabs/Module/Lists.php +++ b/Zotlabs/Module/Lists.php @@ -1,4 +1,5 @@ 100) { - $ret = Activity::paged_collection_init($total,App::$query_string); - } - else { - $members = AccessList::members($group['uid'],$group['id'], false, App::$pager['start'], App::$pager['itemspage']); - $ret = Activity::encode_follow_collection($members, App::$query_string, 'OrderedCollection',$total); - } + if (App::$pager['unset'] && $total > 100) { + $ret = Activity::paged_collection_init($total, App::$query_string); + } else { + $members = AccessList::members($group['uid'], $group['id'], false, App::$pager['start'], App::$pager['itemspage']); + $ret = Activity::encode_follow_collection($members, App::$query_string, 'OrderedCollection', $total); + } - as_return_and_die($ret,$channel); + as_return_and_die($ret, $channel); + } - } + if (!local_channel()) { + notice(t('Permission denied.') . EOL); + return; + } - if (! local_channel()) { - notice( t('Permission denied.') . EOL); - return; - } + App::$profile_uid = local_channel(); + nav_set_selected('Access Lists'); + } - App::$profile_uid = local_channel(); - nav_set_selected('Access Lists'); - } + public function post() + { - function post() { - - if (! local_channel()) { - notice( t('Permission denied.') . EOL); - return; - } - - if ((argc() == 2) && (argv(1) === 'new')) { - check_form_security_token_redirectOnErr('/lists/new', 'group_edit'); - - $name = notags(trim($_POST['groupname'])); - $public = intval($_POST['public']); - $r = AccessList::add(local_channel(),$name,$public); - if ($r) { - info( t('Access list created.') . EOL ); - } - else { - notice( t('Could not create access list.') . EOL ); - } - goaway(z_root() . '/lists'); - - } - if ((argc() == 2) && (intval(argv(1)))) { - check_form_security_token_redirectOnErr('/lists', 'group_edit'); - - $r = q("SELECT * FROM pgrp WHERE id = %d AND uid = %d LIMIT 1", - intval(argv(1)), - intval(local_channel()) - ); - if (! $r) { - $r = q("select * from pgrp where id = %d limit 1", - intval(argv(1)) - ); - if ($r) { - notice( t('Permission denied.') . EOL ); - } else { - notice( t('Access list not found.') . EOL ); - } - goaway(z_root() . '/connections'); - - } - $group = array_shift($r); - $groupname = notags(trim($_POST['groupname'])); - $public = intval($_POST['public']); - - if ((strlen($groupname)) && (($groupname != $group['gname']) || ($public != $group['visible']))) { - $r = q("UPDATE pgrp SET gname = '%s', visible = %d WHERE uid = %d AND id = %d", - dbesc($groupname), - intval($public), - intval(local_channel()), - intval($group['id']) - ); - if ($r) { - info( t('Access list updated.') . EOL ); - } - Libsync::build_sync_packet(local_channel(),null,true); - } - - goaway(z_root() . '/lists/' . argv(1) . '/' . argv(2)); - } - return; - } - - function get() { + if (!local_channel()) { + notice(t('Permission denied.') . EOL); + return; + } - $change = false; - - logger('mod_lists: ' . App::$cmd,LOGGER_DEBUG); - + if ((argc() == 2) && (argv(1) === 'new')) { + check_form_security_token_redirectOnErr('/lists/new', 'group_edit'); - if (argc() > 2 && argv(1) === 'view') { - $grp = argv(2); - if ($grp) { - $r = q("select * from pgrp where hash = '%s' and deleted = 0", - dbesc($grp) - ); - if ($r) { - $uid = $r[0]['uid']; - if (local_channel() && local_channel() == $uid) { - goaway(z_root() . '/lists/' . $r[0]['id']); - } - if (! ($r[0]['visible'] && perm_is_allowed($uid,get_observer_hash(),'view_contacts'))) { - notice( t('Permission denied') . EOL); - return; - } - $members = []; - $memberlist = AccessList::members($uid, $r[0]['id']); + $name = notags(trim($_POST['groupname'])); + $public = intval($_POST['public']); + $r = AccessList::add(local_channel(), $name, $public); + if ($r) { + info(t('Access list created.') . EOL); + } else { + notice(t('Could not create access list.') . EOL); + } + goaway(z_root() . '/lists'); + } + if ((argc() == 2) && (intval(argv(1)))) { + check_form_security_token_redirectOnErr('/lists', 'group_edit'); - if ($memberlist) { - foreach ($memberlist as $member) { - $members[] = micropro($member,true,'mpgroup', 'card'); - } - } - $o = replace_macros(get_markup_template('listmembers.tpl'), [ - '$title' => t('List members'), - '$members' => $members - ]); - return $o; - } - else { - notice ( t('List not found') . EOL); - return; - } - } - } + $r = q( + "SELECT * FROM pgrp WHERE id = %d AND uid = %d LIMIT 1", + intval(argv(1)), + intval(local_channel()) + ); + if (!$r) { + $r = q( + "select * from pgrp where id = %d limit 1", + intval(argv(1)) + ); + if ($r) { + notice(t('Permission denied.') . EOL); + } else { + notice(t('Access list not found.') . EOL); + } + goaway(z_root() . '/connections'); + } + $group = array_shift($r); + $groupname = notags(trim($_POST['groupname'])); + $public = intval($_POST['public']); + + if ((strlen($groupname)) && (($groupname != $group['gname']) || ($public != $group['visible']))) { + $r = q( + "UPDATE pgrp SET gname = '%s', visible = %d WHERE uid = %d AND id = %d", + dbesc($groupname), + intval($public), + intval(local_channel()), + intval($group['id']) + ); + if ($r) { + info(t('Access list updated.') . EOL); + } + Libsync::build_sync_packet(local_channel(), null, true); + } + + goaway(z_root() . '/lists/' . argv(1) . '/' . argv(2)); + } + return; + } + + public function get() + { + + $change = false; + + // logger('mod_lists: ' . App::$cmd, LOGGER_DEBUG); + + // Switch to text mode interface if we have more than 'n' contacts or group members, else loading avatars will lead to poor interactivity + + $switchtotext = get_pconfig(local_channel(), 'system', 'listedit_image_limit', get_config('system', 'listedit_image_limit', 1000)); + + if ((argc() == 1) || ((argc() == 2) && (argv(1) === 'new'))) { + if (!local_channel()) { + notice(t('Permission denied') . EOL); + return; + } + + $new = (((argc() == 2) && (argv(1) === 'new')) ? true : false); + + $groups = q( + "SELECT id, gname FROM pgrp WHERE deleted = 0 AND uid = %d ORDER BY gname ASC", + intval(local_channel()) + ); + + $i = 0; + foreach ($groups as $group) { + $entries[$i]['name'] = $group['gname']; + $entries[$i]['id'] = $group['id']; + $entries[$i]['count'] = count(AccessList::members(local_channel(), $group['id'])); + $i++; + } + + $tpl = get_markup_template('privacy_groups.tpl'); + $o = replace_macros($tpl, [ + '$title' => t('Access Lists'), + '$add_new_label' => t('Create access list'), + '$new' => $new, + + // new group form + '$gname' => array('groupname', t('Access list name')), + '$public' => array('public', t('Members are visible to other channels'), false), + '$form_security_token' => get_form_security_token("group_edit"), + '$submit' => t('Submit'), + + // groups list + '$title' => t('Access Lists'), + '$name_label' => t('Name'), + '$count_label' => t('Members'), + '$entries' => $entries + ]); + + return $o; + } + + $context = array('$submit' => t('Submit')); + $tpl = get_markup_template('group_edit.tpl'); + + if ((argc() == 3) && (argv(1) === 'drop')) { + if (!local_channel()) { + notice(t('Permission denied') . EOL); + return; + } - - - if (! local_channel()) { - notice( t('Permission denied') . EOL); - return; - } - - - - // Switch to text mode interface if we have more than 'n' contacts or group members, else loading avatars will lead to poor interactivity - - $switchtotext = get_pconfig(local_channel(),'system','listedit_image_limit',get_config('system','listedit_image_limit', 1000)); - - if ((argc() == 1) || ((argc() == 2) && (argv(1) === 'new'))) { - - $new = (((argc() == 2) && (argv(1) === 'new')) ? true : false); - - $groups = q("SELECT id, gname FROM pgrp WHERE deleted = 0 AND uid = %d ORDER BY gname ASC", - intval(local_channel()) - ); - - $i = 0; - foreach ($groups as $group) { - $entries[$i]['name'] = $group['gname']; - $entries[$i]['id'] = $group['id']; - $entries[$i]['count'] = count(AccessList::members(local_channel(),$group['id'])); - $i++; - } - - $tpl = get_markup_template('privacy_groups.tpl'); - $o = replace_macros($tpl, [ - '$title' => t('Access Lists'), - '$add_new_label' => t('Create access list'), - '$new' => $new, - - // new group form - '$gname' => array('groupname',t('Access list name')), - '$public' => array('public',t('Members are visible to other channels'), false), - '$form_security_token' => get_form_security_token("group_edit"), - '$submit' => t('Submit'), - - // groups list - '$title' => t('Access Lists'), - '$name_label' => t('Name'), - '$count_label' => t('Members'), - '$entries' => $entries - ]); - - return $o; - - } - - $context = array('$submit' => t('Submit')); - $tpl = get_markup_template('group_edit.tpl'); - - if((argc() == 3) && (argv(1) === 'drop')) { check_form_security_token_redirectOnErr('/lists', 'group_drop', 't'); - - if(intval(argv(2))) { - $r = q("SELECT gname FROM pgrp WHERE id = %d AND uid = %d LIMIT 1", - intval(argv(2)), - intval(local_channel()) - ); - if($r) - $result = AccessList::remove(local_channel(),$r[0]['gname']); - if($result) - info( t('Access list removed.') . EOL); - else - notice( t('Unable to remove access list.') . EOL); - } - goaway(z_root() . '/lists'); - // NOTREACHED - } - - - if((argc() > 2) && intval(argv(1)) && argv(2)) { - - check_form_security_token_ForbiddenOnErr('group_member_change', 't'); - - $r = q("SELECT abook_xchan from abook left join xchan on abook_xchan = xchan_hash where abook_xchan = '%s' and abook_channel = %d and xchan_deleted = 0 and abook_self = 0 and abook_blocked = 0 and abook_pending = 0 limit 1", - dbesc(base64url_decode(argv(2))), - intval(local_channel()) - ); - if(count($r)) - $change = base64url_decode(argv(2)); - - } - if(argc() > 1) { - - require_once('include/acl_selectors.php'); + if (intval(argv(2))) { + $r = q( + "SELECT gname FROM pgrp WHERE id = %d AND uid = %d LIMIT 1", + intval(argv(2)), + intval(local_channel()) + ); + if ($r) { + $result = AccessList::remove(local_channel(), $r[0]['gname']); + } + if ($result) { + info(t('Access list removed.') . EOL); + } else { + notice(t('Unable to remove access list.') . EOL); + } + } + goaway(z_root() . '/lists'); + // NOTREACHED + } - if (strlen(argv(1)) <= 11 && intval(argv(1))) { - $r = q("SELECT * FROM pgrp WHERE id = %d AND uid = %d AND deleted = 0 LIMIT 1", - intval(argv(1)), - intval(local_channel()) - ); - } - else { - $r = q("SELECT * FROM pgrp WHERE hash = '%s' AND uid = %d AND deleted = 0 LIMIT 1", - dbesc(argv(1)), - intval(local_channel()) - ); - } - - if(! $r) { - $r = q("SELECT * FROM pgrp WHERE id = %d AND deleted = 0 LIMIT 1", - intval(argv(1)), - ); - if ($r) { - notice( t('Permission denied.') . EOL ); - } - else { - notice( t('Access list not found.') . EOL ); - } - goaway(z_root() . '/connections'); - } - $group = $r[0]; - $members = AccessList::members(local_channel(), $group['id']); + if ((argc() > 2) && intval(argv(1)) && argv(2)) { + if (!local_channel()) { + notice(t('Permission denied') . EOL); + return; + } - $preselected = []; - if(count($members)) { - foreach($members as $member) - if(! in_array($member['xchan_hash'],$preselected)) - $preselected[] = $member['xchan_hash']; - } - - if($change) { - - if(in_array($change,$preselected)) { - AccessList::member_remove(local_channel(),$group['gname'],$change); - } - else { - AccessList::member_add(local_channel(),$group['gname'],$change); - } - - $members = AccessList::members(local_channel(), $group['id']); - - $preselected = []; - if(count($members)) { - foreach($members as $member) - $preselected[] = $member['xchan_hash']; - } + check_form_security_token_ForbiddenOnErr('group_member_change', 't'); + + $r = q( + "SELECT abook_xchan from abook left join xchan on abook_xchan = xchan_hash where abook_xchan = '%s' and abook_channel = %d and xchan_deleted = 0 and abook_self = 0 and abook_blocked = 0 and abook_pending = 0 limit 1", + dbesc(base64url_decode(argv(2))), + intval(local_channel()) + ); + if (count($r)) { + $change = base64url_decode(argv(2)); + } + } + + if (argc() > 1) { + require_once('include/acl_selectors.php'); + + if (strlen(argv(1)) <= 11 && intval(argv(1))) { + $r = q( + "SELECT * FROM pgrp WHERE id = %d AND deleted = 0 LIMIT 1", + intval(argv(1)) + ); + } else { + $r = q( + "SELECT * FROM pgrp WHERE hash = '%s' AND deleted = 0 LIMIT 1", + dbesc(argv(1)) + ); + } + + if (! $r) { + notice(t('Access list not found.') . EOL); + return; } - $context = $context + array( - '$title' => sprintf(t('Access List: %s'), $group['gname']), - '$details_label' => t('Edit'), - '$gname' => array('groupname',t('Access list name: '),$group['gname'], ''), - '$gid' => $group['id'], - '$drop' => $drop_txt, - '$public' => array('public',t('Members are visible to other channels'), $group['visible'], ''), - '$form_security_token_edit' => get_form_security_token('group_edit'), - '$delete' => t('Delete access list'), - '$form_security_token_drop' => get_form_security_token("group_drop"), - ); - - } - - if(! isset($group)) - return; - - $groupeditor = array( - 'label_members' => t('List members'), - 'members' => [], - 'label_contacts' => t('Not in this list'), - 'contacts' => [], - ); - - $sec_token = addslashes(get_form_security_token('group_member_change')); - $textmode = (($switchtotext && (count($members) > $switchtotext)) ? true : 'card'); - foreach($members as $member) { - if($member['xchan_url']) { - $member['archived'] = (intval($member['abook_archived']) ? true : false); - $member['click'] = 'groupChangeMember(' . $group['id'] . ',\'' . base64url_encode($member['xchan_hash']) . '\',\'' . $sec_token . '\'); return false;'; - $groupeditor['members'][] = micropro($member,true,'mpgroup', $textmode); + $group = array_shift($r); + $uid = $group['uid']; + $owner = (local_channel() && intval(local_channel()) === intval($group['uid'])); + + if (!$owner) { + // public view of group members if permitted + if (!($group['visible'] && perm_is_allowed($uid, get_observer_hash(), 'view_contacts'))) { + notice(t('Permission denied') . EOL); + return; + } + $members = []; + $memberlist = AccessList::members($uid, $group['id']); + + if ($memberlist) { + foreach ($memberlist as $member) { + $members[] = micropro($member, true, 'mpgroup', 'card'); + } + } + $o = replace_macros(get_markup_template('listmembers.tpl'), [ + '$title' => t('List members'), + '$members' => $members + ]); + return $o; } - else - AccessList::member_remove(local_channel(),$group['gname'],$member['xchan_hash']); - } - - $r = q("SELECT abook.*, xchan.* FROM abook left join xchan on abook_xchan = xchan_hash WHERE abook_channel = %d AND abook_self = 0 and abook_blocked = 0 and abook_pending = 0 and xchan_deleted = 0 order by xchan_name asc", - intval(local_channel()) - ); - - if(count($r)) { - $textmode = (($switchtotext && (count($r) > $switchtotext)) ? true : 'card'); - foreach($r as $member) { - if(! in_array($member['xchan_hash'],$preselected)) { - $member['archived'] = (intval($member['abook_archived']) ? true : false); - $member['click'] = 'groupChangeMember(' . $group['id'] . ',\'' . base64url_encode($member['xchan_hash']) . '\',\'' . $sec_token . '\'); return false;'; - $groupeditor['contacts'][] = micropro($member,true,'mpall', $textmode); - } - } - } - - $context['$groupeditor'] = $groupeditor; - $context['$desc'] = t('Select a channel to toggle membership'); - - if($change) { - $tpl = get_markup_template('groupeditor.tpl'); - echo replace_macros($tpl, $context); - killme(); - } - - return replace_macros($tpl, $context); - - } - - + + $members = AccessList::members(local_channel(), $group['id']); + + $preselected = []; + if (count($members)) { + foreach ($members as $member) { + if (!in_array($member['xchan_hash'], $preselected)) { + $preselected[] = $member['xchan_hash']; + } + } + } + + if ($change) { + if (in_array($change, $preselected)) { + AccessList::member_remove(local_channel(), $group['gname'], $change); + } else { + AccessList::member_add(local_channel(), $group['gname'], $change); + } + + $members = AccessList::members(local_channel(), $group['id']); + + $preselected = []; + if (count($members)) { + foreach ($members as $member) { + $preselected[] = $member['xchan_hash']; + } + } + } + + $context = $context + array( + '$title' => sprintf(t('Access List: %s'), $group['gname']), + '$details_label' => t('Edit'), + '$gname' => array('groupname', t('Access list name: '), $group['gname'], ''), + '$gid' => $group['id'], + '$drop' => $drop_txt, + '$public' => array('public', t('Members are visible to other channels'), $group['visible'], ''), + '$form_security_token_edit' => get_form_security_token('group_edit'), + '$delete' => t('Delete access list'), + '$form_security_token_drop' => get_form_security_token("group_drop"), + ); + } + + if (!isset($group)) { + return; + } + + $groupeditor = array( + 'label_members' => t('List members'), + 'members' => [], + 'label_contacts' => t('Not in this list'), + 'contacts' => [], + ); + + $sec_token = addslashes(get_form_security_token('group_member_change')); + $textmode = (($switchtotext && (count($members) > $switchtotext)) ? true : 'card'); + foreach ($members as $member) { + if ($member['xchan_url']) { + $member['archived'] = (intval($member['abook_archived']) ? true : false); + $member['click'] = 'groupChangeMember(' . $group['id'] . ',\'' . base64url_encode($member['xchan_hash']) . '\',\'' . $sec_token . '\'); return false;'; + $groupeditor['members'][] = micropro($member, true, 'mpgroup', $textmode); + } else { + AccessList::member_remove(local_channel(), $group['gname'], $member['xchan_hash']); + } + } + + $r = q( + "SELECT abook.*, xchan.* FROM abook left join xchan on abook_xchan = xchan_hash WHERE abook_channel = %d AND abook_self = 0 and abook_blocked = 0 and abook_pending = 0 and xchan_deleted = 0 order by xchan_name asc", + intval(local_channel()) + ); + + if (count($r)) { + $textmode = (($switchtotext && (count($r) > $switchtotext)) ? true : 'card'); + foreach ($r as $member) { + if (!in_array($member['xchan_hash'], $preselected)) { + $member['archived'] = (intval($member['abook_archived']) ? true : false); + $member['click'] = 'groupChangeMember(' . $group['id'] . ',\'' . base64url_encode($member['xchan_hash']) . '\',\'' . $sec_token . '\'); return false;'; + $groupeditor['contacts'][] = micropro($member, true, 'mpall', $textmode); + } + } + } + + $context['$groupeditor'] = $groupeditor; + $context['$desc'] = t('Select a channel to toggle membership'); + + if ($change) { + $tpl = get_markup_template('groupeditor.tpl'); + echo replace_macros($tpl, $context); + killme(); + } + + return replace_macros($tpl, $context); + } } diff --git a/Zotlabs/Module/Lockview.php b/Zotlabs/Module/Lockview.php index cbca9cdd6..6bb6bce31 100644 --- a/Zotlabs/Module/Lockview.php +++ b/Zotlabs/Module/Lockview.php @@ -1,176 +1,187 @@ 1) ? argv(1) : 0); - if (is_numeric($type)) { - $item_id = intval($type); - $type='item'; - } - else { - $item_id = ((argc() > 2) ? intval(argv(2)) : 0); - } - - if(! $item_id) - killme(); - - if (! in_array($type, array('item', 'photo', 'attach', 'event', 'menu_item', 'chatroom'))) - killme(); - - // we have different naming in in menu_item table and chatroom table - switch($type) { - case 'menu_item': - $id = 'mitem_id'; - break; - case 'chatroom': - $id = 'cr_id'; - break; - default: - $id = 'id'; - break; - } - - $r = q("SELECT * FROM %s WHERE $id = %d LIMIT 1", - dbesc($type), - intval($item_id) - ); - - if(! $r) - killme(); - - $item = $r[0]; - - // we have different naming in in menu_item table and chatroom table - switch($type) { - case 'menu_item': - $uid = $item['mitem_channel_id']; - break; - case 'chatroom': - $uid = $item['cr_uid']; - break; - default: - $uid = $item['uid']; - break; - } + if (local_channel()) { + $at = q( + "select * from atoken where atoken_uid = %d", + intval(local_channel()) + ); + if ($at) { + foreach ($at as $t) { + $atokens[] = atoken_xchan($t); + } + } + } - if ($type === 'item') { - $recips = get_iconfig($item['id'],'activitypub','recips'); - if ($recips) { - $o = ''; - $l = []; - if (isset($recips['to'])) { - if (! is_array($recips['to'])) { - $recips['to'] = [ $recips['to'] ]; - } - $l = array_merge($l,$recips['to']); - } - if (isset($recips['cc'])) { - if (! is_array($recips['cc'])) { - $recips['cc'] = [ $recips['cc'] ]; - } - $l = array_merge($l,$recips['cc']); - } - for ($x = 0; $x < count($l); $x ++) { - if($l[$x] !== ACTIVITY_PUBLIC_INBOX) { - $l[$x] = '' . $l[$x] . ''; - } - } - echo $o . implode(', ',$l); - killme(); - } - } + $type = ((argc() > 1) ? argv(1) : 0); + if (is_numeric($type)) { + $item_id = intval($type); + $type = 'item'; + } else { + $item_id = ((argc() > 2) ? intval(argv(2)) : 0); + } - - if(intval($item['item_private']) && (! strlen($item['allow_cid'])) && (! strlen($item['allow_gid'])) - && (! strlen($item['deny_cid'])) && (! strlen($item['deny_gid']))) { + if (!$item_id) { + killme(); + } - if ($item['mid'] === $item['parent_mid']) { - echo ''; - killme(); - } - } - - $allowed_users = expand_acl($item['allow_cid']); - $allowed_groups = expand_acl($item['allow_gid']); - $deny_users = expand_acl($item['deny_cid']); - $deny_groups = expand_acl($item['deny_gid']); - - $o = ''; - $l = []; - - stringify_array_elms($allowed_groups,true); - stringify_array_elms($allowed_users,true); - stringify_array_elms($deny_groups,true); - stringify_array_elms($deny_users,true); - + if (!in_array($type, array('item', 'photo', 'attach', 'event', 'menu_item', 'chatroom'))) { + killme(); + } - if(count($allowed_groups)) { - $r = q("SELECT gname FROM pgrp WHERE hash IN ( " . implode(', ', $allowed_groups) . " )"); - if($r) - foreach($r as $rr) - $l[] = ''; - } - if(count($allowed_users)) { - $r = q("SELECT xchan_name FROM xchan WHERE xchan_hash IN ( " . implode(', ',$allowed_users) . " )"); - if($r) - foreach($r as $rr) - $l[] = ''; - if($atokens) { - foreach($atokens as $at) { - if(in_array("'" . $at['xchan_hash'] . "'",$allowed_users)) { - $l[] = ''; - } - } - } - } + // we have different naming in in menu_item table and chatroom table + switch ($type) { + case 'menu_item': + $id = 'mitem_id'; + break; + case 'chatroom': + $id = 'cr_id'; + break; + default: + $id = 'id'; + break; + } - if(count($deny_groups)) { - $r = q("SELECT gname FROM pgrp WHERE hash IN ( " . implode(', ', $deny_groups) . " )"); - if($r) - foreach($r as $rr) - $l[] = ''; - } - if(count($deny_users)) { - $r = q("SELECT xchan_name FROM xchan WHERE xchan_hash IN ( " . implode(', ', $deny_users) . " )"); - if($r) - foreach($r as $rr) - $l[] = ''; + $r = q( + "SELECT * FROM %s WHERE $id = %d LIMIT 1", + dbesc($type), + intval($item_id) + ); - if($atokens) { - foreach($atokens as $at) { - if(in_array("'" . $at['xchan_hash'] . "'",$deny_users)) { - $l[] = ''; - } - } - } + if (!$r) { + killme(); + } + + $item = $r[0]; + + // we have different naming in in menu_item table and chatroom table + switch ($type) { + case 'menu_item': + $uid = $item['mitem_channel_id']; + break; + case 'chatroom': + $uid = $item['cr_uid']; + break; + default: + $uid = $item['uid']; + break; + } + + if ($type === 'item') { + $recips = get_iconfig($item['id'], 'activitypub', 'recips'); + if ($recips) { + $o = ''; + $l = []; + if (isset($recips['to'])) { + if (!is_array($recips['to'])) { + $recips['to'] = [$recips['to']]; + } + $l = array_merge($l, $recips['to']); + } + if (isset($recips['cc'])) { + if (!is_array($recips['cc'])) { + $recips['cc'] = [$recips['cc']]; + } + $l = array_merge($l, $recips['cc']); + } + for ($x = 0; $x < count($l); $x++) { + if ($l[$x] !== ACTIVITY_PUBLIC_INBOX) { + $l[$x] = '' . $l[$x] . ''; + } + } + echo $o . implode(', ', $l); + killme(); + } + } - } - - echo $o . implode($l); - killme(); - - - } - + if ( + intval($item['item_private']) && (!strlen($item['allow_cid'])) && (!strlen($item['allow_gid'])) + && (!strlen($item['deny_cid'])) && (!strlen($item['deny_gid'])) + ) { + if ($item['mid'] === $item['parent_mid']) { + echo ''; + killme(); + } + } + + $allowed_users = expand_acl($item['allow_cid']); + $allowed_groups = expand_acl($item['allow_gid']); + $deny_users = expand_acl($item['deny_cid']); + $deny_groups = expand_acl($item['deny_gid']); + + $o = ''; + $l = []; + + stringify_array_elms($allowed_groups, true); + stringify_array_elms($allowed_users, true); + stringify_array_elms($deny_groups, true); + stringify_array_elms($deny_users, true); + + + if (count($allowed_groups)) { + $r = q("SELECT gname FROM pgrp WHERE hash IN ( " . implode(', ', $allowed_groups) . " )"); + if ($r) { + foreach ($r as $rr) { + $l[] = ''; + } + } + } + if (count($allowed_users)) { + $r = q("SELECT xchan_name FROM xchan WHERE xchan_hash IN ( " . implode(', ', $allowed_users) . " )"); + if ($r) { + foreach ($r as $rr) { + $l[] = ''; + } + } + if ($atokens) { + foreach ($atokens as $at) { + if (in_array("'" . $at['xchan_hash'] . "'", $allowed_users)) { + $l[] = ''; + } + } + } + } + + if (count($deny_groups)) { + $r = q("SELECT gname FROM pgrp WHERE hash IN ( " . implode(', ', $deny_groups) . " )"); + if ($r) { + foreach ($r as $rr) { + $l[] = ''; + } + } + } + if (count($deny_users)) { + $r = q("SELECT xchan_name FROM xchan WHERE xchan_hash IN ( " . implode(', ', $deny_users) . " )"); + if ($r) { + foreach ($r as $rr) { + $l[] = ''; + } + } + + if ($atokens) { + foreach ($atokens as $at) { + if (in_array("'" . $at['xchan_hash'] . "'", $deny_users)) { + $l[] = ''; + } + } + } + } + + echo $o . implode($l); + killme(); + } } diff --git a/Zotlabs/Module/Locs.php b/Zotlabs/Module/Locs.php index b6ecc9161..b95d8ad46 100644 --- a/Zotlabs/Module/Locs.php +++ b/Zotlabs/Module/Locs.php @@ -1,138 +1,147 @@ t('Manage Channel Locations'), - '$loc' => t('Location'), - '$addr' => t('Address'), - '$mkprm' => t('Primary'), - '$drop' => t('Drop'), - '$submit' => t('Submit'), - '$sync' => t('Publish these settings'), - '$sync_text' => t('Please wait several minutes between consecutive operations.'), - '$drop_text' => t('When possible, drop a location by logging into that website/hub and removing your channel.'), - '$last_resort' => t('Use this form to drop the location if the hub is no longer operating.'), - '$hubs' => $r, - '$base_url' => z_root() - ] ); - - return $o; - } - + if ($_REQUEST['primary']) { + $hubloc_id = intval($_REQUEST['primary']); + if ($hubloc_id) { + $r = q( + "select hubloc_id from hubloc where hubloc_id = %d and hubloc_hash = '%s' limit 1", + intval($hubloc_id), + dbesc($channel['channel_hash']) + ); + + if (!$r) { + notice(t('Location not found.') . EOL); + return; + } + + $r = q( + "update hubloc set hubloc_primary = 0 where hubloc_primary = 1 and hubloc_hash = '%s' ", + dbesc($channel['channel_hash']) + ); + $r = q( + "update hubloc set hubloc_primary = 1 where hubloc_id = %d and hubloc_hash = '%s'", + intval($hubloc_id), + dbesc($channel['channel_hash']) + ); + + $x = q( + "select * from hubloc where hubloc_id = %d and hubloc_hash = '%s' ", + intval($hubloc_id), + dbesc($channel['channel_hash']) + ); + if ($x) { + hubloc_change_primary($x[0]); + } + + Run::Summon(['Notifier', 'refresh_all', $channel['channel_id']]); + return; + } + } + + + if ($_REQUEST['drop']) { + $hubloc_id = intval($_REQUEST['drop']); + + if ($hubloc_id) { + $r = q( + "select * from hubloc where hubloc_id = %d and hubloc_url != '%s' and hubloc_hash = '%s' limit 1", + intval($hubloc_id), + dbesc(z_root()), + dbesc($channel['channel_hash']) + ); + + if (!$r) { + notice(t('Location not found.') . EOL); + return; + } + if (intval($r[0]['hubloc_primary'])) { + $x = q( + "select hubloc_id from hubloc where hubloc_primary = 1 and hubloc_hash = '%s'", + dbesc($channel['channel_hash']) + ); + if (!$x) { + notice(t('Location lookup failed.')); + return; + } + if (count($x) == 1) { + notice(t('Please select another location to become primary before removing the primary location.') . EOL); + return; + } + } + + $r = q( + "update hubloc set hubloc_deleted = 1 where hubloc_id = %d and hubloc_hash = '%s'", + intval($hubloc_id), + dbesc($channel['channel_hash']) + ); + Run::Summon(['Notifier', 'refresh_all', $channel['channel_id']]); + return; + } + } + } + + + public function get() + { + + + if (!local_channel()) { + notice(t('Permission denied.') . EOL); + return; + } + + $channel = App::get_channel(); + + if ($_REQUEST['sync']) { + Run::Summon(['Notifier', 'refresh_all', $channel['channel_id']]); + info(t('Pushing location info') . EOL); + goaway(z_root() . '/locs'); + } + + + $r = q( + "select * from hubloc where hubloc_hash = '%s'", + dbesc($channel['channel_hash']) + ); + + if (!$r) { + notice(t('No locations found.') . EOL); + return; + } + + $o = replace_macros(get_markup_template('locmanage.tpl'), [ + '$header' => t('Manage Channel Locations'), + '$loc' => t('Location'), + '$addr' => t('Address'), + '$mkprm' => t('Primary'), + '$drop' => t('Drop'), + '$submit' => t('Submit'), + '$sync' => t('Publish these settings'), + '$sync_text' => t('Please wait several minutes between consecutive operations.'), + '$drop_text' => t('When possible, drop a location by logging into that website/hub and removing your channel.'), + '$last_resort' => t('Use this form to drop the location if the hub is no longer operating.'), + '$hubs' => $r, + '$base_url' => z_root() + ]); + + return $o; + } } diff --git a/Zotlabs/Module/Login.php b/Zotlabs/Module/Login.php index 6430939b4..930cf70f5 100644 --- a/Zotlabs/Module/Login.php +++ b/Zotlabs/Module/Login.php @@ -1,16 +1,21 @@ nuke(); - } - goaway(z_root()); +class Logout extends Controller +{ - } + public function init() + { + if ($_SESSION['delegate'] && $_SESSION['delegate_push']) { + $_SESSION = $_SESSION['delegate_push']; + } else { + App::$session->nuke(); + } + goaway(z_root()); + } } diff --git a/Zotlabs/Module/Lostpass.php b/Zotlabs/Module/Lostpass.php index 072657d7b..b274372e2 100644 --- a/Zotlabs/Module/Lostpass.php +++ b/Zotlabs/Module/Lostpass.php @@ -1,141 +1,147 @@ get_config('system', 'sitename'), + '$siteurl' => z_root(), + '$username' => sprintf(t('Site Member (%s)'), $email), + '$email' => $email, + '$reset_link' => z_root() . '/lostpass?verify=' . $hash + )); + + $subject = email_header_encode(sprintf(t('Password reset requested at %s'), get_config('system', 'sitename')), 'UTF-8'); + + $res = z_mail( + [ + 'toEmail' => $email, + 'messageSubject' => sprintf(t('Password reset requested at %s'), get_config('system', 'sitename')), + 'textVersion' => $message, + ] + ); + + goaway(z_root()); + } -class Lostpass extends \Zotlabs\Web\Controller { + public function get() + { - function post() { - - $loginame = notags(trim($_POST['login-name'])); - if(! $loginame) - goaway(z_root()); - - $r = q("SELECT * FROM account WHERE account_email = '%s' LIMIT 1", - dbesc($loginame) - ); - - if(! $r) { - notice( t('No valid account found.') . EOL); - goaway(z_root()); - } - - $aid = $r[0]['account_id']; - $email = $r[0]['account_email']; - - $hash = random_string(); - - $r = q("UPDATE account SET account_reset = '%s' WHERE account_id = %d", - dbesc($hash), - intval($aid) - ); - if($r) - info( t('Password reset request issued. Check your email.') . EOL); - - $email_tpl = get_intltext_template("lostpass_eml.tpl"); - $message = replace_macros($email_tpl, array( - '$sitename' => get_config('system','sitename'), - '$siteurl' => z_root(), - '$username' => sprintf( t('Site Member (%s)'), $email), - '$email' => $email, - '$reset_link' => z_root() . '/lostpass?verify=' . $hash - )); - - $subject = email_header_encode(sprintf( t('Password reset requested at %s'),get_config('system','sitename')), 'UTF-8'); - - $res = z_mail( - [ - 'toEmail' => $email, - 'messageSubject' => sprintf( t('Password reset requested at %s'), get_config('system','sitename')), - 'textVersion' => $message, - ] - ); - goaway(z_root()); - } - - - function get() { - - - if(x($_GET,'verify')) { - $verify = $_GET['verify']; - - $r = q("SELECT * FROM account WHERE account_reset = '%s' LIMIT 1", - dbesc($verify) - ); - if(! $r) { - notice( t("Request could not be verified. (You may have previously submitted it.) Password reset failed.") . EOL); - goaway(z_root()); - return; - } - - $aid = $r[0]['account_id']; - $email = $r[0]['account_email']; - - $new_password = autoname(6) . mt_rand(100,9999); - - $salt = random_string(32); - $password_encoded = hash('whirlpool', $salt . $new_password); - - $r = q("UPDATE account SET account_salt = '%s', account_password = '%s', account_reset = '', account_flags = (account_flags & ~%d) where account_id = %d", - dbesc($salt), - dbesc($password_encoded), - intval(ACCOUNT_UNVERIFIED), - intval($aid) - ); - - if($r) { - $tpl = get_markup_template('pwdreset.tpl'); - $o .= replace_macros($tpl,array( - '$lbl1' => t('Password Reset'), - '$lbl2' => t('Your password has been reset as requested.'), - '$lbl3' => t('Your new password is'), - '$lbl4' => t('Save or copy your new password - and then'), - '$lbl5' => '' . t('click here to login') . '.', - '$lbl6' => t('Your password may be changed from the Settings page after successful login.'), - '$newpass' => $new_password, - '$baseurl' => z_root() - - )); - - info("Your password has been reset." . EOL); - - $email_tpl = get_intltext_template("passchanged_eml.tpl"); - $message = replace_macros($email_tpl, array( - '$sitename' => \App::$config['sitename'], - '$siteurl' => z_root(), - '$username' => sprintf( t('Site Member (%s)'), $email), - '$email' => $email, - '$new_password' => $new_password, - '$uid' => $newuid ) - ); - - $res = z_mail( - [ - 'toEmail' => $email, - 'messageSubject' => sprintf( t('Your password has changed at %s'), get_config('system','sitename')), - 'textVersion' => $message, - ] - ); + if (x($_GET, 'verify')) { + $verify = $_GET['verify']; - return $o; - } - - } - else { - $tpl = get_markup_template('lostpass.tpl'); - - $o .= replace_macros($tpl,array( - '$title' => t('Forgot your Password?'), - '$desc' => t('Enter your email address and submit to have your password reset. Then check your email for further instructions.'), - '$name' => t('Email Address'), - '$submit' => t('Reset') - )); - - return $o; - } - - } - + $r = q( + "SELECT * FROM account WHERE account_reset = '%s' LIMIT 1", + dbesc($verify) + ); + if (!$r) { + notice(t("Request could not be verified. (You may have previously submitted it.) Password reset failed.") . EOL); + goaway(z_root()); + return; + } + + $aid = $r[0]['account_id']; + $email = $r[0]['account_email']; + + $new_password = autoname(6) . mt_rand(100, 9999); + + $salt = random_string(32); + $password_encoded = hash('whirlpool', $salt . $new_password); + + $r = q( + "UPDATE account SET account_salt = '%s', account_password = '%s', account_reset = '', account_flags = (account_flags & ~%d) where account_id = %d", + dbesc($salt), + dbesc($password_encoded), + intval(ACCOUNT_UNVERIFIED), + intval($aid) + ); + + if ($r) { + $tpl = get_markup_template('pwdreset.tpl'); + $o .= replace_macros($tpl, array( + '$lbl1' => t('Password Reset'), + '$lbl2' => t('Your password has been reset as requested.'), + '$lbl3' => t('Your new password is'), + '$lbl4' => t('Save or copy your new password - and then'), + '$lbl5' => '' . t('click here to login') . '.', + '$lbl6' => t('Your password may be changed from the Settings page after successful login.'), + '$newpass' => $new_password, + '$baseurl' => z_root() + + )); + + info("Your password has been reset." . EOL); + + $email_tpl = get_intltext_template("passchanged_eml.tpl"); + $message = replace_macros($email_tpl, array( + '$sitename' => App::$config['sitename'], + '$siteurl' => z_root(), + '$username' => sprintf(t('Site Member (%s)'), $email), + '$email' => $email, + '$new_password' => $new_password, + '$uid' => $newuid)); + + $res = z_mail( + [ + 'toEmail' => $email, + 'messageSubject' => sprintf(t('Your password has changed at %s'), get_config('system', 'sitename')), + 'textVersion' => $message, + ] + ); + + return $o; + } + } else { + $tpl = get_markup_template('lostpass.tpl'); + + $o .= replace_macros($tpl, array( + '$title' => t('Forgot your Password?'), + '$desc' => t('Enter your email address and submit to have your password reset. Then check your email for further instructions.'), + '$name' => t('Email Address'), + '$submit' => t('Reset') + )); + + return $o; + } + } } diff --git a/Zotlabs/Module/Magic.php b/Zotlabs/Module/Magic.php index a9e41cd06..12261aacd 100644 --- a/Zotlabs/Module/Magic.php +++ b/Zotlabs/Module/Magic.php @@ -1,6 +1,6 @@ false, - 'url' => '', - 'message' => '' - ]; - - logger('mod_magic: invoked', LOGGER_DEBUG); - - logger('args: ' . print_r($_REQUEST,true),LOGGER_DATA); - - $addr = ((x($_REQUEST,'addr')) ? $_REQUEST['addr'] : ''); - $bdest = ((x($_REQUEST,'bdest')) ? $_REQUEST['bdest'] : ''); - $dest = ((x($_REQUEST,'dest')) ? $_REQUEST['dest'] : ''); - $rev = ((x($_REQUEST,'rev')) ? intval($_REQUEST['rev']) : 0); - $owa = ((x($_REQUEST,'owa')) ? intval($_REQUEST['owa']) : 0); - $delegate = ((x($_REQUEST,'delegate')) ? $_REQUEST['delegate'] : ''); + $ret = [ + 'success' => false, + 'url' => '', + 'message' => '' + ]; - // bdest is preferred as it is hex-encoded and can survive url rewrite and argument parsing - - if ($bdest) { - $dest = hex2bin($bdest); - } - - $parsed = parse_url($dest); + logger('mod_magic: invoked', LOGGER_DEBUG); - if (! $parsed) { - goaway($dest); - } - - $basepath = $parsed['scheme'] . '://' . $parsed['host'] . (($parsed['port']) ? ':' . $parsed['port'] : ''); - $owapath = SConfig::get($basepath,'system','openwebauth', $basepath . '/owa'); - - // This is ready-made for a plugin that provides a blacklist or "ask me" before blindly authenticating. - // By default, we'll proceed without asking. - - $arr = [ - 'channel_id' => local_channel(), - 'destination' => $dest, - 'proceed' => true - ]; - - call_hooks('magic_auth',$arr); - $dest = $arr['destination']; - if (! $arr['proceed']) { - goaway($dest); - } - - if((get_observer_hash()) && (stripos($dest,z_root()) === 0)) { + logger('args: ' . print_r($_REQUEST, true), LOGGER_DATA); - // We are already authenticated on this site and a registered observer. - // First check if this is a delegate request on the local system and process accordingly. - // Otherwise redirect. - - if ($delegate) { + $addr = ((x($_REQUEST, 'addr')) ? $_REQUEST['addr'] : ''); + $bdest = ((x($_REQUEST, 'bdest')) ? $_REQUEST['bdest'] : ''); + $dest = ((x($_REQUEST, 'dest')) ? $_REQUEST['dest'] : ''); + $rev = ((x($_REQUEST, 'rev')) ? intval($_REQUEST['rev']) : 0); + $owa = ((x($_REQUEST, 'owa')) ? intval($_REQUEST['owa']) : 0); + $delegate = ((x($_REQUEST, 'delegate')) ? $_REQUEST['delegate'] : ''); - $r = q("select * from channel left join hubloc on channel_hash = hubloc_hash where hubloc_addr = '%s' limit 1", - dbesc($delegate) - ); - - if ($r) { - $c = array_shift($r); - if (perm_is_allowed($c['channel_id'],get_observer_hash(),'delegate')) { - $tmp = $_SESSION; - $_SESSION['delegate_push'] = $tmp; - $_SESSION['delegate_channel'] = $c['channel_id']; - $_SESSION['delegate'] = get_observer_hash(); - $_SESSION['account_id'] = intval($c['channel_account_id']); + // bdest is preferred as it is hex-encoded and can survive url rewrite and argument parsing - change_channel($c['channel_id']); - } - } - } - - goaway($dest); - } - - if (local_channel()) { - $channel = App::get_channel(); - - // OpenWebAuth + if ($bdest) { + $dest = hex2bin($bdest); + } - if ($owa) { + $parsed = parse_url($dest); - $dest = strip_zids($dest); - $dest = strip_query_param($dest,'f'); + if (!$parsed) { + goaway($dest); + } - // We now post to the OWA endpoint. This improves security by providing a signed digest - - $data = json_encode([ 'OpenWebAuth' => random_string() ]); - - $headers = []; + $basepath = $parsed['scheme'] . '://' . $parsed['host'] . (($parsed['port']) ? ':' . $parsed['port'] : ''); + $owapath = SConfig::get($basepath, 'system', 'openwebauth', $basepath . '/owa'); + + // This is ready-made for a plugin that provides a blacklist or "ask me" before blindly authenticating. + // By default, we'll proceed without asking. + + $arr = [ + 'channel_id' => local_channel(), + 'destination' => $dest, + 'proceed' => true + ]; + + call_hooks('magic_auth', $arr); + $dest = $arr['destination']; + if (!$arr['proceed']) { + goaway($dest); + } + + if ((get_observer_hash()) && (stripos($dest, z_root()) === 0)) { + // We are already authenticated on this site and a registered observer. + // First check if this is a delegate request on the local system and process accordingly. + // Otherwise redirect. + + if ($delegate) { + $r = q( + "select * from channel left join hubloc on channel_hash = hubloc_hash where hubloc_addr = '%s' limit 1", + dbesc($delegate) + ); + + if ($r) { + $c = array_shift($r); + if (perm_is_allowed($c['channel_id'], get_observer_hash(), 'delegate')) { + $tmp = $_SESSION; + $_SESSION['delegate_push'] = $tmp; + $_SESSION['delegate_channel'] = $c['channel_id']; + $_SESSION['delegate'] = get_observer_hash(); + $_SESSION['account_id'] = intval($c['channel_account_id']); + + change_channel($c['channel_id']); + } + } + } + + goaway($dest); + } + + if (local_channel()) { + $channel = App::get_channel(); + + // OpenWebAuth + + if ($owa) { + $dest = strip_zids($dest); + $dest = strip_query_param($dest, 'f'); + + // We now post to the OWA endpoint. This improves security by providing a signed digest + + $data = json_encode(['OpenWebAuth' => random_string()]); + + $headers = []; $headers['Accept'] = 'application/x-nomad+json, application/x-zot+json'; $headers['Content-Type'] = 'application/x-nomad+json'; - $headers['X-Open-Web-Auth'] = random_string(); - $headers['Digest'] = HTTPSig::generate_digest_header($data); - $headers['Host'] = $parsed['host']; - $headers['(request-target)'] = 'post ' . '/owa'; + $headers['X-Open-Web-Auth'] = random_string(); + $headers['Digest'] = HTTPSig::generate_digest_header($data); + $headers['Host'] = $parsed['host']; + $headers['(request-target)'] = 'post ' . '/owa'; - $headers = HTTPSig::create_sig($headers,$channel['channel_prvkey'], channel_url($channel),true,'sha512'); - $x = z_post_url($owapath,$data,$redirects,[ 'headers' => $headers ]); - logger('owa fetch returned: ' . print_r($x,true),LOGGER_DATA); - if ($x['success']) { - $j = json_decode($x['body'],true); - if ($j['success'] && $j['encrypted_token']) { - // decrypt the token using our private key - $token = ''; - openssl_private_decrypt(base64url_decode($j['encrypted_token']),$token,$channel['channel_prvkey']); - $x = strpbrk($dest,'?&'); - // redirect using the encrypted token which will be exchanged for an authenticated session - $args = (($x) ? '&owt=' . $token : '?f=&owt=' . $token) . (($delegate) ? '&delegate=1' : ''); - goaway($dest . $args); - } - } - } - } - - goaway($dest); - } - + $headers = HTTPSig::create_sig($headers, $channel['channel_prvkey'], channel_url($channel), true, 'sha512'); + $x = z_post_url($owapath, $data, $redirects, ['headers' => $headers]); + logger('owa fetch returned: ' . print_r($x, true), LOGGER_DATA); + if ($x['success']) { + $j = json_decode($x['body'], true); + if ($j['success'] && $j['encrypted_token']) { + // decrypt the token using our private key + $token = ''; + openssl_private_decrypt(base64url_decode($j['encrypted_token']), $token, $channel['channel_prvkey']); + $x = strpbrk($dest, '?&'); + // redirect using the encrypted token which will be exchanged for an authenticated session + $args = (($x) ? '&owt=' . $token : '?f=&owt=' . $token) . (($delegate) ? '&delegate=1' : ''); + goaway($dest . $args); + } + } + } + } + goaway($dest); + } } diff --git a/Zotlabs/Module/Manage.php b/Zotlabs/Module/Manage.php index abc57f73e..e78444cfb 100644 --- a/Zotlabs/Module/Manage.php +++ b/Zotlabs/Module/Manage.php @@ -1,4 +1,5 @@ 1) ? intval(argv(1)) : 0); - - if (argc() > 2) { - if (argv(2) === 'default') { - $r = q("select channel_id from channel where channel_id = %d and channel_account_id = %d limit 1", - intval($change_channel), - intval(get_account_id()) - ); - if ($r) { - q("update account set account_default_channel = %d where account_id = %d", - intval($change_channel), - intval(get_account_id()) - ); - } - goaway(z_root() . '/manage'); - } - elseif (argv(2) === 'menu') { - $state = intval(PConfig::get($change_channel,'system','include_in_menu', 0)); - PConfig::set($change_channel,'system','include_in_menu',1 - $state); - goaway(z_root() . '/manage'); - } - - } + if ((!get_account_id()) || ($_SESSION['delegate'])) { + notice(t('Permission denied.') . EOL); + return; + } - - if ($change_channel) { + nav_set_selected('Manage'); - $r = change_channel($change_channel); - if ((argc() > 2) && !(argv(2) === 'default')) { - goaway(z_root() . '/' . implode('/',array_slice(App::$argv,2))); // Go to whatever is after /manage/, but with the new channel - } - elseif ($r && $r['channel_startpage']) { - goaway(z_root() . '/' . $r['channel_startpage']); // If nothing extra is specified, go to the default page - } - goaway(z_root()); - } - - $channels = null; - - $r = q("select channel.*, xchan.* from channel left join xchan on channel.channel_hash = xchan.xchan_hash where channel.channel_account_id = %d and channel_removed = 0 order by channel_name ", - intval(get_account_id()) - ); - - $account = App::get_account(); - - if ($r && count($r)) { + $change_channel = ((argc() > 1) ? intval(argv(1)) : 0); - $channels = ((is_site_admin()) ? array_merge ([ get_sys_channel() ], $r) : $r); - for ($x = 0; $x < count($channels); $x ++) { - $channels[$x]['link'] = 'manage/' . intval($channels[$x]['channel_id']); - $channels[$x]['include_in_menu'] = intval(PConfig::get($channels[$x]['channel_id'],'system','include_in_menu',0)); - $channels[$x]['default'] = (($channels[$x]['channel_id'] == $account['account_default_channel']) ? "1" : ''); - $channels[$x]['default_links'] = '1'; - $channels[$x]['collections_label'] = t('Collection'); - $channels[$x]['forum_label'] = t('Group'); - - $c = q("SELECT id, item_wall FROM item + if (argc() > 2) { + if (argv(2) === 'default') { + $r = q( + "select channel_id from channel where channel_id = %d and channel_account_id = %d limit 1", + intval($change_channel), + intval(get_account_id()) + ); + if ($r) { + q( + "update account set account_default_channel = %d where account_id = %d", + intval($change_channel), + intval(get_account_id()) + ); + } + goaway(z_root() . '/manage'); + } elseif (argv(2) === 'menu') { + $state = intval(PConfig::get($change_channel, 'system', 'include_in_menu', 0)); + PConfig::set($change_channel, 'system', 'include_in_menu', 1 - $state); + goaway(z_root() . '/manage'); + } + } + + + if ($change_channel) { + $r = change_channel($change_channel); + + if ((argc() > 2) && !(argv(2) === 'default')) { + goaway(z_root() . '/' . implode('/', array_slice(App::$argv, 2))); // Go to whatever is after /manage/, but with the new channel + } elseif ($r && $r['channel_startpage']) { + goaway(z_root() . '/' . $r['channel_startpage']); // If nothing extra is specified, go to the default page + } + goaway(z_root()); + } + + $channels = null; + + $r = q( + "select channel.*, xchan.* from channel left join xchan on channel.channel_hash = xchan.xchan_hash where channel.channel_account_id = %d and channel_removed = 0 order by channel_name ", + intval(get_account_id()) + ); + + $account = App::get_account(); + + if ($r && count($r)) { + $channels = ((is_site_admin()) ? array_merge([get_sys_channel()], $r) : $r); + for ($x = 0; $x < count($channels); $x++) { + $channels[$x]['link'] = 'manage/' . intval($channels[$x]['channel_id']); + $channels[$x]['include_in_menu'] = intval(PConfig::get($channels[$x]['channel_id'], 'system', 'include_in_menu', 0)); + $channels[$x]['default'] = (($channels[$x]['channel_id'] == $account['account_default_channel']) ? "1" : ''); + $channels[$x]['default_links'] = '1'; + $channels[$x]['collections_label'] = t('Collection'); + $channels[$x]['forum_label'] = t('Group'); + + $c = q( + "SELECT id, item_wall FROM item WHERE item_unseen = 1 and uid = %d " . item_normal(), - intval($channels[$x]['channel_id']) - ); - - if ($c) { - foreach ($c as $it) { - if (intval($it['item_wall'])) { - $channels[$x]['home'] ++; - } - else { - $channels[$x]['network'] ++; - } - } - } - - - $intr = q("SELECT COUNT(abook.abook_id) AS total FROM abook left join xchan on abook.abook_xchan = xchan.xchan_hash where abook_channel = %d and abook_pending = 1 and abook_self = 0 and abook_ignored = 0 and xchan_deleted = 0 and xchan_orphan = 0 ", - intval($channels[$x]['channel_id']) - ); - - if ($intr) { - $channels[$x]['intros'] = intval($intr[0]['total']); - } - - $events = q("SELECT etype, dtstart, adjust FROM event + intval($channels[$x]['channel_id']) + ); + + if ($c) { + foreach ($c as $it) { + if (intval($it['item_wall'])) { + $channels[$x]['home']++; + } else { + $channels[$x]['network']++; + } + } + } + + + $intr = q( + "SELECT COUNT(abook.abook_id) AS total FROM abook left join xchan on abook.abook_xchan = xchan.xchan_hash where abook_channel = %d and abook_pending = 1 and abook_self = 0 and abook_ignored = 0 and xchan_deleted = 0 and xchan_orphan = 0 ", + intval($channels[$x]['channel_id']) + ); + + if ($intr) { + $channels[$x]['intros'] = intval($intr[0]['total']); + } + + $events = q( + "SELECT etype, dtstart, adjust FROM event WHERE event.uid = %d AND dtstart < '%s' AND dtstart > '%s' and dismissed = 0 ORDER BY dtstart ASC ", - intval($channels[$x]['channel_id']), - dbesc(datetime_convert('UTC', date_default_timezone_get(), 'now + 7 days')), - dbesc(datetime_convert('UTC', date_default_timezone_get(), 'now - 1 days')) - ); - - if ($events) { - $channels[$x]['all_events'] = count($events); + intval($channels[$x]['channel_id']), + dbesc(datetime_convert('UTC', date_default_timezone_get(), 'now + 7 days')), + dbesc(datetime_convert('UTC', date_default_timezone_get(), 'now - 1 days')) + ); - if ($channels[$x]['all_events']) { - $str_now = datetime_convert('UTC', date_default_timezone_get(), 'now', 'Y-m-d'); - foreach ($events as $e) { - $bd = false; - if ($e['etype'] === 'birthday') { - $channels[$x]['birthdays'] ++; - $bd = true; - } - else { - $channels[$x]['events'] ++; - } - if (datetime_convert('UTC', ((intval($e['adjust'])) ? date_default_timezone_get() : 'UTC'), $e['dtstart'], 'Y-m-d') === $str_now) { - $channels[$x]['all_events_today'] ++; - if ($bd) { - $channels[$x]['birthdays_today'] ++; - } - else { - $channels[$x]['events_today'] ++; - } - } - } - } - } - } + if ($events) { + $channels[$x]['all_events'] = count($events); - } - - $r = q("select count(channel_id) as total from channel where channel_account_id = %d and channel_removed = 0", - intval(get_account_id()) - ); - $limit = account_service_class_fetch(get_account_id(),'total_identities'); - if ($limit !== false) { - $channel_usage_message = sprintf( t("You have created %1$.0f of %2$.0f allowed channels."), $r[0]['total'], $limit); - } - else { - $channel_usage_message = ''; - } - - - $create = [ 'new_channel', t('Create a new channel'), t('Create New') ]; - - $delegates = null; + if ($channels[$x]['all_events']) { + $str_now = datetime_convert('UTC', date_default_timezone_get(), 'now', 'Y-m-d'); + foreach ($events as $e) { + $bd = false; + if ($e['etype'] === 'birthday') { + $channels[$x]['birthdays']++; + $bd = true; + } else { + $channels[$x]['events']++; + } + if (datetime_convert('UTC', ((intval($e['adjust'])) ? date_default_timezone_get() : 'UTC'), $e['dtstart'], 'Y-m-d') === $str_now) { + $channels[$x]['all_events_today']++; + if ($bd) { + $channels[$x]['birthdays_today']++; + } else { + $channels[$x]['events_today']++; + } + } + } + } + } + } + } - if (local_channel()) { - $delegates = q("select * from abook left join xchan on abook_xchan = xchan_hash where + $r = q( + "select count(channel_id) as total from channel where channel_account_id = %d and channel_removed = 0", + intval(get_account_id()) + ); + $limit = account_service_class_fetch(get_account_id(), 'total_identities'); + if ($limit !== false) { + $channel_usage_message = sprintf(t("You have created %1$.0f of %2$.0f allowed channels."), $r[0]['total'], $limit); + } else { + $channel_usage_message = ''; + } + + + $create = ['new_channel', t('Create a new channel'), t('Create New')]; + + $delegates = null; + + if (local_channel()) { + $delegates = q( + "select * from abook left join xchan on abook_xchan = xchan_hash where abook_channel = %d and abook_xchan in ( select xchan from abconfig where chan = %d and cat = 'system' and k = 'their_perms' and v like '%s' )", - intval(local_channel()), - intval(local_channel()), - dbesc('%delegate%') - ); - } - - if ($delegates) { - for ($x = 0; $x < count($delegates); $x ++) { - $delegates[$x]['link'] = 'magic?f=&bdest=' . bin2hex($delegates[$x]['xchan_url']) - . '&delegate=' . urlencode($delegates[$x]['xchan_addr']); - $delegates[$x]['channel_name'] = $delegates[$x]['xchan_name']; - $delegates[$x]['delegate'] = 1; - $delegates[$x]['collections_label'] = t('Collection'); - $delegates[$x]['forum_label'] = t('Group'); + intval(local_channel()), + intval(local_channel()), + dbesc('%delegate%') + ); + } - } - } - else { - $delegates = null; - } - - return replace_macros(get_markup_template('channels.tpl'), [ - '$header' => t('Channels'), - '$msg_selected' => t('Current Channel'), - '$selected' => local_channel(), - '$desc' => t('Switch to one of your channels by selecting it.'), - '$msg_default' => t('Default Login Channel'), - '$msg_make_default' => t('Make Default'), - '$msg_include' => t('Add to menu'), - '$msg_no_include' => t('Add to menu'), - '$create' => $create, - '$all_channels' => $channels, - '$mail_format' => t('%d new messages'), - '$intros_format' => t('%d new introductions'), - '$channel_usage_message' => $channel_usage_message, - '$delegated_desc' => t('Delegated Channel'), - '$delegates' => $delegates - ]); - } - + if ($delegates) { + for ($x = 0; $x < count($delegates); $x++) { + $delegates[$x]['link'] = 'magic?f=&bdest=' . bin2hex($delegates[$x]['xchan_url']) + . '&delegate=' . urlencode($delegates[$x]['xchan_addr']); + $delegates[$x]['channel_name'] = $delegates[$x]['xchan_name']; + $delegates[$x]['delegate'] = 1; + $delegates[$x]['collections_label'] = t('Collection'); + $delegates[$x]['forum_label'] = t('Group'); + } + } else { + $delegates = null; + } + + return replace_macros(get_markup_template('channels.tpl'), [ + '$header' => t('Channels'), + '$msg_selected' => t('Current Channel'), + '$selected' => local_channel(), + '$desc' => t('Switch to one of your channels by selecting it.'), + '$msg_default' => t('Default Login Channel'), + '$msg_make_default' => t('Make Default'), + '$msg_include' => t('Add to menu'), + '$msg_no_include' => t('Add to menu'), + '$create' => $create, + '$all_channels' => $channels, + '$mail_format' => t('%d new messages'), + '$intros_format' => t('%d new introductions'), + '$channel_usage_message' => $channel_usage_message, + '$delegated_desc' => t('Delegated Channel'), + '$delegates' => $delegates + ]); + } } diff --git a/Zotlabs/Module/Manifest.php b/Zotlabs/Module/Manifest.php index 778539748..ab3163196 100644 --- a/Zotlabs/Module/Manifest.php +++ b/Zotlabs/Module/Manifest.php @@ -1,60 +1,50 @@ System::get_platform_name(), - 'short_name' => System::get_platform_name(), - 'icons' => [ - [ 'src' => '/images/' . System::get_platform_name() . '-64' . '.png', 'sizes' => '64x64' ], - [ 'src' => '/images/' . System::get_platform_name() . '-192' . '.png', 'sizes' => '192x192' ], - [ 'src' => '/images/' . System::get_platform_name() . '-512' . '.png', 'sizes' => '512x512' ], - [ 'src' => '/images/' . System::get_platform_name() . '.svg', 'sizes' => '600x600' ], - ], - 'scope' => '/', - 'start_url' => z_root(), - 'display' => 'fullscreen', - 'orientation' => 'any', - 'theme_color' => 'blue', - 'background_color' => 'white', - 'share_target' => [ - 'action' => '/rpost', - 'method' => 'POST', - 'enctype' => 'multipart/form-data', - 'params' => [ - 'title' => 'title', - 'text' => 'body', - 'url' => 'url', - 'files' => [ - [ 'name' => 'userfile', - 'accept' => [ 'image/*', 'audio/*', 'video/*', 'text/*', 'application/*' ] - ] - ] - ] - ] - - ]; - - - json_return_and_die($ret,'application/manifest+json'); - } - - - - - - - - - + public function init() + { + $ret = [ + 'name' => System::get_platform_name(), + 'short_name' => System::get_platform_name(), + 'icons' => [ + ['src' => '/images/' . System::get_platform_name() . '-64' . '.png', 'sizes' => '64x64'], + ['src' => '/images/' . System::get_platform_name() . '-192' . '.png', 'sizes' => '192x192'], + ['src' => '/images/' . System::get_platform_name() . '-512' . '.png', 'sizes' => '512x512'], + ['src' => '/images/' . System::get_platform_name() . '.svg', 'sizes' => '600x600'], + ], + 'scope' => '/', + 'start_url' => z_root(), + 'display' => 'fullscreen', + 'orientation' => 'any', + 'theme_color' => 'blue', + 'background_color' => 'white', + 'share_target' => [ + 'action' => '/rpost', + 'method' => 'POST', + 'enctype' => 'multipart/form-data', + 'params' => [ + 'title' => 'title', + 'text' => 'body', + 'url' => 'url', + 'files' => [ + ['name' => 'userfile', + 'accept' => ['image/*', 'audio/*', 'video/*', 'text/*', 'application/*'] + ] + ] + ] + ] + ]; + json_return_and_die($ret, 'application/manifest+json'); + } } diff --git a/Zotlabs/Module/Markup.php b/Zotlabs/Module/Markup.php index 1f43697f1..3641a3ade 100644 --- a/Zotlabs/Module/Markup.php +++ b/Zotlabs/Module/Markup.php @@ -6,17 +6,17 @@ use Zotlabs\Lib\Apps; use Zotlabs\Lib\Libsync; use Zotlabs\Web\Controller; -class Markup extends Controller { +class Markup extends Controller +{ - function get() { + public function get() + { $desc = t('This app adds editor buttons for bold, italic, underline, quote, and possibly other common richtext constructs.'); $text = ''; - return $text; - - } - + return $text; + } } diff --git a/Zotlabs/Module/Menu.php b/Zotlabs/Module/Menu.php index 0fdb398bd..8879a448c 100644 --- a/Zotlabs/Module/Menu.php +++ b/Zotlabs/Module/Menu.php @@ -1,4 +1,5 @@ 1 && argv(1) === 'sys' && is_site_admin()) { - $sys = get_sys_channel(); - if($sys && intval($sys['channel_id'])) { - \App::$is_sys = true; - } - } + if (argc() > 1 && argv(1) === 'sys' && is_site_admin()) { + $sys = get_sys_channel(); + if ($sys && intval($sys['channel_id'])) { + App::$is_sys = true; + } + } - if(argc() > 1) - $which = argv(1); - else - return; + if (argc() > 1) { + $which = argv(1); + } else { + return; + } - Libprofile::load($which); - - } - - - function post() { - - if(! \App::$profile) { - return; - } - - $which = argv(1); + Libprofile::load($which); + } - $uid = \App::$profile['channel_id']; - - if(array_key_exists('sys', $_REQUEST) && $_REQUEST['sys'] && is_site_admin()) { - $sys = get_sys_channel(); - $uid = intval($sys['channel_id']); - \App::$is_sys = true; - } - - if(! $uid) - return; - - $_REQUEST['menu_channel_id'] = $uid; - - if($_REQUEST['menu_bookmark']) - $_REQUEST['menu_flags'] |= MENU_BOOKMARK; - if($_REQUEST['menu_system']) - $_REQUEST['menu_flags'] |= MENU_SYSTEM; - - $menu_id = ((argc() > 1) ? intval(argv(1)) : 0); - if($menu_id) { - $_REQUEST['menu_id'] = intval(argv(1)); - $r = menu_edit($_REQUEST); - if($r) { - menu_sync_packet($uid,get_observer_hash(),$menu_id); - //info( t('Menu updated.') . EOL); - goaway(z_root() . '/mitem/' . $which . '/' . $menu_id . ((\App::$is_sys) ? '?f=&sys=1' : '')); - } - else - notice( t('Unable to update menu.'). EOL); - } - else { - $r = menu_create($_REQUEST); - if($r) { - menu_sync_packet($uid,get_observer_hash(),$r); - - //info( t('Menu created.') . EOL); - goaway(z_root() . '/mitem/' . $which . '/' . $r . ((\App::$is_sys) ? '?f=&sys=1' : '')); - } - else - notice( t('Unable to create menu.'). EOL); - - } - } - - - - - function get() { - + public function post() + { + + if (!App::$profile) { + return; + } + + $which = argv(1); - if(! \App::$profile) { - notice( t('Requested profile is not available.') . EOL ); - \App::$error = 404; - return; - } + $uid = App::$profile['channel_id']; - $which = argv(1); + if (array_key_exists('sys', $_REQUEST) && $_REQUEST['sys'] && is_site_admin()) { + $sys = get_sys_channel(); + $uid = intval($sys['channel_id']); + App::$is_sys = true; + } - $_SESSION['return_url'] = \App::$query_string; + if (!$uid) { + return; + } - $uid = local_channel(); - $owner = 0; - $channel = null; - $observer = \App::get_observer(); + $_REQUEST['menu_channel_id'] = $uid; - $channel = \App::get_channel(); + if ($_REQUEST['menu_bookmark']) { + $_REQUEST['menu_flags'] |= MENU_BOOKMARK; + } + if ($_REQUEST['menu_system']) { + $_REQUEST['menu_flags'] |= MENU_SYSTEM; + } - if(\App::$is_sys && is_site_admin()) { - $sys = get_sys_channel(); - if($sys && intval($sys['channel_id'])) { - $uid = $owner = intval($sys['channel_id']); - $channel = $sys; - $observer = $sys; - } - } + $menu_id = ((argc() > 1) ? intval(argv(1)) : 0); + if ($menu_id) { + $_REQUEST['menu_id'] = intval(argv(1)); + $r = menu_edit($_REQUEST); + if ($r) { + menu_sync_packet($uid, get_observer_hash(), $menu_id); + //info( t('Menu updated.') . EOL); + goaway(z_root() . '/mitem/' . $which . '/' . $menu_id . ((App::$is_sys) ? '?f=&sys=1' : '')); + } else { + notice(t('Unable to update menu.') . EOL); + } + } else { + $r = menu_create($_REQUEST); + if ($r) { + menu_sync_packet($uid, get_observer_hash(), $r); - if(! $owner) { - // Figure out who the page owner is. - $r = channelx_by_nick($which); - if($r) { - $owner = intval($r['channel_id']); - } - } + //info( t('Menu created.') . EOL); + goaway(z_root() . '/mitem/' . $which . '/' . $r . ((App::$is_sys) ? '?f=&sys=1' : '')); + } else { + notice(t('Unable to create menu.') . EOL); + } + } + } - $ob_hash = (($observer) ? $observer['xchan_hash'] : ''); - $perms = get_all_perms($owner,$ob_hash); + public function get() + { - if(! $perms['write_pages']) { - notice( t('Permission denied.') . EOL); - return; - } - // Get the observer, check their permissions + if (!App::$profile) { + notice(t('Requested profile is not available.') . EOL); + App::$error = 404; + return; + } - $ob_hash = (($observer) ? $observer['xchan_hash'] : ''); + $which = argv(1); - $perms = get_all_perms($owner,$ob_hash); + $_SESSION['return_url'] = App::$query_string; - if(! $perms['write_pages']) { - notice( t('Permission denied.') . EOL); - return; - } + $uid = local_channel(); + $owner = 0; + $channel = null; + $observer = App::get_observer(); - if(argc() == 2) { - - $channel = (($sys) ? $sys : channelx_by_n($owner)); - - // list menus - $x = menu_list($owner); - if($x) { - for($y = 0; $y < count($x); $y ++) { - $m = menu_fetch($x[$y]['menu_name'],$owner,get_observer_hash()); - if($m) - $x[$y]['element'] = '[element]' . base64url_encode(json_encode(menu_element($channel,$m))) . '[/element]'; - $x[$y]['bookmark'] = (($x[$y]['menu_flags'] & MENU_BOOKMARK) ? true : false); - } - } - - $create = replace_macros(get_markup_template('menuedit.tpl'), array( - '$menu_name' => array('menu_name', t('Menu Name'), '', t('Unique name (not visible on webpage) - required'), '*'), - '$menu_desc' => array('menu_desc', t('Menu Title'), '', t('Visible on webpage - leave empty for no title'), ''), - '$menu_bookmark' => array('menu_bookmark', t('Allow Bookmarks'), 0 , t('Menu may be used to store saved bookmarks'), array(t('No'), t('Yes'))), - '$submit' => t('Submit and proceed'), - '$sys' => \App::$is_sys, - '$nick' => $which, - '$display' => 'none' - )); - - $o = replace_macros(get_markup_template('menulist.tpl'),array( - '$title' => t('Menus'), - '$create' => $create, - '$menus' => $x, - '$nametitle' => t('Menu Name'), - '$desctitle' => t('Menu Title'), - '$edit' => t('Edit'), - '$drop' => t('Drop'), - '$created' => t('Created'), - '$edited' => t('Edited'), - '$new' => t('New'), - '$bmark' => t('Bookmarks allowed'), - '$hintnew' => t('Create'), - '$hintdrop' => t('Delete this menu'), - '$hintcontent' => t('Edit menu contents'), - '$hintedit' => t('Edit this menu'), - '$nick' => $which, - '$sys' => \App::$is_sys - )); - - return $o; - - } - - if(argc() > 2) { - if(intval(argv(2))) { - - if(argc() == 4 && argv(3) == 'drop') { - menu_sync_packet($owner,get_observer_hash(),intval(argv(1)),true); - $r = menu_delete_id(intval(argv(2)),$owner); - if(!$r) - notice( t('Menu could not be deleted.'). EOL); - - goaway(z_root() . '/menu/' . $which . ((\App::$is_sys) ? '?f=&sys=1' : '')); - } - - $m = menu_fetch_id(intval(argv(2)),$owner); - - if(! $m) { - notice( t('Menu not found.') . EOL); - return ''; - } - - $o = replace_macros(get_markup_template('menuedit.tpl'), array( - '$header' => t('Edit Menu'), - '$sys' => \App::$is_sys, - '$menu_id' => intval(argv(2)), - '$menu_edit_link' => 'mitem/' . $which . '/' . intval(argv(1)) . ((\App::$is_sys) ? '?f=&sys=1' : ''), - '$hintedit' => t('Add or remove entries to this menu'), - '$editcontents' => t('Edit menu contents'), - '$menu_name' => array('menu_name', t('Menu name'), $m['menu_name'], t('Must be unique, only seen by you'), '*'), - '$menu_desc' => array('menu_desc', t('Menu title'), $m['menu_desc'], t('Menu title as seen by others'), ''), - '$menu_bookmark' => array('menu_bookmark', t('Allow bookmarks'), (($m['menu_flags'] & MENU_BOOKMARK) ? 1 : 0), t('Menu may be used to store saved bookmarks'), array(t('No'), t('Yes'))), - '$menu_system' => (($m['menu_flags'] & MENU_SYSTEM) ? 1 : 0), - '$nick' => $which, - '$submit' => t('Submit and proceed') - )); - - return $o; - - } - else { - notice( t('Not found.') . EOL); - return; - } - } - - } - + $channel = App::get_channel(); + + if (App::$is_sys && is_site_admin()) { + $sys = get_sys_channel(); + if ($sys && intval($sys['channel_id'])) { + $uid = $owner = intval($sys['channel_id']); + $channel = $sys; + $observer = $sys; + } + } + + if (!$owner) { + // Figure out who the page owner is. + $r = channelx_by_nick($which); + if ($r) { + $owner = intval($r['channel_id']); + } + } + + $ob_hash = (($observer) ? $observer['xchan_hash'] : ''); + + $perms = get_all_perms($owner, $ob_hash); + + if (!$perms['write_pages']) { + notice(t('Permission denied.') . EOL); + return; + } + + // Get the observer, check their permissions + + $ob_hash = (($observer) ? $observer['xchan_hash'] : ''); + + $perms = get_all_perms($owner, $ob_hash); + + if (!$perms['write_pages']) { + notice(t('Permission denied.') . EOL); + return; + } + + if (argc() == 2) { + $channel = (($sys) ? $sys : channelx_by_n($owner)); + + // list menus + $x = menu_list($owner); + if ($x) { + for ($y = 0; $y < count($x); $y++) { + $m = menu_fetch($x[$y]['menu_name'], $owner, get_observer_hash()); + if ($m) { + $x[$y]['element'] = '[element]' . base64url_encode(json_encode(menu_element($channel, $m))) . '[/element]'; + } + $x[$y]['bookmark'] = (($x[$y]['menu_flags'] & MENU_BOOKMARK) ? true : false); + } + } + + $create = replace_macros(get_markup_template('menuedit.tpl'), array( + '$menu_name' => array('menu_name', t('Menu Name'), '', t('Unique name (not visible on webpage) - required'), '*'), + '$menu_desc' => array('menu_desc', t('Menu Title'), '', t('Visible on webpage - leave empty for no title'), ''), + '$menu_bookmark' => array('menu_bookmark', t('Allow Bookmarks'), 0, t('Menu may be used to store saved bookmarks'), array(t('No'), t('Yes'))), + '$submit' => t('Submit and proceed'), + '$sys' => App::$is_sys, + '$nick' => $which, + '$display' => 'none' + )); + + $o = replace_macros(get_markup_template('menulist.tpl'), array( + '$title' => t('Menus'), + '$create' => $create, + '$menus' => $x, + '$nametitle' => t('Menu Name'), + '$desctitle' => t('Menu Title'), + '$edit' => t('Edit'), + '$drop' => t('Drop'), + '$created' => t('Created'), + '$edited' => t('Edited'), + '$new' => t('New'), + '$bmark' => t('Bookmarks allowed'), + '$hintnew' => t('Create'), + '$hintdrop' => t('Delete this menu'), + '$hintcontent' => t('Edit menu contents'), + '$hintedit' => t('Edit this menu'), + '$nick' => $which, + '$sys' => App::$is_sys + )); + + return $o; + } + + if (argc() > 2) { + if (intval(argv(2))) { + if (argc() == 4 && argv(3) == 'drop') { + menu_sync_packet($owner, get_observer_hash(), intval(argv(1)), true); + $r = menu_delete_id(intval(argv(2)), $owner); + if (!$r) { + notice(t('Menu could not be deleted.') . EOL); + } + + goaway(z_root() . '/menu/' . $which . ((App::$is_sys) ? '?f=&sys=1' : '')); + } + + $m = menu_fetch_id(intval(argv(2)), $owner); + + if (!$m) { + notice(t('Menu not found.') . EOL); + return ''; + } + + $o = replace_macros(get_markup_template('menuedit.tpl'), array( + '$header' => t('Edit Menu'), + '$sys' => App::$is_sys, + '$menu_id' => intval(argv(2)), + '$menu_edit_link' => 'mitem/' . $which . '/' . intval(argv(1)) . ((App::$is_sys) ? '?f=&sys=1' : ''), + '$hintedit' => t('Add or remove entries to this menu'), + '$editcontents' => t('Edit menu contents'), + '$menu_name' => array('menu_name', t('Menu name'), $m['menu_name'], t('Must be unique, only seen by you'), '*'), + '$menu_desc' => array('menu_desc', t('Menu title'), $m['menu_desc'], t('Menu title as seen by others'), ''), + '$menu_bookmark' => array('menu_bookmark', t('Allow bookmarks'), (($m['menu_flags'] & MENU_BOOKMARK) ? 1 : 0), t('Menu may be used to store saved bookmarks'), array(t('No'), t('Yes'))), + '$menu_system' => (($m['menu_flags'] & MENU_SYSTEM) ? 1 : 0), + '$nick' => $which, + '$submit' => t('Submit and proceed') + )); + + return $o; + } else { + notice(t('Not found.') . EOL); + return; + } + } + } } diff --git a/Zotlabs/Module/Mitem.php b/Zotlabs/Module/Mitem.php index 5aadc790f..e0df97a52 100644 --- a/Zotlabs/Module/Mitem.php +++ b/Zotlabs/Module/Mitem.php @@ -1,7 +1,9 @@ 1 && argv(1) === 'sys' && is_site_admin()) { - $sys = get_sys_channel(); - if($sys && intval($sys['channel_id'])) { - \App::$is_sys = true; - } - } + if (argc() > 1 && argv(1) === 'sys' && is_site_admin()) { + $sys = get_sys_channel(); + if ($sys && intval($sys['channel_id'])) { + App::$is_sys = true; + } + } - if(argc() > 1) - $which = argv(1); - else - return; + if (argc() > 1) { + $which = argv(1); + } else { + return; + } - Libprofile::load($which); - - if(argc() < 3) - return; - - $m = menu_fetch_id(intval(argv(2)),\App::$profile['channel_id']); - if(! $m) { - notice( t('Menu not found.') . EOL); - return ''; - } - \App::$data['menu'] = $m; - - } - - function post() { - - if(! \App::$profile) { - return; - } + Libprofile::load($which); - $which = argv(1); + if (argc() < 3) { + return; + } + + $m = menu_fetch_id(intval(argv(2)), App::$profile['channel_id']); + if (!$m) { + notice(t('Menu not found.') . EOL); + return ''; + } + App::$data['menu'] = $m; + } + + public function post() + { + + if (!App::$profile) { + return; + } + + $which = argv(1); - $uid = \App::$profile['channel_id']; - - if(array_key_exists('sys', $_REQUEST) && $_REQUEST['sys'] && is_site_admin()) { - $sys = get_sys_channel(); - $uid = intval($sys['channel_id']); - \App::$is_sys = true; - } - - if(! $uid) - return; + $uid = App::$profile['channel_id']; + + if (array_key_exists('sys', $_REQUEST) && $_REQUEST['sys'] && is_site_admin()) { + $sys = get_sys_channel(); + $uid = intval($sys['channel_id']); + App::$is_sys = true; + } + + if (!$uid) { + return; + } - if(! \App::$data['menu']) - return; - - if(!$_REQUEST['mitem_desc'] || !$_REQUEST['mitem_link']) { - notice( t('Unable to create element.') . EOL); - return; - } - - $_REQUEST['mitem_channel_id'] = $uid; - $_REQUEST['menu_id'] = \App::$data['menu']['menu_id']; - - $_REQUEST['mitem_flags'] = 0; - if($_REQUEST['usezid']) - $_REQUEST['mitem_flags'] |= MENU_ITEM_ZID; - if($_REQUEST['newwin']) - $_REQUEST['mitem_flags'] |= MENU_ITEM_NEWWIN; - - - $mitem_id = ((argc() > 3) ? intval(argv(3)) : 0); - if($mitem_id) { - $_REQUEST['mitem_id'] = $mitem_id; - $r = menu_edit_item($_REQUEST['menu_id'],$uid,$_REQUEST); - if($r) { - menu_sync_packet($uid,get_observer_hash(),$_REQUEST['menu_id']); - //info( t('Menu element updated.') . EOL); - goaway(z_root() . '/mitem/' . $which . '/' . $_REQUEST['menu_id'] . ((\App::$is_sys) ? '?f=&sys=1' : '')); - } - else - notice( t('Unable to update menu element.') . EOL); - - } - else { - $r = menu_add_item($_REQUEST['menu_id'],$uid,$_REQUEST); - if($r) { - menu_sync_packet($uid,get_observer_hash(),$_REQUEST['menu_id']); - //info( t('Menu element added.') . EOL); - if($_REQUEST['submit']) { - goaway(z_root() . '/menu/' . $which . ((\App::$is_sys) ? '?f=&sys=1' : '')); - } - if($_REQUEST['submit-more']) { - goaway(z_root() . '/mitem/' . $which . '/' . $_REQUEST['menu_id'] . '?f=&display=block' . ((\App::$is_sys) ? '&sys=1' : '') ); - } - } - else - notice( t('Unable to add menu element.') . EOL); - - } - - } - - - function get() { - - $uid = local_channel(); - $owner = \App::$profile['channel_id']; - $channel = channelx_by_n($owner); - $observer = \App::get_observer(); + if (!App::$data['menu']) { + return; + } - $which = argv(1); + if (!$_REQUEST['mitem_desc'] || !$_REQUEST['mitem_link']) { + notice(t('Unable to create element.') . EOL); + return; + } - $ob_hash = (($observer) ? $observer['xchan_hash'] : ''); - - if(\App::$is_sys && is_site_admin()) { - $sys = get_sys_channel(); - $uid = intval($sys['channel_id']); - $channel = $sys; - $ob_hash = $sys['xchan_hash']; - } - - if(! $uid) { - notice( t('Permission denied.') . EOL); - return ''; - } - - if(argc() < 3 || (! \App::$data['menu'])) { - notice( t('Not found.') . EOL); - return ''; - } - - $m = menu_fetch(\App::$data['menu']['menu_name'],$owner,$ob_hash); - \App::$data['menu_item'] = $m; - - $menu_list = menu_list($owner); - - foreach($menu_list as $menus) { - if($menus['menu_name'] != $m['menu']['menu_name']) - $menu_names[] = $menus['menu_name']; - } - - $acl = new \Zotlabs\Access\AccessControl($channel); - - $lockstate = (($channel['channel_allow_cid'] || $channel['channel_allow_gid'] || $channel['channel_deny_cid'] || $channel['channel_deny_gid']) ? 'lock' : 'unlock'); - - if(argc() == 3) { - $r = q("select * from menu_item where mitem_menu_id = %d and mitem_channel_id = %d order by mitem_order asc, mitem_desc asc", - intval(\App::$data['menu']['menu_id']), - intval($owner) - ); - - if($_GET['display']) { - $display = $_GET['display']; - } - else { - $display = (($r) ? 'none' : 'block'); - } + $_REQUEST['mitem_channel_id'] = $uid; + $_REQUEST['menu_id'] = App::$data['menu']['menu_id']; - $create = replace_macros(get_markup_template('mitemedit.tpl'), array( - '$menu_id' => \App::$data['menu']['menu_id'], - '$permissions' => t('Menu Item Permissions'), - '$permdesc' => t("\x28click to open/close\x29"), - '$aclselect' => populate_acl($acl->get(),false), - '$allow_cid' => acl2json($acl->get()['allow_cid']), - '$allow_gid' => acl2json($acl->get()['allow_gid']), - '$deny_cid' => acl2json($acl->get()['deny_cid']), - '$deny_gid' => acl2json($acl->get()['deny_gid']), - '$mitem_desc' => array('mitem_desc', t('Link Name'), '', 'Visible name of the link','*'), - '$mitem_link' => array('mitem_link', t('Link or Submenu Target'), '', t('Enter URL of the link or select a menu name to create a submenu'), '*', 'list="menu-names"'), - '$usezid' => array('usezid', t('Use magic-auth if available'), true, '', array(t('No'), t('Yes'))), - '$newwin' => array('newwin', t('Open link in new window'), false,'', array(t('No'), t('Yes'))), - '$mitem_order' => array('mitem_order', t('Order in list'),'0',t('Higher numbers will sink to bottom of listing')), - '$submit' => t('Submit and finish'), - '$submit_more' => t('Submit and continue'), - '$display' => $display, - '$lockstate' => $lockstate, - '$menu_names' => $menu_names, - '$nick' => $which, - '$sys' => \App::$is_sys - )); - - $o .= replace_macros(get_markup_template('mitemlist.tpl'),array( - '$title' => t('Menu:'), - '$create' => $create, - '$nametitle' => t('Link Name'), - '$targettitle' => t('Link Target'), - '$menuname' => \App::$data['menu']['menu_name'], - '$menudesc' => \App::$data['menu']['menu_desc'], - '$edmenu' => t('Edit menu'), - '$menu_id' => \App::$data['menu']['menu_id'], - '$mlist' => $r, - '$edit' => t('Edit element'), - '$drop' => t('Drop element'), - '$new' => t('New element'), - '$hintmenu' => t('Edit this menu container'), - '$hintnew' => t('Add menu element'), - '$hintdrop' => t('Delete this menu item'), - '$hintedit' => t('Edit this menu item'), - '$nick' => $which, - )); - - return $o; - } - - - if(argc() > 3) { + $_REQUEST['mitem_flags'] = 0; + if ($_REQUEST['usezid']) { + $_REQUEST['mitem_flags'] |= MENU_ITEM_ZID; + } + if ($_REQUEST['newwin']) { + $_REQUEST['mitem_flags'] |= MENU_ITEM_NEWWIN; + } - if(intval(argv(3))) { - - $m = q("select * from menu_item where mitem_id = %d and mitem_channel_id = %d limit 1", - intval(argv(3)), - intval($owner) - ); - - if(! $m) { - notice( t('Menu item not found.') . EOL); - goaway(z_root() . '/menu/'. $which . ((\App::$is_sys) ? '?f=&sys=1' : '')); - } - - $mitem = $m[0]; - - $lockstate = (($mitem['allow_cid'] || $mitem['allow_gid'] || $mitem['deny_cid'] || $mitem['deny_gid']) ? 'lock' : 'unlock'); - - if(argc() == 5 && argv(4) == 'drop') { - menu_sync_packet($owner,get_observer_hash(),$mitem['mitem_menu_id']); - $r = menu_del_item($mitem['mitem_menu_id'], $owner, intval(argv(3))); - menu_sync_packet($owner,get_observer_hash(),$mitem['mitem_menu_id']); - if($r) - info( t('Menu item deleted.') . EOL); - else - notice( t('Menu item could not be deleted.'). EOL); - - goaway(z_root() . '/mitem/' . $which . '/' . $mitem['mitem_menu_id'] . ((\App::$is_sys) ? '?f=&sys=1' : '')); - } - - // edit menu item - $o = replace_macros(get_markup_template('mitemedit.tpl'), array( - '$header' => t('Edit Menu Element'), - '$menu_id' => \App::$data['menu']['menu_id'], - '$permissions' => t('Menu Item Permissions'), - '$permdesc' => t("\x28click to open/close\x29"), - '$aclselect' => populate_acl($mitem,false), - '$allow_cid' => acl2json($mitem['allow_cid']), - '$allow_gid' => acl2json($mitem['allow_gid']), - '$deny_cid' => acl2json($mitem['deny_cid']), - '$deny_gid' => acl2json($mitem['deny_gid']), - '$mitem_id' => intval(argv(3)), - '$mitem_desc' => array('mitem_desc', t('Link text'), $mitem['mitem_desc'], '','*'), - '$mitem_link' => array('mitem_link', t('Link or Submenu Target'), $mitem['mitem_link'], 'Enter URL of the link or select a menu name to create a submenu', '*', 'list="menu-names"'), - '$usezid' => array('usezid', t('Use magic-auth if available'), (($mitem['mitem_flags'] & MENU_ITEM_ZID) ? 1 : 0), '', array(t('No'), t('Yes'))), - '$newwin' => array('newwin', t('Open link in new window'), (($mitem['mitem_flags'] & MENU_ITEM_NEWWIN) ? 1 : 0),'', array(t('No'), t('Yes'))), - '$mitem_order' => array('mitem_order', t('Order in list'),$mitem['mitem_order'],t('Higher numbers will sink to bottom of listing')), - '$submit' => t('Submit'), - '$lockstate' => $lockstate, - '$menu_names' => $menu_names, - '$nick' => $which - )); - - return $o; - } - } - } - + + $mitem_id = ((argc() > 3) ? intval(argv(3)) : 0); + if ($mitem_id) { + $_REQUEST['mitem_id'] = $mitem_id; + $r = menu_edit_item($_REQUEST['menu_id'], $uid, $_REQUEST); + if ($r) { + menu_sync_packet($uid, get_observer_hash(), $_REQUEST['menu_id']); + //info( t('Menu element updated.') . EOL); + goaway(z_root() . '/mitem/' . $which . '/' . $_REQUEST['menu_id'] . ((App::$is_sys) ? '?f=&sys=1' : '')); + } else { + notice(t('Unable to update menu element.') . EOL); + } + } else { + $r = menu_add_item($_REQUEST['menu_id'], $uid, $_REQUEST); + if ($r) { + menu_sync_packet($uid, get_observer_hash(), $_REQUEST['menu_id']); + //info( t('Menu element added.') . EOL); + if ($_REQUEST['submit']) { + goaway(z_root() . '/menu/' . $which . ((App::$is_sys) ? '?f=&sys=1' : '')); + } + if ($_REQUEST['submit-more']) { + goaway(z_root() . '/mitem/' . $which . '/' . $_REQUEST['menu_id'] . '?f=&display=block' . ((App::$is_sys) ? '&sys=1' : '')); + } + } else { + notice(t('Unable to add menu element.') . EOL); + } + } + } + + + public function get() + { + + $uid = local_channel(); + $owner = App::$profile['channel_id']; + $channel = channelx_by_n($owner); + $observer = App::get_observer(); + + $which = argv(1); + + $ob_hash = (($observer) ? $observer['xchan_hash'] : ''); + + if (App::$is_sys && is_site_admin()) { + $sys = get_sys_channel(); + $uid = intval($sys['channel_id']); + $channel = $sys; + $ob_hash = $sys['xchan_hash']; + } + + if (!$uid) { + notice(t('Permission denied.') . EOL); + return ''; + } + + if (argc() < 3 || (!App::$data['menu'])) { + notice(t('Not found.') . EOL); + return ''; + } + + $m = menu_fetch(App::$data['menu']['menu_name'], $owner, $ob_hash); + App::$data['menu_item'] = $m; + + $menu_list = menu_list($owner); + + foreach ($menu_list as $menus) { + if ($menus['menu_name'] != $m['menu']['menu_name']) { + $menu_names[] = $menus['menu_name']; + } + } + + $acl = new AccessControl($channel); + + $lockstate = (($channel['channel_allow_cid'] || $channel['channel_allow_gid'] || $channel['channel_deny_cid'] || $channel['channel_deny_gid']) ? 'lock' : 'unlock'); + + if (argc() == 3) { + $r = q( + "select * from menu_item where mitem_menu_id = %d and mitem_channel_id = %d order by mitem_order asc, mitem_desc asc", + intval(App::$data['menu']['menu_id']), + intval($owner) + ); + + if ($_GET['display']) { + $display = $_GET['display']; + } else { + $display = (($r) ? 'none' : 'block'); + } + + $create = replace_macros(get_markup_template('mitemedit.tpl'), array( + '$menu_id' => App::$data['menu']['menu_id'], + '$permissions' => t('Menu Item Permissions'), + '$permdesc' => t("\x28click to open/close\x29"), + '$aclselect' => populate_acl($acl->get(), false), + '$allow_cid' => acl2json($acl->get()['allow_cid']), + '$allow_gid' => acl2json($acl->get()['allow_gid']), + '$deny_cid' => acl2json($acl->get()['deny_cid']), + '$deny_gid' => acl2json($acl->get()['deny_gid']), + '$mitem_desc' => array('mitem_desc', t('Link Name'), '', 'Visible name of the link', '*'), + '$mitem_link' => array('mitem_link', t('Link or Submenu Target'), '', t('Enter URL of the link or select a menu name to create a submenu'), '*', 'list="menu-names"'), + '$usezid' => array('usezid', t('Use magic-auth if available'), true, '', array(t('No'), t('Yes'))), + '$newwin' => array('newwin', t('Open link in new window'), false, '', array(t('No'), t('Yes'))), + '$mitem_order' => array('mitem_order', t('Order in list'), '0', t('Higher numbers will sink to bottom of listing')), + '$submit' => t('Submit and finish'), + '$submit_more' => t('Submit and continue'), + '$display' => $display, + '$lockstate' => $lockstate, + '$menu_names' => $menu_names, + '$nick' => $which, + '$sys' => App::$is_sys + )); + + $o .= replace_macros(get_markup_template('mitemlist.tpl'), array( + '$title' => t('Menu:'), + '$create' => $create, + '$nametitle' => t('Link Name'), + '$targettitle' => t('Link Target'), + '$menuname' => App::$data['menu']['menu_name'], + '$menudesc' => App::$data['menu']['menu_desc'], + '$edmenu' => t('Edit menu'), + '$menu_id' => App::$data['menu']['menu_id'], + '$mlist' => $r, + '$edit' => t('Edit element'), + '$drop' => t('Drop element'), + '$new' => t('New element'), + '$hintmenu' => t('Edit this menu container'), + '$hintnew' => t('Add menu element'), + '$hintdrop' => t('Delete this menu item'), + '$hintedit' => t('Edit this menu item'), + '$nick' => $which, + )); + + return $o; + } + + + if (argc() > 3) { + if (intval(argv(3))) { + $m = q( + "select * from menu_item where mitem_id = %d and mitem_channel_id = %d limit 1", + intval(argv(3)), + intval($owner) + ); + + if (!$m) { + notice(t('Menu item not found.') . EOL); + goaway(z_root() . '/menu/' . $which . ((App::$is_sys) ? '?f=&sys=1' : '')); + } + + $mitem = $m[0]; + + $lockstate = (($mitem['allow_cid'] || $mitem['allow_gid'] || $mitem['deny_cid'] || $mitem['deny_gid']) ? 'lock' : 'unlock'); + + if (argc() == 5 && argv(4) == 'drop') { + menu_sync_packet($owner, get_observer_hash(), $mitem['mitem_menu_id']); + $r = menu_del_item($mitem['mitem_menu_id'], $owner, intval(argv(3))); + menu_sync_packet($owner, get_observer_hash(), $mitem['mitem_menu_id']); + if ($r) { + info(t('Menu item deleted.') . EOL); + } else { + notice(t('Menu item could not be deleted.') . EOL); + } + + goaway(z_root() . '/mitem/' . $which . '/' . $mitem['mitem_menu_id'] . ((App::$is_sys) ? '?f=&sys=1' : '')); + } + + // edit menu item + $o = replace_macros(get_markup_template('mitemedit.tpl'), array( + '$header' => t('Edit Menu Element'), + '$menu_id' => App::$data['menu']['menu_id'], + '$permissions' => t('Menu Item Permissions'), + '$permdesc' => t("\x28click to open/close\x29"), + '$aclselect' => populate_acl($mitem, false), + '$allow_cid' => acl2json($mitem['allow_cid']), + '$allow_gid' => acl2json($mitem['allow_gid']), + '$deny_cid' => acl2json($mitem['deny_cid']), + '$deny_gid' => acl2json($mitem['deny_gid']), + '$mitem_id' => intval(argv(3)), + '$mitem_desc' => array('mitem_desc', t('Link text'), $mitem['mitem_desc'], '', '*'), + '$mitem_link' => array('mitem_link', t('Link or Submenu Target'), $mitem['mitem_link'], 'Enter URL of the link or select a menu name to create a submenu', '*', 'list="menu-names"'), + '$usezid' => array('usezid', t('Use magic-auth if available'), (($mitem['mitem_flags'] & MENU_ITEM_ZID) ? 1 : 0), '', array(t('No'), t('Yes'))), + '$newwin' => array('newwin', t('Open link in new window'), (($mitem['mitem_flags'] & MENU_ITEM_NEWWIN) ? 1 : 0), '', array(t('No'), t('Yes'))), + '$mitem_order' => array('mitem_order', t('Order in list'), $mitem['mitem_order'], t('Higher numbers will sink to bottom of listing')), + '$submit' => t('Submit'), + '$lockstate' => $lockstate, + '$menu_names' => $menu_names, + '$nick' => $which + )); + + return $o; + } + } + } } diff --git a/Zotlabs/Module/Moderate.php b/Zotlabs/Module/Moderate.php index fa80bf8c3..3397991fe 100644 --- a/Zotlabs/Module/Moderate.php +++ b/Zotlabs/Module/Moderate.php @@ -10,115 +10,118 @@ use Zotlabs\Daemon\Run; require_once('include/conversation.php'); -class Moderate extends Controller { +class Moderate extends Controller +{ - function get() { - if(! local_channel()) { - notice( t('Permission denied.') . EOL); - return; - } + public function get() + { + if (!local_channel()) { + notice(t('Permission denied.') . EOL); + return; + } - App::set_pager_itemspage(60); - $pager_sql = sprintf(" LIMIT %d OFFSET %d ", intval(App::$pager['itemspage']), intval(App::$pager['start'])); + App::set_pager_itemspage(60); + $pager_sql = sprintf(" LIMIT %d OFFSET %d ", intval(App::$pager['itemspage']), intval(App::$pager['start'])); - //show all items - if(argc() == 1) { - $r = q("select item.id as item_id, item.* from item where item.uid = %d and item_blocked = %d and item_deleted = 0 order by created desc $pager_sql", - intval(local_channel()), - intval(ITEM_MODERATED) - ); - if(! $r) { - info( t('No entries.') . EOL); - } + //show all items + if (argc() == 1) { + $r = q( + "select item.id as item_id, item.* from item where item.uid = %d and item_blocked = %d and item_deleted = 0 order by created desc $pager_sql", + intval(local_channel()), + intval(ITEM_MODERATED) + ); + if (!$r) { + info(t('No entries.') . EOL); + } + } - } + // show a single item + if (argc() == 2) { + $post_id = unpack_link_id(escape_tags(argv(1))); - // show a single item - if(argc() == 2) { - $post_id = unpack_link_id(escape_tags(argv(1))); + $r = q( + "select item.id as item_id, item.* from item where item.mid = '%s' and item.uid = %d and item_blocked = %d and item_deleted = 0 order by created desc $pager_sql", + dbesc($post_id), + intval(local_channel()), + intval(ITEM_MODERATED) + ); + } - $r = q("select item.id as item_id, item.* from item where item.mid = '%s' and item.uid = %d and item_blocked = %d and item_deleted = 0 order by created desc $pager_sql", - dbesc($post_id), - intval(local_channel()), - intval(ITEM_MODERATED) - ); - } + if (argc() > 2) { + $post_id = intval(argv(1)); + if (!$post_id) { + goaway(z_root() . '/moderate'); + } - if(argc() > 2) { - $post_id = intval(argv(1)); - if(! $post_id) - goaway(z_root() . '/moderate'); + $action = argv(2); - $action = argv(2); + $r = q( + "select * from item where uid = %d and id = %d and item_blocked = %d limit 1", + intval(local_channel()), + intval($post_id), + intval(ITEM_MODERATED) + ); - $r = q("select * from item where uid = %d and id = %d and item_blocked = %d limit 1", - intval(local_channel()), - intval($post_id), - intval(ITEM_MODERATED) - ); + if ($r) { + $item = $r[0]; - if($r) { - $item = $r[0]; + if ($action === 'approve') { + q( + "update item set item_blocked = 0 where uid = %d and id = %d", + intval(local_channel()), + intval($post_id) + ); - if($action === 'approve') { - q("update item set item_blocked = 0 where uid = %d and id = %d", - intval(local_channel()), - intval($post_id) - ); + $item['item_blocked'] = 0; - $item['item_blocked'] = 0; + item_update_parent_commented($item); - item_update_parent_commented($item); + notice(t('Comment approved') . EOL); + } elseif ($action === 'drop') { + drop_item($post_id, false); + notice(t('Comment deleted') . EOL); + } - notice( t('Comment approved') . EOL); - } - elseif($action === 'drop') { - drop_item($post_id,false); - notice( t('Comment deleted') . EOL); - } + // refetch the item after changes have been made - // refetch the item after changes have been made - - $r = q("select * from item where id = %d", - intval($post_id) - ); - if($r) { - xchan_query($r); - $sync_item = fetch_post_tags($r); - Libsync::build_sync_packet(local_channel(),array('item' => array(encode_item($sync_item[0],true)))); - } - if($action === 'approve') { - if ($item['id'] !== $item['parent']) { - // if this is a group comment, call tag_deliver() to generate the associated - // Announce activity so microblog destinations will see it in their home timeline - $role = get_pconfig(local_channel(),'system','permissions_role'); - $rolesettings = PermissionRoles::role_perms($role); - $channel_type = isset($rolesettings['channel_type']) ? $rolesettings['channel_type'] : 'normal'; + $r = q( + "select * from item where id = %d", + intval($post_id) + ); + if ($r) { + xchan_query($r); + $sync_item = fetch_post_tags($r); + Libsync::build_sync_packet(local_channel(), array('item' => array(encode_item($sync_item[0], true)))); + } + if ($action === 'approve') { + if ($item['id'] !== $item['parent']) { + // if this is a group comment, call tag_deliver() to generate the associated + // Announce activity so microblog destinations will see it in their home timeline + $role = get_pconfig(local_channel(), 'system', 'permissions_role'); + $rolesettings = PermissionRoles::role_perms($role); + $channel_type = isset($rolesettings['channel_type']) ? $rolesettings['channel_type'] : 'normal'; - $is_group = (($channel_type === 'group') ? true : false); - if ($is_group) { - tag_deliver(local_channel(),$post_id); - } - } - Run::Summon( [ 'Notifier', 'comment-new', $post_id ] ); - } - goaway(z_root() . '/moderate'); - } - } + $is_group = (($channel_type === 'group') ? true : false); + if ($is_group) { + tag_deliver(local_channel(), $post_id); + } + } + Run::Summon(['Notifier', 'comment-new', $post_id]); + } + goaway(z_root() . '/moderate'); + } + } - if($r) { - xchan_query($r); - $items = fetch_post_tags($r,true); - } - else { - $items = []; - } - - $o = conversation($items,'moderate',false,'traditional'); - $o .= alt_pager(count($items)); - return $o; - - } + if ($r) { + xchan_query($r); + $items = fetch_post_tags($r, true); + } else { + $items = []; + } + $o = conversation($items, 'moderate', false, 'traditional'); + $o .= alt_pager(count($items)); + return $o; + } } diff --git a/Zotlabs/Module/Mood.php b/Zotlabs/Module/Mood.php index aa759e8e5..d3e170ecb 100644 --- a/Zotlabs/Module/Mood.php +++ b/Zotlabs/Module/Mood.php @@ -1,4 +1,5 @@ Mood App (Not Installed):
      '; - $o .= t('Set your current mood and tell your friends'); - return $o; - } + $poster = App::get_observer(); - nav_set_selected('Mood'); + $uuid = new_uuid(); + $mid = z_root() . '/item/' . $uuid; - $parent = ((x($_GET,'parent')) ? intval($_GET['parent']) : '0'); - - $verbs = get_mood_verbs(); - - $shortlist = []; - foreach($verbs as $k => $v) - if($v !== 'NOTRANSLATION') - $shortlist[] = array($k,$v); - - - $tpl = get_markup_template('mood_content.tpl'); - - $o = replace_macros($tpl,array( - '$title' => t('Mood'), - '$desc' => t('Set your current mood and tell your friends'), - '$verbs' => $shortlist, - '$parent' => $parent, - '$submit' => t('Submit'), - )); - - return $o; - - } - + $action = sprintf(t('%1$s is %2$s', 'mood'), '[zrl=' . $poster['xchan_url'] . ']' . $poster['xchan_name'] . '[/zrl]', $verbs[$verb]); + + $arr = []; + + $arr['aid'] = get_account_id(); + $arr['uid'] = $uid; + $arr['uuid'] = $uuid; + $arr['mid'] = $mid; + $arr['parent_mid'] = (($parent_mid) ? $parent_mid : $mid); + $arr['author_xchan'] = $poster['xchan_hash']; + $arr['owner_xchan'] = (($parent_mid) ? $r[0]['owner_xchan'] : $poster['xchan_hash']); + $arr['title'] = ''; + $arr['allow_cid'] = $allow_cid; + $arr['allow_gid'] = $allow_gid; + $arr['deny_cid'] = $deny_cid; + $arr['deny_gid'] = $deny_gid; + $arr['item_private'] = $private; + $arr['verb'] = $activity; + $arr['body'] = $action; + $arr['item_origin'] = 1; + $arr['item_wall'] = 1; + $arr['item_unseen'] = 1; + if (!$parent_mid) { + $item['item_thread_top'] = 1; + } + + if ((!$arr['plink']) && intval($arr['item_thread_top'])) { + $arr['plink'] = z_root() . '/channel/' . $channel['channel_address'] . '/?f=&mid=' . urlencode($arr['mid']); + } + + + $post = item_store($arr); + $item_id = $post['item_id']; + + if ($item_id) { + Run::Summon(['Notifier', 'activity', $item_id]); + } + + call_hooks('post_local_end', $arr); + + if ($_SESSION['return_url']) { + goaway(z_root() . '/' . $_SESSION['return_url']); + } + + return; + } + + + public function get() + { + + if (!local_channel()) { + notice(t('Permission denied.') . EOL); + return; + } + + if (!Apps::system_app_installed(local_channel(), 'Mood')) { + //Do not display any associated widgets at this point + App::$pdl = ''; + + $o = 'Mood App (Not Installed):
      '; + $o .= t('Set your current mood and tell your friends'); + return $o; + } + + nav_set_selected('Mood'); + + $parent = ((x($_GET, 'parent')) ? intval($_GET['parent']) : '0'); + + $verbs = get_mood_verbs(); + + $shortlist = []; + foreach ($verbs as $k => $v) { + if ($v !== 'NOTRANSLATION') { + $shortlist[] = array($k, $v); + } + } + + + $tpl = get_markup_template('mood_content.tpl'); + + $o = replace_macros($tpl, array( + '$title' => t('Mood'), + '$desc' => t('Set your current mood and tell your friends'), + '$verbs' => $shortlist, + '$parent' => $parent, + '$submit' => t('Submit'), + )); + + return $o; + } } diff --git a/Zotlabs/Module/New_channel.php b/Zotlabs/Module/New_channel.php index 2a29bb380..4d1f4007a 100644 --- a/Zotlabs/Module/New_channel.php +++ b/Zotlabs/Module/New_channel.php @@ -1,205 +1,209 @@ 1) ? argv(1) : ''); - $cmd = ((argc() > 1) ? argv(1) : ''); - - if($cmd === 'autofill.json') { - $result = array('error' => false, 'message' => ''); - $n = trim($_REQUEST['name']); + if ($cmd === 'autofill.json') { + $result = array('error' => false, 'message' => ''); + $n = trim($_REQUEST['name']); - $x = false; + $x = false; - if(get_config('system','unicode_usernames')) { - $x = punify(mb_strtolower($n)); - } + if (get_config('system', 'unicode_usernames')) { + $x = punify(mb_strtolower($n)); + } - if((! $x) || strlen($x) > 64) - $x = strtolower(\URLify::transliterate($n)); - - $test = []; - - // first name - if(strpos($x,' ')) - $test[] = legal_webbie(substr($x,0,strpos($x,' '))); - if($test[0]) { - // first name plus first initial of last - $test[] = ((strpos($x,' ')) ? $test[0] . legal_webbie(trim(substr($x,strpos($x,' '),2))) : ''); - // first name plus random number - $test[] = $test[0] . mt_rand(1000,9999); - } - // fullname - $test[] = legal_webbie($x); - // fullname plus random number - $test[] = legal_webbie($x) . mt_rand(1000,9999); + if ((!$x) || strlen($x) > 64) { + $x = strtolower(URLify::transliterate($n)); + } - json_return_and_die(check_webbie($test)); - } - - if($cmd === 'checkaddr.json') { - $result = array('error' => false, 'message' => ''); - $n = trim($_REQUEST['nick']); - if(! $n) { - $n = trim($_REQUEST['name']); - } + $test = []; - $x = false; + // first name + if (strpos($x, ' ')) { + $test[] = legal_webbie(substr($x, 0, strpos($x, ' '))); + } + if ($test[0]) { + // first name plus first initial of last + $test[] = ((strpos($x, ' ')) ? $test[0] . legal_webbie(trim(substr($x, strpos($x, ' '), 2))) : ''); + // first name plus random number + $test[] = $test[0] . mt_rand(1000, 9999); + } + // fullname + $test[] = legal_webbie($x); + // fullname plus random number + $test[] = legal_webbie($x) . mt_rand(1000, 9999); - if(get_config('system','unicode_usernames')) { - $x = punify(mb_strtolower($n)); - } + json_return_and_die(check_webbie($test)); + } - if((! $x) || strlen($x) > 64) - $x = strtolower(\URLify::transliterate($n)); + if ($cmd === 'checkaddr.json') { + $result = array('error' => false, 'message' => ''); + $n = trim($_REQUEST['nick']); + if (!$n) { + $n = trim($_REQUEST['name']); + } + + $x = false; + + if (get_config('system', 'unicode_usernames')) { + $x = punify(mb_strtolower($n)); + } + + if ((!$x) || strlen($x) > 64) { + $x = strtolower(URLify::transliterate($n)); + } - $test = []; - - // first name - if(strpos($x,' ')) - $test[] = legal_webbie(substr($x,0,strpos($x,' '))); - if($test[0]) { - // first name plus first initial of last - $test[] = ((strpos($x,' ')) ? $test[0] . legal_webbie(trim(substr($x,strpos($x,' '),2))) : ''); - // first name plus random number - $test[] = $test[0] . mt_rand(1000,9999); - } + $test = []; - $n = legal_webbie($x); - if(strlen($n)) { - $test[] = $n; - $test[] = $n . mt_rand(1000,9999); - } - - for($y = 0; $y < 100; $y ++) - $test[] = 'id' . mt_rand(1000,9999); - - json_return_and_die(check_webbie($test)); - } - - - } - - function post() { - - $arr = $_POST; - - $acc = App::get_account(); - - if(local_channel()) { - $parent_channel = App::get_channel(); - if($parent_channel) { - $arr['parent_hash'] = $parent_channel['channel_hash']; - } - } + // first name + if (strpos($x, ' ')) { + $test[] = legal_webbie(substr($x, 0, strpos($x, ' '))); + } + if ($test[0]) { + // first name plus first initial of last + $test[] = ((strpos($x, ' ')) ? $test[0] . legal_webbie(trim(substr($x, strpos($x, ' '), 2))) : ''); + // first name plus random number + $test[] = $test[0] . mt_rand(1000, 9999); + } - $arr['account_id'] = get_account_id(); - - // prevent execution by delegated channels as well as those not logged in. - // get_account_id() returns the account_id from the session. But \App::$account - // may point to the original authenticated account. - - if((! $acc) || ($acc['account_id'] != $arr['account_id'])) { - notice( t('Permission denied.') . EOL ); - return; - } - - $result = create_identity($arr); - - if(! $result['success']) { - notice($result['message']); - return; - } - - $newuid = $result['channel']['channel_id']; - - change_channel($result['channel']['channel_id']); - - $next_page = get_config('system', 'workflow_channel_next', 'profiles'); - goaway(z_root() . '/' . $next_page); - - } - - function get() { - - $acc = App::get_account(); - - if((! $acc) || $acc['account_id'] != get_account_id()) { - notice( t('Permission denied.') . EOL); - return; - } - - $default_role = ''; - $aid = get_account_id(); - if($aid) { - $r = q("select count(channel_id) as total from channel where channel_account_id = %d", - intval($aid) - ); - if($r && (! intval($r[0]['total']))) { - $default_role = get_config('system','default_permissions_role','social'); - } - - $limit = account_service_class_fetch(get_account_id(),'total_identities'); - - if($r && ($limit !== false)) { - $channel_usage_message = sprintf( t("You have created %1$.0f of %2$.0f allowed channels."), $r[0]['total'], $limit); - } - else { - $channel_usage_message = ''; - } - } + $n = legal_webbie($x); + if (strlen($n)) { + $test[] = $n; + $test[] = $n . mt_rand(1000, 9999); + } - $name_help = ''; - $name_help .= (($default_role) - ? t('Your real name is recommended.') - : t('Examples: "Bob Jameson", "Lisa and her Horses", "Soccer", "Aviation Group"') - ); - $name_help .= ''; + for ($y = 0; $y < 100; $y++) { + $test[] = 'id' . mt_rand(1000, 9999); + } - $nick_help = ''; - $nick_help .= t('This will be used to create a unique network address (like an email address).'); - if(! get_config('system','unicode_usernames')) { - $nick_help .= ' ' . t('Allowed characters are a-z 0-9, - and _'); - } - $nick_help .= ''; + json_return_and_die(check_webbie($test)); + } + } - $privacy_role = ((x($_REQUEST,'permissions_role')) ? $_REQUEST['permissions_role'] : "" ); + public function post() + { - $perm_roles = \Zotlabs\Access\PermissionRoles::roles(); + $arr = $_POST; - $name = array('name', t('Channel name'), ((x($_REQUEST,'name')) ? $_REQUEST['name'] : ''), $name_help, "*"); - $nickhub = '@' . \App::get_hostname(); - $nickname = array('nickname', t('Choose a short nickname'), ((x($_REQUEST,'nickname')) ? $_REQUEST['nickname'] : ''), $nick_help, "*"); - $role = array('permissions_role' , t('Channel role and privacy'), ($privacy_role) ? $privacy_role : 'social', t('Select a channel permission role compatible with your usage needs and privacy requirements.'),$perm_roles); - - $o = replace_macros(get_markup_template('new_channel.tpl'), array( - '$title' => t('Create a Channel'), - '$desc' => t('A channel is a unique network identity. It can represent a person (social network profile), a forum (group), a business or celebrity page, a newsfeed, and many other things.') , - '$label_import' => t('or import an existing channel from another location.'), - '$name' => $name, - '$role' => $role, - '$default_role' => $default_role, - '$nickname' => $nickname, - '$validate' => t('Validate'), - '$submit' => t('Create'), - '$channel_usage_message' => $channel_usage_message - )); - - return $o; - - } - - + $acc = App::get_account(); + + if (local_channel()) { + $parent_channel = App::get_channel(); + if ($parent_channel) { + $arr['parent_hash'] = $parent_channel['channel_hash']; + } + } + + $arr['account_id'] = get_account_id(); + + // prevent execution by delegated channels as well as those not logged in. + // get_account_id() returns the account_id from the session. But \App::$account + // may point to the original authenticated account. + + if ((!$acc) || ($acc['account_id'] != $arr['account_id'])) { + notice(t('Permission denied.') . EOL); + return; + } + + $result = create_identity($arr); + + if (!$result['success']) { + notice($result['message']); + return; + } + + $newuid = $result['channel']['channel_id']; + + change_channel($result['channel']['channel_id']); + + $next_page = get_config('system', 'workflow_channel_next', 'profiles'); + goaway(z_root() . '/' . $next_page); + } + + public function get() + { + + $acc = App::get_account(); + + if ((!$acc) || $acc['account_id'] != get_account_id()) { + notice(t('Permission denied.') . EOL); + return; + } + + $default_role = ''; + $aid = get_account_id(); + if ($aid) { + $r = q( + "select count(channel_id) as total from channel where channel_account_id = %d", + intval($aid) + ); + if ($r && (!intval($r[0]['total']))) { + $default_role = get_config('system', 'default_permissions_role', 'social'); + } + + $limit = account_service_class_fetch(get_account_id(), 'total_identities'); + + if ($r && ($limit !== false)) { + $channel_usage_message = sprintf(t("You have created %1$.0f of %2$.0f allowed channels."), $r[0]['total'], $limit); + } else { + $channel_usage_message = ''; + } + } + + $name_help = ''; + $name_help .= (($default_role) + ? t('Your real name is recommended.') + : t('Examples: "Bob Jameson", "Lisa and her Horses", "Soccer", "Aviation Group"') + ); + $name_help .= ''; + + $nick_help = ''; + $nick_help .= t('This will be used to create a unique network address (like an email address).'); + if (!get_config('system', 'unicode_usernames')) { + $nick_help .= ' ' . t('Allowed characters are a-z 0-9, - and _'); + } + $nick_help .= ''; + + $privacy_role = ((x($_REQUEST, 'permissions_role')) ? $_REQUEST['permissions_role'] : ""); + + $perm_roles = PermissionRoles::roles(); + + $name = array('name', t('Channel name'), ((x($_REQUEST, 'name')) ? $_REQUEST['name'] : ''), $name_help, "*"); + $nickhub = '@' . App::get_hostname(); + $nickname = array('nickname', t('Choose a short nickname'), ((x($_REQUEST, 'nickname')) ? $_REQUEST['nickname'] : ''), $nick_help, "*"); + $role = array('permissions_role', t('Channel role and privacy'), ($privacy_role) ? $privacy_role : 'social', t('Select a channel permission role compatible with your usage needs and privacy requirements.'), $perm_roles); + + $o = replace_macros(get_markup_template('new_channel.tpl'), array( + '$title' => t('Create a Channel'), + '$desc' => t('A channel is a unique network identity. It can represent a person (social network profile), a forum (group), a business or celebrity page, a newsfeed, and many other things.'), + '$label_import' => t('or import an existing channel from another location.'), + '$name' => $name, + '$role' => $role, + '$default_role' => $default_role, + '$nickname' => $nickname, + '$validate' => t('Validate'), + '$submit' => t('Create'), + '$channel_usage_message' => $channel_usage_message + )); + + return $o; + } } diff --git a/Zotlabs/Module/Notes.php b/Zotlabs/Module/Notes.php index e52742857..5040d4e3b 100644 --- a/Zotlabs/Module/Notes.php +++ b/Zotlabs/Module/Notes.php @@ -1,52 +1,59 @@ true); - if(array_key_exists('note_text',$_REQUEST)) { - $body = escape_tags($_REQUEST['note_text']); - - // I've had my notes vanish into thin air twice in four years. - // Provide a backup copy if there were contents previously - // and there are none being saved now. - - if(! $body) { - $old_text = get_pconfig(local_channel(),'notes','text'); - if($old_text) - set_pconfig(local_channel(),'notes','text.bak',$old_text); - } - set_pconfig(local_channel(),'notes','text',$body); + public function init() + { - - // push updates to channel clones - - if((argc() > 1) && (argv(1) === 'sync')) { - Libsync::build_sync_packet(); - } - - logger('notes saved.', LOGGER_DEBUG); - json_return_and_die($ret); - - } - } + if (!local_channel()) { + return; + } - function get() { + $ret = array('success' => true); + if (array_key_exists('note_text', $_REQUEST)) { + $body = escape_tags($_REQUEST['note_text']); + + // I've had my notes vanish into thin air twice in four years. + // Provide a backup copy if there were contents previously + // and there are none being saved now. + + if (!$body) { + $old_text = get_pconfig(local_channel(), 'notes', 'text'); + if ($old_text) { + set_pconfig(local_channel(), 'notes', 'text.bak', $old_text); + } + } + set_pconfig(local_channel(), 'notes', 'text', $body); + + + // push updates to channel clones + + if ((argc() > 1) && (argv(1) === 'sync')) { + Libsync::build_sync_packet(); + } + + logger('notes saved.', LOGGER_DEBUG); + json_return_and_die($ret); + } + } + + public function get() + { $desc = t('This app allows you to create private notes for your personal use.'); $text = ''; - if(! ( local_channel() && Apps::system_app_installed(local_channel(),'Notes'))) { + if (!(local_channel() && Apps::system_app_installed(local_channel(), 'Notes'))) { return $text; } @@ -55,9 +62,5 @@ class Notes extends Controller { $text = ''; return $text; - } - - - } diff --git a/Zotlabs/Module/Notifications.php b/Zotlabs/Module/Notifications.php index dc0aa00cc..5b9abde5c 100644 --- a/Zotlabs/Module/Notifications.php +++ b/Zotlabs/Module/Notifications.php @@ -1,73 +1,79 @@ 49) { - $r = q("select * from notify where uid = %d + + nav_set_selected('Notifications'); + + $o = ''; + $notif_content = ''; + $notifications_available = false; + + $n = q( + "select count(*) as total from notify where uid = %d and seen = 0", + intval(local_channel()) + ); + if ($n && intval($n[0]['total']) > 49) { + $r = q( + "select * from notify where uid = %d and seen = 0 order by created desc limit 50", - intval(local_channel()) - ); - } - else { - $r1 = q("select * from notify where uid = %d + intval(local_channel()) + ); + } else { + $r1 = q( + "select * from notify where uid = %d and seen = 0 order by created desc limit 50", - intval(local_channel()) - ); + intval(local_channel()) + ); - $r2 = q("select * from notify where uid = %d + $r2 = q( + "select * from notify where uid = %d and seen = 1 order by created desc limit %d", - intval(local_channel()), - intval(50 - intval($n[0]['total'])) - ); - $r = array_merge($r1,$r2); - } - - if($r) { - $notifications_available = true; - foreach ($r as $rr) { - $x = strip_tags(bbcode($rr['msg'])); - $notif_content .= replace_macros(get_markup_template('notify.tpl'),array( - '$item_link' => z_root().'/notify/view/'. $rr['id'], - '$item_image' => $rr['photo'], - '$item_text' => $x, - '$item_when' => relative_date($rr['created']), - '$item_seen' => (($rr['seen']) ? true : false), - '$new' => t('New') - )); - } - } - else { - $notif_content = t('No more system notifications.'); - } - - $o .= replace_macros(get_markup_template('notifications.tpl'),array( - '$notif_header' => t('System Notifications'), - '$notif_link_mark_seen' => t('Mark all seen'), - '$notif_content' => $notif_content, - '$notifications_available' => $notifications_available, - )); - - return $o; - } - + intval(local_channel()), + intval(50 - intval($n[0]['total'])) + ); + $r = array_merge($r1, $r2); + } + + if ($r) { + $notifications_available = true; + foreach ($r as $rr) { + $x = strip_tags(bbcode($rr['msg'])); + $notif_content .= replace_macros(get_markup_template('notify.tpl'), array( + '$item_link' => z_root() . '/notify/view/' . $rr['id'], + '$item_image' => $rr['photo'], + '$item_text' => $x, + '$item_when' => relative_date($rr['created']), + '$item_seen' => (($rr['seen']) ? true : false), + '$new' => t('New') + )); + } + } else { + $notif_content = t('No more system notifications.'); + } + + $o .= replace_macros(get_markup_template('notifications.tpl'), array( + '$notif_header' => t('System Notifications'), + '$notif_link_mark_seen' => t('Mark all seen'), + '$notif_content' => $notif_content, + '$notifications_available' => $notifications_available, + )); + + return $o; + } } diff --git a/Zotlabs/Module/Notify.php b/Zotlabs/Module/Notify.php index e66430849..3bafdfc6a 100644 --- a/Zotlabs/Module/Notify.php +++ b/Zotlabs/Module/Notify.php @@ -1,82 +1,84 @@ 2 && argv(1) === 'view' && intval(argv(2))) { + $r = q( + "select * from notify where id = %d and uid = %d limit 1", + intval(argv(2)), + intval(local_channel()) + ); + if ($r) { + $x = ['channel_id' => local_channel(), 'update' => 'unset']; + call_hooks('update_unseen', $x); + if ((!$_SESSION['sudo']) && ($x['update'] === 'unset' || intval($x['update']))) { + q( + "update notify set seen = 1 where (( parent != '' and parent = '%s' and otype = '%s' ) or link = '%s' ) and uid = %d", + dbesc($r[0]['parent']), + dbesc($r[0]['otype']), + dbesc($r[0]['link']), + intval(local_channel()) + ); + } + goaway($r[0]['link']); + } + notice(sprintf(t('A notification with that id was not found for channel \'%s\''), $channel['channel_name'])); + goaway(z_root()); + } + } - if (argc() > 2 && argv(1) === 'view' && intval(argv(2))) { - $r = q("select * from notify where id = %d and uid = %d limit 1", - intval(argv(2)), - intval(local_channel()) - ); - if ($r) { - $x = [ 'channel_id' => local_channel(), 'update' => 'unset' ]; - call_hooks('update_unseen',$x); - if ((! $_SESSION['sudo']) && ($x['update'] === 'unset' || intval($x['update']))) { - q("update notify set seen = 1 where (( parent != '' and parent = '%s' and otype = '%s' ) or link = '%s' ) and uid = %d", - dbesc($r[0]['parent']), - dbesc($r[0]['otype']), - dbesc($r[0]['link']), - intval(local_channel()) - ); - } - goaway($r[0]['link']); - } - notice( sprintf( t('A notification with that id was not found for channel \'%s\''), $channel['channel_name'])); - goaway(z_root()); - } - - - } - - - function get() { - if (! local_channel()) { - return login(); - } - - $notif_tpl = get_markup_template('notifications.tpl'); - - $not_tpl = get_markup_template('notify.tpl'); - - $r = q("SELECT * from notify where uid = %d and seen = 0 order by created desc", - intval(local_channel()) - ); - - if ($r) { - foreach ($r as $it) { - $notif_content .= replace_macros($not_tpl,array( - '$item_link' => z_root().'/notify/view/'. $it['id'], - '$item_image' => $it['photo'], - '$item_text' => strip_tags(bbcode($it['msg'])), - '$item_when' => relative_date($it['created']) - )); - } - } - else { - $notif_content .= t('No more system notifications.'); - } - - $o .= replace_macros($notif_tpl,array( - '$notif_header' => t('System Notifications'), - '$tabs' => '', // $tabs, - '$notif_content' => $notif_content, - )); - - return $o; - - } + + public function get() + { + if (!local_channel()) { + return login(); + } + + $notif_tpl = get_markup_template('notifications.tpl'); + + $not_tpl = get_markup_template('notify.tpl'); + + $r = q( + "SELECT * from notify where uid = %d and seen = 0 order by created desc", + intval(local_channel()) + ); + + if ($r) { + foreach ($r as $it) { + $notif_content .= replace_macros($not_tpl, array( + '$item_link' => z_root() . '/notify/view/' . $it['id'], + '$item_image' => $it['photo'], + '$item_text' => strip_tags(bbcode($it['msg'])), + '$item_when' => relative_date($it['created']) + )); + } + } else { + $notif_content .= t('No more system notifications.'); + } + + $o .= replace_macros($notif_tpl, array( + '$notif_header' => t('System Notifications'), + '$tabs' => '', // $tabs, + '$notif_content' => $notif_content, + )); + + return $o; + } } diff --git a/Zotlabs/Module/Nullbox.php b/Zotlabs/Module/Nullbox.php index b14fd5dc9..02e90258e 100644 --- a/Zotlabs/Module/Nullbox.php +++ b/Zotlabs/Module/Nullbox.php @@ -4,11 +4,11 @@ namespace Zotlabs\Module; use Zotlabs\Web\Controller; -class Nullbox extends Controller { - - function init() { - http_status_exit(404,'Permission Denied'); - } +class Nullbox extends Controller +{ + public function init() + { + http_status_exit(404, 'Permission Denied'); + } } - diff --git a/Zotlabs/Module/Oauthinfo.php b/Zotlabs/Module/Oauthinfo.php index 2d64b3b8e..7f67f6731 100644 --- a/Zotlabs/Module/Oauthinfo.php +++ b/Zotlabs/Module/Oauthinfo.php @@ -1,22 +1,25 @@ z_root(), - 'authorization_endpoint' => z_root() . '/authorize', - 'jwks_uri' => z_root() . '/jwks', - 'token_endpoint' => z_root() . '/token', - 'userinfo_endpoint' => z_root() . '/userinfo', - 'scopes_supported' => [ 'openid', 'profile', 'email' ], - 'response_types_supported' => [ 'code', 'token', 'id_token', 'code id_token', 'token id_token' ] - ]; + $ret = [ + 'issuer' => z_root(), + 'authorization_endpoint' => z_root() . '/authorize', + 'jwks_uri' => z_root() . '/jwks', + 'token_endpoint' => z_root() . '/token', + 'userinfo_endpoint' => z_root() . '/userinfo', + 'scopes_supported' => ['openid', 'profile', 'email'], + 'response_types_supported' => ['code', 'token', 'id_token', 'code id_token', 'token id_token'] + ]; - json_return_and_die($ret); - } -} \ No newline at end of file + json_return_and_die($ret); + } +} diff --git a/Zotlabs/Module/Oembed.php b/Zotlabs/Module/Oembed.php index aee5ea079..a674805d9 100644 --- a/Zotlabs/Module/Oembed.php +++ b/Zotlabs/Module/Oembed.php @@ -1,36 +1,37 @@ 1) { - if (argv(1) == 'b2h'){ - $url = array( "", trim(hex2bin($_REQUEST['url']))); - echo oembed_replacecb($url); - killme(); - } - - elseif (argv(1) == 'h2b'){ - $text = trim(hex2bin($_REQUEST['text'])); - echo oembed_html2bbcode($text); - killme(); - } - - else { - echo ""; - $src = base64url_decode(argv(1)); - $j = oembed_fetch_url($src); - echo $j['html']; - // logger('mod-oembed ' . $h, LOGGER_ALL); - echo ""; - } - } - killme(); - } - + public function init() + { + // logger('mod_oembed ' . \App::$query_string, LOGGER_ALL); + + if (argc() > 1) { + if (argv(1) == 'b2h') { + $url = array("", trim(hex2bin($_REQUEST['url']))); + echo oembed_replacecb($url); + killme(); + } elseif (argv(1) == 'h2b') { + $text = trim(hex2bin($_REQUEST['text'])); + echo oembed_html2bbcode($text); + killme(); + } else { + echo ""; + $src = base64url_decode(argv(1)); + $j = oembed_fetch_url($src); + echo $j['html']; + // logger('mod-oembed ' . $h, LOGGER_ALL); + echo ""; + } + } + killme(); + } } diff --git a/Zotlabs/Module/Oep.php b/Zotlabs/Module/Oep.php index 06c965e2c..c15eb6fdd 100644 --- a/Zotlabs/Module/Oep.php +++ b/Zotlabs/Module/Oep.php @@ -1,630 +1,679 @@ 1 && argv(1) === 'html') ? true : false); + if ($_REQUEST['url']) { + $_REQUEST['url'] = strip_zids($_REQUEST['url']); + $url = $_REQUEST['url']; + } + + if (!$url) { + http_status_exit(404, 'Not found'); + } + + $maxwidth = $_REQUEST['maxwidth']; + $maxheight = $_REQUEST['maxheight']; + $format = $_REQUEST['format']; + if ($format && $format !== 'json') { + http_status_exit(501, 'Not implemented'); + } + + if (fnmatch('*/photos/*/album/*', $url)) { + $arr = $this->oep_album_reply($_REQUEST); + } elseif (fnmatch('*/photos/*/image/*', $url)) { + $arr = $this->oep_photo_reply($_REQUEST); + } elseif (fnmatch('*/photos*', $url)) { + $arr = $this->oep_phototop_reply($_REQUEST); + } elseif (fnmatch('*/display/*', $url)) { + $arr = $this->oep_display_reply($_REQUEST); + } elseif (fnmatch('*/channel/*mid=*', $url)) { + $arr = $this->oep_mid_reply($_REQUEST); + } elseif (fnmatch('*/channel*', $url)) { + $arr = $this->oep_profile_reply($_REQUEST); + } elseif (fnmatch('*/profile/*', $url)) { + $arr = $this->oep_profile_reply($_REQUEST); + } elseif (fnmatch('*/cards/*', $url)) { + $arr = $this->oep_cards_reply($_REQUEST); + } elseif (fnmatch('*/articles/*', $url)) { + $arr = $this->oep_articles_reply($_REQUEST); + } + + if ($arr) { + if ($html) { + if ($arr['type'] === 'rich') { + header('Content-Type: text/html'); + echo $arr['html']; + } + } else { + header('Content-Type: application/json+oembed'); + echo json_encode($arr); + } + killme(); + } + + http_status_exit(404, 'Not found'); + } + + public function oep_display_reply($args) + { + + $ret = []; + $url = $args['url']; + $maxwidth = intval($args['maxwidth']); + $maxheight = intval($args['maxheight']); + + if (preg_match('#//(.*?)/display/(.*?)(&|\?|$)#', $url, $matches)) { + $res = $matches[2]; + } + + $res = unpack_link_id($res); + + $item_normal = item_normal(); + + $p = q( + "select * from item where mid like '%s' limit 1", + dbesc($res . '%') + ); + + if (!$p) { + return; + } + + $c = channelx_by_n($p[0]['uid']); -class Oep extends \Zotlabs\Web\Controller { + if (!($c && $res)) { + return; + } - function init() { - - logger('oep: ' . print_r($_REQUEST,true), LOGGER_DEBUG, LOG_INFO); - - $html = ((argc() > 1 && argv(1) === 'html') ? true : false); - if($_REQUEST['url']) { - $_REQUEST['url'] = strip_zids($_REQUEST['url']); - $url = $_REQUEST['url']; - } - - if(! $url) - http_status_exit(404, 'Not found'); - - $maxwidth = $_REQUEST['maxwidth']; - $maxheight = $_REQUEST['maxheight']; - $format = $_REQUEST['format']; - if($format && $format !== 'json') - http_status_exit(501, 'Not implemented'); - - if(fnmatch('*/photos/*/album/*',$url)) - $arr = $this->oep_album_reply($_REQUEST); - elseif(fnmatch('*/photos/*/image/*',$url)) - $arr = $this->oep_photo_reply($_REQUEST); - elseif(fnmatch('*/photos*',$url)) - $arr = $this->oep_phototop_reply($_REQUEST); - elseif(fnmatch('*/display/*',$url)) - $arr = $this->oep_display_reply($_REQUEST); - elseif(fnmatch('*/channel/*mid=*',$url)) - $arr = $this->oep_mid_reply($_REQUEST); - elseif(fnmatch('*/channel*',$url)) - $arr = $this->oep_profile_reply($_REQUEST); - elseif(fnmatch('*/profile/*',$url)) - $arr = $this->oep_profile_reply($_REQUEST); - elseif(fnmatch('*/cards/*',$url)) - $arr = $this->oep_cards_reply($_REQUEST); - elseif(fnmatch('*/articles/*',$url)) - $arr = $this->oep_articles_reply($_REQUEST); - - if($arr) { - if($html) { - if($arr['type'] === 'rich') { - header('Content-Type: text/html'); - echo $arr['html']; - } - } - else { - header('Content-Type: application/json+oembed'); - echo json_encode($arr); - } - killme(); - } - - http_status_exit(404,'Not found'); - - } - - function oep_display_reply($args) { - - $ret = []; - $url = $args['url']; - $maxwidth = intval($args['maxwidth']); - $maxheight = intval($args['maxheight']); + if (!perm_is_allowed($c[0]['channel_id'], get_observer_hash(), 'view_stream')) { + return; + } - if(preg_match('#//(.*?)/display/(.*?)(&|\?|$)#',$url,$matches)) { - $res = $matches[2]; - } + $sql_extra = item_permissions_sql($c['channel_id']); - $res = unpack_link_id($res); + $p = q( + "select * from item where mid like '%s' and uid = %d $sql_extra $item_normal limit 1", + dbesc($res . '%'), + intval($c['channel_id']) + ); - $item_normal = item_normal(); + if (!$p) { + return; + } - $p = q("select * from item where mid like '%s' limit 1", - dbesc($res . '%') - ); + xchan_query($p, true); + $p = fetch_post_tags($p, true); - if(! $p) - return; + // This function can get tripped up if the item is already a reshare + // (the multiple share declarations do not parse cleanly if nested) + // So build a template with a known nonsense string as the content, and then + // replace that known string with the actual rendered content, sending + // each content layer through bbcode() separately. - $c = channelx_by_n($p[0]['uid']); + $x = '2eGriplW^*Jmf4'; - - if(! ($c && $res)) - return; - if(! perm_is_allowed($c[0]['channel_id'],get_observer_hash(),'view_stream')) - return; - - $sql_extra = item_permissions_sql($c['channel_id']); - - $p = q("select * from item where mid like '%s' and uid = %d $sql_extra $item_normal limit 1", - dbesc($res . '%'), - intval($c['channel_id']) - ); - - if(! $p) - return; - - xchan_query($p,true); - $p = fetch_post_tags($p,true); - - // This function can get tripped up if the item is already a reshare - // (the multiple share declarations do not parse cleanly if nested) - // So build a template with a known nonsense string as the content, and then - // replace that known string with the actual rendered content, sending - // each content layer through bbcode() separately. - - $x = '2eGriplW^*Jmf4'; - - - $o = "[share author='".urlencode($p[0]['author']['xchan_name']). + $o = "[share author='" . urlencode($p[0]['author']['xchan_name']) . "' profile='" . $p[0]['author']['xchan_url'] . "' portable_id='" . $p[0]['author']['xchan_hash'] . - "' avatar='" . $p[0]['author']['xchan_photo_s']. - "' link='" . $p[0]['plink']. + "' avatar='" . $p[0]['author']['xchan_photo_s'] . + "' link='" . $p[0]['plink'] . "' auth='" . (in_array($p[0]['author']['network'],['nomad','zot6']) ? 'true' : 'false') . - "' posted='" . $p[0]['created']. - "' message_id='" . $p[0]['mid']."']"; - if($p[0]['title']) - $o .= '[b]'.$p[0]['title'].'[/b]'."\r\n"; + "' posted='" . $p[0]['created'] . + "' message_id='" . $p[0]['mid'] . "']"; + if ($p[0]['title']) { + $o .= '[b]' . $p[0]['title'] . '[/b]' . "\r\n"; + } - $o .= $x; - $o .= "[/share]"; - $o = bbcode($o); - - $o = str_replace($x,bbcode($p[0]['body']),$o); - - $ret['type'] = 'rich'; - - $w = (($maxwidth) ? $maxwidth : 640); - $h = (($maxheight) ? $maxheight : intval($w * 2 / 3)); - - $ret['html'] = '
      ' . $o . '
      '; - - $ret['width'] = $w; - $ret['height'] = $h; - - return $ret; - - } + $o .= $x; + $o .= "[/share]"; + $o = bbcode($o); + + $o = str_replace($x, bbcode($p[0]['body']), $o); + + $ret['type'] = 'rich'; + + $w = (($maxwidth) ? $maxwidth : 640); + $h = (($maxheight) ? $maxheight : intval($w * 2 / 3)); + + $ret['html'] = '
      ' . $o . '
      '; + + $ret['width'] = $w; + $ret['height'] = $h; + + return $ret; + } - function oep_cards_reply($args) { - - $ret = []; - $url = $args['url']; - $maxwidth = intval($args['maxwidth']); - $maxheight = intval($args['maxheight']); + public function oep_cards_reply($args) + { - if(preg_match('#//(.*?)/cards/(.*?)/(.*?)(&|\?|$)#',$url,$matches)) { - $nick = $matches[2]; - $res = $matches[3]; - } - if(! ($nick && $res)) - return $ret; + $ret = []; + $url = $args['url']; + $maxwidth = intval($args['maxwidth']); + $maxheight = intval($args['maxheight']); - $channel = channelx_by_nick($nick); + if (preg_match('#//(.*?)/cards/(.*?)/(.*?)(&|\?|$)#', $url, $matches)) { + $nick = $matches[2]; + $res = $matches[3]; + } + if (!($nick && $res)) { + return $ret; + } - if(! $channel) - return $ret; + $channel = channelx_by_nick($nick); + + if (!$channel) { + return $ret; + } - if(! perm_is_allowed($channel['channel_id'],get_observer_hash(),'view_pages')) - return $ret; + if (!perm_is_allowed($channel['channel_id'], get_observer_hash(), 'view_pages')) { + return $ret; + } - $sql_extra = item_permissions_sql($channel['channel_id'],get_observer_hash()); + $sql_extra = item_permissions_sql($channel['channel_id'], get_observer_hash()); - $r = q("select * from iconfig where iconfig.cat = 'system' and iconfig.k = 'CARD' and iconfig.v = '%s' limit 1", - dbesc($res) - ); - if($r) { - $sql_extra .= " and item.id = " . intval($r[0]['iid']) . " "; - } - else { - return $ret; - } + $r = q( + "select * from iconfig where iconfig.cat = 'system' and iconfig.k = 'CARD' and iconfig.v = '%s' limit 1", + dbesc($res) + ); + if ($r) { + $sql_extra .= " and item.id = " . intval($r[0]['iid']) . " "; + } else { + return $ret; + } - $r = q("select * from item + $r = q( + "select * from item where item.uid = %d and item_type = %d $sql_extra order by item.created desc", - intval($channel['channel_id']), - intval(ITEM_TYPE_CARD) - ); + intval($channel['channel_id']), + intval(ITEM_TYPE_CARD) + ); - $item_normal = " and item.item_hidden = 0 and item.item_type in (0,6) and item.item_deleted = 0 + $item_normal = " and item.item_hidden = 0 and item.item_type in (0,6) and item.item_deleted = 0 and item.item_unpublished = 0 and item.item_delayed = 0 and item.item_pending_remove = 0 and item.item_blocked = 0 "; - if($r) { - xchan_query($r); - $p = fetch_post_tags($r, true); - } + if ($r) { + xchan_query($r); + $p = fetch_post_tags($r, true); + } - $x = '2eGriplW^*Jmf4'; + $x = '2eGriplW^*Jmf4'; - - $o = "[share author='".urlencode($p[0]['author']['xchan_name']). - "' profile='".$p[0]['author']['xchan_url'] . - "' portable_id='".$p[0]['author']['xchan_hash'] . - "' avatar='".$p[0]['author']['xchan_photo_s']. - "' link='".$p[0]['plink']. + + $o = "[share author='" . urlencode($p[0]['author']['xchan_name']) . + "' profile='" . $p[0]['author']['xchan_url'] . + "' portable_id='" . $p[0]['author']['xchan_hash'] . + "' avatar='" . $p[0]['author']['xchan_photo_s'] . + "' link='" . $p[0]['plink'] . "' auth='" . (in_array($p[0]['author']['network'],['nomad','zot6']) ? 'true' : 'false') . - "' posted='".$p[0]['created']. - "' message_id='".$p[0]['mid']."']"; - if($p[0]['title']) - $o .= '[b]'.$p[0]['title'].'[/b]'."\r\n"; + "' posted='" . $p[0]['created'] . + "' message_id='" . $p[0]['mid'] . "']"; + if ($p[0]['title']) { + $o .= '[b]' . $p[0]['title'] . '[/b]' . "\r\n"; + } - $o .= $x; - $o .= "[/share]"; - $o = bbcode($o); - - $o = str_replace($x,bbcode($p[0]['body']),$o); - - $ret['type'] = 'rich'; - - $w = (($maxwidth) ? $maxwidth : 640); - $h = (($maxheight) ? $maxheight : intval($w * 2 / 3)); - - $ret['html'] = '
      ' . $o . '
      '; - - $ret['width'] = $w; - $ret['height'] = $h; - - return $ret; - - } + $o .= $x; + $o .= "[/share]"; + $o = bbcode($o); - function oep_articles_reply($args) { - - $ret = []; - $url = $args['url']; - $maxwidth = intval($args['maxwidth']); - $maxheight = intval($args['maxheight']); + $o = str_replace($x, bbcode($p[0]['body']), $o); - if(preg_match('#//(.*?)/articles/(.*?)/(.*?)(&|\?|$)#',$url,$matches)) { - $nick = $matches[2]; - $res = $matches[3]; - } - if(! ($nick && $res)) - return $ret; + $ret['type'] = 'rich'; - $channel = channelx_by_nick($nick); + $w = (($maxwidth) ? $maxwidth : 640); + $h = (($maxheight) ? $maxheight : intval($w * 2 / 3)); - if(! $channel) - return $ret; + $ret['html'] = '
      ' . $o . '
      '; + + $ret['width'] = $w; + $ret['height'] = $h; + + return $ret; + } + + public function oep_articles_reply($args) + { + + $ret = []; + $url = $args['url']; + $maxwidth = intval($args['maxwidth']); + $maxheight = intval($args['maxheight']); + + if (preg_match('#//(.*?)/articles/(.*?)/(.*?)(&|\?|$)#', $url, $matches)) { + $nick = $matches[2]; + $res = $matches[3]; + } + if (!($nick && $res)) { + return $ret; + } + + $channel = channelx_by_nick($nick); + + if (!$channel) { + return $ret; + } - if(! perm_is_allowed($channel['channel_id'],get_observer_hash(),'view_pages')) - return $ret; + if (!perm_is_allowed($channel['channel_id'], get_observer_hash(), 'view_pages')) { + return $ret; + } - $sql_extra = item_permissions_sql($channel['channel_id'],get_observer_hash()); + $sql_extra = item_permissions_sql($channel['channel_id'], get_observer_hash()); - $r = q("select * from iconfig where iconfig.cat = 'system' and iconfig.k = 'ARTICLE' and iconfig.v = '%s' limit 1", - dbesc($res) - ); - if($r) { - $sql_extra .= " and item.id = " . intval($r[0]['iid']) . " "; - } - else { - return $ret; - } + $r = q( + "select * from iconfig where iconfig.cat = 'system' and iconfig.k = 'ARTICLE' and iconfig.v = '%s' limit 1", + dbesc($res) + ); + if ($r) { + $sql_extra .= " and item.id = " . intval($r[0]['iid']) . " "; + } else { + return $ret; + } - $r = q("select * from item + $r = q( + "select * from item where item.uid = %d and item_type = %d $sql_extra order by item.created desc", - intval($channel['channel_id']), - intval(ITEM_TYPE_ARTICLE) - ); + intval($channel['channel_id']), + intval(ITEM_TYPE_ARTICLE) + ); - $item_normal = " and item.item_hidden = 0 and item.item_type in (0,7) and item.item_deleted = 0 + $item_normal = " and item.item_hidden = 0 and item.item_type in (0,7) and item.item_deleted = 0 and item.item_unpublished = 0 and item.item_delayed = 0 and item.item_pending_remove = 0 and item.item_blocked = 0 "; - if($r) { - xchan_query($r); - $p = fetch_post_tags($r, true); - } + if ($r) { + xchan_query($r); + $p = fetch_post_tags($r, true); + } - $x = '2eGriplW^*Jmf4'; + $x = '2eGriplW^*Jmf4'; - - $o = "[share author='".urlencode($p[0]['author']['xchan_name']). - "' profile='".$p[0]['author']['xchan_url'] . - "' portable_id='".$p[0]['author']['xchan_hash'] . - "' avatar='".$p[0]['author']['xchan_photo_s']. - "' link='".$p[0]['plink']. + + $o = "[share author='" . urlencode($p[0]['author']['xchan_name']) . + "' profile='" . $p[0]['author']['xchan_url'] . + "' portable_id='" . $p[0]['author']['xchan_hash'] . + "' avatar='" . $p[0]['author']['xchan_photo_s'] . + "' link='" . $p[0]['plink'] . "' auth='" . (in_array($p[0]['author']['network'],['nomad','zot6']) ? 'true' : 'false') . - "' posted='".$p[0]['created']. - "' message_id='".$p[0]['mid']."']"; - if($p[0]['title']) - $o .= '[b]'.$p[0]['title'].'[/b]'."\r\n"; + "' posted='" . $p[0]['created'] . + "' message_id='" . $p[0]['mid'] . "']"; + if ($p[0]['title']) { + $o .= '[b]' . $p[0]['title'] . '[/b]' . "\r\n"; + } - $o .= $x; - $o .= "[/share]"; - $o = bbcode($o); - - $o = str_replace($x,bbcode($p[0]['body']),$o); - - $ret['type'] = 'rich'; - - $w = (($maxwidth) ? $maxwidth : 640); - $h = (($maxheight) ? $maxheight : intval($w * 2 / 3)); - - $ret['html'] = '
      ' . $o . '
      '; - - $ret['width'] = $w; - $ret['height'] = $h; - - return $ret; - - } + $o .= $x; + $o .= "[/share]"; + $o = bbcode($o); - - function oep_mid_reply($args) { - - $ret = []; - $url = $args['url']; - $maxwidth = intval($args['maxwidth']); - $maxheight = intval($args['maxheight']); - - if(preg_match('#//(.*?)/(.*?)/(.*?)/(.*?)mid\=(.*?)(&|$)#',$url,$matches)) { - $chn = $matches[3]; - $res = $matches[5]; - } - - if(! ($chn && $res)) - return; - $c = q("select * from channel where channel_address = '%s' limit 1", - dbesc($chn) - ); - - if(! $c) - return; + $o = str_replace($x, bbcode($p[0]['body']), $o); - if(! perm_is_allowed($c[0]['channel_id'],get_observer_hash(),'view_stream')) - return; - - $sql_extra = item_permissions_sql($c[0]['channel_id']); - - $p = q("select * from item where mid = '%s' and uid = %d $sql_extra limit 1", - dbesc($res), - intval($c[0]['channel_id']) - ); - if(! $p) - return; - - xchan_query($p,true); - $p = fetch_post_tags($p,true); + $ret['type'] = 'rich'; - // This function can get tripped up if the item is already a reshare - // (the multiple share declarations do not parse cleanly if nested) - // So build a template with a known nonsense string as the content, and then - // replace that known string with the actual rendered content, sending - // each content layer through bbcode() separately. + $w = (($maxwidth) ? $maxwidth : 640); + $h = (($maxheight) ? $maxheight : intval($w * 2 / 3)); - $x = '2eGriplW^*Jmf4'; - - $o = "[share author='".urlencode($p[0]['author']['xchan_name']). - "' profile='".$p[0]['author']['xchan_url'] . - "' portable_id='".$p[0]['author']['xchan_hash'] . - "' avatar='".$p[0]['author']['xchan_photo_s']. - "' link='".$p[0]['plink']. + $ret['html'] = '
      ' . $o . '
      '; + + $ret['width'] = $w; + $ret['height'] = $h; + + return $ret; + } + + + public function oep_mid_reply($args) + { + + $ret = []; + $url = $args['url']; + $maxwidth = intval($args['maxwidth']); + $maxheight = intval($args['maxheight']); + + if (preg_match('#//(.*?)/(.*?)/(.*?)/(.*?)mid\=(.*?)(&|$)#', $url, $matches)) { + $chn = $matches[3]; + $res = $matches[5]; + } + + if (!($chn && $res)) { + return; + } + $c = q( + "select * from channel where channel_address = '%s' limit 1", + dbesc($chn) + ); + + if (!$c) { + return; + } + + if (!perm_is_allowed($c[0]['channel_id'], get_observer_hash(), 'view_stream')) { + return; + } + + $sql_extra = item_permissions_sql($c[0]['channel_id']); + + $p = q( + "select * from item where mid = '%s' and uid = %d $sql_extra limit 1", + dbesc($res), + intval($c[0]['channel_id']) + ); + if (!$p) { + return; + } + + xchan_query($p, true); + $p = fetch_post_tags($p, true); + + // This function can get tripped up if the item is already a reshare + // (the multiple share declarations do not parse cleanly if nested) + // So build a template with a known nonsense string as the content, and then + // replace that known string with the actual rendered content, sending + // each content layer through bbcode() separately. + + $x = '2eGriplW^*Jmf4'; + + $o = "[share author='" . urlencode($p[0]['author']['xchan_name']) . + "' profile='" . $p[0]['author']['xchan_url'] . + "' portable_id='" . $p[0]['author']['xchan_hash'] . + "' avatar='" . $p[0]['author']['xchan_photo_s'] . + "' link='" . $p[0]['plink'] . "' auth='" . (in_array($p[0]['author']['network'],['nomad','zot6']) ? 'true' : 'false') . - "' posted='".$p[0]['created']. - "' message_id='".$p[0]['mid']."']"; - if($p[0]['title']) - $o .= '[b]'.$p[0]['title'].'[/b]'."\r\n"; - $o .= $x; - $o .= "[/share]"; - $o = bbcode($o); - - $o = str_replace($x,bbcode($p[0]['body']),$o); + "' posted='" . $p[0]['created'] . + "' message_id='" . $p[0]['mid'] . "']"; + if ($p[0]['title']) { + $o .= '[b]' . $p[0]['title'] . '[/b]' . "\r\n"; + } + $o .= $x; + $o .= "[/share]"; + $o = bbcode($o); - $ret['type'] = 'rich'; - - $w = (($maxwidth) ? $maxwidth : 640); - $h = (($maxheight) ? $maxheight : intval($w * 2 / 3)); - - $ret['html'] = '
      ' . $o . '
      '; - - $ret['width'] = $w; - $ret['height'] = $h; - - return $ret; - - } - - function oep_profile_reply($args) { - - - require_once('include/channel.php'); + $o = str_replace($x, bbcode($p[0]['body']), $o); - $url = $args['url']; - - if(preg_match('#//(.*?)/(.*?)/(.*?)(/|\?|&|$)#',$url,$matches)) { - $chn = $matches[3]; - } - - if(! $chn) - return; - - $c = channelx_by_nick($chn); - - if(! $c) - return; - - - $maxwidth = intval($args['maxwidth']); - $maxheight = intval($args['maxheight']); - - $width = 800; - $height = 375; - - if($maxwidth) { - $width = $maxwidth; - $height = (375 / 800) * $width; - } - if($maxheight) { - if($maxheight < $height) { - $width = (800 / 375) * $maxheight; - $height = $maxheight; - } - } - $ret = []; - - $ret['type'] = 'rich'; - $ret['width'] = intval($width); - $ret['height'] = intval($height); - - $ret['html'] = get_zcard_embed($c,get_observer_hash(),array('width' => $width, 'height' => $height)); - - return $ret; - - } - - function oep_album_reply($args) { - - $ret = []; - $url = $args['url']; - $maxwidth = intval($args['maxwidth']); - $maxheight = intval($args['maxheight']); - - if(preg_match('|//(.*?)/(.*?)/(.*?)/album/|',$url,$matches)) { - $chn = $matches[3]; - $res = basename($url); - } - - if(! ($chn && $res)) - return; - $c = q("select * from channel where channel_address = '%s' limit 1", - dbesc($chn) - ); - - if(! $c) - return; - - if(! perm_is_allowed($c[0]['channel_id'],get_observer_hash(),'view_files')) - return; + $ret['type'] = 'rich'; - $sql_extra = permissions_sql($c[0]['channel_id']); - - $p = q("select resource_id from photo where album = '%s' and uid = %d and imgscale = 0 $sql_extra order by created desc limit 1", - dbesc($res), - intval($c[0]['channel_id']) - ); - if(! $p) - return; - - $res = $p[0]['resource_id']; - - $r = q("select height, width, imgscale, resource_id from photo where uid = %d and resource_id = '%s' $sql_extra order by imgscale asc", - intval($c[0]['channel_id']), - dbesc($res) - ); - - if($r) { - foreach($r as $rr) { - $foundres = false; - if($maxheight && $rr['height'] > $maxheight) - continue; - if($maxwidth && $rr['width'] > $maxwidth) - continue; - $foundres = true; - break; - } - - if($foundres) { - $ret['type'] = 'link'; - $ret['thumbnail_url'] = z_root() . '/photo/' . '/' . $rr['resource_id'] . '-' . $rr['imgscale']; - $ret['thumbnail_width'] = $rr['width']; - $ret['thumbnail_height'] = $rr['height']; - } - - - } - return $ret; - - } - - - function oep_phototop_reply($args) { - - $ret = []; - $url = $args['url']; - $maxwidth = intval($args['maxwidth']); - $maxheight = intval($args['maxheight']); - - if(preg_match('|//(.*?)/(.*?)/(.*?)$|',$url,$matches)) { - $chn = $matches[3]; - } - - if(! $chn) - return; - $c = q("select * from channel where channel_address = '%s' limit 1", - dbesc($chn) - ); - - if(! $c) - return; - - if(! perm_is_allowed($c[0]['channel_id'],get_observer_hash(),'view_files')) - return; + $w = (($maxwidth) ? $maxwidth : 640); + $h = (($maxheight) ? $maxheight : intval($w * 2 / 3)); - $sql_extra = permissions_sql($c[0]['channel_id']); - - $p = q("select resource_id from photo where uid = %d and imgscale = 0 $sql_extra order by created desc limit 1", - intval($c[0]['channel_id']) - ); - if(! $p) - return; - - $res = $p[0]['resource_id']; - - $r = q("select height, width, imgscale, resource_id from photo where uid = %d and resource_id = '%s' $sql_extra order by imgscale asc", - intval($c[0]['channel_id']), - dbesc($res) - ); - - if($r) { - foreach($r as $rr) { - $foundres = false; - if($maxheight && $rr['height'] > $maxheight) - continue; - if($maxwidth && $rr['width'] > $maxwidth) - continue; - $foundres = true; - break; - } - - if($foundres) { - $ret['type'] = 'link'; - $ret['thumbnail_url'] = z_root() . '/photo/' . '/' . $rr['resource_id'] . '-' . $rr['imgscale']; - $ret['thumbnail_width'] = $rr['width']; - $ret['thumbnail_height'] = $rr['height']; - } - - - } - return $ret; - - } - - - function oep_photo_reply($args) { - - $ret = []; - $url = $args['url']; - $maxwidth = intval($args['maxwidth']); - $maxheight = intval($args['maxheight']); - - if(preg_match('|//(.*?)/(.*?)/(.*?)/image/|',$url,$matches)) { - $chn = $matches[3]; - $res = basename($url); - } - - if(! ($chn && $res)) - return; - $c = q("select * from channel where channel_address = '%s' limit 1", - dbesc($chn) - ); - - if(! $c) - return; + $ret['html'] = '
      ' . $o . '
      '; - if(! perm_is_allowed($c[0]['channel_id'],get_observer_hash(),'view_files')) - return; + $ret['width'] = $w; + $ret['height'] = $h; - $sql_extra = permissions_sql($c[0]['channel_id']); - - - $r = q("select height, width, imgscale, resource_id from photo where uid = %d and resource_id = '%s' $sql_extra order by imgscale asc", - intval($c[0]['channel_id']), - dbesc($res) - ); - - if($r) { - foreach($r as $rr) { - $foundres = false; - if($maxheight && $rr['height'] > $maxheight) - continue; - if($maxwidth && $rr['width'] > $maxwidth) - continue; - $foundres = true; - break; - } - - if($foundres) { - $ret['type'] = 'link'; - $ret['thumbnail_url'] = z_root() . '/photo/' . '/' . $rr['resource_id'] . '-' . $rr['imgscale']; - $ret['thumbnail_width'] = $rr['width']; - $ret['thumbnail_height'] = $rr['height']; - } - - - } - return $ret; - - } + return $ret; + } + + public function oep_profile_reply($args) + { + + + require_once('include/channel.php'); + + $url = $args['url']; + + if (preg_match('#//(.*?)/(.*?)/(.*?)(/|\?|&|$)#', $url, $matches)) { + $chn = $matches[3]; + } + + if (!$chn) { + return; + } + + $c = channelx_by_nick($chn); + + if (!$c) { + return; + } + + + $maxwidth = intval($args['maxwidth']); + $maxheight = intval($args['maxheight']); + + $width = 800; + $height = 375; + + if ($maxwidth) { + $width = $maxwidth; + $height = (375 / 800) * $width; + } + if ($maxheight) { + if ($maxheight < $height) { + $width = (800 / 375) * $maxheight; + $height = $maxheight; + } + } + $ret = []; + + $ret['type'] = 'rich'; + $ret['width'] = intval($width); + $ret['height'] = intval($height); + + $ret['html'] = get_zcard_embed($c, get_observer_hash(), array('width' => $width, 'height' => $height)); + + return $ret; + } + + public function oep_album_reply($args) + { + + $ret = []; + $url = $args['url']; + $maxwidth = intval($args['maxwidth']); + $maxheight = intval($args['maxheight']); + + if (preg_match('|//(.*?)/(.*?)/(.*?)/album/|', $url, $matches)) { + $chn = $matches[3]; + $res = basename($url); + } + + if (!($chn && $res)) { + return; + } + $c = q( + "select * from channel where channel_address = '%s' limit 1", + dbesc($chn) + ); + + if (!$c) { + return; + } + + if (!perm_is_allowed($c[0]['channel_id'], get_observer_hash(), 'view_files')) { + return; + } + + $sql_extra = permissions_sql($c[0]['channel_id']); + + $p = q( + "select resource_id from photo where album = '%s' and uid = %d and imgscale = 0 $sql_extra order by created desc limit 1", + dbesc($res), + intval($c[0]['channel_id']) + ); + if (!$p) { + return; + } + + $res = $p[0]['resource_id']; + + $r = q( + "select height, width, imgscale, resource_id from photo where uid = %d and resource_id = '%s' $sql_extra order by imgscale asc", + intval($c[0]['channel_id']), + dbesc($res) + ); + + if ($r) { + foreach ($r as $rr) { + $foundres = false; + if ($maxheight && $rr['height'] > $maxheight) { + continue; + } + if ($maxwidth && $rr['width'] > $maxwidth) { + continue; + } + $foundres = true; + break; + } + + if ($foundres) { + $ret['type'] = 'link'; + $ret['thumbnail_url'] = z_root() . '/photo/' . '/' . $rr['resource_id'] . '-' . $rr['imgscale']; + $ret['thumbnail_width'] = $rr['width']; + $ret['thumbnail_height'] = $rr['height']; + } + } + return $ret; + } + + + public function oep_phototop_reply($args) + { + + $ret = []; + $url = $args['url']; + $maxwidth = intval($args['maxwidth']); + $maxheight = intval($args['maxheight']); + + if (preg_match('|//(.*?)/(.*?)/(.*?)$|', $url, $matches)) { + $chn = $matches[3]; + } + + if (!$chn) { + return; + } + $c = q( + "select * from channel where channel_address = '%s' limit 1", + dbesc($chn) + ); + + if (!$c) { + return; + } + + if (!perm_is_allowed($c[0]['channel_id'], get_observer_hash(), 'view_files')) { + return; + } + + $sql_extra = permissions_sql($c[0]['channel_id']); + + $p = q( + "select resource_id from photo where uid = %d and imgscale = 0 $sql_extra order by created desc limit 1", + intval($c[0]['channel_id']) + ); + if (!$p) { + return; + } + + $res = $p[0]['resource_id']; + + $r = q( + "select height, width, imgscale, resource_id from photo where uid = %d and resource_id = '%s' $sql_extra order by imgscale asc", + intval($c[0]['channel_id']), + dbesc($res) + ); + + if ($r) { + foreach ($r as $rr) { + $foundres = false; + if ($maxheight && $rr['height'] > $maxheight) { + continue; + } + if ($maxwidth && $rr['width'] > $maxwidth) { + continue; + } + $foundres = true; + break; + } + + if ($foundres) { + $ret['type'] = 'link'; + $ret['thumbnail_url'] = z_root() . '/photo/' . '/' . $rr['resource_id'] . '-' . $rr['imgscale']; + $ret['thumbnail_width'] = $rr['width']; + $ret['thumbnail_height'] = $rr['height']; + } + } + return $ret; + } + + + public function oep_photo_reply($args) + { + + $ret = []; + $url = $args['url']; + $maxwidth = intval($args['maxwidth']); + $maxheight = intval($args['maxheight']); + + if (preg_match('|//(.*?)/(.*?)/(.*?)/image/|', $url, $matches)) { + $chn = $matches[3]; + $res = basename($url); + } + + if (!($chn && $res)) { + return; + } + $c = q( + "select * from channel where channel_address = '%s' limit 1", + dbesc($chn) + ); + + if (!$c) { + return; + } + + if (!perm_is_allowed($c[0]['channel_id'], get_observer_hash(), 'view_files')) { + return; + } + + $sql_extra = permissions_sql($c[0]['channel_id']); + + + $r = q( + "select height, width, imgscale, resource_id from photo where uid = %d and resource_id = '%s' $sql_extra order by imgscale asc", + intval($c[0]['channel_id']), + dbesc($res) + ); + + if ($r) { + foreach ($r as $rr) { + $foundres = false; + if ($maxheight && $rr['height'] > $maxheight) { + continue; + } + if ($maxwidth && $rr['width'] > $maxwidth) { + continue; + } + $foundres = true; + break; + } + + if ($foundres) { + $ret['type'] = 'link'; + $ret['thumbnail_url'] = z_root() . '/photo/' . '/' . $rr['resource_id'] . '-' . $rr['imgscale']; + $ret['thumbnail_width'] = $rr['width']; + $ret['thumbnail_height'] = $rr['height']; + } + } + return $ret; + } } diff --git a/Zotlabs/Module/Oexchange.php b/Zotlabs/Module/Oexchange.php index 950965a95..502878497 100644 --- a/Zotlabs/Module/Oexchange.php +++ b/Zotlabs/Module/Oexchange.php @@ -5,73 +5,72 @@ namespace Zotlabs\Module; use App; use Zotlabs\Web\Controller; -class Oexchange extends Controller { +class Oexchange extends Controller +{ - function init() { - - if ((argc() > 1) && (argv(1) === 'xrd')) { - echo replace_macros(get_markup_template('oexchange_xrd.tpl'), [ '$base' => z_root() ] ); - killme(); - } - } - - function get() { - if (! local_channel()) { - if (remote_channel()) { - $observer = App::get_observer(); - if ($observer && $observer['xchan_url']) { - $parsed = @parse_url($observer['xchan_url']); - if (! $parsed) { - notice( t('Unable to find your site.') . EOL); - return; - } - $url = $parsed['scheme'] . '://' . $parsed['host'] . (($parsed['port']) ? ':' . $parsed['port'] : ''); - $url .= '/oexchange'; - $result = z_post_url($url,$_REQUEST); - json_return_and_die($result); - } - } - - return login(false); - } - - if ((argc() > 1) && argv(1) === 'done') { - info( t('Post successful.') . EOL); - return; - } - - $url = (((x($_REQUEST,'url')) && strlen($_REQUEST['url'])) - ? urlencode(notags(trim($_REQUEST['url']))) : ''); - $title = (((x($_REQUEST,'title')) && strlen($_REQUEST['title'])) - ? '&title=' . urlencode(notags(trim($_REQUEST['title']))) : ''); - $description = (((x($_REQUEST,'description')) && strlen($_REQUEST['description'])) - ? '&description=' . urlencode(notags(trim($_REQUEST['description']))) : ''); - $tags = (((x($_REQUEST,'tags')) && strlen($_REQUEST['tags'])) - ? '&tags=' . urlencode(notags(trim($_REQUEST['tags']))) : ''); - - $ret = z_fetch_url(z_root() . '/linkinfo?f=&url=' . $url . $title . $description . $tags); - - if ($ret['success']) { - $s = $ret['body']; - } - - if (! strlen($s)) { - return; - } - - $post = []; - - $post['profile_uid'] = local_channel(); - $post['return'] = '/oexchange/done' ; - $post['body'] = $s; - $post['type'] = 'wall'; - - $_REQUEST = $post; - $mod = new Item(); - $mod->post(); - - } - - - + public function init() + { + + if ((argc() > 1) && (argv(1) === 'xrd')) { + echo replace_macros(get_markup_template('oexchange_xrd.tpl'), ['$base' => z_root()]); + killme(); + } + } + + public function get() + { + if (!local_channel()) { + if (remote_channel()) { + $observer = App::get_observer(); + if ($observer && $observer['xchan_url']) { + $parsed = @parse_url($observer['xchan_url']); + if (!$parsed) { + notice(t('Unable to find your site.') . EOL); + return; + } + $url = $parsed['scheme'] . '://' . $parsed['host'] . (($parsed['port']) ? ':' . $parsed['port'] : ''); + $url .= '/oexchange'; + $result = z_post_url($url, $_REQUEST); + json_return_and_die($result); + } + } + + return login(false); + } + + if ((argc() > 1) && argv(1) === 'done') { + info(t('Post successful.') . EOL); + return; + } + + $url = (((x($_REQUEST, 'url')) && strlen($_REQUEST['url'])) + ? urlencode(notags(trim($_REQUEST['url']))) : ''); + $title = (((x($_REQUEST, 'title')) && strlen($_REQUEST['title'])) + ? '&title=' . urlencode(notags(trim($_REQUEST['title']))) : ''); + $description = (((x($_REQUEST, 'description')) && strlen($_REQUEST['description'])) + ? '&description=' . urlencode(notags(trim($_REQUEST['description']))) : ''); + $tags = (((x($_REQUEST, 'tags')) && strlen($_REQUEST['tags'])) + ? '&tags=' . urlencode(notags(trim($_REQUEST['tags']))) : ''); + + $ret = z_fetch_url(z_root() . '/linkinfo?f=&url=' . $url . $title . $description . $tags); + + if ($ret['success']) { + $s = $ret['body']; + } + + if (!strlen($s)) { + return; + } + + $post = []; + + $post['profile_uid'] = local_channel(); + $post['return'] = '/oexchange/done'; + $post['body'] = $s; + $post['type'] = 'wall'; + + $_REQUEST = $post; + $mod = new Item(); + $mod->post(); + } } diff --git a/Zotlabs/Module/Online.php b/Zotlabs/Module/Online.php index 30cd8d95e..c3d46940b 100644 --- a/Zotlabs/Module/Online.php +++ b/Zotlabs/Module/Online.php @@ -3,13 +3,15 @@ namespace Zotlabs\Module; use Zotlabs\Web\Controller; -class Online extends Controller { +class Online extends Controller +{ - function init() { - $ret = [ 'result' => false ]; - if (argc() != 2) { - json_return_and_die($ret); - } - json_return_and_die(get_online_status(argv(1))); - } + public function init() + { + $ret = ['result' => false]; + if (argc() != 2) { + json_return_and_die($ret); + } + json_return_and_die(get_online_status(argv(1))); + } } diff --git a/Zotlabs/Module/Outbox.php b/Zotlabs/Module/Outbox.php index 53fcbf516..14d7270f8 100644 --- a/Zotlabs/Module/Outbox.php +++ b/Zotlabs/Module/Outbox.php @@ -15,300 +15,303 @@ use Zotlabs\Lib\Config; /** * Implements an ActivityPub outbox. */ +class Outbox extends Controller +{ + public function post() + { + if (argc() < 2) { + killme(); + } -class Outbox extends Controller { + $channel = channelx_by_nick(argv(1)); + if (!$channel) { + killme(); + } - function post() { - if (argc() < 2) { - killme(); - } + if (intval($channel['channel_system'])) { + killme(); + } - $channel = channelx_by_nick(argv(1)); - if (! $channel) { - killme(); - } + // At this point there is unlikely to be an authenticated observer using the C2S ActivityPub API. + // Mostly we're protecting the page from malicious mischief until the project's OAuth2 interface + // is linked to this page. - if (intval($channel['channel_system'])) { - killme(); - } + $observer = App::get_observer(); + if (!$observer) { + killme(); + } - // At this point there is unlikely to be an authenticated observer using the C2S ActivityPub API. - // Mostly we're protecting the page from malicious mischief until the project's OAuth2 interface - // is linked to this page. - - $observer = App::get_observer(); - if (! $observer) { - killme(); - } - - if ($observer['xchan_hash'] !== $channel['channel_hash']) { - if (! perm_is_allowed($channel['channel_id'],$observer['xchan_hash'],'post_wall')) { - logger('outbox post permission denied to ' . $observer['xchan_name']); - killme(); - } - } + if ($observer['xchan_hash'] !== $channel['channel_hash']) { + if (!perm_is_allowed($channel['channel_id'], $observer['xchan_hash'], 'post_wall')) { + logger('outbox post permission denied to ' . $observer['xchan_name']); + killme(); + } + } // disable C2S until we've fully tested it. -return; + return; - $data = file_get_contents('php://input'); - if (! $data) { - return; - } + $data = file_get_contents('php://input'); + if (!$data) { + return; + } - logger('outbox_activity: ' . jindent($data), LOGGER_DATA); - - $AS = new ActivityStreams($data); + logger('outbox_activity: ' . jindent($data), LOGGER_DATA); - if (! $AS->is_valid()) { - return; - } - - if (! PConfig::Get($channel['channel_id'],'system','activitypub',Config::Get('system','activitypub',ACTIVITYPUB_ENABLED))) { - return; - } - - logger('outbox_channel: ' . $channel['channel_address'],LOGGER_DEBUG); + $AS = new ActivityStreams($data); -// switch ($AS->type) { -// case 'Follow': -// if (is_array($AS->obj) && array_key_exists('type', $AS->obj) && ActivityStreams::is_an_actor($AS->obj['type']) && isset($AS->obj['id'])) { -// // do follow activity -// Activity::follow($channel,$AS); -// } -// break; -// case 'Invite': -// if (is_array($AS->obj) && array_key_exists('type', $AS->obj) && $AS->obj['type'] === 'Group') { -// // do follow activity -// Activity::follow($channel,$AS); -// } -// break; -// case 'Join': -// if (is_array($AS->obj) && array_key_exists('type', $AS->obj) && $AS->obj['type'] === 'Group') { -// // do follow activity -// Activity::follow($channel,$AS); -// } -// break; -// case 'Accept': -// // Activitypub for wordpress sends lowercase 'follow' on accept. -// // https://github.com/pfefferle/wordpress-activitypub/issues/97 -// // Mobilizon sends Accept/"Member" (not in vocabulary) in response to Join/Group -// if (is_array($AS->obj) && array_key_exists('type', $AS->obj) && in_array($AS->obj['type'], ['Follow','follow', 'Member'])) { -// // do follow activity -// Activity::follow($channel,$AS); -// } -// break; -// case 'Reject': -// default: -// break; + if (!$AS->is_valid()) { + return; + } + + if (!PConfig::Get($channel['channel_id'], 'system', 'activitypub', Config::Get('system', 'activitypub', ACTIVITYPUB_ENABLED))) { + return; + } + + logger('outbox_channel: ' . $channel['channel_address'], LOGGER_DEBUG); + +// switch ($AS->type) { +// case 'Follow': +// if (is_array($AS->obj) && array_key_exists('type', $AS->obj) && ActivityStreams::is_an_actor($AS->obj['type']) && isset($AS->obj['id'])) { +// // do follow activity +// Activity::follow($channel,$AS); +// } +// break; +// case 'Invite': +// if (is_array($AS->obj) && array_key_exists('type', $AS->obj) && $AS->obj['type'] === 'Group') { +// // do follow activity +// Activity::follow($channel,$AS); +// } +// break; +// case 'Join': +// if (is_array($AS->obj) && array_key_exists('type', $AS->obj) && $AS->obj['type'] === 'Group') { +// // do follow activity +// Activity::follow($channel,$AS); +// } +// break; +// case 'Accept': +// // Activitypub for wordpress sends lowercase 'follow' on accept. +// // https://github.com/pfefferle/wordpress-activitypub/issues/97 +// // Mobilizon sends Accept/"Member" (not in vocabulary) in response to Join/Group +// if (is_array($AS->obj) && array_key_exists('type', $AS->obj) && in_array($AS->obj['type'], ['Follow','follow', 'Member'])) { +// // do follow activity +// Activity::follow($channel,$AS); +// } +// break; +// case 'Reject': +// default: +// break; // -// } +// } - // These activities require permissions + // These activities require permissions - $item = null; + $item = null; - switch ($AS->type) { - case 'Update': - if (is_array($AS->obj) && array_key_exists('type',$AS->obj) && ActivityStreams::is_an_actor($AS->obj['type'])) { - // pretend this is an old cache entry to force an update of all the actor details - $AS->obj['cached'] = true; - $AS->obj['updated'] = datetime_convert('UTC','UTC','1980-01-01', ATOM_TIME); - Activity::actor_store($AS->obj['id'],$AS->obj); - break; - } - case 'Accept': - if (is_array($AS->obj) && array_key_exists('type',$AS->obj) && (ActivityStreams::is_an_actor($AS->obj['type']) || $AS->obj['type'] === 'Member')) { - break; - } - case 'Create': - case 'Like': - case 'Dislike': - case 'Announce': - case 'Reject': - case 'TentativeAccept': - case 'TentativeReject': - case 'Add': - case 'Arrive': - case 'Block': - case 'Flag': - case 'Ignore': - case 'Invite': - case 'Listen': - case 'Move': - case 'Offer': - case 'Question': - case 'Read': - case 'Travel': - case 'View': - case 'emojiReaction': - case 'EmojiReaction': - case 'EmojiReact': - // These require a resolvable object structure - if (is_array($AS->obj)) { - // The boolean flag enables html cache of the item - $item = Activity::decode_note($AS,true); - } - else { - logger('unresolved object: ' . print_r($AS->obj,true)); - } - break; - case 'Undo': - if ($AS->obj && is_array($AS->obj) && array_key_exists('type', $AS->obj) && $AS->obj['type'] === 'Follow') { - // do unfollow activity - Activity::unfollow($channel,$AS); - break; - } - case 'Leave': - if ($AS->obj && is_array($AS->obj) && array_key_exists('type', $AS->obj) && $AS->obj['type'] === 'Group') { - // do unfollow activity - Activity::unfollow($channel,$AS); - break; - } - case 'Tombstone': - case 'Delete': - Activity::drop($channel,$observer_hash,$AS); - break; - case 'Move': - if($observer_hash && $observer_hash === $AS->actor - && is_array($AS->obj) && array_key_exists('type', $AS->obj) && ActivityStream::is_an_actor($AS->obj['type']) - && is_array($AS->tgt) && array_key_exists('type', $AS->tgt) && ActivityStream::is_an_actor($AS->tgt['type'])) { - ActivityPub::move($AS->obj,$AS->tgt); - } - break; - case 'Add': - case 'Remove': - default: - break; + switch ($AS->type) { + case 'Update': + if (is_array($AS->obj) && array_key_exists('type', $AS->obj) && ActivityStreams::is_an_actor($AS->obj['type'])) { + // pretend this is an old cache entry to force an update of all the actor details + $AS->obj['cached'] = true; + $AS->obj['updated'] = datetime_convert('UTC', 'UTC', '1980-01-01', ATOM_TIME); + Activity::actor_store($AS->obj['id'], $AS->obj); + break; + } + case 'Accept': + if (is_array($AS->obj) && array_key_exists('type', $AS->obj) && (ActivityStreams::is_an_actor($AS->obj['type']) || $AS->obj['type'] === 'Member')) { + break; + } + case 'Create': + case 'Like': + case 'Dislike': + case 'Announce': + case 'Reject': + case 'TentativeAccept': + case 'TentativeReject': + case 'Add': + case 'Arrive': + case 'Block': + case 'Flag': + case 'Ignore': + case 'Invite': + case 'Listen': + case 'Move': + case 'Offer': + case 'Question': + case 'Read': + case 'Travel': + case 'View': + case 'emojiReaction': + case 'EmojiReaction': + case 'EmojiReact': + // These require a resolvable object structure + if (is_array($AS->obj)) { + // The boolean flag enables html cache of the item + $item = Activity::decode_note($AS, true); + } else { + logger('unresolved object: ' . print_r($AS->obj, true)); + } + break; + case 'Undo': + if ($AS->obj && is_array($AS->obj) && array_key_exists('type', $AS->obj) && $AS->obj['type'] === 'Follow') { + // do unfollow activity + Activity::unfollow($channel, $AS); + break; + } + case 'Leave': + if ($AS->obj && is_array($AS->obj) && array_key_exists('type', $AS->obj) && $AS->obj['type'] === 'Group') { + // do unfollow activity + Activity::unfollow($channel, $AS); + break; + } + case 'Tombstone': + case 'Delete': + Activity::drop($channel, $observer_hash, $AS); + break; + case 'Move': + if ( + $observer_hash && $observer_hash === $AS->actor + && is_array($AS->obj) && array_key_exists('type', $AS->obj) && ActivityStream::is_an_actor($AS->obj['type']) + && is_array($AS->tgt) && array_key_exists('type', $AS->tgt) && ActivityStream::is_an_actor($AS->tgt['type']) + ) { + ActivityPub::move($AS->obj, $AS->tgt); + } + break; + case 'Add': + case 'Remove': + default: + break; + } - } + if ($item) { + logger('parsed_item: ' . print_r($item, true), LOGGER_DATA); + Activity::store($channel, $observer_hash, $AS, $item); + } - if ($item) { - logger('parsed_item: ' . print_r($item,true),LOGGER_DATA); - Activity::store($channel,$observer_hash,$AS,$item); - } - - http_status_exit(200,'OK'); - return; - - } + http_status_exit(200, 'OK'); + return; + } - function get() { + public function get() + { - if (observer_prohibited(true)) { - killme(); - } + if (observer_prohibited(true)) { + killme(); + } - if (argc() < 2) { - killme(); - } + if (argc() < 2) { + killme(); + } - $channel = channelx_by_nick(argv(1)); - if (! $channel) { - killme(); - } + $channel = channelx_by_nick(argv(1)); + if (!$channel) { + killme(); + } -// if (intval($channel['channel_system'])) { -// killme(); -// } - - if (ActivityStreams::is_as_request()) { - $sigdata = HTTPSig::verify(($_SERVER['REQUEST_METHOD'] === 'POST') ? file_get_contents('php://input') : EMPTY_STR); - if ($sigdata['portable_id'] && $sigdata['header_valid']) { - $portable_id = $sigdata['portable_id']; - if (! check_channelallowed($portable_id)) { - http_status_exit(403, 'Permission denied'); - } - if (! check_siteallowed($sigdata['signer'])) { - http_status_exit(403, 'Permission denied'); - } - observer_auth($portable_id); - } - elseif (Config::get('system','require_authenticated_fetch',false)) { - http_status_exit(403,'Permission denied'); - } +// if (intval($channel['channel_system'])) { +// killme(); +// } - $observer_hash = get_observer_hash(); + if (ActivityStreams::is_as_request()) { + $sigdata = HTTPSig::verify(($_SERVER['REQUEST_METHOD'] === 'POST') ? file_get_contents('php://input') : EMPTY_STR); + if ($sigdata['portable_id'] && $sigdata['header_valid']) { + $portable_id = $sigdata['portable_id']; + if (!check_channelallowed($portable_id)) { + http_status_exit(403, 'Permission denied'); + } + if (!check_siteallowed($sigdata['signer'])) { + http_status_exit(403, 'Permission denied'); + } + observer_auth($portable_id); + } elseif (Config::get('system', 'require_authenticated_fetch', false)) { + http_status_exit(403, 'Permission denied'); + } - $params = []; - - $params['begin'] = ((x($_REQUEST,'date_begin')) ? $_REQUEST['date_begin'] : NULL_DATE); - $params['end'] = ((x($_REQUEST,'date_end')) ? $_REQUEST['date_end'] : ''); - $params['type'] = 'json'; - $params['pages'] = ((x($_REQUEST,'pages')) ? intval($_REQUEST['pages']) : 0); - $params['top'] = ((x($_REQUEST,'top')) ? intval($_REQUEST['top']) : 0); - $params['direction'] = ((x($_REQUEST,'direction')) ? dbesc($_REQUEST['direction']) : 'desc'); // unimplemented - $params['cat'] = ((x($_REQUEST,'cat')) ? escape_tags($_REQUEST['cat']) : ''); - $params['compat'] = 1; + $observer_hash = get_observer_hash(); - - $total = items_fetch( - [ - 'total' => true, - 'wall' => '1', - 'datequery' => $params['end'], - 'datequery2' => $params['begin'], - 'direction' => dbesc($params['direction']), - 'pages' => $params['pages'], - 'order' => dbesc('post'), - 'top' => $params['top'], - 'cat' => $params['cat'], - 'compat' => $params['compat'] - ], $channel, $observer_hash, CLIENT_MODE_NORMAL, App::$module - ); + $params = []; - if ($total) { - App::set_pager_total($total); - App::set_pager_itemspage(100); - } + $params['begin'] = ((x($_REQUEST, 'date_begin')) ? $_REQUEST['date_begin'] : NULL_DATE); + $params['end'] = ((x($_REQUEST, 'date_end')) ? $_REQUEST['date_end'] : ''); + $params['type'] = 'json'; + $params['pages'] = ((x($_REQUEST, 'pages')) ? intval($_REQUEST['pages']) : 0); + $params['top'] = ((x($_REQUEST, 'top')) ? intval($_REQUEST['top']) : 0); + $params['direction'] = ((x($_REQUEST, 'direction')) ? dbesc($_REQUEST['direction']) : 'desc'); // unimplemented + $params['cat'] = ((x($_REQUEST, 'cat')) ? escape_tags($_REQUEST['cat']) : ''); + $params['compat'] = 1; - if (App::$pager['unset'] && $total > 100) { - $ret = Activity::paged_collection_init($total,App::$query_string); - } - else { - $items = items_fetch( - [ - 'wall' => '1', - 'datequery' => $params['end'], - 'datequery2' => $params['begin'], - 'records' => intval(App::$pager['itemspage']), - 'start' => intval(App::$pager['start']), - 'direction' => dbesc($params['direction']), - 'pages' => $params['pages'], - 'order' => dbesc('post'), - 'top' => $params['top'], - 'cat' => $params['cat'], - 'compat' => $params['compat'] - ], $channel, $observer_hash, CLIENT_MODE_NORMAL, App::$module - ); - if ($items && $observer_hash) { + $total = items_fetch( + [ + 'total' => true, + 'wall' => '1', + 'datequery' => $params['end'], + 'datequery2' => $params['begin'], + 'direction' => dbesc($params['direction']), + 'pages' => $params['pages'], + 'order' => dbesc('post'), + 'top' => $params['top'], + 'cat' => $params['cat'], + 'compat' => $params['compat'] + ], + $channel, + $observer_hash, + CLIENT_MODE_NORMAL, + App::$module + ); - // check to see if this observer is a connection. If not, register any items - // belonging to this channel for notification of deletion/expiration - - $x = q("select abook_id from abook where abook_channel = %d and abook_xchan = '%s'", - intval($channel['channel_id']), - dbesc($observer_hash) - ); - if (! $x) { - foreach ($items as $item) { - if (strpos($item['mid'], z_root()) === 0) { - ThreadListener::store($item['mid'],$observer_hash); - } - } - } - } + if ($total) { + App::set_pager_total($total); + App::set_pager_itemspage(100); + } - $ret = Activity::encode_item_collection($items, App::$query_string, 'OrderedCollection',true, $total); - } + if (App::$pager['unset'] && $total > 100) { + $ret = Activity::paged_collection_init($total, App::$query_string); + } else { + $items = items_fetch( + [ + 'wall' => '1', + 'datequery' => $params['end'], + 'datequery2' => $params['begin'], + 'records' => intval(App::$pager['itemspage']), + 'start' => intval(App::$pager['start']), + 'direction' => dbesc($params['direction']), + 'pages' => $params['pages'], + 'order' => dbesc('post'), + 'top' => $params['top'], + 'cat' => $params['cat'], + 'compat' => $params['compat'] + ], + $channel, + $observer_hash, + CLIENT_MODE_NORMAL, + App::$module + ); - as_return_and_die($ret,$channel); - } - } + if ($items && $observer_hash) { + // check to see if this observer is a connection. If not, register any items + // belonging to this channel for notification of deletion/expiration + + $x = q( + "select abook_id from abook where abook_channel = %d and abook_xchan = '%s'", + intval($channel['channel_id']), + dbesc($observer_hash) + ); + if (!$x) { + foreach ($items as $item) { + if (strpos($item['mid'], z_root()) === 0) { + ThreadListener::store($item['mid'], $observer_hash); + } + } + } + } + + $ret = Activity::encode_item_collection($items, App::$query_string, 'OrderedCollection', true, $total); + } + + as_return_and_die($ret, $channel); + } + } } - - - diff --git a/Zotlabs/Module/Owa.php b/Zotlabs/Module/Owa.php index 5b52e788f..a18f2e2b1 100644 --- a/Zotlabs/Module/Owa.php +++ b/Zotlabs/Module/Owa.php @@ -11,63 +11,66 @@ use Zotlabs\Web\Controller; * See spec/OpenWebAuth/Home.md * Requests to this endpoint should be signed using HTTP Signatures * using the 'Authorization: Signature' authentication method - * If the signature verifies a token is returned. + * If the signature verifies a token is returned. * - * This token may be exchanged for an authenticated cookie. + * This token may be exchanged for an authenticated cookie. */ +class Owa extends Controller +{ -class Owa extends Controller { + public function init() + { - function init() { + $ret = ['success' => false]; - $ret = [ 'success' => false ]; + if (array_key_exists('REDIRECT_REMOTE_USER', $_SERVER) && (!array_key_exists('HTTP_AUTHORIZATION', $_SERVER))) { + $_SERVER['HTTP_AUTHORIZATION'] = $_SERVER['REDIRECT_REMOTE_USER']; + } - if (array_key_exists('REDIRECT_REMOTE_USER',$_SERVER) && (! array_key_exists('HTTP_AUTHORIZATION',$_SERVER))) { - $_SERVER['HTTP_AUTHORIZATION'] = $_SERVER['REDIRECT_REMOTE_USER']; - } - - if (array_key_exists('HTTP_AUTHORIZATION',$_SERVER) && substr(trim($_SERVER['HTTP_AUTHORIZATION']),0,9) === 'Signature') { - $sigblock = HTTPSig::parse_sigheader($_SERVER['HTTP_AUTHORIZATION']); - if ($sigblock) { - $keyId = $sigblock['keyId']; - if ($keyId) { - $r = q("select * from hubloc left join xchan on hubloc_hash = xchan_hash + if (array_key_exists('HTTP_AUTHORIZATION', $_SERVER) && substr(trim($_SERVER['HTTP_AUTHORIZATION']), 0, 9) === 'Signature') { + $sigblock = HTTPSig::parse_sigheader($_SERVER['HTTP_AUTHORIZATION']); + if ($sigblock) { + $keyId = $sigblock['keyId']; + if ($keyId) { + $r = q( + "select * from hubloc left join xchan on hubloc_hash = xchan_hash where ( hubloc_addr = '%s' or hubloc_id_url = '%s' ) and xchan_pubkey != '' ", - dbesc(str_replace('acct:','',$keyId)), - dbesc($keyId) - ); - if (! $r) { - $found = discover_by_webbie(str_replace('acct:','',$keyId)); - if ($found) { - $r = q("select * from hubloc left join xchan on hubloc_hash = xchan_hash + dbesc(str_replace('acct:', '', $keyId)), + dbesc($keyId) + ); + if (!$r) { + $found = discover_by_webbie(str_replace('acct:', '', $keyId)); + if ($found) { + $r = q( + "select * from hubloc left join xchan on hubloc_hash = xchan_hash where ( hubloc_addr = '%s' or hubloc_id_url = '%s' ) and xchan_pubkey != '' ", - dbesc(str_replace('acct:','',$keyId)), - dbesc($keyId) - ); - } - } - if ($r) { - foreach ($r as $hubloc) { - $verified = HTTPSig::verify(file_get_contents('php://input'), $hubloc['xchan_pubkey']); - if ($verified && $verified['header_signed'] && $verified['header_valid'] && ($verified['content_valid'] || (! $verified['content_signed']))) { - logger('OWA header: ' . print_r($verified,true),LOGGER_DATA); - logger('OWA success: ' . $hubloc['hubloc_addr'],LOGGER_DATA); - $ret['success'] = true; - $token = random_string(32); - Verify::create('owt',0,$token,$hubloc['hubloc_addr']); - $result = ''; - openssl_public_encrypt($token,$result,$hubloc['xchan_pubkey']); - $ret['encrypted_token'] = base64url_encode($result); - break; - } else { - logger('OWA fail: ' . $hubloc['hubloc_id'] . ' ' . $hubloc['hubloc_addr']); - } - } - } - } - } - } - json_return_and_die($ret,'application/x-nomad+json'); - } + dbesc(str_replace('acct:', '', $keyId)), + dbesc($keyId) + ); + } + } + if ($r) { + foreach ($r as $hubloc) { + $verified = HTTPSig::verify(file_get_contents('php://input'), $hubloc['xchan_pubkey']); + if ($verified && $verified['header_signed'] && $verified['header_valid'] && ($verified['content_valid'] || (!$verified['content_signed']))) { + logger('OWA header: ' . print_r($verified, true), LOGGER_DATA); + logger('OWA success: ' . $hubloc['hubloc_addr'], LOGGER_DATA); + $ret['success'] = true; + $token = random_string(32); + Verify::create('owt', 0, $token, $hubloc['hubloc_addr']); + $result = ''; + openssl_public_encrypt($token, $result, $hubloc['xchan_pubkey']); + $ret['encrypted_token'] = base64url_encode($result); + break; + } else { + logger('OWA fail: ' . $hubloc['hubloc_id'] . ' ' . $hubloc['hubloc_addr']); + } + } + } + } + } + } + json_return_and_die($ret, 'application/x-nomad+json'); + } } diff --git a/Zotlabs/Module/Page.php b/Zotlabs/Module/Page.php index c7ca27cfe..93d606fbb 100644 --- a/Zotlabs/Module/Page.php +++ b/Zotlabs/Module/Page.php @@ -1,194 +1,202 @@ parse($r[0]['body']); - \App::$pdl = $r[0]['body']; - } - elseif($r[0]['layout_mid']) { - $l = q("select body from item where mid = '%s' and uid = %d limit 1", - dbesc($r[0]['layout_mid']), - intval($u[0]['channel_id']) - ); - - if($l) { - \App::$comanche = new \Zotlabs\Render\Comanche(); - \App::$comanche->parse($l[0]['body']); - \App::$pdl = $l[0]['body']; - } - } - - \App::$data['webpage'] = $r; - - } - - function get() { - - $r = \App::$data['webpage']; - if(! $r) - return; - - if($r[0]['item_type'] == ITEM_TYPE_PDL) { - $r[0]['body'] = t('Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.'); - $r[0]['mimetype'] = 'text/plain'; - $r[0]['title'] = ''; - - } - - xchan_query($r); - $r = fetch_post_tags($r,true); - - if($r[0]['mimetype'] === 'application/x-pdl') - \App::$page['pdl_content'] = true; - - $o .= prepare_page($r[0]); - return $o; - - } - + if ($x) { + // Yes, it's there. You just aren't allowed to see it. + notice(t('Permission denied.') . EOL); + } else { + notice(t('Page not found.') . EOL); + } + return; + } + + if ($r[0]['title']) { + App::$page['title'] = escape_tags($r[0]['title']); + } + + if ($r[0]['item_type'] == ITEM_TYPE_PDL) { + App::$comanche = new Comanche(); + App::$comanche->parse($r[0]['body']); + App::$pdl = $r[0]['body']; + } elseif ($r[0]['layout_mid']) { + $l = q( + "select body from item where mid = '%s' and uid = %d limit 1", + dbesc($r[0]['layout_mid']), + intval($u[0]['channel_id']) + ); + + if ($l) { + App::$comanche = new Comanche(); + App::$comanche->parse($l[0]['body']); + App::$pdl = $l[0]['body']; + } + } + + App::$data['webpage'] = $r; + } + + public function get() + { + + $r = App::$data['webpage']; + if (!$r) { + return; + } + + if ($r[0]['item_type'] == ITEM_TYPE_PDL) { + $r[0]['body'] = t('Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.'); + $r[0]['mimetype'] = 'text/plain'; + $r[0]['title'] = ''; + } + + xchan_query($r); + $r = fetch_post_tags($r, true); + + if ($r[0]['mimetype'] === 'application/x-pdl') { + App::$page['pdl_content'] = true; + } + + $o .= prepare_page($r[0]); + return $o; + } } diff --git a/Zotlabs/Module/Pconfig.php b/Zotlabs/Module/Pconfig.php index a1e677928..52da386ef 100644 --- a/Zotlabs/Module/Pconfig.php +++ b/Zotlabs/Module/Pconfig.php @@ -1,145 +1,142 @@ disallowed_pconfig())) { + notice(t('This setting requires special processing and editing has been blocked.') . EOL); + return; + } + + if (strpos($k, 'password') !== false) { + $v = obscurify($v); + } + + set_pconfig(local_channel(), $cat, $k, $v); + Libsync::build_sync_packet(); + + if ($aj) { + killme(); + } + + goaway(z_root() . '/pconfig/' . $cat . '/' . $k); + } -class Pconfig extends Controller { + public function get() + { - function post() { - - if (! local_channel()) { - return; - } - - if (isset($_SESSION['delegate']) && $_SESSION['delegate']) { - return; - } - - check_form_security_token_redirectOnErr('/pconfig', 'pconfig'); - - $cat = trim(escape_tags((isset($_POST['cat']) && $_POST['cat']) ? $_POST['cat'] : EMPTY_STR)); - $k = trim(escape_tags((isset($_POST['k']) && $_POST['k']) ? $_POST['k'] : EMPTY_STR)); - $v = trim((isset($_POST['v']) && $_POST['v']) ? $_POST['v'] : EMPTY_STR); - $aj = intval((isset($_POST['aj']) && $_POST['aj']) ? $_POST['aj'] : 0); + if (!local_channel()) { + return login(); + } - // Do not store "serialized" data received in the $_POST - - if (preg_match('|^a:[0-9]+:{.*}$|s',$v)) { - return; - } - - if (in_array(argv(2),$this->disallowed_pconfig())) { - notice( t('This setting requires special processing and editing has been blocked.') . EOL); - return; - } - - if (strpos($k,'password') !== false) { - $v = obscurify($v); - } - - set_pconfig(local_channel(),$cat,$k,$v); - Libsync::build_sync_packet(); - - if ($aj) { - killme(); - } - - goaway(z_root() . '/pconfig/' . $cat . '/' . $k); - - } - - - function get() { - - if (! local_channel()) { - return login(); - } - - $content = '

      ' . t('Configuration Editor') . '

      '; - $content .= '
      ' . t('Warning: Changing some settings could render your channel inoperable. Please leave this page unless you are comfortable with and knowledgeable about how to correctly use this feature.') . '
      ' . EOL . EOL; - - - - if(argc() == 3) { - $content .= 'pconfig[' . local_channel() . ']' . EOL; - $content .= 'pconfig[' . local_channel() . '][' . escape_tags(argv(1)) . ']' . EOL . EOL; - $content .= 'pconfig[' . local_channel() . '][' . escape_tags(argv(1)) . '][' . escape_tags(argv(2)) . '] = ' . get_pconfig(local_channel(),escape_tags(argv(1)),escape_tags(argv(2))) . EOL; - - if(in_array(argv(2),$this->disallowed_pconfig())) { - notice( t('This setting requires special processing and editing has been blocked.') . EOL); - return $content; - } - else - $content .= $this->pconfig_form(escape_tags(argv(1)),escape_tags(argv(2))); - } - - - if(argc() == 2) { - $content .= 'pconfig[' . local_channel() . ']' . EOL; - load_pconfig(local_channel(),escape_tags(argv(1))); - if(\App::$config[local_channel()][escape_tags(argv(1))]) { - foreach(\App::$config[local_channel()][escape_tags(argv(1))] as $k => $x) { - $content .= 'pconfig[' . local_channel() . '][' . escape_tags(argv(1)) . '][' . $k . '] = ' . escape_tags($x) . EOL; - } - } - } - - if(argc() == 1) { - - $r = q("select * from pconfig where uid = " . local_channel()); - if($r) { - foreach($r as $rr) { - $content .= 'pconfig[' . local_channel() . '][' . escape_tags($rr['cat']) . '][' . escape_tags($rr['k']) . '] = ' . escape_tags($rr['v']) . EOL; - } - } - } - return $content; - - } - - - function pconfig_form($cat,$k) { - - $o = '
      '; - $o .= ''; - - $v = get_pconfig(local_channel(),$cat,$k); - if(strpos($k,'password') !== false) - $v = unobscurify($v); - - $o .= ''; - $o .= ''; - - - - if(strpos($v,"\n")) - $o .= ''; - else { - if (is_array($v)) { - $o .= '
      ' . "\n" . print_array($v) . '
      '; - $o .= ''; - } - else { - $o .= ''; - } - } - $o .= EOL . EOL; - $o .= ''; - $o .= '
      '; - - return $o; - - } + $content = '

      ' . t('Configuration Editor') . '

      '; + $content .= '
      ' . t('Warning: Changing some settings could render your channel inoperable. Please leave this page unless you are comfortable with and knowledgeable about how to correctly use this feature.') . '
      ' . EOL . EOL; + if (argc() == 3) { + $content .= 'pconfig[' . local_channel() . ']' . EOL; + $content .= 'pconfig[' . local_channel() . '][' . escape_tags(argv(1)) . ']' . EOL . EOL; + $content .= 'pconfig[' . local_channel() . '][' . escape_tags(argv(1)) . '][' . escape_tags(argv(2)) . '] = ' . get_pconfig(local_channel(), escape_tags(argv(1)), escape_tags(argv(2))) . EOL; - function disallowed_pconfig() { - return array( - 'permissions_role' - ); - } - + if (in_array(argv(2), $this->disallowed_pconfig())) { + notice(t('This setting requires special processing and editing has been blocked.') . EOL); + return $content; + } else { + $content .= $this->pconfig_form(escape_tags(argv(1)), escape_tags(argv(2))); + } + } + + + if (argc() == 2) { + $content .= 'pconfig[' . local_channel() . ']' . EOL; + load_pconfig(local_channel(), escape_tags(argv(1))); + if (App::$config[local_channel()][escape_tags(argv(1))]) { + foreach (App::$config[local_channel()][escape_tags(argv(1))] as $k => $x) { + $content .= 'pconfig[' . local_channel() . '][' . escape_tags(argv(1)) . '][' . $k . '] = ' . escape_tags($x) . EOL; + } + } + } + + if (argc() == 1) { + $r = q("select * from pconfig where uid = " . local_channel()); + if ($r) { + foreach ($r as $rr) { + $content .= 'pconfig[' . local_channel() . '][' . escape_tags($rr['cat']) . '][' . escape_tags($rr['k']) . '] = ' . escape_tags($rr['v']) . EOL; + } + } + } + return $content; + } + + + public function pconfig_form($cat, $k) + { + + $o = '
      '; + $o .= ''; + + $v = get_pconfig(local_channel(), $cat, $k); + if (strpos($k, 'password') !== false) { + $v = unobscurify($v); + } + + $o .= ''; + $o .= ''; + + + if (strpos($v, "\n")) { + $o .= ''; + } else { + if (is_array($v)) { + $o .= '
      ' . "\n" . print_array($v) . '
      '; + $o .= ''; + } else { + $o .= ''; + } + } + $o .= EOL . EOL; + $o .= ''; + $o .= '
      '; + + return $o; + } + + + public function disallowed_pconfig() + { + return array( + 'permissions_role' + ); + } } diff --git a/Zotlabs/Module/Pdledit.php b/Zotlabs/Module/Pdledit.php index 13f7bc0da..756601242 100644 --- a/Zotlabs/Module/Pdledit.php +++ b/Zotlabs/Module/Pdledit.php @@ -1,111 +1,119 @@ 2 && argv(2) === 'reset') { - del_pconfig(local_channel(),'system','mod_' . argv(1) . '.pdl'); - goaway(z_root() . '/pdledit'); - } - if(argc() > 1) - $module = 'mod_' . argv(1) . '.pdl'; - else { - $o .= '
      '; - $o .= '

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

      '; + public function get() + { - $edited = []; + if (!local_channel()) { + notice(t('Permission denied.') . EOL); + return; + } - $r = q("select k from pconfig where uid = %d and cat = 'system' and k like '%s' ", - intval(local_channel()), - dbesc('mod_%.pdl') - ); + if (argc() > 2 && argv(2) === 'reset') { + del_pconfig(local_channel(), 'system', 'mod_' . argv(1) . '.pdl'); + goaway(z_root() . '/pdledit'); + } - if($r) { - foreach($r as $rv) { - $edited[] = substr(str_replace('.pdl','',$rv['k']),4); - } - } - - $files = glob('Zotlabs/Module/*.php'); - if($files) { - foreach($files as $f) { - $name = lcfirst(basename($f,'.php')); - $x = theme_include('mod_' . $name . '.pdl'); - if($x) { - $o .= '' . $name . '' . ((in_array($name,$edited)) ? ' ' . t('(modified)') . ' ' . t('Reset') . '': '' ) . '
      '; - } - } - } - $addons = glob('addon/*/*.pdl'); - if($addons) { - foreach($addons as $a) { - $name = substr(basename($a, '.pdl'),4); - $o .= '' . $name . '' . ((in_array($name,$edited)) ? ' ' . t('(modified)') . ' ' . t('Reset') . '': '' ) . '
      '; - } - } + if (argc() > 1) { + $module = 'mod_' . argv(1) . '.pdl'; + } else { + $o .= '
      '; + $o .= '

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

      '; - $o .= '
      '; - - // list module pdl files - return $o; - } - - $t = get_pconfig(local_channel(),'system',$module); - $s = @file_get_contents(theme_include($module)); - if(! $s) { - $a = glob('addon/*/' . $module); - if($a) - $s = @file_get_contents($a[0]); - } - if(! $t) { - $t = $s; - } - if(! $t) { - notice( t('Layout not found.') . EOL); - return ''; - } - - $o = replace_macros(get_markup_template('pdledit.tpl'),array( - '$header' => t('Edit System Page Description'), - '$mname' => t('Module Name:'), - '$help' => t('Layout Help'), - '$another' => t('Edit another layout'), - '$original' => t('System layout'), - '$module' => argv(1), - '$src' => $s, - '$content' => htmlspecialchars($t,ENT_COMPAT,'UTF-8'), - '$submit' => t('Submit') - )); - - return $o; - } - + $edited = []; + + $r = q( + "select k from pconfig where uid = %d and cat = 'system' and k like '%s' ", + intval(local_channel()), + dbesc('mod_%.pdl') + ); + + if ($r) { + foreach ($r as $rv) { + $edited[] = substr(str_replace('.pdl', '', $rv['k']), 4); + } + } + + $files = glob('Zotlabs/Module/*.php'); + if ($files) { + foreach ($files as $f) { + $name = lcfirst(basename($f, '.php')); + $x = theme_include('mod_' . $name . '.pdl'); + if ($x) { + $o .= '' . $name . '' . ((in_array($name, $edited)) ? ' ' . t('(modified)') . ' ' . t('Reset') . '' : '') . '
      '; + } + } + } + $addons = glob('addon/*/*.pdl'); + if ($addons) { + foreach ($addons as $a) { + $name = substr(basename($a, '.pdl'), 4); + $o .= '' . $name . '' . ((in_array($name, $edited)) ? ' ' . t('(modified)') . ' ' . t('Reset') . '' : '') . '
      '; + } + } + + $o .= '
      '; + + // list module pdl files + return $o; + } + + $t = get_pconfig(local_channel(), 'system', $module); + $s = @file_get_contents(theme_include($module)); + if (!$s) { + $a = glob('addon/*/' . $module); + if ($a) { + $s = @file_get_contents($a[0]); + } + } + if (!$t) { + $t = $s; + } + if (!$t) { + notice(t('Layout not found.') . EOL); + return ''; + } + + $o = replace_macros(get_markup_template('pdledit.tpl'), array( + '$header' => t('Edit System Page Description'), + '$mname' => t('Module Name:'), + '$help' => t('Layout Help'), + '$another' => t('Edit another layout'), + '$original' => t('System layout'), + '$module' => argv(1), + '$src' => $s, + '$content' => htmlspecialchars($t, ENT_COMPAT, 'UTF-8'), + '$submit' => t('Submit') + )); + + return $o; + } } diff --git a/Zotlabs/Module/Permcat.php b/Zotlabs/Module/Permcat.php index 064c9cefb..6e49ea6ec 100644 --- a/Zotlabs/Module/Permcat.php +++ b/Zotlabs/Module/Permcat.php @@ -2,24 +2,26 @@ namespace Zotlabs\Module; -use \Zotlabs\Lib as Zlib; +use Zotlabs\Lib as Zlib; +use Zotlabs\Web\Controller; -class Permcat extends \Zotlabs\Web\Controller { +class Permcat extends Controller +{ - private $permcats = []; + private $permcats = []; - public function init() { - if(! local_channel()) - return; + public function init() + { + if (! local_channel()) { + return; + } - $permcat = new Zlib\Permcat(local_channel()); + $permcat = new Zlib\Permcat(local_channel()); - if(argc() > 1) - json_return_and_die($permcat->fetch(argv(1))); + if (argc() > 1) { + json_return_and_die($permcat->fetch(argv(1))); + } - json_return_and_die($permcat->listing()); - - } - - -} \ No newline at end of file + json_return_and_die($permcat->listing()); + } +} diff --git a/Zotlabs/Module/Photo.php b/Zotlabs/Module/Photo.php index 5db3e0870..f08258bad 100644 --- a/Zotlabs/Module/Photo.php +++ b/Zotlabs/Module/Photo.php @@ -1,4 +1,5 @@ $resolution, 'channel_id' => $uid, 'default' => $default, 'data' => '', 'mimetype' => '' ]; - call_hooks('get_profile_photo',$d); - - $resolution = $d['imgscale']; - $uid = $d['channel_id']; - $default = $d['default']; - $data = $d['data']; - $mimetype = $d['mimetype']; - - if (! $data) { - $r = q("SELECT * FROM photo WHERE imgscale = %d AND uid = %d AND photo_usage = %d LIMIT 1", - intval($resolution), - intval($uid), - intval(PHOTO_PROFILE) - ); - if ($r) { - $data = dbunescbin($r[0]['content']); - $mimetype = $r[0]['mimetype']; - if (intval($r[0]['os_storage'])) { - $data = file_get_contents($data); - } - } - } - if (! $data) { - $data = fetch_image_from_url($default,$mimetype); - } - if (! $mimetype) { - $mimetype = 'image/png'; - } - } - else { - - $bear = Activity::token_from_request(); - if ($bear) { - logger('bear: ' . $bear, LOGGER_DEBUG); - } + public function init() + { - /** - * Other photos - */ - - /* Check for a cookie to indicate display pixel density, in order to detect high-resolution - displays. This procedure was derived from the "Retina Images" by Jeremey Worboys, - used in accordance with the Creative Commons Attribution 3.0 Unported License. - Project link: https://github.com/Retina-Images/Retina-Images - License link: http://creativecommons.org/licenses/by/3.0/ - */ + if (ActivityStreams::is_as_request()) { + $sigdata = HTTPSig::verify(EMPTY_STR); + if ($sigdata['portable_id'] && $sigdata['header_valid']) { + $portable_id = $sigdata['portable_id']; + if (!check_channelallowed($portable_id)) { + http_status_exit(403, 'Permission denied'); + } + if (!check_siteallowed($sigdata['signer'])) { + http_status_exit(403, 'Permission denied'); + } + observer_auth($portable_id); + } elseif (Config::get('system', 'require_authenticated_fetch', false)) { + http_status_exit(403, 'Permission denied'); + } - $cookie_value = false; - if (isset($_COOKIE['devicePixelRatio'])) { - $cookie_value = intval($_COOKIE['devicePixelRatio']); - } - else { - // Force revalidation of cache on next request - $cache_directive = 'no-cache'; - $status = 'no cookie'; - } - - $resolution = 0; - - if (strpos($photo,'.') !== false) { - $photo = substr($photo,0,strpos($photo,'.')); - } - - if (substr($photo,-2,1) == '-') { - $resolution = intval(substr($photo,-1,1)); - $photo = substr($photo,0,-2); - // If viewing on a high-res screen, attempt to serve a higher resolution image: - if ($resolution == 2 && ($cookie_value > 1)) { - $resolution = 1; - } - } - - $r = q("SELECT uid, photo_usage FROM photo WHERE resource_id = '%s' AND imgscale = %d LIMIT 1", - dbesc($photo), - intval($resolution) - ); - if ($r) { + $observer_xchan = get_observer_hash(); + $allowed = false; - $allowed = (-1); + $bear = Activity::token_from_request(); + if ($bear) { + logger('bear: ' . $bear, LOGGER_DEBUG); + } - if (intval($r[0]['photo_usage'])) { - $allowed = 1; - if (intval($r[0]['photo_usage']) === PHOTO_COVER) { - if ($resolution < PHOTO_RES_COVER_1200) { - $allowed = (-1); - } - } - if (intval($r[0]['photo_usage']) === PHOTO_PROFILE) { - if (! in_array($resolution,[4,5,6])) { - $allowed = (-1); - } - } - } + $r = q( + "select * from item where resource_type = 'photo' and resource_id = '%s' limit 1", + dbesc(argv(1)) + ); + if ($r) { + $allowed = attach_can_view($r[0]['uid'], $observer_xchan, argv(1), $bear); + } + if (!$allowed) { + http_status_exit(404, 'Permission denied.'); + } + $channel = channelx_by_n($r[0]['uid']); - if ($allowed === (-1)) { - $allowed = attach_can_view($r[0]['uid'],$observer_xchan,$photo,$bear); - } + $obj = json_decode($r[0]['obj'], true); - $channel = channelx_by_n($r[0]['uid']); + as_return_and_die($obj, $channel); + } - // Now we'll see if we can access the photo - $e = q("SELECT * FROM photo WHERE resource_id = '%s' AND imgscale = %d $sql_extra LIMIT 1", - dbesc($photo), - intval($resolution) - ); + $prvcachecontrol = false; + $streaming = null; + $channel = null; + $person = 0; - $exists = (($e) ? true : false); + switch (argc()) { + case 4: + $person = argv(3); + $res = argv(2); + $type = argv(1); + break; + case 2: + $photo = argv(1); + break; + case 1: + default: + killme(); + } - if ($exists && $allowed) { - $data = dbunescbin($e[0]['content']); - $mimetype = $e[0]['mimetype']; - if (intval($e[0]['os_storage'])) { - $streaming = $data; - } + $token = ((isset($_REQUEST['token'])) ? $_REQUEST['token'] : EMPTY_STR); + $observer_xchan = get_observer_hash(); - if ($e[0]['allow_cid'] != '' || $e[0]['allow_gid'] != '' || $e[0]['deny_gid'] != '' || $e[0]['deny_gid'] != '') - $prvcachecontrol = 'no-store, no-cache, must-revalidate'; - } - else { - if (! $allowed) { - http_status_exit(403,'forbidden'); - } - if (! $exists) { - http_status_exit(404,'not found'); - } - } - } - } - - if (! isset($data)) { - if (isset($resolution)) { - switch ($resolution) { - case 4: - $data = fetch_image_from_url(z_root() . '/' . get_default_profile_photo(),$mimetype); - break; - case 5: - $data = fetch_image_from_url(z_root() . '/' . get_default_profile_photo(80),$mimetype); - break; - case 6: - $data = fetch_image_from_url(z_root() . '/' . get_default_profile_photo(48),$mimetype); - break; - default: - killme(); - } - } - } - - if (isset($res) && intval($res) && $res < 500) { - $ph = photo_factory($data, $mimetype); - if ($ph && $ph->is_valid()) { - $ph->scaleImageSquare($res); - $data = $ph->imageString(); - $mimetype = $ph->getType(); - } - } - - if (function_exists('header_remove')) { - header_remove('Pragma'); - header_remove('pragma'); - } - - header("Content-type: " . $mimetype); - - if ($prvcachecontrol) { - - // it is a private photo that they have permission to view. - // tell the browser and infrastructure caches not to cache it, - // in case permissions change before the next access. - - header("Cache-Control: no-store, no-cache, must-revalidate"); - - } - else { - // The cache default for public photos is 1 day to provide a privacy trade-off, - // as somebody reducing photo permissions on a photo that is already - // "in the wild" won't be able to stop the photo from being viewed - // for this amount amount of time once it is cached. - // The privacy expectations of your site members and their perception - // of privacy where it affects the entire project may be affected. - // This has performance considerations but we highly recommend you - // leave it alone. - - $cache = get_config('system','photo_cache_time'); - if (! $cache) { - $cache = (3600 * 24); // 1 day - } - header("Expires: " . gmdate("D, d M Y H:i:s", time() + $cache) . " GMT"); - // Set browser cache age as $cache. But set timeout of 'shared caches' - // much lower in the event that infrastructure caching is present. - $smaxage = intval($cache/12); - header('Cache-Control: s-maxage='.$smaxage.'; max-age=' . $cache . ';'); - - } + $default = z_root() . '/' . get_default_profile_photo(); - // If it's a file resource, stream it. + if (isset($type)) { - if ($streaming && $channel) { - if (strpos($streaming,'store') !== false) { - $istream = fopen($streaming,'rb'); - } - else { - $istream = fopen('store/' . $channel['channel_address'] . '/' . $streaming,'rb'); - } - $ostream = fopen('php://output','wb'); - if ($istream && $ostream) { - pipe_streams($istream,$ostream); - fclose($istream); - fclose($ostream); - } - } - else { - echo $data; - } - killme(); - } - + /** + * Profile photos - Access controls on default profile photos are not honoured since they need to be exchanged with remote sites. + * + */ + + if ($type === 'profile') { + switch ($res) { + case 'm': + $resolution = 5; + $default = z_root() . '/' . get_default_profile_photo(80); + break; + case 's': + $resolution = 6; + $default = z_root() . '/' . get_default_profile_photo(48); + break; + case 'l': + default: + $resolution = 4; + break; + } + } + + $uid = $person; + + $d = ['imgscale' => $resolution, 'channel_id' => $uid, 'default' => $default, 'data' => '', 'mimetype' => '']; + call_hooks('get_profile_photo', $d); + + $resolution = $d['imgscale']; + $uid = $d['channel_id']; + $default = $d['default']; + $data = $d['data']; + $mimetype = $d['mimetype']; + + if (!$data) { + $r = q( + "SELECT * FROM photo WHERE imgscale = %d AND uid = %d AND photo_usage = %d LIMIT 1", + intval($resolution), + intval($uid), + intval(PHOTO_PROFILE) + ); + if ($r) { + $data = dbunescbin($r[0]['content']); + $mimetype = $r[0]['mimetype']; + if (intval($r[0]['os_storage'])) { + $data = file_get_contents($data); + } + } + } + if (!$data) { + $data = fetch_image_from_url($default, $mimetype); + } + if (!$mimetype) { + $mimetype = 'image/png'; + } + } else { + $bear = Activity::token_from_request(); + if ($bear) { + logger('bear: ' . $bear, LOGGER_DEBUG); + } + + + /** + * Other photos + */ + + /* Check for a cookie to indicate display pixel density, in order to detect high-resolution + displays. This procedure was derived from the "Retina Images" by Jeremey Worboys, + used in accordance with the Creative Commons Attribution 3.0 Unported License. + Project link: https://github.com/Retina-Images/Retina-Images + License link: http://creativecommons.org/licenses/by/3.0/ + */ + + $cookie_value = false; + if (isset($_COOKIE['devicePixelRatio'])) { + $cookie_value = intval($_COOKIE['devicePixelRatio']); + } else { + // Force revalidation of cache on next request + $cache_directive = 'no-cache'; + $status = 'no cookie'; + } + + $resolution = 0; + + if (strpos($photo, '.') !== false) { + $photo = substr($photo, 0, strpos($photo, '.')); + } + + if (substr($photo, -2, 1) == '-') { + $resolution = intval(substr($photo, -1, 1)); + $photo = substr($photo, 0, -2); + // If viewing on a high-res screen, attempt to serve a higher resolution image: + if ($resolution == 2 && ($cookie_value > 1)) { + $resolution = 1; + } + } + + $r = q( + "SELECT uid, photo_usage FROM photo WHERE resource_id = '%s' AND imgscale = %d LIMIT 1", + dbesc($photo), + intval($resolution) + ); + if ($r) { + $allowed = (-1); + + if (intval($r[0]['photo_usage'])) { + $allowed = 1; + if (intval($r[0]['photo_usage']) === PHOTO_COVER) { + if ($resolution < PHOTO_RES_COVER_1200) { + $allowed = (-1); + } + } + if (intval($r[0]['photo_usage']) === PHOTO_PROFILE) { + if (!in_array($resolution, [4, 5, 6])) { + $allowed = (-1); + } + } + } + + if ($allowed === (-1)) { + $allowed = attach_can_view($r[0]['uid'], $observer_xchan, $photo, $bear); + } + + $channel = channelx_by_n($r[0]['uid']); + + // Now we'll see if we can access the photo + $e = q( + "SELECT * FROM photo WHERE resource_id = '%s' AND imgscale = %d $sql_extra LIMIT 1", + dbesc($photo), + intval($resolution) + ); + + $exists = (($e) ? true : false); + + if ($exists && $allowed) { + $data = dbunescbin($e[0]['content']); + $mimetype = $e[0]['mimetype']; + if (intval($e[0]['os_storage'])) { + $streaming = $data; + } + + if ($e[0]['allow_cid'] != '' || $e[0]['allow_gid'] != '' || $e[0]['deny_gid'] != '' || $e[0]['deny_gid'] != '') { + $prvcachecontrol = 'no-store, no-cache, must-revalidate'; + } + } else { + if (!$allowed) { + http_status_exit(403, 'forbidden'); + } + if (!$exists) { + http_status_exit(404, 'not found'); + } + } + } + } + + if (!isset($data)) { + if (isset($resolution)) { + switch ($resolution) { + case 4: + $data = fetch_image_from_url(z_root() . '/' . get_default_profile_photo(), $mimetype); + break; + case 5: + $data = fetch_image_from_url(z_root() . '/' . get_default_profile_photo(80), $mimetype); + break; + case 6: + $data = fetch_image_from_url(z_root() . '/' . get_default_profile_photo(48), $mimetype); + break; + default: + killme(); + } + } + } + + if (isset($res) && intval($res) && $res < 500) { + $ph = photo_factory($data, $mimetype); + if ($ph && $ph->is_valid()) { + $ph->scaleImageSquare($res); + $data = $ph->imageString(); + $mimetype = $ph->getType(); + } + } + + if (function_exists('header_remove')) { + header_remove('Pragma'); + header_remove('pragma'); + } + + header("Content-type: " . $mimetype); + + if ($prvcachecontrol) { + // it is a private photo that they have permission to view. + // tell the browser and infrastructure caches not to cache it, + // in case permissions change before the next access. + + header("Cache-Control: no-store, no-cache, must-revalidate"); + } else { + // The cache default for public photos is 1 day to provide a privacy trade-off, + // as somebody reducing photo permissions on a photo that is already + // "in the wild" won't be able to stop the photo from being viewed + // for this amount amount of time once it is cached. + // The privacy expectations of your site members and their perception + // of privacy where it affects the entire project may be affected. + // This has performance considerations but we highly recommend you + // leave it alone. + + $cache = get_config('system', 'photo_cache_time'); + if (!$cache) { + $cache = (3600 * 24); // 1 day + } + header("Expires: " . gmdate("D, d M Y H:i:s", time() + $cache) . " GMT"); + // Set browser cache age as $cache. But set timeout of 'shared caches' + // much lower in the event that infrastructure caching is present. + $smaxage = intval($cache / 12); + header('Cache-Control: s-maxage=' . $smaxage . '; max-age=' . $cache . ';'); + } + + // If it's a file resource, stream it. + + if ($streaming && $channel) { + if (strpos($streaming, 'store') !== false) { + $istream = fopen($streaming, 'rb'); + } else { + $istream = fopen('store/' . $channel['channel_address'] . '/' . $streaming, 'rb'); + } + $ostream = fopen('php://output', 'wb'); + if ($istream && $ostream) { + pipe_streams($istream, $ostream); + fclose($istream); + fclose($ostream); + } + } else { + echo $data; + } + killme(); + } } diff --git a/Zotlabs/Module/Photomap.php b/Zotlabs/Module/Photomap.php index 275159ad6..0b2a75224 100644 --- a/Zotlabs/Module/Photomap.php +++ b/Zotlabs/Module/Photomap.php @@ -1,24 +1,25 @@ ' . $desc . ''; - if(! ( local_channel() && Apps::system_app_installed(local_channel(),'Photomap'))) { + if (!(local_channel() && Apps::system_app_installed(local_channel(), 'Photomap'))) { return $text; } - return $text . '

      ' . t('This app is currently installed.'); - } - - + return $text . '

      ' . t('This app is currently installed.'); + } } diff --git a/Zotlabs/Module/Photos.php b/Zotlabs/Module/Photos.php index 55ec8583e..fbdae071b 100644 --- a/Zotlabs/Module/Photos.php +++ b/Zotlabs/Module/Photos.php @@ -1,4 +1,5 @@ 1) { + public function init() + { - $nick = escape_tags(argv(1)); - - Libprofile::load($nick); - - $channelx = channelx_by_nick($nick); + if (observer_prohibited()) { + return; + } - $profile_uid = 0; - - if ($channelx) { - App::$data['channel'] = $channelx; - head_set_icon($channelx['xchan_photo_s']); - $profile_uid = $channelx['channel_id']; - } - - App::$page['htmlhead'] .= "" ; - App::$data['observer'] = App::get_observer(); - } - } - - - - function post() { - - logger('mod-photos: photos_post: begin' , LOGGER_DEBUG); - - logger('mod_photos: REQUEST ' . print_r($_REQUEST,true), LOGGER_DATA); - logger('mod_photos: FILES ' . print_r($_FILES,true), LOGGER_DATA); - - $ph = photo_factory(''); - - $phototypes = $ph->supportedTypes(); - - $page_owner_uid = App::$data['channel']['channel_id']; - - if (! perm_is_allowed($page_owner_uid,get_observer_hash(),'write_storage')) { - notice( t('Permission denied.') . EOL ); - killme_if_ajax(); - return; - } - - $acl = new AccessControl(App::$data['channel']); - - if ((argc() > 3) && (argv(2) === 'album')) { - - $album = argv(3); + if (argc() > 1) { + $nick = escape_tags(argv(1)); - if (! photos_album_exists($page_owner_uid, get_observer_hash(), $album)) { - notice( t('Album not found.') . EOL); - goaway(z_root() . '/' . $_SESSION['photo_return']); - } - - - /* - * DELETE photo album and all its photos - */ - - if ($_REQUEST['dropalbum'] === t('Delete Album')) { - - $folder_hash = ''; - - $r = q("select hash from attach where is_dir = 1 and uid = %d and hash = '%s'", - intval($page_owner_uid), - dbesc($album) - ); - if (! $r) { - notice( t('Album not found.') . EOL); - return; - } - $folder_hash = $r[0]['hash']; - - - $res = []; - $admin_delete = false; + Libprofile::load($nick); - // get the list of photos we are about to delete - - if (remote_channel() && (! local_channel())) { - $str = photos_album_get_db_idstr($page_owner_uid,$album,remote_channel()); - } - elseif (local_channel()) { - $str = photos_album_get_db_idstr(local_channel(),$album); - } - elseif (is_site_admin()) { - $str = photos_album_get_db_idstr_admin($page_owner_uid,$album); - $admin_delete = true; - } - else { - $str = null; - } - - if (! $str) { - goaway(z_root() . '/' . $_SESSION['photo_return']); - } - - $r = q("select id from item where resource_id in ( $str ) and resource_type = 'photo' and uid = %d " . item_normal(), - intval($page_owner_uid) - ); - if($r) { - foreach($r as $rv) { - attach_delete($page_owner_uid, $rv['resource_id'], true ); - } - } - - // remove the associated photos in case they weren't attached to an item (rare) - - q("delete from photo where resource_id in ( $str ) and uid = %d", - intval($page_owner_uid) - ); + $channelx = channelx_by_nick($nick); - q("delete from attach where hash in ( $str ) and uid = %d", - intval($page_owner_uid) - ); - - if ($folder_hash) { - attach_delete($page_owner_uid, $folder_hash, true ); + $profile_uid = 0; - // Sync this action to channel clones, UNLESS it was an admin delete action. - // The admin only has authority to moderate content on their own site. - - if (! $admin_delete) { - $sync = attach_export_data(App::$data['channel'],$folder_hash, true); - if ($sync) { - Libsync::build_sync_packet($page_owner_uid, [ 'file' => [ $sync ] ]); - } - } - } - } - goaway(z_root() . '/photos/' . App::$data['channel']['channel_address']); - } - - if ((argc() > 2) && (x($_REQUEST,'delete')) && ($_REQUEST['delete'] === t('Delete Photo'))) { - // same as above but remove single photo + if ($channelx) { + App::$data['channel'] = $channelx; + head_set_icon($channelx['xchan_photo_s']); + $profile_uid = $channelx['channel_id']; + } - $ob_hash = get_observer_hash(); - - if (! $ob_hash) { - goaway(z_root() . '/' . $_SESSION['photo_return']); - } - - // query to verify ownership of the photo by this viewer - // We've already checked observer permissions to perfom this action - - // This implements the policy that remote channels (visitors and guests) - // which modify content can only modify their own content. - // The page owner can modify anything within their authority, including - // content published by others in their own channel pages. - // The site admin can of course modify anything on their own site for - // maintenance or legal compliance reasons. - - $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()), - dbesc(argv(2)) - ); - - if ($r) { - attach_delete($page_owner_uid, $r[0]['resource_id'], true ); - $sync = attach_export_data(App::$data['channel'],$r[0]['resource_id'], true); - if ($sync) { - Libsync::build_sync_packet($page_owner_uid, [ 'file' => [ $sync ] ]); - } - } - elseif (is_site_admin()) { - // If the admin deletes a photo, don't check ownership or invoke clone sync - attach_delete($page_owner_uid, argv(2), true); - } - - goaway(z_root() . '/photos/' . App::$data['channel']['channel_address'] . '/album/' . $_SESSION['album_return']); - } - - // perform move_to_album - - if ((argc() > 2) && array_key_exists('move_to_album',$_POST)) { - - $m = q("select folder from attach where hash = '%s' and uid = %d limit 1", - dbesc(argv(2)), - intval($page_owner_uid) - ); - - // we should sanitize the post variable, but probably pointless because the move - // will fail if we can't find the target - - if (($m) && ($m[0]['folder'] != $_POST['move_to_album'])) { - attach_move($page_owner_uid,argv(2),$_POST['move_to_album']); - - $sync = attach_export_data(App::$data['channel'],argv(2),false); - if ($sync) { - Libsync::build_sync_packet($page_owner_uid, [ 'file' => [ $sync ] ]); - } - - // return if this is the only thing being edited - - if (! ($_POST['desc'] && $_POST['newtag'])) { - goaway(z_root() . '/' . $_SESSION['photo_return']); - } - } - } - - // this still needs some work - - if(defined('FIXED')) { - if((x($_POST,'rotate') !== false) && ( (intval($_POST['rotate']) == 1) || (intval($_POST['rotate']) == 2) )) { - logger('rotate'); - - $resource_id = argv(2); - - $r = q("select * from photo where resource_id = '%s' and uid = %d and imgscale = 0 limit 1", - dbesc($resource_id), - intval($page_owner_uid) - ); - if ($r) { - - $ph = photo_factory(@file_get_contents(dbunescbin($r[0]['content'])), $r[0]['mimetype']); - if ($ph && $ph->is_valid()) { - $rotate_deg = ( (intval($_POST['rotate']) == 1) ? 270 : 90 ); - $ph->rotate($rotate_deg); - - $edited = datetime_convert(); - - q("update attach set filesize = %d, edited = '%s' where hash = '%s' and uid = %d", - strlen($ph->imageString()), - dbescdate($edited), - dbesc($resource_id), - intval($page_owner_uid) - ); - - $ph->saveImage(dbunescbin($r[0]['content'])); - - $arr = [ - 'aid' => get_account_id(), - 'uid' => intval($page_owner_uid), - 'resource_id' => dbesc($resource_id), - 'filename' => $r[0]['filename'], - 'imgscale' => 0, - 'album' => $r[0]['album'], - 'os_path' => $r[0]['os_path'], - 'os_storage' => 1, - 'os_syspath' => dbunescbin($r[0]['content']), - 'display_path' => $r[0]['display_path'], - 'photo_usage' => PHOTO_NORMAL, - 'edited' => dbescdate($edited) - ]; - - $ph->save($arr); - - unset($arr['os_syspath']); - - if($width > 1024 || $height > 1024) - $ph->scaleImage(1024); - $ph->storeThumbnail($arr, PHOTO_RES_1024); - - if($width > 640 || $height > 640) - $ph->scaleImage(640); - $ph->storeThumbnail($arr, PHOTO_RES_640); - - if($width > 320 || $height > 320) - $ph->scaleImage(320); - $ph->storeThumbnail($arr, PHOTO_RES_320); - } - } - }} // end FIXED + App::$page['htmlhead'] .= ""; + App::$data['observer'] = App::get_observer(); + } + } - // edit existing photo properties - - if (x($_POST,'item_id') !== false && intval($_POST['item_id'])) { + public function post() + { - $title = ((x($_POST,'title')) ? escape_tags(trim($_POST['title'])) : EMPTY_STR ); - $desc = ((x($_POST,'desc')) ? escape_tags(trim($_POST['desc'])) : EMPTY_STR ); - $body = ((x($_POST,'body')) ? trim($_POST['body']) : EMPTY_STR); - - $item_id = ((x($_POST,'item_id')) ? intval($_POST['item_id']) : 0); - $is_nsfw = ((x($_POST,'adult')) ? intval($_POST['adult']) : 0); + logger('mod-photos: photos_post: begin', LOGGER_DEBUG); - // convert any supplied posted permissions for storage - - $acl->set_from_array($_POST); - $perm = $acl->get(); - - $resource_id = argv(2); - - $p = q("SELECT mimetype, is_nsfw, filename, title, description, resource_id, imgscale, allow_cid, allow_gid, deny_cid, deny_gid FROM photo WHERE resource_id = '%s' AND uid = %d ORDER BY imgscale DESC", - dbesc($resource_id), - intval($page_owner_uid) - ); - if ($p) { - // update the photo structure with any of the changed elements which are common to all resolutions - $r = q("UPDATE photo SET title = '%s', description = '%s', allow_cid = '%s', allow_gid = '%s', deny_cid = '%s', deny_gid = '%s' WHERE resource_id = '%s' AND uid = %d", - dbesc($title), - dbesc($desc), - dbesc($perm['allow_cid']), - dbesc($perm['allow_gid']), - dbesc($perm['deny_cid']), - dbesc($perm['deny_gid']), - dbesc($resource_id), - intval($page_owner_uid) - ); - } - - $item_private = (($str_contact_allow || $str_group_allow || $str_contact_deny || $str_group_deny) ? true : false); - - $old_is_nsfw = $p[0]['is_nsfw']; - if ($old_is_nsfw != $is_nsfw) { - $r = q("update photo set is_nsfw = %d where resource_id = '%s' and uid = %d", - intval($is_nsfw), - dbesc($resource_id), - intval($page_owner_uid) - ); - } - - /* Don't make the item visible if the only change was the album name */ - - $visibility = 0; - if ($p[0]['description'] !== $desc || $p[0]['title'] !== $title || $body !== EMPTY_STR) { - $visibility = 1; - } + logger('mod_photos: REQUEST ' . print_r($_REQUEST, true), LOGGER_DATA); + logger('mod_photos: FILES ' . print_r($_FILES, true), LOGGER_DATA); - $r = q("SELECT * FROM item WHERE id = %d AND uid = %d LIMIT 1", - intval($item_id), - intval($page_owner_uid) - ); - if (! $r) { - logger('linked photo item not found.'); - notice ( t('linked item not found.') . EOL); - return; - } + $ph = photo_factory(''); - $linked_item = array_shift($r); - - // extract the original footer text - $footer_text = EMPTY_STR; - $orig_text = $linked_item['body']; - $matches = []; + $phototypes = $ph->supportedTypes(); - if (preg_match('/\[footer\](.*?)\[\/footer\]/ism',$orig_text,$matches)) { - $footer_text = $matches[0]; - } - - $body = cleanup_bbcode($body); - $tags = linkify_tags($body, $page_owner_uid); + $page_owner_uid = App::$data['channel']['channel_id']; - $post_tags = []; - if ($tags) { - foreach ($tags as $tag) { - $success = $tag['success']; - if ($success['replaced']) { - // suppress duplicate mentions/tags - $already_tagged = false; - foreach ($post_tags as $pt) { - if ($pt['term'] === $success['term'] && $pt['url'] === $success['url'] && intval($pt['ttype']) === intval($success['termtype'])) { - $already_tagged = true; - break; - } - } - if ($already_tagged) { - continue; - } + if (!perm_is_allowed($page_owner_uid, get_observer_hash(), 'write_storage')) { + notice(t('Permission denied.') . EOL); + killme_if_ajax(); + return; + } - $post_tags[] = [ - 'uid' => $page_owner_uid, - 'ttype' => $success['termtype'], - 'otype' => TERM_OBJ_POST, - 'term' => $success['term'], - 'url' => $success['url'] - ]; - } - } - } - if ($post_tags) { - q("delete from term where otype = 1 and oid = %d", - intval($linked_item['id']) - ); - foreach($post_tags as $t) { - q("insert into term (uid,oid,otype,ttype,term,url) + $acl = new AccessControl(App::$data['channel']); + + if ((argc() > 3) && (argv(2) === 'album')) { + $album = argv(3); + + if (!photos_album_exists($page_owner_uid, get_observer_hash(), $album)) { + notice(t('Album not found.') . EOL); + goaway(z_root() . '/' . $_SESSION['photo_return']); + } + + + /* + * DELETE photo album and all its photos + */ + + if ($_REQUEST['dropalbum'] === t('Delete Album')) { + $folder_hash = ''; + + $r = q( + "select hash from attach where is_dir = 1 and uid = %d and hash = '%s'", + intval($page_owner_uid), + dbesc($album) + ); + if (!$r) { + notice(t('Album not found.') . EOL); + return; + } + $folder_hash = $r[0]['hash']; + + + $res = []; + $admin_delete = false; + + // get the list of photos we are about to delete + + if (remote_channel() && (!local_channel())) { + $str = photos_album_get_db_idstr($page_owner_uid, $album, remote_channel()); + } elseif (local_channel()) { + $str = photos_album_get_db_idstr(local_channel(), $album); + } elseif (is_site_admin()) { + $str = photos_album_get_db_idstr_admin($page_owner_uid, $album); + $admin_delete = true; + } else { + $str = null; + } + + if (!$str) { + goaway(z_root() . '/' . $_SESSION['photo_return']); + } + + $r = q( + "select id from item where resource_id in ( $str ) and resource_type = 'photo' and uid = %d " . item_normal(), + intval($page_owner_uid) + ); + if ($r) { + foreach ($r as $rv) { + attach_delete($page_owner_uid, $rv['resource_id'], true); + } + } + + // remove the associated photos in case they weren't attached to an item (rare) + + q( + "delete from photo where resource_id in ( $str ) and uid = %d", + intval($page_owner_uid) + ); + + q( + "delete from attach where hash in ( $str ) and uid = %d", + intval($page_owner_uid) + ); + + if ($folder_hash) { + attach_delete($page_owner_uid, $folder_hash, true); + + // Sync this action to channel clones, UNLESS it was an admin delete action. + // The admin only has authority to moderate content on their own site. + + if (!$admin_delete) { + $sync = attach_export_data(App::$data['channel'], $folder_hash, true); + if ($sync) { + Libsync::build_sync_packet($page_owner_uid, ['file' => [$sync]]); + } + } + } + } + goaway(z_root() . '/photos/' . App::$data['channel']['channel_address']); + } + + 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']); + } + + // query to verify ownership of the photo by this viewer + // We've already checked observer permissions to perfom this action + + // This implements the policy that remote channels (visitors and guests) + // which modify content can only modify their own content. + // The page owner can modify anything within their authority, including + // content published by others in their own channel pages. + // The site admin can of course modify anything on their own site for + // maintenance or legal compliance reasons. + + $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()), + dbesc(argv(2)) + ); + + if ($r) { + attach_delete($page_owner_uid, $r[0]['resource_id'], true); + $sync = attach_export_data(App::$data['channel'], $r[0]['resource_id'], true); + if ($sync) { + Libsync::build_sync_packet($page_owner_uid, ['file' => [$sync]]); + } + } elseif (is_site_admin()) { + // If the admin deletes a photo, don't check ownership or invoke clone sync + attach_delete($page_owner_uid, argv(2), true); + } + + goaway(z_root() . '/photos/' . App::$data['channel']['channel_address'] . '/album/' . $_SESSION['album_return']); + } + + // perform move_to_album + + if ((argc() > 2) && array_key_exists('move_to_album', $_POST)) { + $m = q( + "select folder from attach where hash = '%s' and uid = %d limit 1", + dbesc(argv(2)), + intval($page_owner_uid) + ); + + // we should sanitize the post variable, but probably pointless because the move + // will fail if we can't find the target + + if (($m) && ($m[0]['folder'] != $_POST['move_to_album'])) { + attach_move($page_owner_uid, argv(2), $_POST['move_to_album']); + + $sync = attach_export_data(App::$data['channel'], argv(2), false); + if ($sync) { + Libsync::build_sync_packet($page_owner_uid, ['file' => [$sync]]); + } + + // return if this is the only thing being edited + + if (!($_POST['desc'] && $_POST['newtag'])) { + goaway(z_root() . '/' . $_SESSION['photo_return']); + } + } + } + + // this still needs some work + + if (defined('FIXED')) { + if ((x($_POST, 'rotate') !== false) && ((intval($_POST['rotate']) == 1) || (intval($_POST['rotate']) == 2))) { + logger('rotate'); + + $resource_id = argv(2); + + $r = q( + "select * from photo where resource_id = '%s' and uid = %d and imgscale = 0 limit 1", + dbesc($resource_id), + intval($page_owner_uid) + ); + if ($r) { + $ph = photo_factory(@file_get_contents(dbunescbin($r[0]['content'])), $r[0]['mimetype']); + if ($ph && $ph->is_valid()) { + $rotate_deg = ((intval($_POST['rotate']) == 1) ? 270 : 90); + $ph->rotate($rotate_deg); + + $edited = datetime_convert(); + + q( + "update attach set filesize = %d, edited = '%s' where hash = '%s' and uid = %d", + strlen($ph->imageString()), + dbescdate($edited), + dbesc($resource_id), + intval($page_owner_uid) + ); + + $ph->saveImage(dbunescbin($r[0]['content'])); + + $arr = [ + 'aid' => get_account_id(), + 'uid' => intval($page_owner_uid), + 'resource_id' => dbesc($resource_id), + 'filename' => $r[0]['filename'], + 'imgscale' => 0, + 'album' => $r[0]['album'], + 'os_path' => $r[0]['os_path'], + 'os_storage' => 1, + 'os_syspath' => dbunescbin($r[0]['content']), + 'display_path' => $r[0]['display_path'], + 'photo_usage' => PHOTO_NORMAL, + 'edited' => dbescdate($edited) + ]; + + $ph->save($arr); + + unset($arr['os_syspath']); + + if ($width > 1024 || $height > 1024) { + $ph->scaleImage(1024); + } + $ph->storeThumbnail($arr, PHOTO_RES_1024); + + if ($width > 640 || $height > 640) { + $ph->scaleImage(640); + } + $ph->storeThumbnail($arr, PHOTO_RES_640); + + if ($width > 320 || $height > 320) { + $ph->scaleImage(320); + } + $ph->storeThumbnail($arr, PHOTO_RES_320); + } + } + } + } // end FIXED + + + // edit existing photo properties + + if (x($_POST, 'item_id') !== false && intval($_POST['item_id'])) { + $title = ((x($_POST, 'title')) ? escape_tags(trim($_POST['title'])) : EMPTY_STR); + $desc = ((x($_POST, 'desc')) ? escape_tags(trim($_POST['desc'])) : EMPTY_STR); + $body = ((x($_POST, 'body')) ? trim($_POST['body']) : EMPTY_STR); + + $item_id = ((x($_POST, 'item_id')) ? intval($_POST['item_id']) : 0); + $is_nsfw = ((x($_POST, 'adult')) ? intval($_POST['adult']) : 0); + + // convert any supplied posted permissions for storage + + $acl->set_from_array($_POST); + $perm = $acl->get(); + + $resource_id = argv(2); + + $p = q( + "SELECT mimetype, is_nsfw, filename, title, description, resource_id, imgscale, allow_cid, allow_gid, deny_cid, deny_gid FROM photo WHERE resource_id = '%s' AND uid = %d ORDER BY imgscale DESC", + dbesc($resource_id), + intval($page_owner_uid) + ); + if ($p) { + // update the photo structure with any of the changed elements which are common to all resolutions + $r = q( + "UPDATE photo SET title = '%s', description = '%s', allow_cid = '%s', allow_gid = '%s', deny_cid = '%s', deny_gid = '%s' WHERE resource_id = '%s' AND uid = %d", + dbesc($title), + dbesc($desc), + dbesc($perm['allow_cid']), + dbesc($perm['allow_gid']), + dbesc($perm['deny_cid']), + dbesc($perm['deny_gid']), + dbesc($resource_id), + intval($page_owner_uid) + ); + } + + $item_private = (($str_contact_allow || $str_group_allow || $str_contact_deny || $str_group_deny) ? true : false); + + $old_is_nsfw = $p[0]['is_nsfw']; + if ($old_is_nsfw != $is_nsfw) { + $r = q( + "update photo set is_nsfw = %d where resource_id = '%s' and uid = %d", + intval($is_nsfw), + dbesc($resource_id), + intval($page_owner_uid) + ); + } + + /* Don't make the item visible if the only change was the album name */ + + $visibility = 0; + if ($p[0]['description'] !== $desc || $p[0]['title'] !== $title || $body !== EMPTY_STR) { + $visibility = 1; + } + + $r = q( + "SELECT * FROM item WHERE id = %d AND uid = %d LIMIT 1", + intval($item_id), + intval($page_owner_uid) + ); + if (!$r) { + logger('linked photo item not found.'); + notice(t('linked item not found.') . EOL); + return; + } + + $linked_item = array_shift($r); + + // extract the original footer text + $footer_text = EMPTY_STR; + $orig_text = $linked_item['body']; + $matches = []; + + if (preg_match('/\[footer\](.*?)\[\/footer\]/ism', $orig_text, $matches)) { + $footer_text = $matches[0]; + } + + $body = cleanup_bbcode($body); + $tags = linkify_tags($body, $page_owner_uid); + + $post_tags = []; + if ($tags) { + foreach ($tags as $tag) { + $success = $tag['success']; + if ($success['replaced']) { + // suppress duplicate mentions/tags + $already_tagged = false; + foreach ($post_tags as $pt) { + if ($pt['term'] === $success['term'] && $pt['url'] === $success['url'] && intval($pt['ttype']) === intval($success['termtype'])) { + $already_tagged = true; + break; + } + } + if ($already_tagged) { + continue; + } + + $post_tags[] = [ + 'uid' => $page_owner_uid, + 'ttype' => $success['termtype'], + 'otype' => TERM_OBJ_POST, + 'term' => $success['term'], + 'url' => $success['url'] + ]; + } + } + } + if ($post_tags) { + q( + "delete from term where otype = 1 and oid = %d", + intval($linked_item['id']) + ); + foreach ($post_tags as $t) { + q( + "insert into term (uid,oid,otype,ttype,term,url) values(%d,%d,%d,%d,'%s','%s') ", - intval($page_owner_uid), - intval($linked_item['id']), - intval(TERM_OBJ_POST), - intval($t['ttype']), - dbesc($t['term']), - dbesc($t['url']) - ); - } - } + intval($page_owner_uid), + intval($linked_item['id']), + intval(TERM_OBJ_POST), + intval($t['ttype']), + dbesc($t['term']), + dbesc($t['url']) + ); + } + } - $body = z_input_filter($body,'text/x-multicode'); + $body = z_input_filter($body, 'text/x-multicode'); - $obj = EMPTY_STR; + $obj = EMPTY_STR; - if (isset($linked_item['obj']) && strlen($linked_item['obj'])) { - $obj = json_decode($linked_item['obj'],true); - - $obj['name'] = (($title) ? $title : $p[0]['filename']); - $obj['summary'] = (($desc) ? $desc : $p[0]['filename']); - $obj['updated'] = datetime_convert('UTC','UTC','now',ATOM_TIME); - $obj['source'] = [ 'content' => $body, 'mediaType' => 'text/x-multicode' ]; - $obj['content'] = bbcode($body . $footer_text, [ 'export' => true ]); - if (isset($obj['url']) && is_array($obj['url'])) { - for ($x = 0; $x < count($obj['url']); $x ++) { - $obj['url'][$x]['summary'] = $obj['summary']; - } - } - $obj = json_encode($obj); - } + if (isset($linked_item['obj']) && strlen($linked_item['obj'])) { + $obj = json_decode($linked_item['obj'], true); - // make sure the linked item has the same permissions as the photo regardless of any other changes - $x = q("update item set allow_cid = '%s', allow_gid = '%s', deny_cid = '%s', deny_gid = '%s', title = '%s', obj = '%s', body = '%s', edited = '%s', item_private = %d where id = %d", - dbesc($perm['allow_cid']), - dbesc($perm['allow_gid']), - dbesc($perm['deny_cid']), - dbesc($perm['deny_gid']), - dbesc(($desc) ? $desc : $p[0]['filename']), - dbesc($obj), - dbesc($body . $footer_text), - dbesc(datetime_convert()), - intval($acl->is_private()), - intval($item_id) - ); - - // make sure the attach has the same permissions as the photo regardless of any other changes - $x = q("update attach set allow_cid = '%s', allow_gid = '%s', deny_cid = '%s', deny_gid = '%s' where hash = '%s' and uid = %d and is_photo = 1", - dbesc($perm['allow_cid']), - dbesc($perm['allow_gid']), - dbesc($perm['deny_cid']), - dbesc($perm['deny_gid']), - dbesc($resource_id), - intval($page_owner_uid) - ); - + $obj['name'] = (($title) ? $title : $p[0]['filename']); + $obj['summary'] = (($desc) ? $desc : $p[0]['filename']); + $obj['updated'] = datetime_convert('UTC', 'UTC', 'now', ATOM_TIME); + $obj['source'] = ['content' => $body, 'mediaType' => 'text/x-multicode']; + $obj['content'] = bbcode($body . $footer_text, ['export' => true]); + if (isset($obj['url']) && is_array($obj['url'])) { + for ($x = 0; $x < count($obj['url']); $x++) { + $obj['url'][$x]['summary'] = $obj['summary']; + } + } + $obj = json_encode($obj); + } - if($visibility) { - Run::Summon( [ 'Notifier', 'edit_post', $item_id ] ); - } + // make sure the linked item has the same permissions as the photo regardless of any other changes + $x = q( + "update item set allow_cid = '%s', allow_gid = '%s', deny_cid = '%s', deny_gid = '%s', title = '%s', obj = '%s', body = '%s', edited = '%s', item_private = %d where id = %d", + dbesc($perm['allow_cid']), + dbesc($perm['allow_gid']), + dbesc($perm['deny_cid']), + dbesc($perm['deny_gid']), + dbesc(($desc) ? $desc : $p[0]['filename']), + dbesc($obj), + dbesc($body . $footer_text), + dbesc(datetime_convert()), + intval($acl->is_private()), + intval($item_id) + ); - $sync = attach_export_data(App::$data['channel'],$resource_id); - - if($sync) - Libsync::build_sync_packet($page_owner_uid, [ 'file' => [ $sync ] ]); - - goaway(z_root() . '/' . $_SESSION['photo_return']); - return; // NOTREACHED - - - } - - - /** - * default post action - upload a photo - */ - - $channel = App::$data['channel']; - $observer = App::$data['observer']; - - $_REQUEST['source'] = 'photos'; - require_once('include/attach.php'); - - if(! local_channel()) { - $_REQUEST['contact_allow'] = expand_acl($channel['channel_allow_cid']); - $_REQUEST['group_allow'] = expand_acl($channel['channel_allow_gid']); - $_REQUEST['contact_deny'] = expand_acl($channel['channel_deny_cid']); - $_REQUEST['group_deny'] = expand_acl($channel['channel_deny_gid']); - } - + // make sure the attach has the same permissions as the photo regardless of any other changes + $x = q( + "update attach set allow_cid = '%s', allow_gid = '%s', deny_cid = '%s', deny_gid = '%s' where hash = '%s' and uid = %d and is_photo = 1", + dbesc($perm['allow_cid']), + dbesc($perm['allow_gid']), + dbesc($perm['deny_cid']), + dbesc($perm['deny_gid']), + dbesc($resource_id), + intval($page_owner_uid) + ); - $matches = []; - $partial = false; - if(array_key_exists('HTTP_CONTENT_RANGE',$_SERVER)) { - $pm = preg_match('/bytes (\d*)\-(\d*)\/(\d*)/',$_SERVER['HTTP_CONTENT_RANGE'],$matches); - if($pm) { - logger('Content-Range: ' . print_r($matches,true)); - $partial = true; - } - } + if ($visibility) { + Run::Summon(['Notifier', 'edit_post', $item_id]); + } - if($partial) { - $x = save_chunk($channel,$matches[1],$matches[2],$matches[3]); + $sync = attach_export_data(App::$data['channel'], $resource_id); - if($x['partial']) { - header('Range: bytes=0-' . (($x['length']) ? $x['length'] - 1 : 0)); - json_return_and_die($x); - } - else { - header('Range: bytes=0-' . (($x['size']) ? $x['size'] - 1 : 0)); + if ($sync) { + Libsync::build_sync_packet($page_owner_uid, ['file' => [$sync]]); + } - $_FILES['userfile'] = [ - 'name' => $x['name'], - 'type' => $x['type'], - 'tmp_name' => $x['tmp_name'], - 'error' => $x['error'], - 'size' => $x['size'] - ]; - } - } - else { - if(! array_key_exists('userfile',$_FILES)) { - $_FILES['userfile'] = [ - 'name' => $_FILES['files']['name'], - 'type' => $_FILES['files']['type'], - 'tmp_name' => $_FILES['files']['tmp_name'], - 'error' => $_FILES['files']['error'], - 'size' => $_FILES['files']['size'] - ]; - } - } + goaway(z_root() . '/' . $_SESSION['photo_return']); + return; // NOTREACHED + } - $r = attach_store($channel,get_observer_hash(), '', $_REQUEST); - - if(! $r['success']) { - notice($r['message'] . EOL); - if (is_ajax()) { - killme(); - } - goaway(z_root() . '/photos/' . App::$data['channel']['channel_address']); - } - if ($r['success'] && ! intval($r['data']['is_photo'])) { - notice( sprintf( t('%s: Unsupported photo type. Saved as file.'), escape_tags($r['data']['filename']))); - } - if (is_ajax()) { - killme(); - } - - goaway(z_root() . '/photos/' . App::$data['channel']['channel_address'] . '/album/' . $r['data']['folder']); - - } - - - - function get() { - - // URLs: - // photos/name - // photos/name/album/xxxxx (xxxxx is album name) - // photos/name/image/xxxxx - - - if(observer_prohibited()) { - notice( t('Public access denied.') . EOL); - return; - } - - $unsafe = 1 - get_safemode(); - - - if(! x(App::$data,'channel')) { - notice( t('No photos selected') . EOL ); - return; - } - - $ph = photo_factory(''); - $phototypes = $ph->supportedTypes(); - - $_SESSION['photo_return'] = App::$cmd; - - // - // Parse arguments - // - - $can_comment = perm_is_allowed(App::$profile['profile_uid'],get_observer_hash(),'post_comments'); - - if(argc() > 3) { - $datatype = argv(2); - $datum = argv(3); - } - else { - if(argc() > 2) { - $datatype = argv(2); - $datum = ''; - } - else - $datatype = 'summary'; - } - - if(argc() > 4) - $cmd = argv(4); - else - $cmd = 'view'; - - // - // Setup permissions structures - // - - $can_post = false; - $visitor = 0; - - - $owner_uid = App::$data['channel']['channel_id']; - $owner_aid = App::$data['channel']['channel_account_id']; - - $observer = App::get_observer(); - - $can_post = perm_is_allowed($owner_uid,$observer['xchan_hash'],'write_storage'); - $can_view = perm_is_allowed($owner_uid,$observer['xchan_hash'],'view_storage'); - - if(! $can_view) { - notice( t('Access to this item is restricted.') . EOL); - return; - } - - $sql_item = item_permissions_sql($owner_uid,get_observer_hash()); - $sql_extra = permissions_sql($owner_uid,get_observer_hash(),'photo'); - $sql_attach = permissions_sql($owner_uid,get_observer_hash(),'attach'); - nav_set_selected('Photos'); - - $o = ' + /** + * default post action - upload a photo + */ + + $channel = App::$data['channel']; + $observer = App::$data['observer']; + + $_REQUEST['source'] = 'photos'; + require_once('include/attach.php'); + + if (!local_channel()) { + $_REQUEST['contact_allow'] = expand_acl($channel['channel_allow_cid']); + $_REQUEST['group_allow'] = expand_acl($channel['channel_allow_gid']); + $_REQUEST['contact_deny'] = expand_acl($channel['channel_deny_cid']); + $_REQUEST['group_deny'] = expand_acl($channel['channel_deny_gid']); + } + + + $matches = []; + $partial = false; + + if (array_key_exists('HTTP_CONTENT_RANGE', $_SERVER)) { + $pm = preg_match('/bytes (\d*)\-(\d*)\/(\d*)/', $_SERVER['HTTP_CONTENT_RANGE'], $matches); + if ($pm) { + logger('Content-Range: ' . print_r($matches, true)); + $partial = true; + } + } + + if ($partial) { + $x = save_chunk($channel, $matches[1], $matches[2], $matches[3]); + + if ($x['partial']) { + header('Range: bytes=0-' . (($x['length']) ? $x['length'] - 1 : 0)); + json_return_and_die($x); + } else { + header('Range: bytes=0-' . (($x['size']) ? $x['size'] - 1 : 0)); + + $_FILES['userfile'] = [ + 'name' => $x['name'], + 'type' => $x['type'], + 'tmp_name' => $x['tmp_name'], + 'error' => $x['error'], + 'size' => $x['size'] + ]; + } + } else { + if (!array_key_exists('userfile', $_FILES)) { + $_FILES['userfile'] = [ + 'name' => $_FILES['files']['name'], + 'type' => $_FILES['files']['type'], + 'tmp_name' => $_FILES['files']['tmp_name'], + 'error' => $_FILES['files']['error'], + 'size' => $_FILES['files']['size'] + ]; + } + } + + $r = attach_store($channel, get_observer_hash(), '', $_REQUEST); + + if (!$r['success']) { + notice($r['message'] . EOL); + if (is_ajax()) { + killme(); + } + goaway(z_root() . '/photos/' . App::$data['channel']['channel_address']); + } + if ($r['success'] && !intval($r['data']['is_photo'])) { + notice(sprintf(t('%s: Unsupported photo type. Saved as file.'), escape_tags($r['data']['filename']))); + } + if (is_ajax()) { + killme(); + } + + goaway(z_root() . '/photos/' . App::$data['channel']['channel_address'] . '/album/' . $r['data']['folder']); + } + + + public function get() + { + + // URLs: + // photos/name + // photos/name/album/xxxxx (xxxxx is album name) + // photos/name/image/xxxxx + + + if (observer_prohibited()) { + notice(t('Public access denied.') . EOL); + return; + } + + $unsafe = 1 - get_safemode(); + + + if (!x(App::$data, 'channel')) { + notice(t('No photos selected') . EOL); + return; + } + + $ph = photo_factory(''); + $phototypes = $ph->supportedTypes(); + + $_SESSION['photo_return'] = App::$cmd; + + // + // Parse arguments + // + + $can_comment = perm_is_allowed(App::$profile['profile_uid'], get_observer_hash(), 'post_comments'); + + if (argc() > 3) { + $datatype = argv(2); + $datum = argv(3); + } else { + if (argc() > 2) { + $datatype = argv(2); + $datum = ''; + } else { + $datatype = 'summary'; + } + } + + if (argc() > 4) { + $cmd = argv(4); + } else { + $cmd = 'view'; + } + + // + // Setup permissions structures + // + + $can_post = false; + $visitor = 0; + + + $owner_uid = App::$data['channel']['channel_id']; + $owner_aid = App::$data['channel']['channel_account_id']; + + $observer = App::get_observer(); + + $can_post = perm_is_allowed($owner_uid, $observer['xchan_hash'], 'write_storage'); + $can_view = perm_is_allowed($owner_uid, $observer['xchan_hash'], 'view_storage'); + + if (!$can_view) { + notice(t('Access to this item is restricted.') . EOL); + return; + } + + $sql_item = item_permissions_sql($owner_uid, get_observer_hash()); + $sql_extra = permissions_sql($owner_uid, get_observer_hash(), 'photo'); + $sql_attach = permissions_sql($owner_uid, get_observer_hash(), 'attach'); + + nav_set_selected('Photos'); + + $o = ' '; - $o .= "\r\n"; - - $_is_owner = (local_channel() && (local_channel() == $owner_uid)); - - /** - * Display upload form - */ - - if ($can_post) { - - $uploader = ''; - - $ret = array('post_url' => z_root() . '/photos/' . App::$data['channel']['channel_address'], - 'addon_text' => $uploader, - 'default_upload' => true); - - call_hooks('photo_upload_form',$ret); - - /* Show space usage */ - - $r = q("select sum(filesize) as total from photo where aid = %d and imgscale = 0 ", - intval(App::$data['channel']['channel_account_id']) - ); - - - $limit = engr_units_to_bytes(service_class_fetch(App::$data['channel']['channel_id'],'photo_upload_limit')); - if($limit !== false) { - $usage_message = sprintf( t("%1$.2f MB of %2$.2f MB photo storage used."), $r[0]['total'] / 1024000, $limit / 1024000 ); - } - else { - $usage_message = sprintf( t('%1$.2f MB photo storage used.'), $r[0]['total'] / 1024000 ); - } - - if($_is_owner) { - $channel = App::get_channel(); - - $acl = new AccessControl($channel); - $channel_acl = $acl->get(); - - $lockstate = (($acl->is_private()) ? 'lock' : 'unlock'); - } - - $aclselect = (($_is_owner) ? populate_acl($channel_acl,false, PermissionDescription::fromGlobalPermission('view_storage')) : ''); - - // this is wrong but is to work around an issue with js_upload wherein it chokes if these variables - // don't exist. They really should be set to a parseable representation of the channel's default permissions - // which can be processed by getSelected() - - if(! $aclselect) { - $aclselect = ''; - } + $o .= "\r\n"; - $selname = ''; + $_is_owner = (local_channel() && (local_channel() == $owner_uid)); - if($datum) { - $h = attach_by_hash_nodata($datum,get_observer_hash()); - $selname = $h['data']['display_path']; - } + /** + * Display upload form + */ - - $albums = ((array_key_exists('albums', App::$data)) ? App::$data['albums'] : photos_albums_list(App::$data['channel'],App::$data['observer'])); - - if(! $selname) { - $def_album = get_pconfig(App::$data['channel']['channel_id'],'system','photo_path'); - if($def_album) { - $selname = filepath_macro($def_album); - $albums['album'][] = array('text' => $selname); - } - } - - $tpl = get_markup_template('photos_upload.tpl'); - $upload_form = replace_macros($tpl,array( - '$pagename' => t('Upload Photos'), - '$sessid' => session_id(), - '$usage' => $usage_message, - '$nickname' => App::$data['channel']['channel_address'], - '$newalbum_label' => t('Enter an album name'), - '$newalbum_placeholder' => t('or select an existing album (doubleclick)'), - '$visible' => array('visible', t('Create a status post for this upload'), 0, t('If multiple files are selected, the message will be repeated for each photo'), array(t('No'), t('Yes')), 'onclick="showHideBodyTextarea();"'), - '$caption' => array('description', t('Please briefly describe this photo for vision-impaired viewers')), - 'title' => [ 'title', t('Title (optional)') ], - '$body' => array('body', t('Your message (optional)'),'', 'This will only appear in the status post'), - '$albums' => $albums['albums'], - '$selname' => $selname, - '$permissions' => t('Permissions'), - '$aclselect' => $aclselect, - '$allow_cid' => acl2json($channel_acl['allow_cid']), - '$allow_gid' => acl2json($channel_acl['allow_gid']), - '$deny_cid' => acl2json($channel_acl['deny_cid']), - '$deny_gid' => acl2json($channel_acl['deny_gid']), - '$lockstate' => $lockstate, - '$uploader' => $ret['addon_text'], - '$default' => (($ret['default_upload']) ? true : false), - '$uploadurl' => $ret['post_url'], - '$submit' => t('Upload') - - )); - - } - - // - // dispatch request - // - - /* - * Display a single photo album - */ - - if($datatype === 'album') { + if ($can_post) { + $uploader = ''; - head_add_link([ - 'rel' => 'alternate', - 'type' => 'application/json+oembed', - 'href' => z_root() . '/oep?f=&url=' . urlencode(z_root() . '/' . App::$query_string), - 'title' => 'oembed' - ]); + $ret = array('post_url' => z_root() . '/photos/' . App::$data['channel']['channel_address'], + 'addon_text' => $uploader, + 'default_upload' => true); - if($x = photos_album_exists($owner_uid, get_observer_hash(), $datum)) { - App::set_pager_itemspage(60); - $album = $x['display_path']; - } - else { - goaway(z_root() . '/photos/' . App::$data['channel']['channel_address']); - } + call_hooks('photo_upload_form', $ret); - if($_GET['order'] === 'posted') - $order = 'created ASC'; - elseif($_GET['order'] === 'name') - $order = 'filename ASC'; - else - $order = 'created DESC'; + /* Show space usage */ - $r = q("SELECT p.resource_id, p.id, p.filename, p.mimetype, p.imgscale, p.description, p.created FROM photo p INNER JOIN + $r = q( + "select sum(filesize) as total from photo where aid = %d and imgscale = 0 ", + intval(App::$data['channel']['channel_account_id']) + ); + + + $limit = engr_units_to_bytes(service_class_fetch(App::$data['channel']['channel_id'], 'photo_upload_limit')); + if ($limit !== false) { + $usage_message = sprintf(t("%1$.2f MB of %2$.2f MB photo storage used."), $r[0]['total'] / 1024000, $limit / 1024000); + } else { + $usage_message = sprintf(t('%1$.2f MB photo storage used.'), $r[0]['total'] / 1024000); + } + + if ($_is_owner) { + $channel = App::get_channel(); + + $acl = new AccessControl($channel); + $channel_acl = $acl->get(); + + $lockstate = (($acl->is_private()) ? 'lock' : 'unlock'); + } + + $aclselect = (($_is_owner) ? populate_acl($channel_acl, false, PermissionDescription::fromGlobalPermission('view_storage')) : ''); + + // this is wrong but is to work around an issue with js_upload wherein it chokes if these variables + // don't exist. They really should be set to a parseable representation of the channel's default permissions + // which can be processed by getSelected() + + if (!$aclselect) { + $aclselect = ''; + } + + $selname = ''; + + if ($datum) { + $h = attach_by_hash_nodata($datum, get_observer_hash()); + $selname = $h['data']['display_path']; + } + + + $albums = ((array_key_exists('albums', App::$data)) ? App::$data['albums'] : photos_albums_list(App::$data['channel'], App::$data['observer'])); + + if (!$selname) { + $def_album = get_pconfig(App::$data['channel']['channel_id'], 'system', 'photo_path'); + if ($def_album) { + $selname = filepath_macro($def_album); + $albums['album'][] = array('text' => $selname); + } + } + + $tpl = get_markup_template('photos_upload.tpl'); + $upload_form = replace_macros($tpl, array( + '$pagename' => t('Upload Photos'), + '$sessid' => session_id(), + '$usage' => $usage_message, + '$nickname' => App::$data['channel']['channel_address'], + '$newalbum_label' => t('Enter an album name'), + '$newalbum_placeholder' => t('or select an existing album (doubleclick)'), + '$visible' => array('visible', t('Create a status post for this upload'), 0, t('If multiple files are selected, the message will be repeated for each photo'), array(t('No'), t('Yes')), 'onclick="showHideBodyTextarea();"'), + '$caption' => array('description', t('Please briefly describe this photo for vision-impaired viewers')), + 'title' => ['title', t('Title (optional)')], + '$body' => array('body', t('Your message (optional)'), '', 'This will only appear in the status post'), + '$albums' => $albums['albums'], + '$selname' => $selname, + '$permissions' => t('Permissions'), + '$aclselect' => $aclselect, + '$allow_cid' => acl2json($channel_acl['allow_cid']), + '$allow_gid' => acl2json($channel_acl['allow_gid']), + '$deny_cid' => acl2json($channel_acl['deny_cid']), + '$deny_gid' => acl2json($channel_acl['deny_gid']), + '$lockstate' => $lockstate, + '$uploader' => $ret['addon_text'], + '$default' => (($ret['default_upload']) ? true : false), + '$uploadurl' => $ret['post_url'], + '$submit' => t('Upload') + + )); + } + + // + // dispatch request + // + + /* + * Display a single photo album + */ + + if ($datatype === 'album') { + head_add_link([ + 'rel' => 'alternate', + 'type' => 'application/json+oembed', + 'href' => z_root() . '/oep?f=&url=' . urlencode(z_root() . '/' . App::$query_string), + 'title' => 'oembed' + ]); + + if ($x = photos_album_exists($owner_uid, get_observer_hash(), $datum)) { + App::set_pager_itemspage(60); + $album = $x['display_path']; + } else { + goaway(z_root() . '/photos/' . App::$data['channel']['channel_address']); + } + + if ($_GET['order'] === 'posted') { + $order = 'created ASC'; + } elseif ($_GET['order'] === 'name') { + $order = 'filename ASC'; + } else { + $order = 'created 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 left join attach on folder = '%s' and photo.resource_id = attach.hash WHERE attach.uid = %d AND imgscale <= 4 AND photo_usage IN ( %d, %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) ORDER BY $order LIMIT %d OFFSET %d", - dbesc($x['hash']), - intval($owner_uid), - intval(PHOTO_NORMAL), - intval(PHOTO_PROFILE), - intval(PHOTO_COVER), - intval($unsafe), - intval(App::$pager['itemspage']), - intval(App::$pager['start']) - ); + dbesc($x['hash']), + intval($owner_uid), + intval(PHOTO_NORMAL), + intval(PHOTO_PROFILE), + intval(PHOTO_COVER), + intval($unsafe), + intval(App::$pager['itemspage']), + intval(App::$pager['start']) + ); - // edit album name - $album_edit = null; + // edit album name + $album_edit = null; - if($can_post) { - $album_e = $album; - $albums = ((array_key_exists('albums', App::$data)) ? App::$data['albums'] : photos_albums_list(App::$data['channel'],App::$data['observer'])); - - // @fixme - syncronise actions with DAV - - // $edit_tpl = get_markup_template('album_edit.tpl'); - // $album_edit = replace_macros($edit_tpl,array( - // '$nametext' => t('Enter a new album name'), - // '$name_placeholder' => t('or select an existing one (doubleclick)'), - // '$nickname' => App::$data['channel']['channel_address'], - // '$album' => $album_e, - // '$albums' => $albums['albums'], - // '$hexalbum' => bin2hex($album), - // '$submit' => t('Submit'), - // '$dropsubmit' => t('Delete Album') - // )); - - } - - $order = [ - [ t('Date descending'), z_root() . '/photos/' . App::$data['channel']['channel_address'] . '/album/' . $datum ], - [ t('Date ascending'), z_root() . '/photos/' . App::$data['channel']['channel_address'] . '/album/' . $datum . '?f=&order=posted'], - [ t('Name ascending'), z_root() . '/photos/' . App::$data['channel']['channel_address'] . '/album/' . $datum . '?f=&order=name'] - ]; - - - $photos = []; - if(count($r)) { - $twist = 'rotright'; - foreach($r as $rr) { - - if($twist == 'rotright') - $twist = 'rotleft'; - else - $twist = 'rotright'; - - $ext = $phototypes[$rr['mimetype']]; - - $imgalt_e = $rr['filename']; - $desc_e = $rr['description']; - - $imagelink = (z_root() . '/photos/' . App::$data['channel']['channel_address'] . '/image/' . $rr['resource_id'] - . (($_GET['order'] === 'posted') ? '?f=&order=posted' : '')); - - $photos[] = array( - 'id' => $rr['id'], - 'twist' => ' ' . $twist . rand(2,4), - 'link' => $imagelink, - 'title' => t('View Photo'), - 'src' => z_root() . '/photo/' . $rr['resource_id'] . '-' . $rr['imgscale'] . '.' .$ext, - 'alt' => $imgalt_e, - 'desc'=> $desc_e, - 'ext' => $ext, - 'hash'=> $rr['resource_id'], - 'unknown' => t('Unknown') - ); - } - } - - if($_REQUEST['aj']) { - if($photos) { - $o = replace_macros(get_markup_template('photosajax.tpl'),array( - '$photos' => $photos, - '$album_id' => $datum - )); - } - else { - $o = '
      '; - } - echo $o; - killme(); - } - else { - $o .= ""; - $tpl = get_markup_template('photo_album.tpl'); - $o .= replace_macros($tpl, array( - '$photos' => $photos, - '$album' => $album, - '$album_id' => $datum, - '$file_view' => t('View files'), - '$files_path' => z_root() . '/cloud/' . App::$data['channel']['channel_address'] . '/' . $x['display_path'], - '$album_edit' => array(t('Edit Album'), $album_edit), - '$can_post' => $can_post, - '$upload' => array(t('Add Photos'), z_root() . '/photos/' . App::$data['channel']['channel_address'] . '/upload/' . $datum), - '$order' => $order, - '$sort' => t('Sort'), - '$upload_form' => $upload_form, - '$usage' => $usage_message - )); + if ($can_post) { + $album_e = $album; + $albums = ((array_key_exists('albums', App::$data)) ? App::$data['albums'] : photos_albums_list(App::$data['channel'], App::$data['observer'])); - return $o; - } - } - - /** - * Display one photo - */ - - if($datatype === 'image') { + // @fixme - syncronise actions with DAV - head_add_link([ - 'rel' => 'alternate', - 'type' => 'application/json+oembed', - 'href' => z_root() . '/oep?f=&url=' . urlencode(z_root() . '/' . App::$query_string), - 'title' => 'oembed' - ]); + // $edit_tpl = get_markup_template('album_edit.tpl'); + // $album_edit = replace_macros($edit_tpl,array( + // '$nametext' => t('Enter a new album name'), + // '$name_placeholder' => t('or select an existing one (doubleclick)'), + // '$nickname' => App::$data['channel']['channel_address'], + // '$album' => $album_e, + // '$albums' => $albums['albums'], + // '$hexalbum' => bin2hex($album), + // '$submit' => t('Submit'), + // '$dropsubmit' => t('Delete Album') + // )); + } - $x = q("select folder from attach where hash = '%s' and uid = %d $sql_attach limit 1", - dbesc($datum), - intval($owner_uid) - ); + $order = [ + [t('Date descending'), z_root() . '/photos/' . App::$data['channel']['channel_address'] . '/album/' . $datum], + [t('Date ascending'), z_root() . '/photos/' . App::$data['channel']['channel_address'] . '/album/' . $datum . '?f=&order=posted'], + [t('Name ascending'), z_root() . '/photos/' . App::$data['channel']['channel_address'] . '/album/' . $datum . '?f=&order=name'] + ]; - // fetch image, item containing image, then comments - - $ph = q("SELECT id,aid,uid,xchan,resource_id,created,edited,title,description,album,filename,mimetype,height,width,filesize,imgscale,photo_usage,is_nsfw,allow_cid,allow_gid,deny_cid,deny_gid FROM photo WHERE uid = %d AND resource_id = '%s' + + $photos = []; + if (count($r)) { + $twist = 'rotright'; + foreach ($r as $rr) { + if ($twist == 'rotright') { + $twist = 'rotleft'; + } else { + $twist = 'rotright'; + } + + $ext = $phototypes[$rr['mimetype']]; + + $imgalt_e = $rr['filename']; + $desc_e = $rr['description']; + + $imagelink = (z_root() . '/photos/' . App::$data['channel']['channel_address'] . '/image/' . $rr['resource_id'] + . (($_GET['order'] === 'posted') ? '?f=&order=posted' : '')); + + $photos[] = array( + 'id' => $rr['id'], + 'twist' => ' ' . $twist . rand(2, 4), + 'link' => $imagelink, + 'title' => t('View Photo'), + 'src' => z_root() . '/photo/' . $rr['resource_id'] . '-' . $rr['imgscale'] . '.' . $ext, + 'alt' => $imgalt_e, + 'desc' => $desc_e, + 'ext' => $ext, + 'hash' => $rr['resource_id'], + 'unknown' => t('Unknown') + ); + } + } + + if ($_REQUEST['aj']) { + if ($photos) { + $o = replace_macros(get_markup_template('photosajax.tpl'), array( + '$photos' => $photos, + '$album_id' => $datum + )); + } else { + $o = '
      '; + } + echo $o; + killme(); + } else { + $o .= ""; + $tpl = get_markup_template('photo_album.tpl'); + $o .= replace_macros($tpl, array( + '$photos' => $photos, + '$album' => $album, + '$album_id' => $datum, + '$file_view' => t('View files'), + '$files_path' => z_root() . '/cloud/' . App::$data['channel']['channel_address'] . '/' . $x['display_path'], + '$album_edit' => array(t('Edit Album'), $album_edit), + '$can_post' => $can_post, + '$upload' => array(t('Add Photos'), z_root() . '/photos/' . App::$data['channel']['channel_address'] . '/upload/' . $datum), + '$order' => $order, + '$sort' => t('Sort'), + '$upload_form' => $upload_form, + '$usage' => $usage_message + )); + + return $o; + } + } + + /** + * Display one photo + */ + + if ($datatype === 'image') { + head_add_link([ + 'rel' => 'alternate', + 'type' => 'application/json+oembed', + 'href' => z_root() . '/oep?f=&url=' . urlencode(z_root() . '/' . App::$query_string), + 'title' => 'oembed' + ]); + + $x = q( + "select folder from attach where hash = '%s' and uid = %d $sql_attach limit 1", + dbesc($datum), + intval($owner_uid) + ); + + // fetch image, item containing image, then comments + + $ph = q( + "SELECT id,aid,uid,xchan,resource_id,created,edited,title,description,album,filename,mimetype,height,width,filesize,imgscale,photo_usage,is_nsfw,allow_cid,allow_gid,deny_cid,deny_gid FROM photo WHERE uid = %d AND resource_id = '%s' $sql_extra ORDER BY imgscale ASC ", - intval($owner_uid), - dbesc($datum) - ); - - if(! ($ph && $x)) { - - /* Check again - this time without specifying permissions */ - - $ph = q("SELECT id FROM photo WHERE uid = %d AND resource_id = '%s' LIMIT 1", - intval($owner_uid), - dbesc($datum) - ); - if($ph) - notice( t('Permission denied. Access to this item may be restricted.') . EOL); - else - notice( t('Photo not available') . EOL ); - return; - } - - - - $prevlink = ''; - $nextlink = ''; - - if($_GET['order'] === 'posted') - $order = 'created ASC'; - elseif ($_GET['order'] === 'name') - $order = 'filename ASC'; - else - $order = 'created DESC'; - + intval($owner_uid), + dbesc($datum) + ); - $prvnxt = q("SELECT hash FROM attach WHERE folder = '%s' AND uid = %d AND is_photo = 1 + if (!($ph && $x)) { + /* Check again - this time without specifying permissions */ + + $ph = q( + "SELECT id FROM photo WHERE uid = %d AND resource_id = '%s' LIMIT 1", + intval($owner_uid), + dbesc($datum) + ); + if ($ph) { + notice(t('Permission denied. Access to this item may be restricted.') . EOL); + } else { + notice(t('Photo not available') . EOL); + } + return; + } + + + $prevlink = ''; + $nextlink = ''; + + if ($_GET['order'] === 'posted') { + $order = 'created ASC'; + } elseif ($_GET['order'] === 'name') { + $order = 'filename ASC'; + } else { + $order = 'created DESC'; + } + + + $prvnxt = q( + "SELECT hash FROM attach WHERE folder = '%s' AND uid = %d AND is_photo = 1 $sql_attach ORDER BY $order ", - dbesc($x[0]['folder']), - intval($owner_uid) - ); + dbesc($x[0]['folder']), + intval($owner_uid) + ); - if(count($prvnxt)) { - for($z = 0; $z < count($prvnxt); $z++) { - if($prvnxt[$z]['hash'] == $ph[0]['resource_id']) { - $prv = $z - 1; - $nxt = $z + 1; - if($prv < 0) - $prv = count($prvnxt) - 1; - if($nxt >= count($prvnxt)) - $nxt = 0; - break; - } - } - - $prevlink = z_root() . '/photos/' . App::$data['channel']['channel_address'] . '/image/' . $prvnxt[$prv]['hash'] . (($_GET['order']) ? '?f=&order=' . $_GET['order'] : ''); - $nextlink = z_root() . '/photos/' . App::$data['channel']['channel_address'] . '/image/' . $prvnxt[$nxt]['hash'] . (($_GET['order']) ? '?f=&order=' . $_GET['order'] : ''); - } - - - if(count($ph) == 1) - $hires = $lores = $ph[0]; - if(count($ph) > 1) { - if($ph[1]['imgscale'] == 2) { - // original is 640 or less, we can display it directly - $hires = $lores = $ph[0]; - } - else { - $hires = $ph[0]; - $lores = $ph[1]; - } - } - - $album_link = z_root() . '/photos/' . App::$data['channel']['channel_address'] . '/album/' . $x[0]['folder']; - $tools = Null; - $lock = Null; - - if($can_post && ($ph[0]['uid'] == $owner_uid)) { - $tools = array( - 'profile'=>array(z_root() . '/profile_photo/use/'.$ph[0]['resource_id'], t('Use as profile photo')), - 'cover'=>array(z_root() . '/cover_photo/use/'.$ph[0]['resource_id'], t('Use as cover photo')), - ); - } - - // lockstate - $lockstate = ( ( (strlen($ph[0]['allow_cid']) || strlen($ph[0]['allow_gid']) - || strlen($ph[0]['deny_cid']) || strlen($ph[0]['deny_gid'])) ) - ? array('lock', t('Private Photo')) - : array('unlock', Null)); - - App::$page['htmlhead'] .= ''; - - if($prevlink) - $prevlink = array($prevlink, t('Previous')); - - $photo = array( - 'href' => z_root() . '/photo/' . $hires['resource_id'] . '-' . $hires['imgscale'] . '.' . $phototypes[$hires['mimetype']], - 'title'=> t('View Full Size'), - 'src' => z_root() . '/photo/' . $lores['resource_id'] . '-' . $lores['imgscale'] . '.' . $phototypes[$lores['mimetype']] . '?f=&_u=' . datetime_convert('','','','ymdhis') - ); - - if($nextlink) - $nextlink = array($nextlink, t('Next')); - - - // Do we have an item for this photo? - - $linked_items = q("SELECT * FROM item WHERE resource_id = '%s' and resource_type = 'photo' and uid = %d + if (count($prvnxt)) { + for ($z = 0; $z < count($prvnxt); $z++) { + if ($prvnxt[$z]['hash'] == $ph[0]['resource_id']) { + $prv = $z - 1; + $nxt = $z + 1; + if ($prv < 0) { + $prv = count($prvnxt) - 1; + } + if ($nxt >= count($prvnxt)) { + $nxt = 0; + } + break; + } + } + + $prevlink = z_root() . '/photos/' . App::$data['channel']['channel_address'] . '/image/' . $prvnxt[$prv]['hash'] . (($_GET['order']) ? '?f=&order=' . $_GET['order'] : ''); + $nextlink = z_root() . '/photos/' . App::$data['channel']['channel_address'] . '/image/' . $prvnxt[$nxt]['hash'] . (($_GET['order']) ? '?f=&order=' . $_GET['order'] : ''); + } + + + if (count($ph) == 1) { + $hires = $lores = $ph[0]; + } + if (count($ph) > 1) { + if ($ph[1]['imgscale'] == 2) { + // original is 640 or less, we can display it directly + $hires = $lores = $ph[0]; + } else { + $hires = $ph[0]; + $lores = $ph[1]; + } + } + + $album_link = z_root() . '/photos/' . App::$data['channel']['channel_address'] . '/album/' . $x[0]['folder']; + $tools = null; + $lock = null; + + if ($can_post && ($ph[0]['uid'] == $owner_uid)) { + $tools = array( + 'profile' => array(z_root() . '/profile_photo/use/' . $ph[0]['resource_id'], t('Use as profile photo')), + 'cover' => array(z_root() . '/cover_photo/use/' . $ph[0]['resource_id'], t('Use as cover photo')), + ); + } + + // lockstate + $lockstate = (((strlen($ph[0]['allow_cid']) || strlen($ph[0]['allow_gid']) + || strlen($ph[0]['deny_cid']) || strlen($ph[0]['deny_gid']))) + ? array('lock', t('Private Photo')) + : array('unlock', null)); + + App::$page['htmlhead'] .= ''; + + if ($prevlink) { + $prevlink = array($prevlink, t('Previous')); + } + + $photo = array( + 'href' => z_root() . '/photo/' . $hires['resource_id'] . '-' . $hires['imgscale'] . '.' . $phototypes[$hires['mimetype']], + 'title' => t('View Full Size'), + 'src' => z_root() . '/photo/' . $lores['resource_id'] . '-' . $lores['imgscale'] . '.' . $phototypes[$lores['mimetype']] . '?f=&_u=' . datetime_convert('', '', '', 'ymdhis') + ); + + if ($nextlink) { + $nextlink = array($nextlink, t('Next')); + } + + + // Do we have an item for this photo? + + $linked_items = q( + "SELECT * FROM item WHERE resource_id = '%s' and resource_type = 'photo' and uid = %d $sql_item LIMIT 1", - dbesc($datum), - intval($owner_uid) - ); - - $map = null; - $link_item = null; - - if($linked_items) { - - xchan_query($linked_items); - $linked_items = fetch_post_tags($linked_items,true); - - $link_item = $linked_items[0]; - $item_normal = item_normal(); - - $r = q("select * from item where parent_mid = '%s' + dbesc($datum), + intval($owner_uid) + ); + + $map = null; + $link_item = null; + + if ($linked_items) { + xchan_query($linked_items); + $linked_items = fetch_post_tags($linked_items, true); + + $link_item = $linked_items[0]; + $item_normal = item_normal(); + + $r = q( + "select * from item where parent_mid = '%s' $item_normal and uid = %d $sql_item ", - dbesc($link_item['mid']), - intval($link_item['uid']) - - ); - - if($r) { - xchan_query($r); - $items = fetch_post_tags($r,true); - $sorted_items = conv_sort($items,'commented'); - } - - $tags = []; - if($link_item['term']) { - $cnt = 0; - foreach($link_item['term'] as $t) { - $tags[$cnt] = array(0 => format_term_for_display($t)); - if($can_post && ($ph[0]['uid'] == $owner_uid)) { - $tags[$cnt][1] = 'tagrm/drop/' . $link_item['id'] . '/' . bin2hex($t['term']); //?f=&item=' . $link_item['id']; - $tags[$cnt][2] = t('Remove'); - } - $cnt ++; - } - } - - if((local_channel()) && (local_channel() == $link_item['uid'])) { - q("UPDATE item SET item_unseen = 0 WHERE parent = %d and uid = %d and item_unseen = 1", - intval($link_item['parent']), - intval(local_channel()) - ); - } - - if($link_item['coord'] && Apps::system_app_installed($owner_uid,'Photomap')) { - $map = generate_map($link_item['coord']); - } - } - - // logger('mod_photo: link_item' . print_r($link_item,true)); - - // FIXME - remove this when we move to conversation module - - $comment_items = $sorted_items[0]['children']; + dbesc($link_item['mid']), + intval($link_item['uid']) + ); - $edit = null; - if($can_post) { + if ($r) { + xchan_query($r); + $items = fetch_post_tags($r, true); + $sorted_items = conv_sort($items, 'commented'); + } - $album_e = $ph[0]['album']; - $caption_e = $ph[0]['description']; - $aclselect_e = (($_is_owner) ? populate_acl($ph[0], true, \Zotlabs\Lib\PermissionDescription::fromGlobalPermission('view_storage')) : ''); - $albums = ((array_key_exists('albums', App::$data)) ? App::$data['albums'] : photos_albums_list(App::$data['channel'],App::$data['observer'])); - - $_SESSION['album_return'] = bin2hex($ph[0]['album']); + $tags = []; + if ($link_item['term']) { + $cnt = 0; + foreach ($link_item['term'] as $t) { + $tags[$cnt] = array(0 => format_term_for_display($t)); + if ($can_post && ($ph[0]['uid'] == $owner_uid)) { + $tags[$cnt][1] = 'tagrm/drop/' . $link_item['id'] . '/' . bin2hex($t['term']); //?f=&item=' . $link_item['id']; + $tags[$cnt][2] = t('Remove'); + } + $cnt++; + } + } - $folder_list = attach_folder_select_list($ph[0]['uid']); - $edit_body = htmlspecialchars_decode(undo_post_tagging($link_item['body']),ENT_COMPAT); - // We will regenerate the body footer - $edit_body = preg_replace('/\[footer\](.*?)\[\/footer\]/ism','',$edit_body); + if ((local_channel()) && (local_channel() == $link_item['uid'])) { + q( + "UPDATE item SET item_unseen = 0 WHERE parent = %d and uid = %d and item_unseen = 1", + intval($link_item['parent']), + intval(local_channel()) + ); + } - $edit = [ - 'edit' => t('Edit photo'), - 'id' => $link_item['id'], - 'albums' => $albums['albums'], - 'album' => $album_e, - 'album_select' => [ 'move_to_album', t('Move photo to album'), $x[0]['folder'], '', $folder_list ], - 'newalbum_label' => t('Enter a new album name'), - 'newalbum_placeholder' => t('or select an existing one (doubleclick)'), - 'nickname' => App::$data['channel']['channel_address'], - 'resource_id' => $ph[0]['resource_id'], - 'desc' => [ 'desc', t('Please briefly describe this photo for vision-impaired viewers'), $ph[0]['description'] ], - 'title' => [ 'title', t('Title (optional)'), $ph[0]['title'] ], - 'body' => [ 'body', t('Your message (optional)'),$edit_body, t('This will only appear in the optional status post attached to this photo') ], - 'tag_label' => t('Add a Tag'), - 'permissions' => t('Permissions'), - 'aclselect' => $aclselect_e, - 'allow_cid' => acl2json($ph[0]['allow_cid']), - 'allow_gid' => acl2json($ph[0]['allow_gid']), - 'deny_cid' => acl2json($ph[0]['deny_cid']), - 'deny_gid' => acl2json($ph[0]['deny_gid']), - 'lockstate' => $lockstate[0], - 'help_tags' => t('Example: @bob, @Barbara_Jensen, @jim@example.com'), - 'item_id' => ((count($linked_items)) ? $link_item['id'] : 0), - 'adult_enabled' => feature_enabled($owner_uid,'adult_photo_flagging'), - 'adult' => array('adult',t('Flag as adult in album view'), intval($ph[0]['is_nsfw']),''), - 'submit' => t('Submit'), - 'delete' => t('Delete Photo'), - 'expandform' => ((x($_GET,'expandform')) ? true : false) - ]; - } - - if(count($linked_items)) { - - $cmnt_tpl = get_markup_template('comment_item.tpl'); - $tpl = get_markup_template('photo_item.tpl'); - $return_url = App::$cmd; - - $like_tpl = get_markup_template('like_noshare.tpl'); - - $likebuttons = ''; - $ilike = false; - $inolike = false; + if ($link_item['coord'] && Apps::system_app_installed($owner_uid, 'Photomap')) { + $map = generate_map($link_item['coord']); + } + } + + // logger('mod_photo: link_item' . print_r($link_item,true)); + + // FIXME - remove this when we move to conversation module + + $comment_items = $sorted_items[0]['children']; + + $edit = null; + if ($can_post) { + $album_e = $ph[0]['album']; + $caption_e = $ph[0]['description']; + $aclselect_e = (($_is_owner) ? populate_acl($ph[0], true, PermissionDescription::fromGlobalPermission('view_storage')) : ''); + $albums = ((array_key_exists('albums', App::$data)) ? App::$data['albums'] : photos_albums_list(App::$data['channel'], App::$data['observer'])); + + $_SESSION['album_return'] = bin2hex($ph[0]['album']); + + $folder_list = attach_folder_select_list($ph[0]['uid']); + $edit_body = htmlspecialchars_decode(undo_post_tagging($link_item['body']), ENT_COMPAT); + // We will regenerate the body footer + $edit_body = preg_replace('/\[footer\](.*?)\[\/footer\]/ism', '', $edit_body); + + $edit = [ + 'edit' => t('Edit photo'), + 'id' => $link_item['id'], + 'albums' => $albums['albums'], + 'album' => $album_e, + 'album_select' => ['move_to_album', t('Move photo to album'), $x[0]['folder'], '', $folder_list], + 'newalbum_label' => t('Enter a new album name'), + 'newalbum_placeholder' => t('or select an existing one (doubleclick)'), + 'nickname' => App::$data['channel']['channel_address'], + 'resource_id' => $ph[0]['resource_id'], + 'desc' => ['desc', t('Please briefly describe this photo for vision-impaired viewers'), $ph[0]['description']], + 'title' => ['title', t('Title (optional)'), $ph[0]['title']], + 'body' => ['body', t('Your message (optional)'), $edit_body, t('This will only appear in the optional status post attached to this photo')], + 'tag_label' => t('Add a Tag'), + 'permissions' => t('Permissions'), + 'aclselect' => $aclselect_e, + 'allow_cid' => acl2json($ph[0]['allow_cid']), + 'allow_gid' => acl2json($ph[0]['allow_gid']), + 'deny_cid' => acl2json($ph[0]['deny_cid']), + 'deny_gid' => acl2json($ph[0]['deny_gid']), + 'lockstate' => $lockstate[0], + 'help_tags' => t('Example: @bob, @Barbara_Jensen, @jim@example.com'), + 'item_id' => ((count($linked_items)) ? $link_item['id'] : 0), + 'adult_enabled' => feature_enabled($owner_uid, 'adult_photo_flagging'), + 'adult' => array('adult', t('Flag as adult in album view'), intval($ph[0]['is_nsfw']), ''), + 'submit' => t('Submit'), + 'delete' => t('Delete Photo'), + 'expandform' => ((x($_GET, 'expandform')) ? true : false) + ]; + } + + if (count($linked_items)) { + $cmnt_tpl = get_markup_template('comment_item.tpl'); + $tpl = get_markup_template('photo_item.tpl'); + $return_url = App::$cmd; + + $like_tpl = get_markup_template('like_noshare.tpl'); + + $likebuttons = ''; + $ilike = false; + $inolike = false; - if ($items) { - foreach ($items as $i) { - if ($i['verb'] === 'Like' && $i['author_xchan'] === get_observer_hash() && $i['thr_parent'] = $link_item['mid']) { - $ilike = true; - } - if ($i['verb'] === 'Dislike' && $i['author_xchan'] === get_observer_hash() && $i['thr_parent'] === $link_item['mid']) { - $inolike = true; - } - } - } - - if($observer && ($can_post || $can_comment)) { - $likebuttons = [ - 'id' => $link_item['id'], - 'likethis' => t('I like this'), - 'ilike' => $ilike, - 'inolike' => $inolike, - 'nolike' => t('I don\'t like this'), - 'unlikethis' => t('Undo like'), - 'unnolike' => t('Undo dislike'), - 'share' => t('Share'), - 'wait' => t('Please wait') - ]; - } + if ($items) { + foreach ($items as $i) { + if ($i['verb'] === 'Like' && $i['author_xchan'] === get_observer_hash() && $i['thr_parent'] = $link_item['mid']) { + $ilike = true; + } + if ($i['verb'] === 'Dislike' && $i['author_xchan'] === get_observer_hash() && $i['thr_parent'] === $link_item['mid']) { + $inolike = true; + } + } + } - $comments = ''; - if(! $comment_items) { - if($observer && ($can_post || $can_comment)) { - $commentbox = replace_macros($cmnt_tpl,array( - '$return_path' => '', - '$mode' => 'photos', - '$jsreload' => $return_url, - '$type' => 'wall-comment', - '$id' => $link_item['id'], - '$parent' => $link_item['id'], - '$profile_uid' => $owner_uid, - '$mylink' => $observer['xchan_url'], - '$mytitle' => t('This is you'), - '$myphoto' => $observer['xchan_photo_s'], - '$comment' => t('Comment'), - '$submit' => t('Submit'), - '$preview' => t('Preview'), - '$auto_save_draft' => 'true', - '$ww' => '', - '$feature_encrypt' => false - )); - } - } - - $alike = []; - $dlike = []; - - $like = ''; - $dislike = ''; - - $conv_responses = [ - 'like' => [ 'title' => t('Likes','title') ], - 'dislike' => [ 'title' => t('Dislikes','title') ], - 'attendyes' => [ 'title' => t('Attending','title') ], - 'attendno' => [ 'title' => t('Not attending','title') ], - 'attendmaybe' => [ 'title' => t('Might attend' ,'title') ] - ]; - - if($r) { - - foreach($r as $item) { - builtin_activity_puller($item, $conv_responses); - } - - $like_count = ((x($alike,$link_item['mid'])) ? $alike[$link_item['mid']] : ''); - $like_list = ((x($alike,$link_item['mid'])) ? $alike[$link_item['mid'] . '-l'] : ''); + if ($observer && ($can_post || $can_comment)) { + $likebuttons = [ + 'id' => $link_item['id'], + 'likethis' => t('I like this'), + 'ilike' => $ilike, + 'inolike' => $inolike, + 'nolike' => t('I don\'t like this'), + 'unlikethis' => t('Undo like'), + 'unnolike' => t('Undo dislike'), + 'share' => t('Share'), + 'wait' => t('Please wait') + ]; + } - if(is_array($like_list) && (count($like_list) > MAX_LIKERS)) { - $like_list_part = array_slice($like_list, 0, MAX_LIKERS); - array_push($like_list_part, '' . t('View all') . ''); - } else { - $like_list_part = ''; - } - $like_button_label = tt('Like','Likes',$like_count,'noun'); - - $dislike_count = ((x($dlike,$link_item['mid'])) ? $dlike[$link_item['mid']] : ''); - $dislike_list = ((x($dlike,$link_item['mid'])) ? $dlike[$link_item['mid'] . '-l'] : ''); - $dislike_button_label = tt('Dislike','Dislikes',$dislike_count,'noun'); - if (is_array($dislike_list) && (count($dislike_list) > MAX_LIKERS)) { - $dislike_list_part = array_slice($dislike_list, 0, MAX_LIKERS); - array_push($dislike_list_part, '' . t('View all') . ''); - } else { - $dislike_list_part = ''; - } + $comments = ''; + if (!$comment_items) { + if ($observer && ($can_post || $can_comment)) { + $commentbox = replace_macros($cmnt_tpl, array( + '$return_path' => '', + '$mode' => 'photos', + '$jsreload' => $return_url, + '$type' => 'wall-comment', + '$id' => $link_item['id'], + '$parent' => $link_item['id'], + '$profile_uid' => $owner_uid, + '$mylink' => $observer['xchan_url'], + '$mytitle' => t('This is you'), + '$myphoto' => $observer['xchan_photo_s'], + '$comment' => t('Comment'), + '$submit' => t('Submit'), + '$preview' => t('Preview'), + '$auto_save_draft' => 'true', + '$ww' => '', + '$feature_encrypt' => false + )); + } + } - - - $like = ((isset($alike[$link_item['mid']])) ? format_like($alike[$link_item['mid']],$alike[$link_item['mid'] . '-l'],'like',$link_item['mid']) : ''); - $dislike = ((isset($dlike[$link_item['mid']])) ? format_like($dlike[$link_item['mid']],$dlike[$link_item['mid'] . '-l'],'dislike',$link_item['mid']) : ''); - - // display comments - - foreach ($comment_items as $item) { - $comment = ''; - $template = $tpl; - $sparkle = ''; - - if (! visible_activity($item)) { - continue; - } - - $profile_url = zid($item['author']['xchan_url']); - $profile_name = $item['author']['xchan_name']; - $profile_avatar = $item['author']['xchan_photo_m']; - - $profile_link = $profile_url; - - $drop = ''; - - if($observer['xchan_hash'] === $item['author_xchan'] || $observer['xchan_hash'] === $item['owner_xchan']) - $drop = replace_macros(get_markup_template('photo_drop.tpl'), array('$id' => $item['id'], '$delete' => t('Delete'))); - - - $name_e = $profile_name; - $title_e = $item['title']; - unobscure($item); - $body_e = prepare_text($item['body'],$item['mimetype']); - - $comments .= replace_macros($template,array( - '$id' => $item['id'], - '$mode' => 'photos', - '$profile_url' => $profile_link, - '$name' => $name_e, - '$thumb' => $profile_avatar, - '$sparkle' => $sparkle, - '$title' => $title_e, - '$body' => $body_e, - '$ago' => relative_date($item['created']), - '$indent' => (($item['parent'] != $item['id']) ? ' comment' : ''), - '$drop' => $drop, - '$comment' => $comment - )); - - } - - if($observer && ($can_post || $can_comment)) { - $commentbox = replace_macros($cmnt_tpl,array( - '$return_path' => '', - '$jsreload' => $return_url, - '$type' => 'wall-comment', - '$id' => $link_item['id'], - '$parent' => $link_item['id'], - '$profile_uid' => $owner_uid, - '$mylink' => $observer['xchan_url'], - '$mytitle' => t('This is you'), - '$myphoto' => $observer['xchan_photo_s'], - '$comment' => t('Comment'), - '$submit' => t('Submit'), - '$ww' => '' - )); - } - - } - $paginate = paginate($a); - } - - $album_e = [ $album_link, $ph[0]['album'] ]; - $like_e = $like; - $dislike_e = $dislike; - - - $response_verbs = array('like','dislike'); - - $responses = get_responses($conv_responses,$response_verbs,'',$link_item); - - $o .= replace_macros(get_markup_template('photo_view.tpl'), [ - '$id' => $ph[0]['id'], - '$album' => $album_e, - '$tools_label' => t('Photo Tools'), - '$tools' => $tools, - '$lock' => $lockstate[1], - '$photo' => $photo, - '$prevlink' => $prevlink, - '$nextlink' => $nextlink, - '$title' => $ph[0]['title'], - '$desc' => $ph[0]['description'], - '$filename' => $ph[0]['filename'], - '$unknown' => t('Unknown'), - '$tag_hdr' => t('In This Photo:'), - '$tags' => $tags, - 'responses' => $responses, - '$edit' => $edit, - '$map' => $map, - '$map_text' => t('Map'), - '$likebuttons' => $likebuttons, - '$like' => $like_e, - '$dislike' => $dislike_e, - '$like_count' => $like_count, - '$like_list' => $like_list, - '$like_list_part' => $like_list_part, - '$like_button_label' => $like_button_label, - '$like_modal_title' => t('Likes','noun'), - '$dislike_modal_title' => t('Dislikes','noun'), - '$dislike_count' => $dislike_count, - '$dislike_list' => $dislike_list, - '$dislike_list_part' => $dislike_list_part, - '$dislike_button_label' => $dislike_button_label, - '$modal_dismiss' => t('Close'), - '$comments' => $comments, - '$commentbox' => $commentbox, - '$paginate' => $paginate, - ]); - - App::$data['photo_html'] = $o; - return $o; - } - - // Default - show recent photos - - head_add_link([ - 'rel' => 'alternate', - 'type' => 'application/json+oembed', - 'href' => z_root() . '/oep?f=&url=' . urlencode(z_root() . '/' . \App::$query_string), - 'title' => 'oembed' - ]); + $alike = []; + $dlike = []; - App::set_pager_itemspage(60); - - $r = q("SELECT p.resource_id, p.id, p.filename, p.mimetype, p.album, p.imgscale, p.created, p.display_path FROM photo p + $like = ''; + $dislike = ''; + + $conv_responses = [ + 'like' => ['title' => t('Likes', 'title')], + 'dislike' => ['title' => t('Dislikes', 'title')], + 'attendyes' => ['title' => t('Attending', 'title')], + 'attendno' => ['title' => t('Not attending', 'title')], + 'attendmaybe' => ['title' => t('Might attend', 'title')] + ]; + + if ($r) { + foreach ($r as $item) { + builtin_activity_puller($item, $conv_responses); + } + + $like_count = ((x($alike, $link_item['mid'])) ? $alike[$link_item['mid']] : ''); + $like_list = ((x($alike, $link_item['mid'])) ? $alike[$link_item['mid'] . '-l'] : ''); + + if (is_array($like_list) && (count($like_list) > MAX_LIKERS)) { + $like_list_part = array_slice($like_list, 0, MAX_LIKERS); + array_push($like_list_part, '' . t('View all') . ''); + } else { + $like_list_part = ''; + } + $like_button_label = tt('Like', 'Likes', $like_count, 'noun'); + + $dislike_count = ((x($dlike, $link_item['mid'])) ? $dlike[$link_item['mid']] : ''); + $dislike_list = ((x($dlike, $link_item['mid'])) ? $dlike[$link_item['mid'] . '-l'] : ''); + $dislike_button_label = tt('Dislike', 'Dislikes', $dislike_count, 'noun'); + if (is_array($dislike_list) && (count($dislike_list) > MAX_LIKERS)) { + $dislike_list_part = array_slice($dislike_list, 0, MAX_LIKERS); + array_push($dislike_list_part, '' . t('View all') . ''); + } else { + $dislike_list_part = ''; + } + + + $like = ((isset($alike[$link_item['mid']])) ? format_like($alike[$link_item['mid']], $alike[$link_item['mid'] . '-l'], 'like', $link_item['mid']) : ''); + $dislike = ((isset($dlike[$link_item['mid']])) ? format_like($dlike[$link_item['mid']], $dlike[$link_item['mid'] . '-l'], 'dislike', $link_item['mid']) : ''); + + // display comments + + foreach ($comment_items as $item) { + $comment = ''; + $template = $tpl; + $sparkle = ''; + + if (!visible_activity($item)) { + continue; + } + + $profile_url = zid($item['author']['xchan_url']); + $profile_name = $item['author']['xchan_name']; + $profile_avatar = $item['author']['xchan_photo_m']; + + $profile_link = $profile_url; + + $drop = ''; + + if ($observer['xchan_hash'] === $item['author_xchan'] || $observer['xchan_hash'] === $item['owner_xchan']) { + $drop = replace_macros(get_markup_template('photo_drop.tpl'), array('$id' => $item['id'], '$delete' => t('Delete'))); + } + + + $name_e = $profile_name; + $title_e = $item['title']; + unobscure($item); + $body_e = prepare_text($item['body'], $item['mimetype']); + + $comments .= replace_macros($template, array( + '$id' => $item['id'], + '$mode' => 'photos', + '$profile_url' => $profile_link, + '$name' => $name_e, + '$thumb' => $profile_avatar, + '$sparkle' => $sparkle, + '$title' => $title_e, + '$body' => $body_e, + '$ago' => relative_date($item['created']), + '$indent' => (($item['parent'] != $item['id']) ? ' comment' : ''), + '$drop' => $drop, + '$comment' => $comment + )); + } + + if ($observer && ($can_post || $can_comment)) { + $commentbox = replace_macros($cmnt_tpl, array( + '$return_path' => '', + '$jsreload' => $return_url, + '$type' => 'wall-comment', + '$id' => $link_item['id'], + '$parent' => $link_item['id'], + '$profile_uid' => $owner_uid, + '$mylink' => $observer['xchan_url'], + '$mytitle' => t('This is you'), + '$myphoto' => $observer['xchan_photo_s'], + '$comment' => t('Comment'), + '$submit' => t('Submit'), + '$ww' => '' + )); + } + } + $paginate = paginate($a); + } + + $album_e = [$album_link, $ph[0]['album']]; + $like_e = $like; + $dislike_e = $dislike; + + + $response_verbs = array('like', 'dislike'); + + $responses = get_responses($conv_responses, $response_verbs, '', $link_item); + + $o .= replace_macros(get_markup_template('photo_view.tpl'), [ + '$id' => $ph[0]['id'], + '$album' => $album_e, + '$tools_label' => t('Photo Tools'), + '$tools' => $tools, + '$lock' => $lockstate[1], + '$photo' => $photo, + '$prevlink' => $prevlink, + '$nextlink' => $nextlink, + '$title' => $ph[0]['title'], + '$desc' => $ph[0]['description'], + '$filename' => $ph[0]['filename'], + '$unknown' => t('Unknown'), + '$tag_hdr' => t('In This Photo:'), + '$tags' => $tags, + 'responses' => $responses, + '$edit' => $edit, + '$map' => $map, + '$map_text' => t('Map'), + '$likebuttons' => $likebuttons, + '$like' => $like_e, + '$dislike' => $dislike_e, + '$like_count' => $like_count, + '$like_list' => $like_list, + '$like_list_part' => $like_list_part, + '$like_button_label' => $like_button_label, + '$like_modal_title' => t('Likes', 'noun'), + '$dislike_modal_title' => t('Dislikes', 'noun'), + '$dislike_count' => $dislike_count, + '$dislike_list' => $dislike_list, + '$dislike_list_part' => $dislike_list_part, + '$dislike_button_label' => $dislike_button_label, + '$modal_dismiss' => t('Close'), + '$comments' => $comments, + '$commentbox' => $commentbox, + '$paginate' => $paginate, + ]); + + App::$data['photo_html'] = $o; + return $o; + } + + // Default - show recent photos + + head_add_link([ + 'rel' => 'alternate', + 'type' => 'application/json+oembed', + 'href' => z_root() . '/oep?f=&url=' . urlencode(z_root() . '/' . App::$query_string), + 'title' => 'oembed' + ]); + + App::set_pager_itemspage(60); + + $r = q( + "SELECT p.resource_id, p.id, p.filename, p.mimetype, p.album, p.imgscale, p.created, p.display_path FROM photo p INNER JOIN ( SELECT resource_id, max(imgscale) imgscale FROM photo WHERE photo.uid = %d 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) ORDER by p.created DESC LIMIT %d OFFSET %d", - intval(App::$data['channel']['channel_id']), - intval(PHOTO_NORMAL), - intval(PHOTO_PROFILE), - intval($unsafe), - intval(App::$pager['itemspage']), - intval(App::$pager['start']) - ); - - $photos = []; - if($r) { - $twist = 'rotright'; - foreach($r as $rr) { + intval(App::$data['channel']['channel_id']), + intval(PHOTO_NORMAL), + intval(PHOTO_PROFILE), + intval($unsafe), + intval(App::$pager['itemspage']), + intval(App::$pager['start']) + ); - if(! attach_can_view_folder(App::$data['channel']['channel_id'],get_observer_hash(),$rr['resource_id'])) - continue; + $photos = []; + if ($r) { + $twist = 'rotright'; + foreach ($r as $rr) { + if (!attach_can_view_folder(App::$data['channel']['channel_id'], get_observer_hash(), $rr['resource_id'])) { + continue; + } - if($twist == 'rotright') - $twist = 'rotleft'; - else - $twist = 'rotright'; - $ext = $phototypes[$rr['mimetype']]; - - $alt_e = $rr['filename']; - $name_e = dirname($rr['display_path']); + if ($twist == 'rotright') { + $twist = 'rotleft'; + } else { + $twist = 'rotright'; + } + $ext = $phototypes[$rr['mimetype']]; - $photos[] = [ - 'id' => $rr['id'], - 'twist' => ' ' . $twist . rand(2,4), - 'link' => z_root() . '/photos/' . App::$data['channel']['channel_address'] . '/image/' . $rr['resource_id'], - 'title' => t('View Photo'), - 'src' => z_root() . '/photo/' . $rr['resource_id'] . '-' . ((($rr['imgscale']) == 6) ? 4 : $rr['imgscale']) . '.' . $ext, - 'alt' => $alt_e, - 'album' => [ 'name' => $name_e ], - ]; - } - } - - if($_REQUEST['aj']) { - if($photos) { - $o = replace_macros(get_markup_template('photosajax.tpl'), [ - '$photos' => $photos, - '$album_id' => bin2hex(t('Recent Photos')) - ]); - } - else { - $o = '
      '; - } - echo $o; - killme(); - } - else { + $alt_e = $rr['filename']; + $name_e = dirname($rr['display_path']); - $o .= ""; + $photos[] = [ + 'id' => $rr['id'], + 'twist' => ' ' . $twist . rand(2, 4), + 'link' => z_root() . '/photos/' . App::$data['channel']['channel_address'] . '/image/' . $rr['resource_id'], + 'title' => t('View Photo'), + 'src' => z_root() . '/photo/' . $rr['resource_id'] . '-' . ((($rr['imgscale']) == 6) ? 4 : $rr['imgscale']) . '.' . $ext, + 'alt' => $alt_e, + 'album' => ['name' => $name_e], + ]; + } + } - $o .= replace_macros(get_markup_template('photos_recent.tpl'), [ - '$title' => t('Recent Photos'), - '$album_id' => bin2hex(t('Recent Photos')), - '$file_view' => t('View files'), - '$files_path' => z_root() . '/cloud/' . App::$data['channel']['channel_address'], - '$can_post' => $can_post, - '$upload' => t('Add Photos'), - '$photos' => $photos, - '$upload_form' => $upload_form, - '$usage' => $usage_message - ]); + if ($_REQUEST['aj']) { + if ($photos) { + $o = replace_macros(get_markup_template('photosajax.tpl'), [ + '$photos' => $photos, + '$album_id' => bin2hex(t('Recent Photos')) + ]); + } else { + $o = '
      '; + } + echo $o; + killme(); + } else { + $o .= ""; - return $o; - } - } + $o .= replace_macros(get_markup_template('photos_recent.tpl'), [ + '$title' => t('Recent Photos'), + '$album_id' => bin2hex(t('Recent Photos')), + '$file_view' => t('View files'), + '$files_path' => z_root() . '/cloud/' . App::$data['channel']['channel_address'], + '$can_post' => $can_post, + '$upload' => t('Add Photos'), + '$photos' => $photos, + '$upload_form' => $upload_form, + '$usage' => $usage_message + ]); + + return $o; + } + } } diff --git a/Zotlabs/Module/Pin.php b/Zotlabs/Module/Pin.php index 1daa7c524..547882cf6 100644 --- a/Zotlabs/Module/Pin.php +++ b/Zotlabs/Module/Pin.php @@ -1,4 +1,5 @@ '%s' $seenstr $item_normal $sql_extra", - intval($sys['channel_id']), - dbesc($loadtime) - ); + intval($sys['channel_id']), + dbesc($loadtime) + ); - if ($pubs) { - foreach($pubs as $p) { - if ($p['author_xchan'] === get_observer_hash()) { - $my_activity ++; - } - else { - $result['pubs'] ++; - } - } - } - } - - if ((argc() > 1) && (argv(1) === 'pubs') && ($notify_pubs)) { + if ($pubs) { + foreach ($pubs as $p) { + if ($p['author_xchan'] === get_observer_hash()) { + $my_activity++; + } else { + $result['pubs']++; + } + } + } + } - $local_result = []; + if ((argc() > 1) && (argv(1) === 'pubs') && ($notify_pubs)) { + $local_result = []; - $r = q("SELECT * FROM item + $r = q( + "SELECT * FROM item WHERE uid = %d AND author_xchan != '%s' AND created > '%s' @@ -191,150 +197,156 @@ class Ping extends Controller { $sql_extra ORDER BY created DESC LIMIT 300", - intval($sys['channel_id']), - dbesc(get_observer_hash()), - dbesc($loadtime) - ); + intval($sys['channel_id']), + dbesc(get_observer_hash()), + dbesc($loadtime) + ); - if ($r) { - xchan_query($r); - foreach ($r as $rr) { - $rr['llink'] = str_replace('display/', 'pubstream/?f=&mid=', $rr['llink']); - $z = Enotify::format($rr); - if ($z) { - $local_result[] = $z; - } - } - } + if ($r) { + xchan_query($r); + foreach ($r as $rr) { + $rr['llink'] = str_replace('display/', 'pubstream/?f=&mid=', $rr['llink']); + $z = Enotify::format($rr); + if ($z) { + $local_result[] = $z; + } + } + } - json_return_and_die( [ 'notify' => $local_result ] ); - } + json_return_and_die(['notify' => $local_result]); + } - if ((! local_channel()) || ($result['invalid'])) { - json_return_and_die($result); - } + if ((!local_channel()) || ($result['invalid'])) { + json_return_and_die($result); + } - /** - * Everything following is only permitted under the context of a locally authenticated site member. - */ + /** + * Everything following is only permitted under the context of a locally authenticated site member. + */ - /** - * Handle "mark all xyz notifications read" requests. - */ + /** + * Handle "mark all xyz notifications read" requests. + */ - // mark all items read - if (x($_REQUEST, 'markRead') && local_channel() && (! $_SESSION['sudo'])) { - switch ($_REQUEST['markRead']) { - case 'stream': - $r = q("UPDATE item SET item_unseen = 0 WHERE uid = %d AND item_unseen = 1", - intval(local_channel()) - ); - $_SESSION['loadtime_stream'] = datetime_convert(); - PConfig::Set(local_channel(),'system','loadtime_stream',$_SESSION['loadtime_stream']); - $_SESSION['loadtime_channel'] = datetime_convert(); - PConfig::Set(local_channel(),'system','loadtime_channel',$_SESSION['loadtime_channel']); - break; - case 'home': - $r = q("UPDATE item SET item_unseen = 0 WHERE uid = %d AND item_unseen = 1 AND item_wall = 1", - intval(local_channel()) - ); - $_SESSION['loadtime_channel'] = datetime_convert(); - PConfig::Set(local_channel(),'system','loadtime_channel',$_SESSION['loadtime_channel']); - break; - case 'all_events': - $r = q("UPDATE event SET dismissed = 1 WHERE uid = %d AND dismissed = 0 AND dtstart < '%s' AND dtstart > '%s' ", - intval(local_channel()), - dbesc(datetime_convert('UTC', date_default_timezone_get(), 'now + ' . intval($evdays) . ' days')), - dbesc(datetime_convert('UTC', date_default_timezone_get(), 'now - 1 days')) - ); - break; - case 'notify': - $r = q("update notify set seen = 1 where uid = %d", - intval(local_channel()) - ); - break; - case 'pubs': + // mark all items read + if (x($_REQUEST, 'markRead') && local_channel() && (!$_SESSION['sudo'])) { + switch ($_REQUEST['markRead']) { + case 'stream': + $r = q( + "UPDATE item SET item_unseen = 0 WHERE uid = %d AND item_unseen = 1", + intval(local_channel()) + ); + $_SESSION['loadtime_stream'] = datetime_convert(); + PConfig::Set(local_channel(), 'system', 'loadtime_stream', $_SESSION['loadtime_stream']); + $_SESSION['loadtime_channel'] = datetime_convert(); + PConfig::Set(local_channel(), 'system', 'loadtime_channel', $_SESSION['loadtime_channel']); + break; + case 'home': + $r = q( + "UPDATE item SET item_unseen = 0 WHERE uid = %d AND item_unseen = 1 AND item_wall = 1", + intval(local_channel()) + ); + $_SESSION['loadtime_channel'] = datetime_convert(); + PConfig::Set(local_channel(), 'system', 'loadtime_channel', $_SESSION['loadtime_channel']); + break; + case 'all_events': + $r = q( + "UPDATE event SET dismissed = 1 WHERE uid = %d AND dismissed = 0 AND dtstart < '%s' AND dtstart > '%s' ", + intval(local_channel()), + dbesc(datetime_convert('UTC', date_default_timezone_get(), 'now + ' . intval($evdays) . ' days')), + dbesc(datetime_convert('UTC', date_default_timezone_get(), 'now - 1 days')) + ); + break; + case 'notify': + $r = q( + "update notify set seen = 1 where uid = %d", + intval(local_channel()) + ); + break; + case 'pubs': + $_SESSION['loadtime_pubstream'] = datetime_convert(); + PConfig::Set(local_channel(), 'system', 'loadtime_pubstream', $_SESSION['loadtime_pubstream']); + break; + default: + break; + } + } - $_SESSION['loadtime_pubstream'] = datetime_convert(); - PConfig::Set(local_channel(),'system','loadtime_pubstream',$_SESSION['loadtime_pubstream']); - break; - default: - break; - } - } + if (x($_REQUEST, 'markItemRead') && local_channel() && (!$_SESSION['sudo'])) { + $r = q( + "UPDATE item SET item_unseen = 0 WHERE uid = %d AND parent = %d", + intval(local_channel()), + intval($_REQUEST['markItemRead']) + ); + $id = intval($_REQUEST['markItemRead']); + $seen = PConfig::Get(local_channel(), 'system', 'seen_items', []); + if (!in_array($id, $seen)) { + $seen[] = $id; + } + PConfig::Set(local_channel(), 'system', 'seen_items', $seen); + } - if (x($_REQUEST, 'markItemRead') && local_channel() && (! $_SESSION['sudo'])) { - $r = q("UPDATE item SET item_unseen = 0 WHERE uid = %d AND parent = %d", - intval(local_channel()), - intval($_REQUEST['markItemRead']) - ); - $id = intval($_REQUEST['markItemRead']); - $seen = PConfig::Get(local_channel(),'system','seen_items',[]); - if (! in_array($id,$seen)) { - $seen[] = $id; - } - PConfig::Set(local_channel(),'system','seen_items',$seen); - } + /** + * URL ping/something will return detail for "something", e.g. a json list with which to populate a notification + * dropdown menu. + */ - /** - * URL ping/something will return detail for "something", e.g. a json list with which to populate a notification - * dropdown menu. - */ - - if (argc() > 1 && argv(1) === 'notify') { + if (argc() > 1 && argv(1) === 'notify') { + $t = q( + "SELECT * FROM notify WHERE uid = %d AND seen = 0 ORDER BY CREATED DESC", + intval(local_channel()) + ); - $t = q("SELECT * FROM notify WHERE uid = %d AND seen = 0 ORDER BY CREATED DESC", - intval(local_channel()) - ); + if ($t) { + foreach ($t as $tt) { + $message = trim(strip_tags(bbcode($tt['msg']))); - if ($t) { - foreach ($t as $tt) { - $message = trim(strip_tags(bbcode($tt['msg']))); - - if (strpos($message, $tt['xname']) === 0) - $message = substr($message, strlen($tt['xname']) + 1); + if (strpos($message, $tt['xname']) === 0) { + $message = substr($message, strlen($tt['xname']) + 1); + } - $mid = basename($tt['link']); - $mid = unpack_link_id($mid); + $mid = basename($tt['link']); + $mid = unpack_link_id($mid); - if (in_array($tt['verb'], [ACTIVITY_LIKE, ACTIVITY_DISLIKE])) { - // we need the thread parent - $r = q("select thr_parent from item where mid = '%s' and uid = %d limit 1", - dbesc($mid), - intval(local_channel()) - ); + if (in_array($tt['verb'], [ACTIVITY_LIKE, ACTIVITY_DISLIKE])) { + // we need the thread parent + $r = q( + "select thr_parent from item where mid = '%s' and uid = %d limit 1", + dbesc($mid), + intval(local_channel()) + ); - $b64mid = ((strpos($r[0]['thr_parent'], 'b64.') === 0) ? $r[0]['thr_parent'] : gen_link_id($r[0]['thr_parent'])); - } - else { - $b64mid = ((strpos($mid, 'b64.') === 0) ? $mid : gen_link_id($mid)); - } + $b64mid = ((strpos($r[0]['thr_parent'], 'b64.') === 0) ? $r[0]['thr_parent'] : gen_link_id($r[0]['thr_parent'])); + } else { + $b64mid = ((strpos($mid, 'b64.') === 0) ? $mid : gen_link_id($mid)); + } - $notifs[] = array( - 'notify_link' => z_root() . '/notify/view/' . $tt['id'], - 'name' => $tt['xname'], - 'url' => $tt['url'], - 'photo' => $tt['photo'], - 'when' => relative_date($tt['created']), - 'hclass' => (($tt['seen']) ? 'notify-seen' : 'notify-unseen'), - 'b64mid' => (($tt['otype'] == 'item') ? $b64mid : 'undefined'), - 'notify_id' => (($tt['otype'] == 'item') ? $tt['id'] : 'undefined'), - 'message' => $message - ); - } - } + $notifs[] = array( + 'notify_link' => z_root() . '/notify/view/' . $tt['id'], + 'name' => $tt['xname'], + 'url' => $tt['url'], + 'photo' => $tt['photo'], + 'when' => relative_date($tt['created']), + 'hclass' => (($tt['seen']) ? 'notify-seen' : 'notify-unseen'), + 'b64mid' => (($tt['otype'] == 'item') ? $b64mid : 'undefined'), + 'notify_id' => (($tt['otype'] == 'item') ? $tt['id'] : 'undefined'), + 'message' => $message + ); + } + } - json_return_and_die( [ 'notify' => $notifs ] ); - } + json_return_and_die(['notify' => $notifs]); + } - if (argc() > 1 && (argv(1) === 'stream')) { - $local_result = []; + if (argc() > 1 && (argv(1) === 'stream')) { + $local_result = []; - $item_normal_moderate = $item_normal; - $loadtime = get_loadtime('stream'); - - $r = q("SELECT * FROM item + $item_normal_moderate = $item_normal; + $loadtime = get_loadtime('stream'); + + $r = q( + "SELECT * FROM item WHERE uid = %d AND author_xchan != '%s' AND changed > '%s' @@ -343,34 +355,35 @@ class Ping extends Controller { $sql_extra ORDER BY created DESC LIMIT 300", - intval(local_channel()), - dbesc($ob_hash), - dbesc($loadtime) - ); - if ($r) { - xchan_query($r); - foreach ($r as $item) { - $z = Enotify::format($item); + intval(local_channel()), + dbesc($ob_hash), + dbesc($loadtime) + ); + if ($r) { + xchan_query($r); + foreach ($r as $item) { + $z = Enotify::format($item); - if($z) { - $local_result[] = $z; - } - } - } + if ($z) { + $local_result[] = $z; + } + } + } - json_return_and_die( [ 'notify' => $local_result ] ); - } + json_return_and_die(['notify' => $local_result]); + } - if (argc() > 1 && (argv(1) === 'home')) { - $local_result = []; - $item_normal_moderate = $item_normal; + if (argc() > 1 && (argv(1) === 'home')) { + $local_result = []; + $item_normal_moderate = $item_normal; - $sql_extra .= " and item_wall = 1 "; - $item_normal_moderate = item_normal_moderate(); + $sql_extra .= " and item_wall = 1 "; + $item_normal_moderate = item_normal_moderate(); - $loadtime = get_loadtime('channel'); + $loadtime = get_loadtime('channel'); - $r = q("SELECT * FROM item + $r = q( + "SELECT * FROM item WHERE uid = %d AND author_xchan != '%s' AND changed > '%s' @@ -379,406 +392,416 @@ class Ping extends Controller { $sql_extra ORDER BY created DESC LIMIT 300", - intval(local_channel()), - dbesc($ob_hash), - dbesc($loadtime) - ); - if ($r) { - xchan_query($r); - foreach ($r as $item) { - $z = Enotify::format($item); + intval(local_channel()), + dbesc($ob_hash), + dbesc($loadtime) + ); + if ($r) { + xchan_query($r); + foreach ($r as $item) { + $z = Enotify::format($item); - if($z) { - $local_result[] = $z; - } - } - } + if ($z) { + $local_result[] = $z; + } + } + } - json_return_and_die( [ 'notify' => $local_result ] ); - } + json_return_and_die(['notify' => $local_result]); + } + if (argc() > 1 && (argv(1) === 'intros')) { + $local_result = []; - if (argc() > 1 && (argv(1) === 'intros')) { - $local_result = []; + $r = q( + "SELECT * FROM abook left join xchan on abook.abook_xchan = xchan.xchan_hash where abook_channel = %d and abook_pending = 1 and abook_self = 0 and abook_ignored = 0 and xchan_deleted = 0 and xchan_orphan = 0 ORDER BY abook_created DESC LIMIT 50", + intval(local_channel()) + ); - $r = q("SELECT * FROM abook left join xchan on abook.abook_xchan = xchan.xchan_hash where abook_channel = %d and abook_pending = 1 and abook_self = 0 and abook_ignored = 0 and xchan_deleted = 0 and xchan_orphan = 0 ORDER BY abook_created DESC LIMIT 50", - intval(local_channel()) - ); + if ($r) { + foreach ($r as $rr) { + $local_result[] = [ + 'notify_link' => z_root() . '/connections/' . $rr['abook_id'], + 'name' => $rr['xchan_name'], + 'addr' => $rr['xchan_addr'], + 'url' => $rr['xchan_url'], + 'photo' => $rr['xchan_photo_s'], + 'when' => relative_date($rr['abook_created']), + 'hclass' => ('notify-unseen'), + 'message' => t('added your channel') + ]; + } + } - if ($r) { - foreach ($r as $rr) { - $local_result[] = [ - 'notify_link' => z_root() . '/connections/' . $rr['abook_id'], - 'name' => $rr['xchan_name'], - 'addr' => $rr['xchan_addr'], - 'url' => $rr['xchan_url'], - 'photo' => $rr['xchan_photo_s'], - 'when' => relative_date($rr['abook_created']), - 'hclass' => ('notify-unseen'), - 'message' => t('added your channel') - ]; - } - } + json_return_and_die(['notify' => $local_result]); + } - json_return_and_die( [ 'notify' => $local_result ] ); - } + if ((argc() > 1 && (argv(1) === 'register')) && is_site_admin()) { + $result = []; - if( (argc() > 1 && (argv(1) === 'register')) && is_site_admin()) { - $result = []; + $r = q( + "SELECT account_email, account_created from account where (account_flags & %d) > 0", + intval(ACCOUNT_PENDING) + ); + if ($r) { + foreach ($r as $rr) { + $result[] = array( + 'notify_link' => z_root() . '/admin/accounts', + 'name' => $rr['account_email'], + 'addr' => $rr['account_email'], + 'url' => '', + 'photo' => z_root() . '/' . get_default_profile_photo(48), + 'when' => relative_date($rr['account_created']), + 'hclass' => ('notify-unseen'), + 'message' => t('requires approval') + ); + } + } - $r = q("SELECT account_email, account_created from account where (account_flags & %d) > 0", - intval(ACCOUNT_PENDING) - ); - if ($r) { - foreach ($r as $rr) { - $result[] = array( - 'notify_link' => z_root() . '/admin/accounts', - 'name' => $rr['account_email'], - 'addr' => $rr['account_email'], - 'url' => '', - 'photo' => z_root() . '/' . get_default_profile_photo(48), - 'when' => relative_date($rr['account_created']), - 'hclass' => ('notify-unseen'), - 'message' => t('requires approval') - ); - } - } + json_return_and_die(['notify' => $result]); + } - json_return_and_die( [ 'notify' => $result ] ); - } + if (argc() > 1 && (argv(1) === 'all_events')) { + $bd_format = t('g A l F d'); // 8 AM Friday January 18 - if (argc() > 1 && (argv(1) === 'all_events')) { - $bd_format = t('g A l F d') ; // 8 AM Friday January 18 + $result = []; - $result = []; - - $r = q("SELECT * FROM event left join xchan on event_xchan = xchan_hash + $r = q( + "SELECT * FROM event left join xchan on event_xchan = xchan_hash WHERE event.uid = %d AND dtstart < '%s' AND dtstart > '%s' and dismissed = 0 and etype in ( 'event', 'birthday' ) ORDER BY dtstart DESC LIMIT 1000", - intval(local_channel()), - dbesc(datetime_convert('UTC', date_default_timezone_get(), 'now + ' . intval($evdays) . ' days')), - dbesc(datetime_convert('UTC', date_default_timezone_get(), 'now - 1 days')) - ); + intval(local_channel()), + dbesc(datetime_convert('UTC', date_default_timezone_get(), 'now + ' . intval($evdays) . ' days')), + dbesc(datetime_convert('UTC', date_default_timezone_get(), 'now - 1 days')) + ); - if ($r) { - foreach ($r as $rr) { + if ($r) { + foreach ($r as $rr) { + $strt = datetime_convert('UTC', (($rr['adjust']) ? date_default_timezone_get() : 'UTC'), $rr['dtstart']); + $today = ((substr($strt, 0, 10) === datetime_convert('UTC', date_default_timezone_get(), 'now', 'Y-m-d')) ? true : false); + $when = day_translate(datetime_convert('UTC', (($rr['adjust']) ? date_default_timezone_get() : 'UTC'), $rr['dtstart'], $bd_format)) . (($today) ? ' ' . t('[today]') : ''); - $strt = datetime_convert('UTC', (($rr['adjust']) ? date_default_timezone_get() : 'UTC'), $rr['dtstart']); - $today = ((substr($strt, 0, 10) === datetime_convert('UTC', date_default_timezone_get(), 'now', 'Y-m-d')) ? true : false); - $when = day_translate(datetime_convert('UTC', (($rr['adjust']) ? date_default_timezone_get() : 'UTC'), $rr['dtstart'], $bd_format)) . (($today) ? ' ' . t('[today]') : ''); + $result[] = array( + 'notify_link' => z_root() . '/events', /// @FIXME this takes you to an edit page and it may not be yours, we really want to just view the single event --> '/events/event/' . $rr['event_hash'], + 'name' => $rr['xchan_name'], + 'addr' => $rr['xchan_addr'], + 'url' => $rr['xchan_url'], + 'photo' => $rr['xchan_photo_s'], + 'when' => $when, + 'hclass' => ('notify-unseen'), + 'message' => t('posted an event') + ); + } + } - $result[] = array( - 'notify_link' => z_root() . '/events', /// @FIXME this takes you to an edit page and it may not be yours, we really want to just view the single event --> '/events/event/' . $rr['event_hash'], - 'name' => $rr['xchan_name'], - 'addr' => $rr['xchan_addr'], - 'url' => $rr['xchan_url'], - 'photo' => $rr['xchan_photo_s'], - 'when' => $when, - 'hclass' => ('notify-unseen'), - 'message' => t('posted an event') - ); - } - } + json_return_and_die(['notify' => $result]); + } - json_return_and_die( [ 'notify' => $result ] ); - } + if (argc() > 1 && (argv(1) === 'files')) { + $result = []; - if (argc() > 1 && (argv(1) === 'files')) { - $result = []; - - $r = q("SELECT item.created, xchan.xchan_name, xchan.xchan_addr, xchan.xchan_url, xchan.xchan_photo_s FROM item + $r = q( + "SELECT item.created, xchan.xchan_name, xchan.xchan_addr, xchan.xchan_url, xchan.xchan_photo_s FROM item LEFT JOIN xchan on author_xchan = xchan_hash WHERE item.verb = '%s' AND item.obj_type = '%s' AND item.uid = %d AND item.owner_xchan != '%s' AND item.item_unseen = 1", - dbesc(ACTIVITY_POST), - dbesc(ACTIVITY_OBJ_FILE), - intval(local_channel()), - dbesc($ob_hash) - ); - if ($r) { - foreach ($r as $rr) { - $result[] = array( - 'notify_link' => z_root() . '/sharedwithme', - 'name' => $rr['xchan_name'], - 'addr' => $rr['xchan_addr'], - 'url' => $rr['xchan_url'], - 'photo' => $rr['xchan_photo_s'], - 'when' => relative_date($rr['created']), - 'hclass' => ('notify-unseen'), - 'message' => t('shared a file with you') - ); - } - } + dbesc(ACTIVITY_POST), + dbesc(ACTIVITY_OBJ_FILE), + intval(local_channel()), + dbesc($ob_hash) + ); + if ($r) { + foreach ($r as $rr) { + $result[] = array( + 'notify_link' => z_root() . '/sharedwithme', + 'name' => $rr['xchan_name'], + 'addr' => $rr['xchan_addr'], + 'url' => $rr['xchan_url'], + 'photo' => $rr['xchan_photo_s'], + 'when' => relative_date($rr['created']), + 'hclass' => ('notify-unseen'), + 'message' => t('shared a file with you') + ); + } + } - json_return_and_die( [ 'notify' => $result ] ); - } + json_return_and_die(['notify' => $result]); + } - if (argc() > 1 && (argv(1) === 'reports') && is_site_admin()) { + if (argc() > 1 && (argv(1) === 'reports') && is_site_admin()) { + $local_result = []; - $local_result = []; - - $r = q("SELECT item.created, xchan.xchan_name, xchan.xchan_addr, xchan.xchan_url, xchan.xchan_photo_s FROM item + $r = q( + "SELECT item.created, xchan.xchan_name, xchan.xchan_addr, xchan.xchan_url, xchan.xchan_photo_s FROM item LEFT JOIN xchan on author_xchan = xchan_hash WHERE item.type = '%s' AND item.item_unseen = 1", - dbesc(ITEM_TYPE_REPORT) - ); + dbesc(ITEM_TYPE_REPORT) + ); - if ($r) { - foreach ($r as $rv) { - $result[] = [ - 'notify_link' => z_root() . '/reports', - 'name' => $rv['xchan_name'], - 'addr' => $rv['xchan_addr'], - 'url' => $rv['xchan_url'], - 'photo' => $rv['xchan_photo_s'], - 'when' => relative_date($rv['created']), - 'hclass' => ('notify-unseen'), - 'message' => t('reported content') - ]; - } - } + if ($r) { + foreach ($r as $rv) { + $result[] = [ + 'notify_link' => z_root() . '/reports', + 'name' => $rv['xchan_name'], + 'addr' => $rv['xchan_addr'], + 'url' => $rv['xchan_url'], + 'photo' => $rv['xchan_photo_s'], + 'when' => relative_date($rv['created']), + 'hclass' => ('notify-unseen'), + 'message' => t('reported content') + ]; + } + } - json_return_and_die( [ 'notify' => $result ] ); - } + json_return_and_die(['notify' => $result]); + } + /** + * Normal ping - just the counts, no detail + */ - /** - * Normal ping - just the counts, no detail - */ + if ($vnotify & VNOTIFY_SYSTEM) { + $t = q( + "select count(*) as total from notify where uid = %d and seen = 0", + intval(local_channel()) + ); + if ($t) { + $result['notify'] = intval($t[0]['total']); + } + } - - if ($vnotify & VNOTIFY_SYSTEM) { - $t = q("select count(*) as total from notify where uid = %d and seen = 0", - intval(local_channel()) - ); - if ($t) - $result['notify'] = intval($t[0]['total']); - } - - if ($vnotify & VNOTIFY_FILES) { - $files = q("SELECT count(id) as total FROM item + if ($vnotify & VNOTIFY_FILES) { + $files = q( + "SELECT count(id) as total FROM item WHERE verb = '%s' AND obj_type = '%s' AND uid = %d AND owner_xchan != '%s' AND item_unseen = 1", - dbesc(ACTIVITY_POST), - dbesc(ACTIVITY_OBJ_FILE), - intval(local_channel()), - dbesc($ob_hash) - ); - if ($files) - $result['files'] = intval($files[0]['total']); - } + dbesc(ACTIVITY_POST), + dbesc(ACTIVITY_OBJ_FILE), + intval(local_channel()), + dbesc($ob_hash) + ); + if ($files) { + $result['files'] = intval($files[0]['total']); + } + } - if ($vnotify & VNOTIFY_NETWORK) { - $loadtime = get_loadtime('stream'); - $r = q("SELECT id, author_xchan FROM item + if ($vnotify & VNOTIFY_NETWORK) { + $loadtime = get_loadtime('stream'); + $r = q( + "SELECT id, author_xchan FROM item WHERE uid = %d and changed > '%s' $seenstr $item_normal $sql_extra ", - intval(local_channel()), - dbesc($loadtime) - ); + intval(local_channel()), + dbesc($loadtime) + ); - if($r) { - $arr = array('items' => $r); - call_hooks('network_ping', $arr); + if ($r) { + $arr = array('items' => $r); + call_hooks('network_ping', $arr); - foreach ($r as $it) { - if ($it['author_xchan'] === $ob_hash) { - $my_activity ++; - } - else { - $result['stream'] ++; - } - } - } - } - if (! ($vnotify & VNOTIFY_NETWORK)) { - $result['stream'] = 0; - } + foreach ($r as $it) { + if ($it['author_xchan'] === $ob_hash) { + $my_activity++; + } else { + $result['stream']++; + } + } + } + } + if (!($vnotify & VNOTIFY_NETWORK)) { + $result['stream'] = 0; + } - if ($vnotify & VNOTIFY_CHANNEL) { - $loadtime = get_loadtime('channel'); - $r = q("SELECT id, author_xchan FROM item + if ($vnotify & VNOTIFY_CHANNEL) { + $loadtime = get_loadtime('channel'); + $r = q( + "SELECT id, author_xchan FROM item WHERE item_wall = 1 and uid = %d and changed > '%s' $seenstr $item_normal $sql_extra ", - intval(local_channel()), - dbesc($loadtime) - ); + intval(local_channel()), + dbesc($loadtime) + ); - if ($r) { - foreach ($r as $it) { - if ($it['author_xchan'] === $ob_hash) { - $my_activity ++; - } - else { - $result['home'] ++; - } - } - } - } - if (! ($vnotify & VNOTIFY_CHANNEL)) { - $result['home'] = 0; - } + if ($r) { + foreach ($r as $it) { + if ($it['author_xchan'] === $ob_hash) { + $my_activity++; + } else { + $result['home']++; + } + } + } + } + if (!($vnotify & VNOTIFY_CHANNEL)) { + $result['home'] = 0; + } - if ($vnotify & VNOTIFY_INTRO) { - $intr = q("SELECT COUNT(abook.abook_id) AS total FROM abook left join xchan on abook.abook_xchan = xchan.xchan_hash where abook_channel = %d and abook_pending = 1 and abook_self = 0 and abook_ignored = 0 and xchan_deleted = 0 and xchan_orphan = 0 ", - intval(local_channel()) - ); + if ($vnotify & VNOTIFY_INTRO) { + $intr = q( + "SELECT COUNT(abook.abook_id) AS total FROM abook left join xchan on abook.abook_xchan = xchan.xchan_hash where abook_channel = %d and abook_pending = 1 and abook_self = 0 and abook_ignored = 0 and xchan_deleted = 0 and xchan_orphan = 0 ", + intval(local_channel()) + ); - if ($intr) - $result['intros'] = intval($intr[0]['total']); - } + if ($intr) { + $result['intros'] = intval($intr[0]['total']); + } + } - $channel = App::get_channel(); + $channel = App::get_channel(); - if ($vnotify & VNOTIFY_REGISTER) { - if (App::$config['system']['register_policy'] == REGISTER_APPROVE && is_site_admin()) { - $regs = q("SELECT count(account_id) as total from account where (account_flags & %d) > 0", - intval(ACCOUNT_PENDING) - ); - if ($regs) - $result['register'] = intval($regs[0]['total']); - } - } + if ($vnotify & VNOTIFY_REGISTER) { + if (App::$config['system']['register_policy'] == REGISTER_APPROVE && is_site_admin()) { + $regs = q( + "SELECT count(account_id) as total from account where (account_flags & %d) > 0", + intval(ACCOUNT_PENDING) + ); + if ($regs) { + $result['register'] = intval($regs[0]['total']); + } + } + } - if ($vnotify & VNOTIFY_REPORTS) { - if (is_site_admin()) { - $reps = q("SELECT count(id) as total from item where item_type = %d", - intval(ITEM_TYPE_REPORT) - ); - if ($reps) - $result['reports'] = intval($reps[0]['total']); - } - } + if ($vnotify & VNOTIFY_REPORTS) { + if (is_site_admin()) { + $reps = q( + "SELECT count(id) as total from item where item_type = %d", + intval(ITEM_TYPE_REPORT) + ); + if ($reps) { + $result['reports'] = intval($reps[0]['total']); + } + } + } - if ($vnotify & (VNOTIFY_EVENT|VNOTIFY_EVENTTODAY|VNOTIFY_BIRTHDAY)) { - $events = q("SELECT etype, dtstart, adjust FROM event + if ($vnotify & (VNOTIFY_EVENT | VNOTIFY_EVENTTODAY | VNOTIFY_BIRTHDAY)) { + $events = q( + "SELECT etype, dtstart, adjust FROM event WHERE event.uid = %d AND dtstart < '%s' AND dtstart > '%s' and dismissed = 0 and etype in ( 'event', 'birthday' ) ORDER BY dtstart ASC ", - intval(local_channel()), - dbesc(datetime_convert('UTC', date_default_timezone_get(), 'now + ' . intval($evdays) . ' days')), - dbesc(datetime_convert('UTC', date_default_timezone_get(), 'now - 1 days')) - ); + intval(local_channel()), + dbesc(datetime_convert('UTC', date_default_timezone_get(), 'now + ' . intval($evdays) . ' days')), + dbesc(datetime_convert('UTC', date_default_timezone_get(), 'now - 1 days')) + ); - if ($events) { - $result['all_events'] = count($events); + if ($events) { + $result['all_events'] = count($events); - if ($result['all_events']) { - $str_now = datetime_convert('UTC', date_default_timezone_get(), 'now', 'Y-m-d'); - foreach ($events as $x) { - $bd = false; - if ($x['etype'] === 'birthday') { - $result['birthdays'] ++; - $bd = true; - } - else { - $result['events'] ++; - } - if (datetime_convert('UTC', ((intval($x['adjust'])) ? date_default_timezone_get() : 'UTC'), $x['dtstart'], 'Y-m-d') === $str_now) { - $result['all_events_today'] ++; - if($bd) - $result['birthdays_today'] ++; - else - $result['events_today'] ++; - } - } - } - } - } + if ($result['all_events']) { + $str_now = datetime_convert('UTC', date_default_timezone_get(), 'now', 'Y-m-d'); + foreach ($events as $x) { + $bd = false; + if ($x['etype'] === 'birthday') { + $result['birthdays']++; + $bd = true; + } else { + $result['events']++; + } + if (datetime_convert('UTC', ((intval($x['adjust'])) ? date_default_timezone_get() : 'UTC'), $x['dtstart'], 'Y-m-d') === $str_now) { + $result['all_events_today']++; + if ($bd) { + $result['birthdays_today']++; + } else { + $result['events_today']++; + } + } + } + } + } + } - if (! ($vnotify & VNOTIFY_EVENT)) - $result['all_events'] = $result['events'] = 0; - if (! ($vnotify & VNOTIFY_EVENTTODAY)) - $result['all_events_today'] = $result['events_today'] = 0; - if (! ($vnotify & VNOTIFY_BIRTHDAY)) - $result['birthdays'] = 0; + if (!($vnotify & VNOTIFY_EVENT)) { + $result['all_events'] = $result['events'] = 0; + } + if (!($vnotify & VNOTIFY_EVENTTODAY)) { + $result['all_events_today'] = $result['events_today'] = 0; + } + if (!($vnotify & VNOTIFY_BIRTHDAY)) { + $result['birthdays'] = 0; + } + if ($vnotify & VNOTIFY_FORUMS) { + $forums = get_forum_channels(local_channel()); - if ($vnotify & VNOTIFY_FORUMS) { - $forums = get_forum_channels(local_channel()); + if ($forums) { + $perms_sql = item_permissions_sql(local_channel()) . item_normal(); + $fcount = count($forums); + $forums['total'] = 0; - if ($forums) { + for ($x = 0; $x < $fcount; $x++) { + $ttype = TERM_FORUM; + $p = q("SELECT oid AS parent FROM term WHERE uid = " . intval(local_channel()) . " AND ttype = $ttype AND term = '" . protect_sprintf(dbesc($forums[$x]['xchan_name'])) . "'"); - $perms_sql = item_permissions_sql(local_channel()) . item_normal(); - $fcount = count($forums); - $forums['total'] = 0; + $p = ids_to_querystr($p, 'parent'); + $pquery = (($p) ? "OR parent IN ( $p )" : ''); - for ($x = 0; $x < $fcount; $x ++) { - $ttype = TERM_FORUM; - $p = q("SELECT oid AS parent FROM term WHERE uid = " . intval(local_channel()) . " AND ttype = $ttype AND term = '" . protect_sprintf(dbesc($forums[$x]['xchan_name'])) . "'"); - - $p = ids_to_querystr($p, 'parent'); - $pquery = (($p) ? "OR parent IN ( $p )" : ''); - - $r = q("select sum(item_unseen) as unseen from item + $r = q( + "select sum(item_unseen) as unseen from item where uid = %d and ( owner_xchan = '%s' $pquery ) and item_unseen = 1 $perms_sql ", - intval(local_channel()), - dbesc($forums[$x]['xchan_hash']) - ); - if ($r[0]['unseen']) { - $forums[$x]['notify_link'] = (($forums[$x]['private_forum']) ? $forums[$x]['xchan_url'] : z_root() . '/stream/?f=&pf=1&cid=' . $forums[$x]['abook_id']); - $forums[$x]['name'] = $forums[$x]['xchan_name']; - $forums[$x]['addr'] = $forums[$x]['xchan_addr']; - $forums[$x]['url'] = $forums[$x]['xchan_url']; - $forums[$x]['photo'] = $forums[$x]['xchan_photo_s']; - $forums[$x]['unseen'] = $r[0]['unseen']; - $forums[$x]['private_forum'] = (($forums[$x]['private_forum']) ? 'lock' : ''); - $forums[$x]['message'] = (($forums[$x]['private_forum']) ? t('Private group') : t('Public group')); + intval(local_channel()), + dbesc($forums[$x]['xchan_hash']) + ); + if ($r[0]['unseen']) { + $forums[$x]['notify_link'] = (($forums[$x]['private_forum']) ? $forums[$x]['xchan_url'] : z_root() . '/stream/?f=&pf=1&cid=' . $forums[$x]['abook_id']); + $forums[$x]['name'] = $forums[$x]['xchan_name']; + $forums[$x]['addr'] = $forums[$x]['xchan_addr']; + $forums[$x]['url'] = $forums[$x]['xchan_url']; + $forums[$x]['photo'] = $forums[$x]['xchan_photo_s']; + $forums[$x]['unseen'] = $r[0]['unseen']; + $forums[$x]['private_forum'] = (($forums[$x]['private_forum']) ? 'lock' : ''); + $forums[$x]['message'] = (($forums[$x]['private_forum']) ? t('Private group') : t('Public group')); - $forums['total'] = $forums['total'] + $r[0]['unseen']; + $forums['total'] = $forums['total'] + $r[0]['unseen']; - unset($forums[$x]['abook_id']); - unset($forums[$x]['xchan_hash']); - unset($forums[$x]['xchan_name']); - unset($forums[$x]['xchan_url']); - unset($forums[$x]['xchan_photo_s']); + unset($forums[$x]['abook_id']); + unset($forums[$x]['xchan_hash']); + unset($forums[$x]['xchan_name']); + unset($forums[$x]['xchan_url']); + unset($forums[$x]['xchan_photo_s']); + } else { + unset($forums[$x]); + } + } + $result['forums'] = $forums['total']; + unset($forums['total']); - } - else { - unset($forums[$x]); - } - } - $result['forums'] = $forums['total']; - unset($forums['total']); + $result['forums_sub'] = $forums; + } + } - $result['forums_sub'] = $forums; - } - } + // Mark all of the stream notifications seen if all three of them are caught up. + // This also resets the pconfig storage for items_seen - // Mark all of the stream notifications seen if all three of them are caught up. - // This also resets the pconfig storage for items_seen - - if ((! $my_activity) && (! (intval($result['home']) + intval($result['stream']) + intval($result['pubs'])))) { - PConfig::Delete(local_channel(),'system','seen_items'); + if ((!$my_activity) && (!(intval($result['home']) + intval($result['stream']) + intval($result['pubs'])))) { + PConfig::Delete(local_channel(), 'system', 'seen_items'); - $_SESSION['loadtime_channel'] = datetime_convert(); - $_SESSION['loadtime_stream'] = datetime_convert(); - $_SESSION['loadtime_pubstream'] = datetime_convert(); - - PConfig::Set(local_channel(),'system','loadtime_channel', $_SESSION['loadtime_channel']); - PConfig::Set(local_channel(),'system','loadtime_stream', $_SESSION['loadtime_stream']); - PConfig::Set(local_channel(),'system','loadtime_pubstream', $_SESSION['loadtime_pubstream']); - } + $_SESSION['loadtime_channel'] = datetime_convert(); + $_SESSION['loadtime_stream'] = datetime_convert(); + $_SESSION['loadtime_pubstream'] = datetime_convert(); - json_return_and_die($result); - } + PConfig::Set(local_channel(), 'system', 'loadtime_channel', $_SESSION['loadtime_channel']); + PConfig::Set(local_channel(), 'system', 'loadtime_stream', $_SESSION['loadtime_stream']); + PConfig::Set(local_channel(), 'system', 'loadtime_pubstream', $_SESSION['loadtime_pubstream']); + } + json_return_and_die($result); + } } diff --git a/Zotlabs/Module/Plike.php b/Zotlabs/Module/Plike.php index d5022f718..016cb9e2b 100644 --- a/Zotlabs/Module/Plike.php +++ b/Zotlabs/Module/Plike.php @@ -1,4 +1,5 @@ 'Like', - 'dislike' => 'Dislike', - ]; + $acts = [ + 'like' => 'Like', + 'dislike' => 'Dislike', + ]; - // unlike (etc.) reactions are an undo of positive reactions, rather than a negative action. - // The activity is the same in undo actions and will have the same activity mapping + // unlike (etc.) reactions are an undo of positive reactions, rather than a negative action. + // The activity is the same in undo actions and will have the same activity mapping - if(substr($reaction,0,2) === 'un') { - $undo = true; - $reaction = substr($reaction,2); - } + if (substr($reaction, 0, 2) === 'un') { + $undo = true; + $reaction = substr($reaction, 2); + } - if(array_key_exists($reaction,$acts)) { - return (($undo) ? 'Undo/' : EMPTY_STR) . $acts[$reaction]; - } + if (array_key_exists($reaction, $acts)) { + return (($undo) ? 'Undo/' : EMPTY_STR) . $acts[$reaction]; + } - return EMPTY_STR; - - } + return EMPTY_STR; + } - public function get() { + public function get() + { - $undo = false; - $object = $target = null; - $owner_uid = 0; - $post_type = EMPTY_STR; - $objtype = EMPTY_STR; - $allow_cid = $allow_gid = $deny_cid = $deny_gid = ''; - $output = EMPTY_STR; + $undo = false; + $object = $target = null; + $owner_uid = 0; + $post_type = EMPTY_STR; + $objtype = EMPTY_STR; + $allow_cid = $allow_gid = $deny_cid = $deny_gid = ''; + $output = EMPTY_STR; - $sys_channel = get_sys_channel(); - $sys_channel_id = (($sys_channel) ? $sys_channel['channel_id'] : 0); + $sys_channel = get_sys_channel(); + $sys_channel_id = (($sys_channel) ? $sys_channel['channel_id'] : 0); - $observer = App::get_observer(); - - $verb = ((array_key_exists('verb', $_GET)) ? notags(trim($_GET['verb'])) : EMPTY_STR); + $observer = App::get_observer(); - // Figure out what action we're performing - - $activity = $this->reaction_to_activity($verb); + $verb = ((array_key_exists('verb', $_GET)) ? notags(trim($_GET['verb'])) : EMPTY_STR); - if (! $activity) { - return EMPTY_STR; - } + // Figure out what action we're performing - // Check for negative (undo) condition - // eg: 'Undo/Like' results in $undo conditional and $activity set to 'Like' - - $test = explode('/', $activity); - if (count($test) > 1) { - $undo = true; - $activity = $test[1]; - } + $activity = $this->reaction_to_activity($verb); - $is_rsvp = in_array($activity, [ 'Accept', 'Reject', 'TentativeAccept' ]); + if (! $activity) { + return EMPTY_STR; + } - // Check for when target is something besides messages, where argv(1) is the type of thing - // and argv(2) is an identifier of things of that type - // We currently only recognise 'profile' but other types could be handled - - if (argc() == 3) { - - if (! $observer) { - killme(); - } - - if($obj_type == 'profile') { - $r = q("select * from profile where profile_guid = '%s' limit 1", - dbesc($obj_id) - ); + // Check for negative (undo) condition + // eg: 'Undo/Like' results in $undo conditional and $activity set to 'Like' - if (! $r) { - killme(); - } - - $profile = array_shift($r); - - $owner_uid = $profile['uid']; + $test = explode('/', $activity); + if (count($test) > 1) { + $undo = true; + $activity = $test[1]; + } - $public = ((intval($profile['is_default'] )) ? true : false); + $is_rsvp = in_array($activity, [ 'Accept', 'Reject', 'TentativeAccept' ]); - // if this is a private profile, select the destination recipients - - if (! $public) { - $d = q("select abook_xchan from abook where abook_profile = '%s' and abook_channel = %d", - dbesc($profile['profile_guid']), - intval($owner_uid) - ); - if (! $d) { - // No profile could be found. - killme(); - } - - // $d now contains a list of those who can see this profile. - // Set the access accordingly. - - foreach ($d as $dd) { - $allow_cid .= '<' . $dd['abook_xchan'] . '>'; - } - } - - $post_type = t('channel'); - $objtype = ACTIVITY_OBJ_PROFILE; - - } + // Check for when target is something besides messages, where argv(1) is the type of thing + // and argv(2) is an identifier of things of that type + // We currently only recognise 'profile' but other types could be handled - // We'll need the owner of the thing from up above to figure out what channel is the target + if (argc() == 3) { + if (! $observer) { + killme(); + } - if (! ($owner_uid)) { - killme(); - } - - // Check permissions of the observer. If this is the owner (mostly this is the case) - // this will return true for all permissions. - - $perms = get_all_perms($owner_uid,$observer['xchan_hash']); - - if (! ($perms['post_like'] && $perms['view_profile'])) { - killme(); - } + if ($obj_type == 'profile') { + $r = q( + "select * from profile where profile_guid = '%s' limit 1", + dbesc($obj_id) + ); - $channel = channelx_by_n($owner_uid); - - if (! $channel) { - killme(); - } - - $object = json_encode(Activity::fetch_profile([ 'id' => channel_url($channel) ])); + if (! $r) { + killme(); + } - // second like of the same thing is "undo" for the first like - - $z = q("select * from likes where channel_id = %d and liker = '%s' and verb = '%s' and target_type = '%s' and target_id = '%s' limit 1", - intval($channel['channel_id']), - dbesc($observer['xchan_hash']), - dbesc($activity), - dbesc(($tgttype)?$tgttype:$objtype), - dbesc($obj_id) - ); - - if($z) { - $z[0]['deleted'] = 1; - Libsync::build_sync_packet($channel['channel_id'],array('likes' => $z)); - - q("delete from likes where id = %d", - intval($z[0]['id']) - ); - if($z[0]['i_mid']) { - $r = q("select id from item where mid = '%s' and uid = %d limit 1", - dbesc($z[0]['i_mid']), - intval($channel['channel_id']) - ); - if($r) - drop_item($r[0]['id'],false); + $profile = array_shift($r); - } - killme(); - } - } + $owner_uid = $profile['uid']; - $uuid = new_uuid(); - - $arr = []; + $public = ((intval($profile['is_default'])) ? true : false); - $arr['uuid'] = $uuid; + // if this is a private profile, select the destination recipients + + if (! $public) { + $d = q( + "select abook_xchan from abook where abook_profile = '%s' and abook_channel = %d", + dbesc($profile['profile_guid']), + intval($owner_uid) + ); + if (! $d) { + // No profile could be found. + killme(); + } + + // $d now contains a list of those who can see this profile. + // Set the access accordingly. + + foreach ($d as $dd) { + $allow_cid .= '<' . $dd['abook_xchan'] . '>'; + } + } + + $post_type = t('channel'); + $objtype = ACTIVITY_OBJ_PROFILE; + } + + // We'll need the owner of the thing from up above to figure out what channel is the target + + if (! ($owner_uid)) { + killme(); + } + + // Check permissions of the observer. If this is the owner (mostly this is the case) + // this will return true for all permissions. + + $perms = get_all_perms($owner_uid, $observer['xchan_hash']); + + if (! ($perms['post_like'] && $perms['view_profile'])) { + killme(); + } + + $channel = channelx_by_n($owner_uid); + + if (! $channel) { + killme(); + } + + $object = json_encode(Activity::fetch_profile([ 'id' => channel_url($channel) ])); + + // second like of the same thing is "undo" for the first like + + $z = q( + "select * from likes where channel_id = %d and liker = '%s' and verb = '%s' and target_type = '%s' and target_id = '%s' limit 1", + intval($channel['channel_id']), + dbesc($observer['xchan_hash']), + dbesc($activity), + dbesc(($tgttype) ? $tgttype : $objtype), + dbesc($obj_id) + ); + + if ($z) { + $z[0]['deleted'] = 1; + Libsync::build_sync_packet($channel['channel_id'], array('likes' => $z)); + + q( + "delete from likes where id = %d", + intval($z[0]['id']) + ); + if ($z[0]['i_mid']) { + $r = q( + "select id from item where mid = '%s' and uid = %d limit 1", + dbesc($z[0]['i_mid']), + intval($channel['channel_id']) + ); + if ($r) { + drop_item($r[0]['id'], false); + } + } + killme(); + } + } + + $uuid = new_uuid(); + + $arr = []; + + $arr['uuid'] = $uuid; $arr['mid'] = z_root() . (($is_rsvp) ? '/activity/' : '/item/' ) . $uuid; - - $arr['item_thread_top'] = 1; - $arr['item_origin'] = 1; - $arr['item_wall'] = 1; - - if($verb === 'like') - $bodyverb = t('%1$s likes %2$s\'s %3$s'); - if($verb === 'dislike') - $bodyverb = t('%1$s doesn\'t like %2$s\'s %3$s'); - - if (! isset($bodyverb)) { - killme(); - } + $arr['item_thread_top'] = 1; + $arr['item_origin'] = 1; + $arr['item_wall'] = 1; - $ulink = '[zrl=' . $channel['xchan_url'] . ']' . $channel['xchan_name'] . '[/zrl]'; - $alink = '[zrl=' . $observer['xchan_url'] . ']' . $observer['xchan_name'] . '[/zrl]'; - $plink = '[zrl=' . z_root() . '/profile/' . $channel['channel_address'] . ']' . $post_type . '[/zrl]'; - $private = (($public) ? 0 : 1); + if ($verb === 'like') { + $bodyverb = t('%1$s likes %2$s\'s %3$s'); + } + if ($verb === 'dislike') { + $bodyverb = t('%1$s doesn\'t like %2$s\'s %3$s'); + } - - $arr['aid'] = $channel['channel_account_id']; - $arr['uid'] = $owner_uid; + if (! isset($bodyverb)) { + killme(); + } - $arr['item_flags'] = $item['item_flags']; - $arr['item_wall'] = $item['item_wall']; - $arr['parent_mid'] = $arr['mid']; - $arr['owner_xchan'] = $channel['xchan_hash']; - $arr['author_xchan'] = $observer['xchan_hash']; - - - $arr['body'] = sprintf( $bodyverb, $alink, $ulink, $plink ); + $ulink = '[zrl=' . $channel['xchan_url'] . ']' . $channel['xchan_name'] . '[/zrl]'; + $alink = '[zrl=' . $observer['xchan_url'] . ']' . $observer['xchan_name'] . '[/zrl]'; + $plink = '[zrl=' . z_root() . '/profile/' . $channel['channel_address'] . ']' . $post_type . '[/zrl]'; + $private = (($public) ? 0 : 1); - if($obj_type === 'profile') { - if($public) { - $arr['body'] .= "\n\n" . '[embed]' . z_root() . '/profile/' . $channel['channel_address'] . '[/embed]'; - } - else - $arr['body'] .= "\n\n[zmg=80x80]" . $profile['thumb'] . '[/zmg]'; - } - - - $arr['verb'] = $activity; - $arr['obj_type'] = $objtype; - $arr['obj'] = $object; - - if ($target) { - $arr['tgt_type'] = $tgttype; - $arr['target'] = $target; - } - - $arr['allow_cid'] = $allow_cid; - $arr['allow_gid'] = $allow_gid; - $arr['deny_cid'] = $deny_cid; - $arr['deny_gid'] = $deny_gid; - $arr['item_private'] = $private; - - call_hooks('post_local',$arr); - - $post = item_store($arr); - $post_id = $post['item_id']; - // save the conversation from expiration + $arr['aid'] = $channel['channel_account_id']; + $arr['uid'] = $owner_uid; - if(local_channel() && array_key_exists('item',$post) && (intval($post['item']['id']) != intval($post['item']['parent']))) - retain_item($post['item']['parent']); - - $arr['id'] = $post_id; - - call_hooks('post_local_end', $arr); - - $r = q("select * from item where id = %d", - intval($post_id) - ); - if($r) { - xchan_query($r); - $sync_item = fetch_post_tags($r); - Libsync::build_sync_packet($channel['channel_id'], [ 'item' => [ encode_item($sync_item[0],true) ] ]); - } - $r = q("insert into likes (channel_id,liker,likee,iid,i_mid,verb,target_type,target_id,target) values (%d,'%s','%s',%d,'%s','%s','%s','%s','%s')", - intval($channel['channel_id']), - dbesc($observer['xchan_hash']), - dbesc($channel['channel_hash']), - intval($post_id), - dbesc($mid), - dbesc($activity), - dbesc(($tgttype)? $tgttype : $objtype), - dbesc($obj_id), - dbesc(($target) ? $target : $object) - ); - $r = q("select * from likes where liker = '%s' and likee = '%s' and i_mid = '%s' and verb = '%s' and target_type = '%s' and target_id = '%s' ", - dbesc($observer['xchan_hash']), - dbesc($channel['channel_hash']), - dbesc($mid), - dbesc($activity), - dbesc(($tgttype)? $tgttype : $objtype), - dbesc($obj_id) - ); - if ($r) { - Libsync::build_sync_packet($channel['channel_id'],array('likes' => $r)); - } - - Run::Summon( [ 'Notifier', 'like', $post_id ] ); - - killme(); - } - - - + $arr['item_flags'] = $item['item_flags']; + $arr['item_wall'] = $item['item_wall']; + $arr['parent_mid'] = $arr['mid']; + $arr['owner_xchan'] = $channel['xchan_hash']; + $arr['author_xchan'] = $observer['xchan_hash']; + + + $arr['body'] = sprintf($bodyverb, $alink, $ulink, $plink); + + if ($obj_type === 'profile') { + if ($public) { + $arr['body'] .= "\n\n" . '[embed]' . z_root() . '/profile/' . $channel['channel_address'] . '[/embed]'; + } else { + $arr['body'] .= "\n\n[zmg=80x80]" . $profile['thumb'] . '[/zmg]'; + } + } + + + $arr['verb'] = $activity; + $arr['obj_type'] = $objtype; + $arr['obj'] = $object; + + if ($target) { + $arr['tgt_type'] = $tgttype; + $arr['target'] = $target; + } + + $arr['allow_cid'] = $allow_cid; + $arr['allow_gid'] = $allow_gid; + $arr['deny_cid'] = $deny_cid; + $arr['deny_gid'] = $deny_gid; + $arr['item_private'] = $private; + + call_hooks('post_local', $arr); + + $post = item_store($arr); + $post_id = $post['item_id']; + + // save the conversation from expiration + + if (local_channel() && array_key_exists('item', $post) && (intval($post['item']['id']) != intval($post['item']['parent']))) { + retain_item($post['item']['parent']); + } + + $arr['id'] = $post_id; + + call_hooks('post_local_end', $arr); + + $r = q( + "select * from item where id = %d", + intval($post_id) + ); + if ($r) { + xchan_query($r); + $sync_item = fetch_post_tags($r); + Libsync::build_sync_packet($channel['channel_id'], [ 'item' => [ encode_item($sync_item[0], true) ] ]); + } + + $r = q( + "insert into likes (channel_id,liker,likee,iid,i_mid,verb,target_type,target_id,target) values (%d,'%s','%s',%d,'%s','%s','%s','%s','%s')", + intval($channel['channel_id']), + dbesc($observer['xchan_hash']), + dbesc($channel['channel_hash']), + intval($post_id), + dbesc($mid), + dbesc($activity), + dbesc(($tgttype) ? $tgttype : $objtype), + dbesc($obj_id), + dbesc(($target) ? $target : $object) + ); + $r = q( + "select * from likes where liker = '%s' and likee = '%s' and i_mid = '%s' and verb = '%s' and target_type = '%s' and target_id = '%s' ", + dbesc($observer['xchan_hash']), + dbesc($channel['channel_hash']), + dbesc($mid), + dbesc($activity), + dbesc(($tgttype) ? $tgttype : $objtype), + dbesc($obj_id) + ); + if ($r) { + Libsync::build_sync_packet($channel['channel_id'], array('likes' => $r)); + } + + Run::Summon([ 'Notifier', 'like', $post_id ]); + + killme(); + } } diff --git a/Zotlabs/Module/Poco.php b/Zotlabs/Module/Poco.php index 90ae049b6..505e5c0ba 100644 --- a/Zotlabs/Module/Poco.php +++ b/Zotlabs/Module/Poco.php @@ -1,19 +1,21 @@ '; - $allow_gid = EMPTY_STR; - $deny_cid = EMPTY_STR; - $deny_gid = EMPTY_STR; - } - - $arr = []; - - $arr['item_wall'] = 1; - $arr['owner_xchan'] = $channel['channel_hash']; - $arr['author_xchan'] = $channel['channel_hash']; - $arr['allow_cid'] = $allow_cid; - $arr['allow_gid'] = $allow_gid; - $arr['deny_cid'] = $deny_cid; - $arr['deny_gid'] = $deny_gid; - $arr['verb'] = 'Create'; - $arr['item_private'] = 1; - $arr['obj_type'] = 'Note'; - $arr['body'] = '[zrl=' . $channel['xchan_url'] . ']' . $channel['xchan_name'] . '[/zrl]' . ' ' . $verbs[$verb][2] . ' ' . '[zrl=' . $target['xchan_url'] . ']' . $target['xchan_name'] . '[/zrl]'; - - - $arr['item_origin'] = 1; - $arr['item_wall'] = 1; + $activity = $verbs[$verb][0]; - $obj = Activity::encode_item($arr,((get_config('system','activitypub', ACTIVITYPUB_ENABLED)) ? true : false)); + $xchan = trim($_REQUEST['xchan']); - $i = post_activity_item($arr); + if (!$xchan) { + return; + } - if ($i['success']) { - $item_id = $i['item_id']; - $r = q("select * from item where id = %d", - intval($item_id) - ); - if ($r) { - xchan_query($r); - $sync_item = fetch_post_tags($r); - Libsync::build_sync_packet($uid, [ 'item' => [ encode_item($sync_item[0],true) ] ] ); - } - - info(sprintf( t('You %1$s %2$s'), $verbs[$verb][2], $target['xchan_name'])); - } + $r = q( + "SELECT * FROM xchan where xchan_hash = '%s' LIMIT 1", + dbesc($xchan) + ); - json_return_and_die([ 'success' => true ]); - } - - - - function get() { - - if(! local_channel()) { - notice( t('Permission denied.') . EOL); - return; - } + if (!$r) { + logger('poke: no target.'); + return; + } - if(! Apps::system_app_installed(local_channel(), 'Poke')) { - $o = '' . t('Poke App (Not Installed)') . '
      '; - $o .= t('Poke or do something else to somebody'); - return $o; - } + $target = $r[0]; + $parent_item = null; - nav_set_selected('Poke'); - - $name = ''; - $id = ''; - - $verbs = get_poke_verbs(); - - $shortlist = []; - $current = get_pconfig(local_channel(),'system','pokeverb','poke'); - foreach($verbs as $k => $v) { - $shortlist[] = [ $k,$v[1], (($k === $current) ? true : false) ]; - } - - - $title = t('Poke'); - $desc = t('Poke, prod or do other things to somebody'); - - $o = replace_macros(get_markup_template('poke_content.tpl'),array( - '$title' => $title, - '$desc' => $desc, - '$clabel' => t('Recipient'), - '$choice' => t('Choose your default action'), - '$verbs' => $shortlist, - '$submit' => t('Submit'), - '$id' => $id - )); - - return $o; - } + $item_private = 1; + + if ($target) { + $allow_cid = '<' . $target['abook_xchan'] . '>'; + $allow_gid = EMPTY_STR; + $deny_cid = EMPTY_STR; + $deny_gid = EMPTY_STR; + } + + $arr = []; + + $arr['item_wall'] = 1; + $arr['owner_xchan'] = $channel['channel_hash']; + $arr['author_xchan'] = $channel['channel_hash']; + $arr['allow_cid'] = $allow_cid; + $arr['allow_gid'] = $allow_gid; + $arr['deny_cid'] = $deny_cid; + $arr['deny_gid'] = $deny_gid; + $arr['verb'] = 'Create'; + $arr['item_private'] = 1; + $arr['obj_type'] = 'Note'; + $arr['body'] = '[zrl=' . $channel['xchan_url'] . ']' . $channel['xchan_name'] . '[/zrl]' . ' ' . $verbs[$verb][2] . ' ' . '[zrl=' . $target['xchan_url'] . ']' . $target['xchan_name'] . '[/zrl]'; + + + $arr['item_origin'] = 1; + $arr['item_wall'] = 1; + + $obj = Activity::encode_item($arr, ((get_config('system', 'activitypub', ACTIVITYPUB_ENABLED)) ? true : false)); + + $i = post_activity_item($arr); + + if ($i['success']) { + $item_id = $i['item_id']; + $r = q( + "select * from item where id = %d", + intval($item_id) + ); + if ($r) { + xchan_query($r); + $sync_item = fetch_post_tags($r); + Libsync::build_sync_packet($uid, ['item' => [encode_item($sync_item[0], true)]]); + } + + info(sprintf(t('You %1$s %2$s'), $verbs[$verb][2], $target['xchan_name'])); + } + + json_return_and_die(['success' => true]); + } + + + public function get() + { + + if (!local_channel()) { + notice(t('Permission denied.') . EOL); + return; + } + + if (!Apps::system_app_installed(local_channel(), 'Poke')) { + $o = '' . t('Poke App (Not Installed)') . '
      '; + $o .= t('Poke or do something else to somebody'); + return $o; + } + + nav_set_selected('Poke'); + + $name = ''; + $id = ''; + + $verbs = get_poke_verbs(); + + $shortlist = []; + $current = get_pconfig(local_channel(), 'system', 'pokeverb', 'poke'); + foreach ($verbs as $k => $v) { + $shortlist[] = [$k, $v[1], (($k === $current) ? true : false)]; + } + + + $title = t('Poke'); + $desc = t('Poke, prod or do other things to somebody'); + + $o = replace_macros(get_markup_template('poke_content.tpl'), array( + '$title' => $title, + '$desc' => $desc, + '$clabel' => t('Recipient'), + '$choice' => t('Choose your default action'), + '$verbs' => $shortlist, + '$submit' => t('Submit'), + '$id' => $id + )); + + return $o; + } } diff --git a/Zotlabs/Module/Poster.php b/Zotlabs/Module/Poster.php index f1aa2d6b5..c0288e3bd 100644 --- a/Zotlabs/Module/Poster.php +++ b/Zotlabs/Module/Poster.php @@ -6,37 +6,40 @@ use Zotlabs\Web\Controller; require_once('include/security.php'); -class Poster extends Controller { +class Poster extends Controller +{ - function init() { + public function init() + { - $nick = argv(1); - $hash = argv(2); + $nick = argv(1); + $hash = argv(2); - if (! ($nick && $hash)) { - return; - } + if (!($nick && $hash)) { + return; + } - $u = channelx_by_nick($nick); + $u = channelx_by_nick($nick); - if (! $u) { - return; - } + if (!$u) { + return; + } - $sql_extra = permissions_sql(intval($u['channel_id'])); + $sql_extra = permissions_sql(intval($u['channel_id'])); - $r = q("select content from attach where hash = '%s' and uid = %d and os_storage = 1 $sql_extra limit 1", - dbesc($hash), - intval($u['channel_id']) - ); - if ($r) { - $path = dbunescbin($r[0]['content']); - if ($path && @file_exists($path . '.thumb')) { - header('Content-Type: image/jpeg'); - echo file_get_contents($path . '.thumb'); - killme(); - } - } - killme(); - } + $r = q( + "select content from attach where hash = '%s' and uid = %d and os_storage = 1 $sql_extra limit 1", + dbesc($hash), + intval($u['channel_id']) + ); + if ($r) { + $path = dbunescbin($r[0]['content']); + if ($path && @file_exists($path . '.thumb')) { + header('Content-Type: image/jpeg'); + echo file_get_contents($path . '.thumb'); + killme(); + } + } + killme(); + } } diff --git a/Zotlabs/Module/Pretheme.php b/Zotlabs/Module/Pretheme.php index 120fd5359..93702195b 100644 --- a/Zotlabs/Module/Pretheme.php +++ b/Zotlabs/Module/Pretheme.php @@ -1,28 +1,30 @@ get_theme_screenshot($theme), 'desc' => $desc, 'version' => $version, 'credits' => $credits)); - } - killme(); - } - + public function init() + { + + if ($_REQUEST['theme']) { + $theme = $_REQUEST['theme']; + $info = get_theme_info($theme); + if ($info) { + // unfortunately there will be no translation for this string + $desc = $info['description']; + $version = $info['version']; + $credits = $info['credits']; + } else { + $desc = ''; + $version = ''; + $credits = ''; + } + echo json_encode(array('img' => get_theme_screenshot($theme), 'desc' => $desc, 'version' => $version, 'credits' => $credits)); + } + killme(); + } } diff --git a/Zotlabs/Module/Profile.php b/Zotlabs/Module/Profile.php index b3fcf3ceb..f3eadf604 100644 --- a/Zotlabs/Module/Profile.php +++ b/Zotlabs/Module/Profile.php @@ -1,4 +1,5 @@ 1) - $which = argv(1); - else { - notice( t('Requested profile is not available.') . EOL ); - App::$error = 404; - return; - } + if (argc() > 1) { + $which = argv(1); + } else { + notice(t('Requested profile is not available.') . EOL); + App::$error = 404; + return; + } - nav_set_selected('Profile'); - - $profile = ''; - $channel = App::get_channel(); - - if((local_channel()) && (argc() > 2) && (argv(2) === 'view')) { - $which = $channel['channel_address']; - $profile = argv(1); - $r = q("select profile_guid from profile where id = %d and uid = %d limit 1", - intval($profile), - intval(local_channel()) - ); - if(! $r) - $profile = ''; - $profile = $r[0]['profile_guid']; - } - - head_add_link( [ - 'rel' => 'alternate', - 'type' => 'application/atom+xml', - 'title' => t('Posts and comments'), - 'href' => z_root() . '/feed/' . $which - ]); + nav_set_selected('Profile'); - head_add_link( [ - 'rel' => 'alternate', - 'type' => 'application/atom+xml', - 'title' => t('Only posts'), - 'href' => z_root() . '/feed/' . $which . '?f=&top=1' - ]); + $profile = ''; + $channel = App::get_channel(); + + if ((local_channel()) && (argc() > 2) && (argv(2) === 'view')) { + $which = $channel['channel_address']; + $profile = argv(1); + $r = q( + "select profile_guid from profile where id = %d and uid = %d limit 1", + intval($profile), + intval(local_channel()) + ); + if (!$r) { + $profile = ''; + } + $profile = $r[0]['profile_guid']; + } + + head_add_link([ + 'rel' => 'alternate', + 'type' => 'application/atom+xml', + 'title' => t('Posts and comments'), + 'href' => z_root() . '/feed/' . $which + ]); + + head_add_link([ + 'rel' => 'alternate', + 'type' => 'application/atom+xml', + 'title' => t('Only posts'), + 'href' => z_root() . '/feed/' . $which . '?f=&top=1' + ]); - if(! $profile) { - $x = q("select channel_id as profile_uid from channel where channel_address = '%s' limit 1", - dbesc(argv(1)) - ); - if($x) { - \App::$profile = $x[0]; - } - } - - - if(ActivityStreams::is_as_request()) { - $chan = channelx_by_nick(argv(1)); - if(! $chan) - http_status_exit(404, 'Not found'); - $p = Activity::encode_person($chan,true,((get_config('system','activitypub', ACTIVITYPUB_ENABLED)) ? true : false)); - if(! $p) { - http_status_exit(404, 'Not found'); - } - - as_return_and_die([ 'type' => 'Profile', 'describes' => $p ], $chan); - } - - Libprofile::load($which,$profile); - - } - - function get() { - - if(observer_prohibited(true)) { - return login(); - } - - $groups = []; + if (!$profile) { + $x = q( + "select channel_id as profile_uid from channel where channel_address = '%s' limit 1", + dbesc(argv(1)) + ); + if ($x) { + App::$profile = $x[0]; + } + } + if (ActivityStreams::is_as_request()) { + $chan = channelx_by_nick(argv(1)); + if (!$chan) { + http_status_exit(404, 'Not found'); + } + $p = Activity::encode_person($chan, true, ((get_config('system', 'activitypub', ACTIVITYPUB_ENABLED)) ? true : false)); + if (!$p) { + http_status_exit(404, 'Not found'); + } - $tab = 'profile'; - $o = ''; - - if(! (perm_is_allowed(\App::$profile['profile_uid'],get_observer_hash(), 'view_profile'))) { - notice( t('Permission denied.') . EOL); - return; - } - + as_return_and_die(['type' => 'Profile', 'describes' => $p], $chan); + } + + Libprofile::load($which, $profile); + } + + public function get() + { + + if (observer_prohibited(true)) { + return login(); + } + + $groups = []; - 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); - - if(\App::$profile['hidewall'] && (! $is_owner) && (! remote_channel())) { - notice( t('Permission denied.') . EOL); - return; - } - - head_add_link([ - 'rel' => 'alternate', - 'type' => 'application/json+oembed', - 'href' => z_root() . '/oep?f=&url=' . urlencode(z_root() . '/' . \App::$query_string), - 'title' => 'oembed' - ]); + $tab = 'profile'; + $o = ''; - $o .= Libprofile::advanced(); - call_hooks('profile_advanced',$o); - return $o; - - } - + if (!(perm_is_allowed(App::$profile['profile_uid'], get_observer_hash(), 'view_profile'))) { + notice(t('Permission denied.') . EOL); + 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); + + if (App::$profile['hidewall'] && (!$is_owner) && (!remote_channel())) { + notice(t('Permission denied.') . EOL); + return; + } + + head_add_link([ + 'rel' => 'alternate', + 'type' => 'application/json+oembed', + 'href' => z_root() . '/oep?f=&url=' . urlencode(z_root() . '/' . App::$query_string), + 'title' => 'oembed' + ]); + + $o .= Libprofile::advanced(); + call_hooks('profile_advanced', $o); + return $o; + } } diff --git a/Zotlabs/Module/Profile_photo.php b/Zotlabs/Module/Profile_photo.php index ce5734b3f..88dae174d 100644 --- a/Zotlabs/Module/Profile_photo.php +++ b/Zotlabs/Module/Profile_photo.php @@ -1,4 +1,5 @@ is_valid()) { - - $im->cropImage(300,$srcX,$srcY,$srcW,$srcH); - - $aid = get_account_id(); - - $p = [ - 'aid' => $aid, - 'uid' => local_channel(), - 'resource_id' => $base_image['resource_id'], - 'filename' => $base_image['filename'], - 'album' => t('Profile Photos'), - 'os_path' => $base_image['os_path'], - 'display_path' => $base_image['display_path'], - 'created' => $base_image['created'], - 'edited' => $base_image['edited'] - ]; - - $animated = get_config('system','animated_avatars',true); - - $p['imgscale'] = PHOTO_RES_PROFILE_300; - $p['photo_usage'] = (($is_default_profile) ? PHOTO_PROFILE : PHOTO_NORMAL); - - $r1 = $im->storeThumbnail($p, PHOTO_RES_PROFILE_300, $animated); - - $im->scaleImage(80); - $p['imgscale'] = PHOTO_RES_PROFILE_80; - - $r2 = $im->storeThumbnail($p, PHOTO_RES_PROFILE_80, $animated); - - $im->scaleImage(48); - $p['imgscale'] = PHOTO_RES_PROFILE_48; - - $r3 = $im->storeThumbnail($p, PHOTO_RES_PROFILE_48, $animated); - - if ($r1 === false || $r2 === false || $r3 === false) { - // if one failed, delete them all so we can start over. - notice( t('Image resize failed.') . EOL ); - $x = q("delete from photo where resource_id = '%s' and uid = %d and imgscale in ( %d, %d, %d ) ", - dbesc($base_image['resource_id']), - local_channel(), - intval(PHOTO_RES_PROFILE_300), - intval(PHOTO_RES_PROFILE_80), - intval(PHOTO_RES_PROFILE_48) - ); - return; - } - - $channel = App::get_channel(); - - // If setting for the default profile, unset the profile photo flag from any other photos I own - - if ($is_default_profile) { - - $r = q("update profile set photo = '%s', thumb = '%s' where is_default = 1 and uid = %d", - dbesc(z_root() . '/photo/profile/l/' . local_channel()), - dbesc(z_root() . '/photo/profile/m/' . local_channel()), - intval(local_channel()) - ); +class Profile_photo extends Controller +{ - $r = q("UPDATE photo SET photo_usage = %d WHERE photo_usage = %d + /* @brief Initalize the profile-photo edit view + * + * @return void + * + */ + + public function init() + { + + if (!local_channel()) { + return; + } + + $channel = App::get_channel(); + Libprofile::load($channel['channel_address']); + } + + /* @brief Evaluate posted values + * + * @param $a Current application + * @return void + * + */ + + public function post() + { + + if (!local_channel()) { + return; + } + + check_form_security_token_redirectOnErr('/profile_photo', 'profile_photo'); + + if ((array_key_exists('cropfinal', $_POST)) && (intval($_POST['cropfinal']) == 1)) { + // logger('crop: ' . print_r($_POST,true)); + + // phase 2 - we have finished cropping + + if (argc() != 2) { + notice(t('Image uploaded but image cropping failed.') . EOL); + return; + } + + $image_id = argv(1); + + if (substr($image_id, -2, 1) == '-') { + $scale = substr($image_id, -1, 1); + $image_id = substr($image_id, 0, -2); + } + + // unless proven otherwise + $is_default_profile = 1; + + if ($_REQUEST['profile']) { + $r = q( + "select id, profile_guid, is_default, gender from profile where id = %d and uid = %d limit 1", + intval($_REQUEST['profile']), + intval(local_channel()) + ); + if ($r) { + $profile = array_shift($r); + if (!intval($profile['is_default'])) { + $is_default_profile = 0; + } + } + } + + $srcX = intval($_POST['xstart']); + $srcY = intval($_POST['ystart']); + $srcW = intval($_POST['xfinal']) - $srcX; + $srcH = intval($_POST['yfinal']) - $srcY; + + $r = q( + "SELECT * FROM photo WHERE resource_id = '%s' AND uid = %d AND imgscale = %d LIMIT 1", + dbesc($image_id), + dbesc(local_channel()), + intval($scale) + ); + if ($r) { + $base_image = array_shift($r); + $base_image['content'] = (($base_image['os_storage']) ? @file_get_contents(dbunescbin($base_image['content'])) : dbunescbin($base_image['content'])); + + $im = photo_factory($base_image['content'], $base_image['mimetype']); + if ($im && $im->is_valid()) { + $im->cropImage(300, $srcX, $srcY, $srcW, $srcH); + + $aid = get_account_id(); + + $p = [ + 'aid' => $aid, + 'uid' => local_channel(), + 'resource_id' => $base_image['resource_id'], + 'filename' => $base_image['filename'], + 'album' => t('Profile Photos'), + 'os_path' => $base_image['os_path'], + 'display_path' => $base_image['display_path'], + 'created' => $base_image['created'], + 'edited' => $base_image['edited'] + ]; + + $animated = get_config('system', 'animated_avatars', true); + + $p['imgscale'] = PHOTO_RES_PROFILE_300; + $p['photo_usage'] = (($is_default_profile) ? PHOTO_PROFILE : PHOTO_NORMAL); + + $r1 = $im->storeThumbnail($p, PHOTO_RES_PROFILE_300, $animated); + + $im->scaleImage(80); + $p['imgscale'] = PHOTO_RES_PROFILE_80; + + $r2 = $im->storeThumbnail($p, PHOTO_RES_PROFILE_80, $animated); + + $im->scaleImage(48); + $p['imgscale'] = PHOTO_RES_PROFILE_48; + + $r3 = $im->storeThumbnail($p, PHOTO_RES_PROFILE_48, $animated); + + if ($r1 === false || $r2 === false || $r3 === false) { + // if one failed, delete them all so we can start over. + notice(t('Image resize failed.') . EOL); + $x = q( + "delete from photo where resource_id = '%s' and uid = %d and imgscale in ( %d, %d, %d ) ", + dbesc($base_image['resource_id']), + local_channel(), + intval(PHOTO_RES_PROFILE_300), + intval(PHOTO_RES_PROFILE_80), + intval(PHOTO_RES_PROFILE_48) + ); + return; + } + + $channel = App::get_channel(); + + // If setting for the default profile, unset the profile photo flag from any other photos I own + + if ($is_default_profile) { + $r = q( + "update profile set photo = '%s', thumb = '%s' where is_default = 1 and uid = %d", + dbesc(z_root() . '/photo/profile/l/' . local_channel()), + dbesc(z_root() . '/photo/profile/m/' . local_channel()), + intval(local_channel()) + ); + + + $r = q( + "UPDATE photo SET photo_usage = %d WHERE photo_usage = %d AND resource_id != '%s' AND uid = %d", - intval(PHOTO_NORMAL), - intval(PHOTO_PROFILE), - dbesc($base_image['resource_id']), - intval(local_channel()) - ); - + intval(PHOTO_NORMAL), + intval(PHOTO_PROFILE), + dbesc($base_image['resource_id']), + intval(local_channel()) + ); - send_profile_photo_activity($channel,$base_image,$profile); - - } - else { - $r = q("update profile set photo = '%s', thumb = '%s' where id = %d and uid = %d", - dbesc(z_root() . '/photo/' . $base_image['resource_id'] . '-4'), - dbesc(z_root() . '/photo/' . $base_image['resource_id'] . '-5'), - intval($_REQUEST['profile']), - intval(local_channel()) - ); - } - - // set $send to false in profiles_build_sync() to return the data - // so that we only send one sync packet. - $sync_profiles = profiles_build_sync(local_channel(),false); - - // We'll set the updated profile-photo timestamp even if it isn't the default profile, - // so that browsers will do a cache update unconditionally - // Also set links back to site-specific profile photo url in case it was - // changed to a generic URL by a clone operation. Otherwise the new photo may - // not get pushed to other sites correctly. - - $r = q("UPDATE xchan set xchan_photo_mimetype = '%s', xchan_photo_date = '%s', xchan_photo_l = '%s', xchan_photo_m = '%s', xchan_photo_s = '%s' + send_profile_photo_activity($channel, $base_image, $profile); + } else { + $r = q( + "update profile set photo = '%s', thumb = '%s' where id = %d and uid = %d", + dbesc(z_root() . '/photo/' . $base_image['resource_id'] . '-4'), + dbesc(z_root() . '/photo/' . $base_image['resource_id'] . '-5'), + intval($_REQUEST['profile']), + intval(local_channel()) + ); + } + + // set $send to false in profiles_build_sync() to return the data + // so that we only send one sync packet. + + $sync_profiles = profiles_build_sync(local_channel(), false); + + // We'll set the updated profile-photo timestamp even if it isn't the default profile, + // so that browsers will do a cache update unconditionally + // Also set links back to site-specific profile photo url in case it was + // changed to a generic URL by a clone operation. Otherwise the new photo may + // not get pushed to other sites correctly. + + $r = q( + "UPDATE xchan set xchan_photo_mimetype = '%s', xchan_photo_date = '%s', xchan_photo_l = '%s', xchan_photo_m = '%s', xchan_photo_s = '%s' where xchan_hash = '%s'", - dbesc($im->getType()), - dbesc(datetime_convert()), - dbesc(z_root() . '/photo/profile/l/' . $channel['channel_id']), - dbesc(z_root() . '/photo/profile/m/' . $channel['channel_id']), - dbesc(z_root() . '/photo/profile/s/' . $channel['channel_id']), - dbesc($channel['xchan_hash']) - ); + dbesc($im->getType()), + dbesc(datetime_convert()), + dbesc(z_root() . '/photo/profile/l/' . $channel['channel_id']), + dbesc(z_root() . '/photo/profile/m/' . $channel['channel_id']), + dbesc(z_root() . '/photo/profile/s/' . $channel['channel_id']), + dbesc($channel['xchan_hash']) + ); - photo_profile_setperms(local_channel(),$base_image['resource_id'],$_REQUEST['profile']); + photo_profile_setperms(local_channel(), $base_image['resource_id'], $_REQUEST['profile']); - $sync = attach_export_data($channel,$base_image['resource_id']); - if ($sync) { - Libsync::build_sync_packet($channel['channel_id'],array('file' => array($sync), 'profile' => $sync_profiles)); - } + $sync = attach_export_data($channel, $base_image['resource_id']); + if ($sync) { + Libsync::build_sync_packet($channel['channel_id'], array('file' => array($sync), 'profile' => $sync_profiles)); + } - // Similarly, tell the nav bar to bypass the cache and update the avatar image. - $_SESSION['reload_avatar'] = true; - - info( t('Shift-reload the page or clear browser cache if the new photo does not display immediately.') . EOL); - - // Update directory in background - Run::Summon( [ 'Directory', $channel['channel_id'] ] ); - - } - else { - notice( t('Unable to process image') . EOL); - } - } - - goaway(z_root() . '/profiles'); - } - - // A new photo was uploaded. Store it and save some important details - // in App::$data for use in the cropping function - - - $hash = photo_new_resource(); - $importing = false; - $smallest = 0; - + // Similarly, tell the nav bar to bypass the cache and update the avatar image. + $_SESSION['reload_avatar'] = true; - if ($_REQUEST['importfile']) { - $hash = $_REQUEST['importfile']; - $importing = true; - } - else { + info(t('Shift-reload the page or clear browser cache if the new photo does not display immediately.') . EOL); - $matches = []; - $partial = false; + // Update directory in background + Run::Summon(['Directory', $channel['channel_id']]); + } else { + notice(t('Unable to process image') . EOL); + } + } - if (array_key_exists('HTTP_CONTENT_RANGE',$_SERVER)) { - $pm = preg_match('/bytes (\d*)\-(\d*)\/(\d*)/',$_SERVER['HTTP_CONTENT_RANGE'],$matches); - if ($pm) { - logger('Content-Range: ' . print_r($matches,true), LOGGER_DEBUG); - $partial = true; - } - } + goaway(z_root() . '/profiles'); + } - if ($partial) { - $x = save_chunk($channel,$matches[1],$matches[2],$matches[3]); + // A new photo was uploaded. Store it and save some important details + // in App::$data for use in the cropping function - if ($x['partial']) { - header('Range: bytes=0-' . (($x['length']) ? $x['length'] - 1 : 0)); - json_return_and_die($x); - } - else { - header('Range: bytes=0-' . (($x['size']) ? $x['size'] - 1 : 0)); - $_FILES['userfile'] = [ - 'name' => $x['name'], - 'type' => $x['type'], - 'tmp_name' => $x['tmp_name'], - 'error' => $x['error'], - 'size' => $x['size'] - ]; - } - } - else { - if (! array_key_exists('userfile',$_FILES)) { - $_FILES['userfile'] = [ - 'name' => $_FILES['files']['name'], - 'type' => $_FILES['files']['type'], - 'tmp_name' => $_FILES['files']['tmp_name'], - 'error' => $_FILES['files']['error'], - 'size' => $_FILES['files']['size'] - ]; - } - } + $hash = photo_new_resource(); + $importing = false; + $smallest = 0; - $res = attach_store(App::get_channel(), get_observer_hash(), '', array('album' => t('Profile Photos'), 'hash' => $hash)); - - logger('attach_store: ' . print_r($res,true), LOGGER_DEBUG); - json_return_and_die([ 'message' => $hash ]); - } - - if (($res && intval($res['data']['is_photo'])) || $importing) { - $i = q("select * from photo where resource_id = '%s' and uid = %d order by imgscale", - dbesc($hash), - intval(local_channel()) - ); - - if (! $i) { - notice( t('Image upload failed.') . EOL ); - return; - } - $os_storage = false; - - foreach ($i as $ii) { - if (intval($ii['imgscale']) < PHOTO_RES_640) { - $smallest = intval($ii['imgscale']); - $os_storage = intval($ii['os_storage']); - $imagedata = $ii['content']; - $filetype = $ii['mimetype']; - } - } - } - - $imagedata = (($os_storage) ? @file_get_contents(dbunescbin($imagedata)) : dbunescbin($imagedata)); - $ph = photo_factory($imagedata, $filetype); - - if (! $ph->is_valid()) { - notice( t('Unable to process image.') . EOL ); - return; - } - - return $this->profile_photo_crop_ui_head($ph, $hash, $smallest); + if ($_REQUEST['importfile']) { + $hash = $_REQUEST['importfile']; + $importing = true; + } else { + $matches = []; + $partial = false; - // This will "fall through" to the get() method, and since - // App::$data['imagecrop'] is set, it will proceed to cropping - // rather than present the upload form - } - - - /* @brief Generate content of profile-photo view - * - * @return void - * - */ - - - function get() { - - if (! local_channel()) { - notice( t('Permission denied.') . EOL ); - return; - } - - $channel = App::get_channel(); - $pf = 0; - $newuser = false; - - if (argc() == 2 && argv(1) === 'new') { - $newuser = true; - } - - if (argv(1) === 'use') { - if (argc() < 3) { - notice( t('Permission denied.') . EOL ); - return; - }; - - $resource_id = argv(2); - - $pf = (($_REQUEST['pf']) ? intval($_REQUEST['pf']) : 0); + if (array_key_exists('HTTP_CONTENT_RANGE', $_SERVER)) { + $pm = preg_match('/bytes (\d*)\-(\d*)\/(\d*)/', $_SERVER['HTTP_CONTENT_RANGE'], $matches); + if ($pm) { + logger('Content-Range: ' . print_r($matches, true), LOGGER_DEBUG); + $partial = true; + } + } - $c = q("select id, is_default from profile where uid = %d", - intval(local_channel()) - ); + if ($partial) { + $x = save_chunk($channel, $matches[1], $matches[2], $matches[3]); - $multi_profiles = true; + if ($x['partial']) { + header('Range: bytes=0-' . (($x['length']) ? $x['length'] - 1 : 0)); + json_return_and_die($x); + } else { + header('Range: bytes=0-' . (($x['size']) ? $x['size'] - 1 : 0)); - if (($c) && (count($c) === 1) && (intval($c[0]['is_default']))) { - $_REQUEST['profile'] = $c[0]['id']; - $multi_profiles = false; - } - else { - $_REQUEST['profile'] = $pf; - } + $_FILES['userfile'] = [ + 'name' => $x['name'], + 'type' => $x['type'], + 'tmp_name' => $x['tmp_name'], + 'error' => $x['error'], + 'size' => $x['size'] + ]; + } + } else { + if (!array_key_exists('userfile', $_FILES)) { + $_FILES['userfile'] = [ + 'name' => $_FILES['files']['name'], + 'type' => $_FILES['files']['type'], + 'tmp_name' => $_FILES['files']['tmp_name'], + 'error' => $_FILES['files']['error'], + 'size' => $_FILES['files']['size'] + ]; + } + } - $r = q("SELECT id, album, imgscale FROM photo WHERE uid = %d AND resource_id = '%s' ORDER BY imgscale ASC", - intval(local_channel()), - dbesc($resource_id) - ); - if (! $r) { - notice( t('Photo not available.') . EOL ); - return; - } - $havescale = false; - foreach ($r as $rr) { - if ($rr['imgscale'] == PHOTO_RES_PROFILE_80) { - $havescale = true; - } - } - - // set an already loaded and cropped photo as profile photo - - if ($havescale) { - // unset any existing profile photos - $r = q("UPDATE photo SET photo_usage = %d WHERE photo_usage = %d AND uid = %d", - intval(PHOTO_NORMAL), - intval(PHOTO_PROFILE), - intval(local_channel()) - ); - - $r = q("UPDATE photo SET photo_usage = %d WHERE uid = %d AND resource_id = '%s'", - intval(PHOTO_PROFILE), - intval(local_channel()), - dbesc($resource_id) - ); - - $r = q("UPDATE xchan set xchan_photo_date = '%s' where xchan_hash = '%s'", - dbesc(datetime_convert()), - dbesc($channel['xchan_hash']) - ); - - photo_profile_setperms(local_channel(),$resource_id,$_REQUEST['profile']); + $res = attach_store(App::get_channel(), get_observer_hash(), '', [ 'album' => t('Profile Photos'), 'hash' => $hash, 'source' => 'photos' ]); - $sync = attach_export_data($channel,$resource_id); - if ($sync) { - Libsync::build_sync_packet($channel['channel_id'],array('file' => array($sync))); - } + logger('attach_store: ' . print_r($res, true), LOGGER_DEBUG); - Run::Summon( [ 'Directory',local_channel() ] ); - goaway(z_root() . '/profiles'); - } - - $r = q("SELECT content, mimetype, resource_id, os_storage FROM photo WHERE id = %d and uid = %d limit 1", - intval($r[0]['id']), - intval(local_channel()) - - ); - if (! $r) { - notice( t('Photo not available.') . EOL ); - return; - } - - if (intval($r[0]['os_storage'])) { - $data = @file_get_contents(dbunescbin($r[0]['content'])); - } - else { - $data = dbunescbin($r[0]['content']); - } - - $ph = photo_factory($data, $r[0]['mimetype']); - $smallest = 0; - if ($ph->is_valid()) { - // go ahead as if we have just uploaded a new photo to crop - $i = q("select resource_id, imgscale from photo where resource_id = '%s' and uid = %d order by imgscale", - dbesc($r[0]['resource_id']), - intval(local_channel()) - ); - - if ($i) { - $hash = $i[0]['resource_id']; - foreach ($i as $ii) { - if (intval($ii['imgscale']) < PHOTO_RES_640) { - $smallest = intval($ii['imgscale']); - } - } - } - } - - if ($multi_profiles) { - App::$data['importfile'] = $resource_id; - } - else { - $this->profile_photo_crop_ui_head($ph, $hash, $smallest); - } + json_return_and_die(['message' => $hash]); + } - // falls through with App::$data['imagecrop'] set so we go straight to the cropping section + if (($res && intval($res['data']['is_photo'])) || $importing) { + $i = q( + "select * from photo where resource_id = '%s' and uid = %d order by imgscale", + dbesc($hash), + intval(local_channel()) + ); - } - - // present an upload form + if (!$i) { + notice(t('Image upload failed.') . EOL); + return; + } + $os_storage = false; - $profiles = q("select id, profile_name as name, is_default from profile where uid = %d order by id asc", - intval(local_channel()) - ); + foreach ($i as $ii) { + if (intval($ii['imgscale']) < PHOTO_RES_640) { + $smallest = intval($ii['imgscale']); + $os_storage = intval($ii['os_storage']); + $imagedata = $ii['content']; + $filetype = $ii['mimetype']; + } + } + } - if ($profiles) { - for ($x = 0; $x < count($profiles); $x ++) { - $profiles[$x]['selected'] = false; - if ($pf && $profiles[$x]['id'] == $pf) { - $profiles[$x]['selected'] = true; - } - if ((! $pf) && $profiles[$x]['is_default']) { - $profiles[$x]['selected'] = true; - } - } - } + $imagedata = (($os_storage) ? @file_get_contents(dbunescbin($imagedata)) : dbunescbin($imagedata)); + $ph = photo_factory($imagedata, $filetype); - $importing = ((array_key_exists('importfile',App::$data)) ? true : false); - - if (! array_key_exists('imagecrop', App::$data)) { - - $tpl = get_markup_template('profile_photo.tpl'); - - $o .= replace_macros($tpl, [ - '$user' => App::$channel['channel_address'], - '$info' => ((count($profiles) > 1) ? t('Your default profile photo is visible to anybody on the internet. Profile photos for alternate profiles will inherit the permissions of the profile') : t('Your profile photo is visible to anybody on the internet and may be distributed to other websites.')), - '$importfile' => (($importing) ? App::$data['importfile'] : ''), - '$lbl_upfile' => t('Upload File:'), - '$lbl_profiles' => t('Select a profile:'), - '$title' => (($importing) ? t('Use Photo for Profile') : t('Change Profile Photo')), - '$submit' => (($importing) ? t('Use') : t('Upload')), - '$profiles' => $profiles, - '$single' => ((count($profiles) == 1) ? true : false), - '$profile0' => $profiles[0], - '$embedPhotos' => t('Use a photo from your albums'), - '$embedPhotosModalTitle' => t('Use a photo from your albums'), - '$embedPhotosModalCancel' => t('Cancel'), - '$embedPhotosModalOK' => t('OK'), - '$modalchooseimages' => t('Choose images to embed'), - '$modalchoosealbum' => t('Choose an album'), - '$modaldiffalbum' => t('Choose a different album'), - '$modalerrorlist' => t('Error getting album list'), - '$modalerrorlink' => t('Error getting photo link'), - '$modalerroralbum' => t('Error getting album'), - '$form_security_token' => get_form_security_token("profile_photo"), - '$select' => t('Select previously uploaded photo'), - ]); - - call_hooks('profile_photo_content_end', $o); - return $o; - } - else { + if (!$ph->is_valid()) { + notice(t('Unable to process image.') . EOL); + return; + } - // present a cropping form + return $this->profile_photo_crop_ui_head($ph, $hash, $smallest); - $filename = App::$data['imagecrop'] . '-' . App::$data['imagecrop_resolution']; - $resolution = App::$data['imagecrop_resolution']; - $o .= replace_macros(get_markup_template('cropbody.tpl'), [ - '$filename' => $filename, - '$profile' => intval($_REQUEST['profile']), - '$resource' => App::$data['imagecrop'] . '-' . App::$data['imagecrop_resolution'], - '$image_url' => z_root() . '/photo/' . $filename, - '$title' => t('Crop Image'), - '$desc' => t('Please adjust the image cropping for optimum viewing.'), - '$form_security_token' => get_form_security_token("profile_photo"), - '$done' => t('Done Editing') - ]); - return $o; - } - } - - /* @brief Generate the UI for photo-cropping - * - * @param $ph Photo-Factory - * @return void - * - */ - - function profile_photo_crop_ui_head($ph, $hash, $smallest) { - $max_length = get_config('system','max_image_length', MAX_IMAGE_LENGTH); - if ($max_length > 0) { - $ph->scaleImage($max_length); - } - - App::$data['width'] = $ph->getWidth(); - App::$data['height'] = $ph->getHeight(); - - if (App::$data['width'] < 500 || App::$data['height'] < 500) { - $ph->scaleImageUp(400); - App::$data['width'] = $ph->getWidth(); - App::$data['height'] = $ph->getHeight(); - } - - App::$data['imagecrop'] = $hash; - App::$data['imagecrop_resolution'] = $smallest; - App::$page['htmlhead'] .= replace_macros(get_markup_template('crophead.tpl'), []); - return; - } + // This will "fall through" to the get() method, and since + // App::$data['imagecrop'] is set, it will proceed to cropping + // rather than present the upload form + } + + + /* @brief Generate content of profile-photo view + * + * @return void + * + */ + + + public function get() + { + + if (!local_channel()) { + notice(t('Permission denied.') . EOL); + return; + } + + $channel = App::get_channel(); + $pf = 0; + $newuser = false; + + if (argc() == 2 && argv(1) === 'new') { + $newuser = true; + } + + if (argv(1) === 'use') { + if (argc() < 3) { + notice(t('Permission denied.') . EOL); + return; + } + + $resource_id = argv(2); + + $pf = (($_REQUEST['pf']) ? intval($_REQUEST['pf']) : 0); + + $c = q( + "select id, is_default from profile where uid = %d", + intval(local_channel()) + ); + + $multi_profiles = true; + + if (($c) && (count($c) === 1) && (intval($c[0]['is_default']))) { + $_REQUEST['profile'] = $c[0]['id']; + $multi_profiles = false; + } else { + $_REQUEST['profile'] = $pf; + } + + $r = q( + "SELECT id, album, imgscale FROM photo WHERE uid = %d AND resource_id = '%s' ORDER BY imgscale ASC", + intval(local_channel()), + dbesc($resource_id) + ); + if (!$r) { + notice(t('Photo not available.') . EOL); + return; + } + $havescale = false; + foreach ($r as $rr) { + if ($rr['imgscale'] == PHOTO_RES_PROFILE_80) { + $havescale = true; + } + } + + // set an already loaded and cropped photo as profile photo + + if ($havescale) { + // unset any existing profile photos + $r = q( + "UPDATE photo SET photo_usage = %d WHERE photo_usage = %d AND uid = %d", + intval(PHOTO_NORMAL), + intval(PHOTO_PROFILE), + intval(local_channel()) + ); + + $r = q( + "UPDATE photo SET photo_usage = %d WHERE uid = %d AND resource_id = '%s'", + intval(PHOTO_PROFILE), + intval(local_channel()), + dbesc($resource_id) + ); + + $r = q( + "UPDATE xchan set xchan_photo_date = '%s' where xchan_hash = '%s'", + dbesc(datetime_convert()), + dbesc($channel['xchan_hash']) + ); + + photo_profile_setperms(local_channel(), $resource_id, $_REQUEST['profile']); + + $sync = attach_export_data($channel, $resource_id); + if ($sync) { + Libsync::build_sync_packet($channel['channel_id'], array('file' => array($sync))); + } + + Run::Summon(['Directory', local_channel()]); + goaway(z_root() . '/profiles'); + } + + $r = q( + "SELECT content, mimetype, resource_id, os_storage FROM photo WHERE id = %d and uid = %d limit 1", + intval($r[0]['id']), + intval(local_channel()) + ); + if (!$r) { + notice(t('Photo not available.') . EOL); + return; + } + + if (intval($r[0]['os_storage'])) { + $data = @file_get_contents(dbunescbin($r[0]['content'])); + } else { + $data = dbunescbin($r[0]['content']); + } + + $ph = photo_factory($data, $r[0]['mimetype']); + $smallest = 0; + if ($ph->is_valid()) { + // go ahead as if we have just uploaded a new photo to crop + $i = q( + "select resource_id, imgscale from photo where resource_id = '%s' and uid = %d order by imgscale", + dbesc($r[0]['resource_id']), + intval(local_channel()) + ); + + if ($i) { + $hash = $i[0]['resource_id']; + foreach ($i as $ii) { + if (intval($ii['imgscale']) < PHOTO_RES_640) { + $smallest = intval($ii['imgscale']); + } + } + } + } + + if ($multi_profiles) { + App::$data['importfile'] = $resource_id; + } else { + $this->profile_photo_crop_ui_head($ph, $hash, $smallest); + } + + // falls through with App::$data['imagecrop'] set so we go straight to the cropping section + } + + // present an upload form + + $profiles = q( + "select id, profile_name as name, is_default from profile where uid = %d order by id asc", + intval(local_channel()) + ); + + if ($profiles) { + for ($x = 0; $x < count($profiles); $x++) { + $profiles[$x]['selected'] = false; + if ($pf && $profiles[$x]['id'] == $pf) { + $profiles[$x]['selected'] = true; + } + if ((!$pf) && $profiles[$x]['is_default']) { + $profiles[$x]['selected'] = true; + } + } + } + + $importing = ((array_key_exists('importfile', App::$data)) ? true : false); + + if (!array_key_exists('imagecrop', App::$data)) { + $tpl = get_markup_template('profile_photo.tpl'); + + $o .= replace_macros($tpl, [ + '$user' => App::$channel['channel_address'], + '$info' => ((count($profiles) > 1) ? t('Your default profile photo is visible to anybody on the internet. Profile photos for alternate profiles will inherit the permissions of the profile') : t('Your profile photo is visible to anybody on the internet and may be distributed to other websites.')), + '$importfile' => (($importing) ? App::$data['importfile'] : ''), + '$lbl_upfile' => t('Upload File:'), + '$lbl_profiles' => t('Select a profile:'), + '$title' => (($importing) ? t('Use Photo for Profile') : t('Change Profile Photo')), + '$submit' => (($importing) ? t('Use') : t('Upload')), + '$profiles' => $profiles, + '$single' => ((count($profiles) == 1) ? true : false), + '$profile0' => $profiles[0], + '$embedPhotos' => t('Use a photo from your albums'), + '$embedPhotosModalTitle' => t('Use a photo from your albums'), + '$embedPhotosModalCancel' => t('Cancel'), + '$embedPhotosModalOK' => t('OK'), + '$modalchooseimages' => t('Choose images to embed'), + '$modalchoosealbum' => t('Choose an album'), + '$modaldiffalbum' => t('Choose a different album'), + '$modalerrorlist' => t('Error getting album list'), + '$modalerrorlink' => t('Error getting photo link'), + '$modalerroralbum' => t('Error getting album'), + '$form_security_token' => get_form_security_token("profile_photo"), + '$select' => t('Select previously uploaded photo'), + ]); + + call_hooks('profile_photo_content_end', $o); + return $o; + } else { + // present a cropping form + + $filename = App::$data['imagecrop'] . '-' . App::$data['imagecrop_resolution']; + $resolution = App::$data['imagecrop_resolution']; + $o .= replace_macros(get_markup_template('cropbody.tpl'), [ + '$filename' => $filename, + '$profile' => intval($_REQUEST['profile']), + '$resource' => App::$data['imagecrop'] . '-' . App::$data['imagecrop_resolution'], + '$image_url' => z_root() . '/photo/' . $filename, + '$title' => t('Crop Image'), + '$desc' => t('Please adjust the image cropping for optimum viewing.'), + '$form_security_token' => get_form_security_token("profile_photo"), + '$done' => t('Done Editing') + ]); + return $o; + } + } + + /* @brief Generate the UI for photo-cropping + * + * @param $ph Photo-Factory + * @return void + * + */ + + public function profile_photo_crop_ui_head($ph, $hash, $smallest) + { + $max_length = get_config('system', 'max_image_length', MAX_IMAGE_LENGTH); + if ($max_length > 0) { + $ph->scaleImage($max_length); + } + + App::$data['width'] = $ph->getWidth(); + App::$data['height'] = $ph->getHeight(); + + if (App::$data['width'] < 500 || App::$data['height'] < 500) { + $ph->scaleImageUp(400); + App::$data['width'] = $ph->getWidth(); + App::$data['height'] = $ph->getHeight(); + } + + App::$data['imagecrop'] = $hash; + App::$data['imagecrop_resolution'] = $smallest; + App::$page['htmlhead'] .= replace_macros(get_markup_template('crophead.tpl'), []); + return; + } } diff --git a/Zotlabs/Module/Profiles.php b/Zotlabs/Module/Profiles.php index 1231e6319..a9630424d 100644 --- a/Zotlabs/Module/Profiles.php +++ b/Zotlabs/Module/Profiles.php @@ -1,519 +1,549 @@ 2) && (argv(1) === "drop") && intval(argv(2))) { - $r = q("SELECT * FROM profile WHERE id = %d AND uid = %d AND is_default = 0 LIMIT 1", - intval(argv(2)), - intval(local_channel()) - ); - if(! $r) { - notice( t('Profile not found.') . EOL); - goaway(z_root() . '/profiles'); - } - $profile_guid = $r[0]['profile_guid']; - - check_form_security_token_redirectOnErr('/profiles', 'profile_drop', 't'); - - // move every contact using this profile as their default to the user default - - $r = q("UPDATE abook SET abook_profile = (SELECT profile_guid FROM profile WHERE is_default = 1 AND uid = %d LIMIT 1) WHERE abook_profile = '%s' AND abook_channel = %d ", - intval(local_channel()), - dbesc($profile_guid), - intval(local_channel()) - ); - $r = q("DELETE FROM profile WHERE id = %d AND uid = %d", - intval(argv(2)), - intval(local_channel()) - ); - if($r) - info( t('Profile deleted.') . EOL); - - // @fixme this is a much more complicated sync - add any changed abook entries and - // also add deleted flag to profile structure - // profiles_build_sync is just here as a placeholder - it doesn't work at all here - - // profiles_build_sync(local_channel()); - - goaway(z_root() . '/profiles'); - return; // NOTREACHED - } - - - - - - if((argc() > 1) && (argv(1) === 'new')) { - - // check_form_security_token_redirectOnErr('/profiles', 'profile_new', 't'); - - $r0 = q("SELECT id FROM profile WHERE uid = %d", - intval(local_channel())); - $num_profiles = count($r0); - - $name = t('Profile-') . ($num_profiles + 1); - - $r1 = q("SELECT fullname, photo, thumb FROM profile WHERE uid = %d AND is_default = 1 LIMIT 1", - intval(local_channel())); - - $r2 = profile_store_lowlevel( - [ - 'aid' => intval(get_account_id()), - 'uid' => intval(local_channel()), - 'profile_guid' => new_uuid(), - 'profile_name' => $name, - 'fullname' => $r1[0]['fullname'], - 'photo' => $r1[0]['photo'], - 'thumb' => $r1[0]['thumb'] - ] - ); - - $r3 = q("SELECT id FROM profile WHERE uid = %d AND profile_name = '%s' LIMIT 1", - intval(local_channel()), - dbesc($name) - ); - - info( t('New profile created.') . EOL); - if(count($r3) == 1) - goaway(z_root() . '/profiles/' . $r3[0]['id']); - - goaway(z_root() . '/profiles'); - } - - if((argc() > 2) && (argv(1) === 'clone')) { - - check_form_security_token_redirectOnErr('/profiles', 'profile_clone', 't'); - - $r0 = q("SELECT id FROM profile WHERE uid = %d", - intval(local_channel())); - $num_profiles = count($r0); - - $name = t('Profile-') . ($num_profiles + 1); - $r1 = q("SELECT * FROM profile WHERE uid = %d AND id = %d LIMIT 1", - intval(local_channel()), - intval(argv(2)) - ); - if(! count($r1)) { - notice( t('Profile unavailable to clone.') . EOL); - App::$error = 404; - return; - } - unset($r1[0]['id']); - $r1[0]['is_default'] = 0; - $r1[0]['publish'] = 0; - $r1[0]['profile_name'] = $name; - $r1[0]['profile_guid'] = new_uuid(); - - create_table_from_array('profile', $r1[0]); - - $r3 = q("SELECT id FROM profile WHERE uid = %d AND profile_name = '%s' LIMIT 1", - intval(local_channel()), - dbesc($name) - ); - info( t('New profile created.') . EOL); - - profiles_build_sync(local_channel()); - - if(($r3) && (count($r3) == 1)) - goaway(z_root() . '/profiles/' . $r3[0]['id']); - - goaway(z_root() . '/profiles'); - - return; // NOTREACHED - } - - if((argc() > 2) && (argv(1) === 'export')) { - - $r1 = q("SELECT * FROM profile WHERE uid = %d AND id = %d LIMIT 1", - intval(local_channel()), - intval(argv(2)) - ); - if(! $r1) { - notice( t('Profile unavailable to export.') . EOL); - App::$error = 404; - return; - } - header('content-type: application/octet_stream'); - header('Content-Disposition: attachment; filename="' . $r1[0]['profile_name'] . '.json"' ); - - unset($r1[0]['id']); - unset($r1[0]['aid']); - unset($r1[0]['uid']); - unset($r1[0]['is_default']); - unset($r1[0]['publish']); - unset($r1[0]['profile_name']); - unset($r1[0]['profile_guid']); - echo json_encode($r1[0]); - killme(); - } - - if(((argc() > 1) && (intval(argv(1)))) || !feature_enabled(local_channel(),'multi_profiles')) { - if(feature_enabled(local_channel(),'multi_profiles')) - $id = argv(1); - else { - $x = q("select id from profile where uid = %d and is_default = 1", - intval(local_channel()) - ); - if($x) - $id = $x[0]['id']; - } - $r = q("SELECT * FROM profile WHERE id = %d AND uid = %d LIMIT 1", - intval($id), - intval(local_channel()) - ); - if(! count($r)) { - notice( t('Profile not found.') . EOL); - App::$error = 404; - return; - } - - $chan = App::get_channel(); - - Libprofile::load($chan['channel_address'],$r[0]['profile_guid']); - } - } - - function post() { - - if(! local_channel()) { - notice( t('Permission denied.') . EOL); - return; - } - - $namechanged = false; - - // import from json export file. - // Only import fields that are allowed on this hub - - if(x($_FILES,'userfile')) { - $src = $_FILES['userfile']['tmp_name']; - $filesize = intval($_FILES['userfile']['size']); - if($filesize) { - $j = @json_decode(@file_get_contents($src),true); - @unlink($src); - if($j) { - $fields = get_profile_fields_advanced(); - if($fields) { - foreach($j as $jj => $v) { - foreach($fields as $f => $n) { - if($jj == $f) { - $_POST[$f] = $v; - break; - } - } - } - } - } - } - } - - call_hooks('profile_post', $_POST); - - - if((argc() > 1) && (argv(1) !== "new") && intval(argv(1))) { - $orig = q("SELECT * FROM profile WHERE id = %d AND uid = %d LIMIT 1", - intval(argv(1)), - intval(local_channel()) - ); - if(! count($orig)) { - notice( t('Profile not found.') . EOL); - return; - } - - check_form_security_token_redirectOnErr('/profiles', 'profile_edit'); - + public function init() + { - $is_default = (($orig[0]['is_default']) ? 1 : 0); - - $profile_name = notags(trim($_POST['profile_name'])); - if(! strlen($profile_name)) { - notice( t('Profile Name is required.') . EOL); - return; - } - - $dob = $_POST['dob'] ? escape_tags(trim($_POST['dob'])) : '0000-00-00'; - - $y = substr($dob,0,4); - if((! ctype_digit($y)) || ($y < 1900)) - $ignore_year = true; - else - $ignore_year = false; - - if($dob !== '0000-00-00') { - if(strpos($dob,'0000-') === 0) { - $ignore_year = true; - $dob = substr($dob,5); - } - $dob = datetime_convert('UTC','UTC',(($ignore_year) ? '1900-' . $dob : $dob),(($ignore_year) ? 'm-d' : 'Y-m-d')); - if($ignore_year) - $dob = '0000-' . $dob; - } - - $name = escape_tags(trim($_POST['name'])); - - if($orig[0]['fullname'] != $name) { - $namechanged = true; - - $v = validate_channelname($name); - if($v) { - notice($v); - $namechanged = false; - $name = $orig[0]['fullname']; - } - } - - $pdesc = escape_tags(trim($_POST['pdesc'])); - $gender = escape_tags(trim($_POST['gender'])); - $address = escape_tags(trim($_POST['address'])); - $locality = escape_tags(trim($_POST['locality'])); - $region = escape_tags(trim($_POST['region'])); - $postal_code = escape_tags(trim($_POST['postal_code'])); - $country_name = escape_tags(trim($_POST['country_name'])); - $keywords = escape_tags(trim($_POST['keywords'])); - $marital = escape_tags(trim($_POST['marital'])); - $howlong = escape_tags(trim($_POST['howlong'])); - $sexual = escape_tags(trim($_POST['sexual'])); - $pronouns = escape_tags(trim($_POST['pronouns'])); - $homepage = escape_tags(trim($_POST['homepage'])); - $hometown = escape_tags(trim($_POST['hometown'])); - $politic = escape_tags(trim($_POST['politic'])); - $religion = escape_tags(trim($_POST['religion'])); - - $likes = escape_tags(trim($_POST['likes'])); - $dislikes = escape_tags(trim($_POST['dislikes'])); - - $about = escape_tags(trim($_POST['about'])); - $interest = escape_tags(trim($_POST['interest'])); - $contact = escape_tags(trim($_POST['contact'])); - $channels = escape_tags(trim($_POST['channels'])); - $music = escape_tags(trim($_POST['music'])); - $book = escape_tags(trim($_POST['book'])); - $tv = escape_tags(trim($_POST['tv'])); - $film = escape_tags(trim($_POST['film'])); - $romance = escape_tags(trim($_POST['romance'])); - $work = escape_tags(trim($_POST['work'])); - $education = escape_tags(trim($_POST['education'])); - - $hide_friends = ((intval($_POST['hide_friends'])) ? 1: 0); - - // start fresh and create a new vcard. - // @TODO: preserve the original guid or whatever else needs saving - // $orig_vcard = (($orig[0]['profile_vcard']) ? Reader::read($orig[0]['profile_vcard']) : null); + nav_set_selected('Profiles'); - $orig_vcard = null; + if (!local_channel()) { + return; + } - $channel = App::get_channel(); + if ((argc() > 2) && (argv(1) === "drop") && intval(argv(2))) { + $r = q( + "SELECT * FROM profile WHERE id = %d AND uid = %d AND is_default = 0 LIMIT 1", + intval(argv(2)), + intval(local_channel()) + ); + if (!$r) { + notice(t('Profile not found.') . EOL); + goaway(z_root() . '/profiles'); + } + $profile_guid = $r[0]['profile_guid']; - $default_vcard_cat = ((defined('DEFAULT_VCARD_CAT')) ? DEFAULT_VCARD_CAT : 'HOME'); + check_form_security_token_redirectOnErr('/profiles', 'profile_drop', 't'); - $defcard = [ - 'fn' => $name, - 'title' => $pdesc, - 'photo' => $channel['xchan_photo_l'], - 'adr' => [], - 'adr_type' => [ $default_vcard_cat ], - 'url' => [ $homepage ], - 'url_type' => [ $default_vcard_cat ] - ]; + // move every contact using this profile as their default to the user default - $defcard['adr'][] = [ - 0 => '', - 1 => '', - 2 => $address, - 3 => $locality, - 4 => $region, - 5 => $postal_code, - 6 => $country_name - ]; - - $profile_vcard = update_vcard($defcard,$orig_vcard); + $r = q( + "UPDATE abook SET abook_profile = (SELECT profile_guid FROM profile WHERE is_default = 1 AND uid = %d LIMIT 1) WHERE abook_profile = '%s' AND abook_channel = %d ", + intval(local_channel()), + dbesc($profile_guid), + intval(local_channel()) + ); + $r = q( + "DELETE FROM profile WHERE id = %d AND uid = %d", + intval(argv(2)), + intval(local_channel()) + ); + if ($r) { + info(t('Profile deleted.') . EOL); + } - $orig_vcard = Reader::read($profile_vcard); + // @fixme this is a much more complicated sync - add any changed abook entries and + // also add deleted flag to profile structure + // profiles_build_sync is just here as a placeholder - it doesn't work at all here - $profile_vcard = update_vcard($_REQUEST,$orig_vcard); + // profiles_build_sync(local_channel()); + + goaway(z_root() . '/profiles'); + return; // NOTREACHED + } - require_once('include/text.php'); - linkify_tags($likes, local_channel()); - linkify_tags($dislikes, local_channel()); - linkify_tags($about, local_channel()); - linkify_tags($interest, local_channel()); - linkify_tags($interest, local_channel()); - linkify_tags($contact, local_channel()); - linkify_tags($channels, local_channel()); - linkify_tags($music, local_channel()); - linkify_tags($book, local_channel()); - linkify_tags($tv, local_channel()); - linkify_tags($film, local_channel()); - linkify_tags($romance, local_channel()); - linkify_tags($work, local_channel()); - linkify_tags($education, local_channel()); - - - $with = ((x($_POST,'with')) ? escape_tags(trim($_POST['with'])) : ''); - - if(! strlen($howlong)) - $howlong = NULL_DATE; - else - $howlong = datetime_convert(date_default_timezone_get(),'UTC',$howlong); - - // linkify the relationship target if applicable - - $withchanged = false; - - if(strlen($with)) { - if($with != strip_tags($orig[0]['partner'])) { - $withchanged = true; - $prf = ''; - $lookup = $with; - if(strpos($lookup,'@') === 0) - $lookup = substr($lookup,1); - $lookup = str_replace('_',' ', $lookup); - $newname = $lookup; - - $r = q("SELECT * FROM abook left join xchan on abook_xchan = xchan_hash WHERE xchan_name = '%s' AND abook_channel = %d LIMIT 1", - dbesc($newname), - intval(local_channel()) - ); - if(! $r) { - $r = q("SELECT * FROM abook left join xchan on abook_xchan = xchan_hash WHERE xchan_addr = '%s' AND abook_channel = %d LIMIT 1", - dbesc($lookup . '@%'), - intval(local_channel()) - ); - } - if($r) { - $prf = $r[0]['xchan_url']; - $newname = $r[0]['xchan_name']; - } - - - if($prf) { - $with = str_replace($lookup,'' . $newname . '', $with); - if(strpos($with,'@') === 0) - $with = substr($with,1); - } - } - else - $with = $orig[0]['partner']; - } - - $profile_fields_basic = get_profile_fields_basic(); - $profile_fields_advanced = get_profile_fields_advanced(); - $advanced = ((feature_enabled(local_channel(),'advanced_profiles')) ? true : false); - if($advanced) - $fields = $profile_fields_advanced; - else - $fields = $profile_fields_basic; - - $z = q("select * from profdef where true"); - if($z) { - foreach($z as $zz) { - if(array_key_exists($zz['field_name'],$fields)) { - $w = q("select * from profext where channel_id = %d and hash = '%s' and k = '%s' limit 1", - intval(local_channel()), - dbesc($orig[0]['profile_guid']), - dbesc($zz['field_name']) - ); - if($w) { - q("update profext set v = '%s' where id = %d", - dbesc(escape_tags(trim($_POST[$zz['field_name']]))), - intval($w[0]['id']) - ); - } - else { - q("insert into profext ( channel_id, hash, k, v ) values ( %d, '%s', '%s', '%s') ", - intval(local_channel()), - dbesc($orig[0]['profile_guid']), - dbesc($zz['field_name']), - dbesc(escape_tags(trim($_POST[$zz['field_name']]))) - ); - } - } - } - } - - $changes = []; - $value = ''; - if($is_default) { - if($marital != $orig[0]['marital']) { - $changes[] = '[color=#ff0000]♥[/color] ' . t('Marital Status'); - $value = $marital; - } - if($withchanged) { - $changes[] = '[color=#ff0000]♥[/color] ' . t('Romantic Partner'); - $value = strip_tags($with); - } - if($likes != $orig[0]['likes']) { - $changes[] = t('Likes'); - $value = $likes; - } - if($dislikes != $orig[0]['dislikes']) { - $changes[] = t('Dislikes'); - $value = $dislikes; - } - if($work != $orig[0]['employment']) { - $changes[] = t('Work/Employment'); - } - if($religion != $orig[0]['religion']) { - $changes[] = t('Religion'); - $value = $religion; - } - if($politic != $orig[0]['politic']) { - $changes[] = t('Political Views'); - $value = $politic; - } - if($gender != $orig[0]['gender']) { - $changes[] = t('Gender'); - $value = $gender; - } - if($sexual != $orig[0]['sexual']) { - $changes[] = t('Sexual Preference'); - $value = $sexual; - } - if($homepage != $orig[0]['homepage']) { - $changes[] = t('Homepage'); - $value = $homepage; - } - if($interest != $orig[0]['interest']) { - $changes[] = t('Interests'); - $value = $interest; - } - if($address != $orig[0]['address']) { - $changes[] = t('Address'); - // New address not sent in notifications, potential privacy issues - // in case this leaks to unintended recipients. Yes, it's in the public - // profile but that doesn't mean we have to broadcast it to everybody. - } - if($locality != $orig[0]['locality'] || $region != $orig[0]['region'] - || $country_name != $orig[0]['country_name']) { - $changes[] = t('Location'); - $comma1 = ((($locality) && ($region || $country_name)) ? ', ' : ' '); - $comma2 = (($region && $country_name) ? ', ' : ''); - $value = $locality . $comma1 . $region . $comma2 . $country_name; - } - - self::profile_activity($changes,$value); - - } - - $r = q("UPDATE profile + if ((argc() > 1) && (argv(1) === 'new')) { + // check_form_security_token_redirectOnErr('/profiles', 'profile_new', 't'); + + $r0 = q( + "SELECT id FROM profile WHERE uid = %d", + intval(local_channel()) + ); + $num_profiles = count($r0); + + $name = t('Profile-') . ($num_profiles + 1); + + $r1 = q( + "SELECT fullname, photo, thumb FROM profile WHERE uid = %d AND is_default = 1 LIMIT 1", + intval(local_channel()) + ); + + $r2 = profile_store_lowlevel( + [ + 'aid' => intval(get_account_id()), + 'uid' => intval(local_channel()), + 'profile_guid' => new_uuid(), + 'profile_name' => $name, + 'fullname' => $r1[0]['fullname'], + 'photo' => $r1[0]['photo'], + 'thumb' => $r1[0]['thumb'] + ] + ); + + $r3 = q( + "SELECT id FROM profile WHERE uid = %d AND profile_name = '%s' LIMIT 1", + intval(local_channel()), + dbesc($name) + ); + + info(t('New profile created.') . EOL); + if (count($r3) == 1) { + goaway(z_root() . '/profiles/' . $r3[0]['id']); + } + + goaway(z_root() . '/profiles'); + } + + if ((argc() > 2) && (argv(1) === 'clone')) { + check_form_security_token_redirectOnErr('/profiles', 'profile_clone', 't'); + + $r0 = q( + "SELECT id FROM profile WHERE uid = %d", + intval(local_channel()) + ); + $num_profiles = count($r0); + + $name = t('Profile-') . ($num_profiles + 1); + $r1 = q( + "SELECT * FROM profile WHERE uid = %d AND id = %d LIMIT 1", + intval(local_channel()), + intval(argv(2)) + ); + if (!count($r1)) { + notice(t('Profile unavailable to clone.') . EOL); + App::$error = 404; + return; + } + unset($r1[0]['id']); + $r1[0]['is_default'] = 0; + $r1[0]['publish'] = 0; + $r1[0]['profile_name'] = $name; + $r1[0]['profile_guid'] = new_uuid(); + + create_table_from_array('profile', $r1[0]); + + $r3 = q( + "SELECT id FROM profile WHERE uid = %d AND profile_name = '%s' LIMIT 1", + intval(local_channel()), + dbesc($name) + ); + info(t('New profile created.') . EOL); + + profiles_build_sync(local_channel()); + + if (($r3) && (count($r3) == 1)) { + goaway(z_root() . '/profiles/' . $r3[0]['id']); + } + + goaway(z_root() . '/profiles'); + + return; // NOTREACHED + } + + if ((argc() > 2) && (argv(1) === 'export')) { + $r1 = q( + "SELECT * FROM profile WHERE uid = %d AND id = %d LIMIT 1", + intval(local_channel()), + intval(argv(2)) + ); + if (!$r1) { + notice(t('Profile unavailable to export.') . EOL); + App::$error = 404; + return; + } + header('content-type: application/octet_stream'); + header('Content-Disposition: attachment; filename="' . $r1[0]['profile_name'] . '.json"'); + + unset($r1[0]['id']); + unset($r1[0]['aid']); + unset($r1[0]['uid']); + unset($r1[0]['is_default']); + unset($r1[0]['publish']); + unset($r1[0]['profile_name']); + unset($r1[0]['profile_guid']); + echo json_encode($r1[0]); + killme(); + } + + if (((argc() > 1) && (intval(argv(1)))) || !feature_enabled(local_channel(), 'multi_profiles')) { + if (feature_enabled(local_channel(), 'multi_profiles')) { + $id = argv(1); + } else { + $x = q( + "select id from profile where uid = %d and is_default = 1", + intval(local_channel()) + ); + if ($x) { + $id = $x[0]['id']; + } + } + $r = q( + "SELECT * FROM profile WHERE id = %d AND uid = %d LIMIT 1", + intval($id), + intval(local_channel()) + ); + if (!count($r)) { + notice(t('Profile not found.') . EOL); + App::$error = 404; + return; + } + + $chan = App::get_channel(); + + Libprofile::load($chan['channel_address'], $r[0]['profile_guid']); + } + } + + public function post() + { + + if (!local_channel()) { + notice(t('Permission denied.') . EOL); + return; + } + + $namechanged = false; + + // import from json export file. + // Only import fields that are allowed on this hub + + if (x($_FILES, 'userfile')) { + $src = $_FILES['userfile']['tmp_name']; + $filesize = intval($_FILES['userfile']['size']); + if ($filesize) { + $j = @json_decode(@file_get_contents($src), true); + @unlink($src); + if ($j) { + $fields = get_profile_fields_advanced(); + if ($fields) { + foreach ($j as $jj => $v) { + foreach ($fields as $f => $n) { + if ($jj == $f) { + $_POST[$f] = $v; + break; + } + } + } + } + } + } + } + + call_hooks('profile_post', $_POST); + + + if ((argc() > 1) && (argv(1) !== "new") && intval(argv(1))) { + $orig = q( + "SELECT * FROM profile WHERE id = %d AND uid = %d LIMIT 1", + intval(argv(1)), + intval(local_channel()) + ); + if (!count($orig)) { + notice(t('Profile not found.') . EOL); + return; + } + + check_form_security_token_redirectOnErr('/profiles', 'profile_edit'); + + + $is_default = (($orig[0]['is_default']) ? 1 : 0); + + $profile_name = notags(trim($_POST['profile_name'])); + if (!strlen($profile_name)) { + notice(t('Profile Name is required.') . EOL); + return; + } + + $dob = $_POST['dob'] ? escape_tags(trim($_POST['dob'])) : '0000-00-00'; + + $y = substr($dob, 0, 4); + if ((!ctype_digit($y)) || ($y < 1900)) { + $ignore_year = true; + } else { + $ignore_year = false; + } + + if ($dob !== '0000-00-00') { + if (strpos($dob, '0000-') === 0) { + $ignore_year = true; + $dob = substr($dob, 5); + } + $dob = datetime_convert('UTC', 'UTC', (($ignore_year) ? '1900-' . $dob : $dob), (($ignore_year) ? 'm-d' : 'Y-m-d')); + if ($ignore_year) { + $dob = '0000-' . $dob; + } + } + + $name = escape_tags(trim($_POST['name'])); + + if ($orig[0]['fullname'] != $name) { + $namechanged = true; + + $v = validate_channelname($name); + if ($v) { + notice($v); + $namechanged = false; + $name = $orig[0]['fullname']; + } + } + + $pdesc = escape_tags(trim($_POST['pdesc'])); + $gender = escape_tags(trim($_POST['gender'])); + $address = escape_tags(trim($_POST['address'])); + $locality = escape_tags(trim($_POST['locality'])); + $region = escape_tags(trim($_POST['region'])); + $postal_code = escape_tags(trim($_POST['postal_code'])); + $country_name = escape_tags(trim($_POST['country_name'])); + $keywords = escape_tags(trim($_POST['keywords'])); + $marital = escape_tags(trim($_POST['marital'])); + $howlong = escape_tags(trim($_POST['howlong'])); + $sexual = escape_tags(trim($_POST['sexual'])); + $pronouns = escape_tags(trim($_POST['pronouns'])); + $homepage = escape_tags(trim($_POST['homepage'])); + $hometown = escape_tags(trim($_POST['hometown'])); + $politic = escape_tags(trim($_POST['politic'])); + $religion = escape_tags(trim($_POST['religion'])); + + $likes = escape_tags(trim($_POST['likes'])); + $dislikes = escape_tags(trim($_POST['dislikes'])); + + $about = escape_tags(trim($_POST['about'])); + $interest = escape_tags(trim($_POST['interest'])); + $contact = escape_tags(trim($_POST['contact'])); + $channels = escape_tags(trim($_POST['channels'])); + $music = escape_tags(trim($_POST['music'])); + $book = escape_tags(trim($_POST['book'])); + $tv = escape_tags(trim($_POST['tv'])); + $film = escape_tags(trim($_POST['film'])); + $romance = escape_tags(trim($_POST['romance'])); + $work = escape_tags(trim($_POST['work'])); + $education = escape_tags(trim($_POST['education'])); + + $hide_friends = ((intval($_POST['hide_friends'])) ? 1 : 0); + + // start fresh and create a new vcard. + // @TODO: preserve the original guid or whatever else needs saving + // $orig_vcard = (($orig[0]['profile_vcard']) ? Reader::read($orig[0]['profile_vcard']) : null); + + $orig_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], + 'url' => [$homepage], + '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); + + $orig_vcard = Reader::read($profile_vcard); + + $profile_vcard = update_vcard($_REQUEST, $orig_vcard); + + + require_once('include/text.php'); + linkify_tags($likes, local_channel()); + linkify_tags($dislikes, local_channel()); + linkify_tags($about, local_channel()); + linkify_tags($interest, local_channel()); + linkify_tags($interest, local_channel()); + linkify_tags($contact, local_channel()); + linkify_tags($channels, local_channel()); + linkify_tags($music, local_channel()); + linkify_tags($book, local_channel()); + linkify_tags($tv, local_channel()); + linkify_tags($film, local_channel()); + linkify_tags($romance, local_channel()); + linkify_tags($work, local_channel()); + linkify_tags($education, local_channel()); + + + $with = ((x($_POST, 'with')) ? escape_tags(trim($_POST['with'])) : ''); + + if (!strlen($howlong)) { + $howlong = NULL_DATE; + } else { + $howlong = datetime_convert(date_default_timezone_get(), 'UTC', $howlong); + } + + // linkify the relationship target if applicable + + $withchanged = false; + + if (strlen($with)) { + if ($with != strip_tags($orig[0]['partner'])) { + $withchanged = true; + $prf = ''; + $lookup = $with; + if (strpos($lookup, '@') === 0) { + $lookup = substr($lookup, 1); + } + $lookup = str_replace('_', ' ', $lookup); + $newname = $lookup; + + $r = q( + "SELECT * FROM abook left join xchan on abook_xchan = xchan_hash WHERE xchan_name = '%s' AND abook_channel = %d LIMIT 1", + dbesc($newname), + intval(local_channel()) + ); + if (!$r) { + $r = q( + "SELECT * FROM abook left join xchan on abook_xchan = xchan_hash WHERE xchan_addr = '%s' AND abook_channel = %d LIMIT 1", + dbesc($lookup . '@%'), + intval(local_channel()) + ); + } + if ($r) { + $prf = $r[0]['xchan_url']; + $newname = $r[0]['xchan_name']; + } + + + if ($prf) { + $with = str_replace($lookup, '' . $newname . '', $with); + if (strpos($with, '@') === 0) { + $with = substr($with, 1); + } + } + } else { + $with = $orig[0]['partner']; + } + } + + $profile_fields_basic = get_profile_fields_basic(); + $profile_fields_advanced = get_profile_fields_advanced(); + $advanced = ((feature_enabled(local_channel(), 'advanced_profiles')) ? true : false); + if ($advanced) { + $fields = $profile_fields_advanced; + } else { + $fields = $profile_fields_basic; + } + + $z = q("select * from profdef where true"); + if ($z) { + foreach ($z as $zz) { + if (array_key_exists($zz['field_name'], $fields)) { + $w = q( + "select * from profext where channel_id = %d and hash = '%s' and k = '%s' limit 1", + intval(local_channel()), + dbesc($orig[0]['profile_guid']), + dbesc($zz['field_name']) + ); + if ($w) { + q( + "update profext set v = '%s' where id = %d", + dbesc(escape_tags(trim($_POST[$zz['field_name']]))), + intval($w[0]['id']) + ); + } else { + q( + "insert into profext ( channel_id, hash, k, v ) values ( %d, '%s', '%s', '%s') ", + intval(local_channel()), + dbesc($orig[0]['profile_guid']), + dbesc($zz['field_name']), + dbesc(escape_tags(trim($_POST[$zz['field_name']]))) + ); + } + } + } + } + + $changes = []; + $value = ''; + if ($is_default) { + if ($marital != $orig[0]['marital']) { + $changes[] = '[color=#ff0000]♥[/color] ' . t('Marital Status'); + $value = $marital; + } + if ($withchanged) { + $changes[] = '[color=#ff0000]♥[/color] ' . t('Romantic Partner'); + $value = strip_tags($with); + } + if ($likes != $orig[0]['likes']) { + $changes[] = t('Likes'); + $value = $likes; + } + if ($dislikes != $orig[0]['dislikes']) { + $changes[] = t('Dislikes'); + $value = $dislikes; + } + if ($work != $orig[0]['employment']) { + $changes[] = t('Work/Employment'); + } + if ($religion != $orig[0]['religion']) { + $changes[] = t('Religion'); + $value = $religion; + } + if ($politic != $orig[0]['politic']) { + $changes[] = t('Political Views'); + $value = $politic; + } + if ($gender != $orig[0]['gender']) { + $changes[] = t('Gender'); + $value = $gender; + } + if ($sexual != $orig[0]['sexual']) { + $changes[] = t('Sexual Preference'); + $value = $sexual; + } + if ($homepage != $orig[0]['homepage']) { + $changes[] = t('Homepage'); + $value = $homepage; + } + if ($interest != $orig[0]['interest']) { + $changes[] = t('Interests'); + $value = $interest; + } + if ($address != $orig[0]['address']) { + $changes[] = t('Address'); + // New address not sent in notifications, potential privacy issues + // in case this leaks to unintended recipients. Yes, it's in the public + // profile but that doesn't mean we have to broadcast it to everybody. + } + if ( + $locality != $orig[0]['locality'] || $region != $orig[0]['region'] + || $country_name != $orig[0]['country_name'] + ) { + $changes[] = t('Location'); + $comma1 = ((($locality) && ($region || $country_name)) ? ', ' : ' '); + $comma2 = (($region && $country_name) ? ', ' : ''); + $value = $locality . $comma1 . $region . $comma2 . $country_name; + } + + self::profile_activity($changes, $value); + } + + $r = q( + "UPDATE profile SET profile_name = '%s', fullname = '%s', pdesc = '%s', @@ -550,532 +580,545 @@ class Profiles extends Controller { hide_friends = %d, profile_vcard = '%s' WHERE id = %d AND uid = %d", - dbesc($profile_name), - dbesc($name), - dbesc($pdesc), - dbesc($gender), - dbesc($dob), - dbesc($address), - dbesc($locality), - dbesc($region), - dbesc($postal_code), - dbesc($country_name), - dbesc($marital), - dbesc($with), - dbesc($howlong), - dbesc($sexual), - dbesc($pronouns), - dbesc($homepage), - dbesc($hometown), - dbesc($politic), - dbesc($religion), - dbesc($keywords), - dbesc($likes), - dbesc($dislikes), - dbesc($about), - dbesc($interest), - dbesc($contact), - dbesc($channels), - dbesc($music), - dbesc($book), - dbesc($tv), - dbesc($film), - dbesc($romance), - dbesc($work), - dbesc($education), - intval($hide_friends), - dbesc($profile_vcard), - intval(argv(1)), - intval(local_channel()) - ); - - if($r) - info( t('Profile updated.') . EOL); - - $sync = q("select * from profile where id = %d and uid = %d limit 1", - intval(argv(1)), - intval(local_channel()) - ); - if($sync) { - Libsync::build_sync_packet(local_channel(),array('profile' => $sync)); - } + dbesc($profile_name), + dbesc($name), + dbesc($pdesc), + dbesc($gender), + dbesc($dob), + dbesc($address), + dbesc($locality), + dbesc($region), + dbesc($postal_code), + dbesc($country_name), + dbesc($marital), + dbesc($with), + dbesc($howlong), + dbesc($sexual), + dbesc($pronouns), + dbesc($homepage), + dbesc($hometown), + dbesc($politic), + dbesc($religion), + dbesc($keywords), + dbesc($likes), + dbesc($dislikes), + dbesc($about), + dbesc($interest), + dbesc($contact), + dbesc($channels), + dbesc($music), + dbesc($book), + dbesc($tv), + dbesc($film), + dbesc($romance), + dbesc($work), + dbesc($education), + intval($hide_friends), + dbesc($profile_vcard), + intval(argv(1)), + intval(local_channel()) + ); - if (is_sys_channel(local_channel())) { - set_config('system','siteinfo', $about); - } + if ($r) { + info(t('Profile updated.') . EOL); + } - $channel = App::get_channel(); - - if($namechanged && $is_default) { - $r = q("UPDATE xchan SET xchan_name = '%s', xchan_name_date = '%s' WHERE xchan_hash = '%s'", - dbesc($name), - dbesc(datetime_convert()), - dbesc($channel['xchan_hash']) - ); - $r = q("UPDATE channel SET channel_name = '%s' WHERE channel_hash = '%s'", - dbesc($name), - dbesc($channel['xchan_hash']) - ); - if (is_sys_channel(local_channel())) { - set_config('system','sitename',$name); - } - } - - if($is_default) { - Run::Summon( [ 'Directory', local_channel() ] ); - goaway(z_root() . '/profiles/' . $sync[0]['id']); - } - } - } - - - function get() { - - $o = ''; - - $channel = App::get_channel(); - - if(! local_channel()) { - notice( t('Permission denied.') . EOL); - return; - } - - require_once('include/channel.php'); - - $profile_fields_basic = get_profile_fields_basic(); - $profile_fields_advanced = get_profile_fields_advanced(); - - if(((argc() > 1) && (intval(argv(1)))) || !feature_enabled(local_channel(),'multi_profiles')) { - if(feature_enabled(local_channel(),'multi_profiles')) - $id = argv(1); - else { - $x = q("select id from profile where uid = %d and is_default = 1", - intval(local_channel()) - ); - if($x) - $id = $x[0]['id']; - } - $r = q("SELECT * FROM profile WHERE id = %d AND uid = %d LIMIT 1", - intval($id), - intval(local_channel()) - ); - if(! $r) { - notice( t('Profile not found.') . EOL); - return; - } - - $editselect = 'none'; - - App::$page['htmlhead'] .= replace_macros(get_markup_template('profed_head.tpl'), array( - '$baseurl' => z_root(), - '$editselect' => $editselect, - )); - - $advanced = ((feature_enabled(local_channel(),'advanced_profiles')) ? true : false); - if($advanced) - $fields = $profile_fields_advanced; - else - $fields = $profile_fields_basic; - - $hide_friends = array( - 'hide_friends', - t('Hide your connections list from viewers of this profile'), - $r[0]['hide_friends'], - '', - array(t('No'),t('Yes')) - ); - - $q = q("select * from profdef where true"); - if($q) { - $extra_fields = []; - - foreach($q as $qq) { - $mine = q("select v from profext where k = '%s' and hash = '%s' and channel_id = %d limit 1", - dbesc($qq['field_name']), - dbesc($r[0]['profile_guid']), - intval(local_channel()) - ); - - if(array_key_exists($qq['field_name'],$fields)) { - $extra_fields[] = array($qq['field_name'],$qq['field_desc'],(($mine) ? $mine[0]['v'] : ''), $qq['field_help']); - } - } - } - - //logger('extra_fields: ' . print_r($extra_fields,true)); + $sync = q( + "select * from profile where id = %d and uid = %d limit 1", + intval(argv(1)), + intval(local_channel()) + ); + if ($sync) { + Libsync::build_sync_packet(local_channel(), array('profile' => $sync)); + } - $vc = $r[0]['profile_vcard']; - $vctmp = (($vc) ? Reader::read($vc) : null); - $vcard = (($vctmp) ? get_vcard_array($vctmp,$r[0]['id']) : [] ); - - $f = get_config('system','birthday_input_format'); - if(! $f) - $f = 'ymd'; - - $is_default = (($r[0]['is_default']) ? 1 : 0); - - $tpl = get_markup_template("profile_edit.tpl"); - $o .= replace_macros($tpl,array( - '$multi_profiles' => ((feature_enabled(local_channel(),'multi_profiles')) ? true : false), - '$form_security_token' => get_form_security_token("profile_edit"), - '$profile_clone_link' => 'profiles/clone/' . $r[0]['id'] . '?t=' . get_form_security_token("profile_clone"), - '$profile_drop_link' => 'profiles/drop/' . $r[0]['id'] . '?t=' . get_form_security_token("profile_drop"), - '$fields' => $fields, - '$vcard' => $vcard, - '$guid' => $r[0]['profile_guid'], - '$banner' => t('Edit Profile Details'), - '$submit' => t('Submit'), - '$viewprof' => t('View this profile'), - '$editvis' => t('Edit visibility'), - '$tools_label' => t('Profile Tools'), - '$coverpic' => t('Change cover photo'), - '$profpic' => t('Change profile photo'), - '$cr_prof' => t('Create a new profile using these settings'), - '$cl_prof' => t('Clone this profile'), - '$del_prof' => t('Delete this profile'), - '$addthing' => t('Add profile things'), - '$personal' => t('Personal'), - '$location' => t('Location'), - '$relation' => t('Relationship'), - '$miscellaneous'=> t('Miscellaneous'), - '$exportable' => feature_enabled(local_channel(),'profile_export'), - '$lbl_import' => t('Import profile from file'), - '$lbl_export' => t('Export profile to file'), - '$lbl_gender' => t('Your gender'), - '$lbl_marital' => t('Marital status'), - '$lbl_sexual' => t('Sexual preference'), - '$lbl_pronouns' => t('Pronouns'), - '$baseurl' => z_root(), - '$profile_id' => $r[0]['id'], - '$profile_name' => array('profile_name', t('Profile name'), $r[0]['profile_name'], t('Required'), '*'), - '$is_default' => $is_default, - '$default' => '', // t('This is your default profile.') . EOL . translate_scope(map_scope(\Zotlabs\Access\PermissionLimits::Get($channel['channel_id'],'view_profile'))), - '$advanced' => $advanced, - '$name' => array('name', t('Your full name'), $r[0]['fullname'], t('Required'), '*'), - '$pdesc' => array('pdesc', t('Title/Description'), $r[0]['pdesc']), - '$dob' => dob($r[0]['dob']), - '$hide_friends' => $hide_friends, - '$address' => array('address', t('Street address'), $r[0]['address']), - '$locality' => array('locality', t('Locality/City'), $r[0]['locality']), - '$region' => array('region', t('Region/State'), $r[0]['region']), - '$postal_code' => array('postal_code', t('Postal/Zip code'), $r[0]['postal_code']), - '$country_name' => array('country_name', t('Country'), $r[0]['country_name']), - '$gender' => self::gender_selector($r[0]['gender']), - '$gender_min' => self::gender_selector_min($r[0]['gender']), - '$gender_text' => self::gender_text($r[0]['gender']), - '$marital' => self::marital_selector($r[0]['marital']), - '$marital_min' => self::marital_selector_min($r[0]['marital']), - '$with' => array('with', t("Who (if applicable)"), $r[0]['partner'], t('Examples: cathy123, Cathy Williams, cathy@example.com')), - '$howlong' => array('howlong', t('Since (date)'), ($r[0]['howlong'] <= NULL_DATE ? '' : datetime_convert('UTC',date_default_timezone_get(),$r[0]['howlong']))), - '$sexual' => self::sexpref_selector($r[0]['sexual']), - '$sexual_min' => self::sexpref_selector_min($r[0]['sexual']), - '$pronouns' => self::pronouns_selector($r[0]['pronouns']), - '$pronouns_min' => self::pronouns_selector($r[0]['pronouns']), - '$about' => array('about', t('Tell us about yourself'), $r[0]['about']), - '$homepage' => array('homepage', t('Homepage URL'), $r[0]['homepage']), - '$hometown' => array('hometown', t('Hometown'), $r[0]['hometown']), - '$politic' => array('politic', t('Political views'), $r[0]['politic']), - '$religion' => array('religion', t('Religious views'), $r[0]['religion']), - '$keywords' => array('keywords', t('Keywords used in directory listings'), $r[0]['keywords'], t('Example: fishing photography software')), - '$likes' => array('likes', t('Likes'), $r[0]['likes']), - '$dislikes' => array('dislikes', t('Dislikes'), $r[0]['dislikes']), - '$music' => array('music', t('Musical interests'), $r[0]['music']), - '$book' => array('book', t('Books, literature'), $r[0]['book']), - '$tv' => array('tv', t('Television'), $r[0]['tv']), - '$film' => array('film', t('Film/Dance/Culture/Entertainment'), $r[0]['film']), - '$interest' => array('interest', t('Hobbies/Interests'), $r[0]['interest']), - '$romance' => array('romance',t('Love/Romance'), $r[0]['romance']), - '$employ' => array('work', t('Work/Employment'), $r[0]['employment']), - '$education' => array('education', t('School/Education'), $r[0]['education']), - '$contact' => array('contact', t('Contact information and social networks'), $r[0]['contact']), - '$channels' => array('channels', t('My other channels'), $r[0]['channels']), - '$extra_fields' => $extra_fields, - '$comms' => t('Communications'), - '$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'), - )); - - $arr = array('profile' => $r[0], 'entry' => $o); - call_hooks('profile_edit', $arr); - - return $o; - } - else { - - $r = q("SELECT * FROM profile WHERE uid = %d", - local_channel()); - if($r) { - - $tpl = get_markup_template('profile_entry.tpl'); - foreach($r as $rr) { - $profiles .= replace_macros($tpl, array( - '$photo' => $rr['thumb'], - '$id' => $rr['id'], - '$alt' => t('Profile Image'), - '$profile_name' => $rr['profile_name'], - '$visible' => (($rr['is_default']) - ? '' . translate_scope(map_scope(\Zotlabs\Access\PermissionLimits::Get($channel['channel_id'],'view_profile'))) . '' - : '' . t('Edit visibility') . '') - )); - } - - $tpl_header = get_markup_template('profile_listing_header.tpl'); - $o .= replace_macros($tpl_header,array( - '$header' => t('Edit Profiles'), - '$cr_new' => t('Create New'), - '$cr_new_link' => 'profiles/new?t=' . get_form_security_token("profile_new"), - '$profiles' => $profiles - )); - - } - return $o; - } - - } + if (is_sys_channel(local_channel())) { + set_config('system', 'siteinfo', $about); + } - static function profile_activity($changed, $value) { + $channel = App::get_channel(); - if(! local_channel() || ! is_array($changed) || ! count($changed)) - return; + if ($namechanged && $is_default) { + $r = q( + "UPDATE xchan SET xchan_name = '%s', xchan_name_date = '%s' WHERE xchan_hash = '%s'", + dbesc($name), + dbesc(datetime_convert()), + dbesc($channel['xchan_hash']) + ); + $r = q( + "UPDATE channel SET channel_name = '%s' WHERE channel_hash = '%s'", + dbesc($name), + dbesc($channel['xchan_hash']) + ); + if (is_sys_channel(local_channel())) { + set_config('system', 'sitename', $name); + } + } - if(! get_pconfig(local_channel(),'system','post_profilechange')) - return; - - $self = App::get_channel(); - - if(! $self) - return; - - $arr = []; - $uuid = new_uuid(); - $mid = z_root() . '/item/' . $uuid; - - $arr['uuid'] = $uuid; - $arr['mid'] = $arr['parent_mid'] = $mid; - $arr['uid'] = local_channel(); - $arr['aid'] = $self['channel_account_id']; - $arr['owner_xchan'] = $arr['author_xchan'] = $self['xchan_hash']; - - $arr['item_wall'] = 1; - $arr['item_origin'] = 1; - $arr['item_thread_top'] = 1; - $arr['verb'] = ACTIVITY_UPDATE; - $arr['obj_type'] = ACTIVITY_OBJ_PROFILE; - - $arr['plink'] = z_root() . '/channel/' . $self['channel_address'] . '/?f=&mid=' . urlencode($arr['mid']); - - $A = '[url=' . z_root() . '/channel/' . $self['channel_address'] . ']' . $self['channel_name'] . '[/url]'; + if ($is_default) { + Run::Summon(['Directory', local_channel()]); + goaway(z_root() . '/profiles/' . $sync[0]['id']); + } + } + } - $changes = ''; - $t = count($changed); - $z = 0; - foreach($changed as $ch) { - if(strlen($changes)) { - if ($z == ($t - 1)) - $changes .= t(' and '); - else - $changes .= t(', '); - } - $z ++; - $changes .= $ch; - } + public function get() + { - $prof = '[url=' . z_root() . '/profile/' . $self['channel_address'] . ']' . t('public profile') . '[/url]'; + $o = ''; - if($t == 1 && strlen($value)) { - // if it's a url, the HTML quotes will mess it up, so link it and don't try and zidify it because we don't know what it points to. - $value = preg_replace_callback("/([^\]\='".'"'."]|^|\#\^)(https?\:\/\/[a-zA-Z0-9\pL\:\/\-\?\&\;\.\=\@\_\~\#\%\$\!\+\,]+)/ismu", 'red_zrl_callback', $value); - // take out the bookmark indicator - if(substr($value,0,2) === '#^') - $value = str_replace('#^','',$value); + $channel = App::get_channel(); - $message = sprintf( t('%1$s changed %2$s to “%3$s”'), $A, $changes, $value); - $message .= "\n\n" . sprintf( t('Visit %1$s\'s %2$s'), $A, $prof); - } - else { - $message = sprintf( t('%1$s has an updated %2$s, changing %3$s.'), $A, $prof, $changes); - } + if (!local_channel()) { + notice(t('Permission denied.') . EOL); + return; + } - $arr['body'] = $message; + require_once('include/channel.php'); - $arr['obj'] = [ - 'type' => ACTIVITY_OBJ_PROFILE, - 'summary' => bbcode($message), - 'source' => [ 'mediaType' => 'text/x-multicode', 'summary' => $message ], - 'id' => $self['xchan_url'], - 'url' => z_root() . '/profile/' . $self['channel_address'] - ]; + $profile_fields_basic = get_profile_fields_basic(); + $profile_fields_advanced = get_profile_fields_advanced(); - - $arr['allow_cid'] = $self['channel_allow_cid']; - $arr['allow_gid'] = $self['channel_allow_gid']; - $arr['deny_cid'] = $self['channel_deny_cid']; - $arr['deny_gid'] = $self['channel_deny_gid']; + if (((argc() > 1) && (intval(argv(1)))) || !feature_enabled(local_channel(), 'multi_profiles')) { + if (feature_enabled(local_channel(), 'multi_profiles')) { + $id = argv(1); + } else { + $x = q( + "select id from profile where uid = %d and is_default = 1", + intval(local_channel()) + ); + if ($x) { + $id = $x[0]['id']; + } + } + $r = q( + "SELECT * FROM profile WHERE id = %d AND uid = %d LIMIT 1", + intval($id), + intval(local_channel()) + ); + if (!$r) { + notice(t('Profile not found.') . EOL); + return; + } - $res = item_store($arr); - $i = $res['item_id']; + $editselect = 'none'; - if($i) { - // FIXME - limit delivery in notifier.php to those specificed in the perms argument - Run::Summon( [ 'Notifier','activity', $i, 'PERMS_R_PROFILE' ] ); - } + App::$page['htmlhead'] .= replace_macros(get_markup_template('profed_head.tpl'), array( + '$baseurl' => z_root(), + '$editselect' => $editselect, + )); - } + $advanced = ((feature_enabled(local_channel(), 'advanced_profiles')) ? true : false); + if ($advanced) { + $fields = $profile_fields_advanced; + } else { + $fields = $profile_fields_basic; + } -static function gender_selector($current="",$suffix="") { - $o = ''; - $select = array('', t('Male'), t('Female'), t('Currently Male'), t('Currently Female'), t('Mostly Male'), t('Mostly Female'), t('Transgender'), t('Intersex'), t('Transsexual'), t('Hermaphrodite'), t('Neuter'), t('Non-specific'), t('Other'), t('Undecided')); + $hide_friends = array( + 'hide_friends', + t('Hide your connections list from viewers of this profile'), + $r[0]['hide_friends'], + '', + array(t('No'), t('Yes')) + ); - call_hooks('gender_selector', $select); + $q = q("select * from profdef where true"); + if ($q) { + $extra_fields = []; - $o .= "'; - return $o; -} + foreach ($q as $qq) { + $mine = q( + "select v from profext where k = '%s' and hash = '%s' and channel_id = %d limit 1", + dbesc($qq['field_name']), + dbesc($r[0]['profile_guid']), + intval(local_channel()) + ); -static function gender_selector_min($current="",$suffix="") { - $o = ''; - $select = array('', t('Male'), t('Female'), t('Other')); + if (array_key_exists($qq['field_name'], $fields)) { + $extra_fields[] = array($qq['field_name'], $qq['field_desc'], (($mine) ? $mine[0]['v'] : ''), $qq['field_help']); + } + } + } - call_hooks('gender_selector_min', $select); + //logger('extra_fields: ' . print_r($extra_fields,true)); - $o .= "'; - return $o; -} + $vc = $r[0]['profile_vcard']; + $vctmp = (($vc) ? Reader::read($vc) : null); + $vcard = (($vctmp) ? get_vcard_array($vctmp, $r[0]['id']) : []); -static function pronouns_selector($current="",$suffix="") { - $o = ''; - $select = array('', t('He/Him'), t('She/Her'), t('They/Them')); + $f = get_config('system', 'birthday_input_format'); + if (!$f) { + $f = 'ymd'; + } - call_hooks('pronouns_selector', $select); + $is_default = (($r[0]['is_default']) ? 1 : 0); - $o .= "'; - return $o; -} + $tpl = get_markup_template("profile_edit.tpl"); + $o .= replace_macros($tpl, array( + '$multi_profiles' => ((feature_enabled(local_channel(), 'multi_profiles')) ? true : false), + '$form_security_token' => get_form_security_token("profile_edit"), + '$profile_clone_link' => 'profiles/clone/' . $r[0]['id'] . '?t=' . get_form_security_token("profile_clone"), + '$profile_drop_link' => 'profiles/drop/' . $r[0]['id'] . '?t=' . get_form_security_token("profile_drop"), + '$fields' => $fields, + '$vcard' => $vcard, + '$guid' => $r[0]['profile_guid'], + '$banner' => t('Edit Profile Details'), + '$submit' => t('Submit'), + '$viewprof' => t('View this profile'), + '$editvis' => t('Edit visibility'), + '$tools_label' => t('Profile Tools'), + '$coverpic' => t('Change cover photo'), + '$profpic' => t('Change profile photo'), + '$cr_prof' => t('Create a new profile using these settings'), + '$cl_prof' => t('Clone this profile'), + '$del_prof' => t('Delete this profile'), + '$addthing' => t('Add profile things'), + '$personal' => t('Personal'), + '$location' => t('Location'), + '$relation' => t('Relationship'), + '$miscellaneous' => t('Miscellaneous'), + '$exportable' => feature_enabled(local_channel(), 'profile_export'), + '$lbl_import' => t('Import profile from file'), + '$lbl_export' => t('Export profile to file'), + '$lbl_gender' => t('Your gender'), + '$lbl_marital' => t('Marital status'), + '$lbl_sexual' => t('Sexual preference'), + '$lbl_pronouns' => t('Pronouns'), + '$baseurl' => z_root(), + '$profile_id' => $r[0]['id'], + '$profile_name' => array('profile_name', t('Profile name'), $r[0]['profile_name'], t('Required'), '*'), + '$is_default' => $is_default, + '$default' => '', // t('This is your default profile.') . EOL . translate_scope(map_scope(\Zotlabs\Access\PermissionLimits::Get($channel['channel_id'],'view_profile'))), + '$advanced' => $advanced, + '$name' => array('name', t('Your full name'), $r[0]['fullname'], t('Required'), '*'), + '$pdesc' => array('pdesc', t('Title/Description'), $r[0]['pdesc']), + '$dob' => dob($r[0]['dob']), + '$hide_friends' => $hide_friends, + '$address' => array('address', t('Street address'), $r[0]['address']), + '$locality' => array('locality', t('Locality/City'), $r[0]['locality']), + '$region' => array('region', t('Region/State'), $r[0]['region']), + '$postal_code' => array('postal_code', t('Postal/Zip code'), $r[0]['postal_code']), + '$country_name' => array('country_name', t('Country'), $r[0]['country_name']), + '$gender' => self::gender_selector($r[0]['gender']), + '$gender_min' => self::gender_selector_min($r[0]['gender']), + '$gender_text' => self::gender_text($r[0]['gender']), + '$marital' => self::marital_selector($r[0]['marital']), + '$marital_min' => self::marital_selector_min($r[0]['marital']), + '$with' => array('with', t("Who (if applicable)"), $r[0]['partner'], t('Examples: cathy123, Cathy Williams, cathy@example.com')), + '$howlong' => array('howlong', t('Since (date)'), ($r[0]['howlong'] <= NULL_DATE ? '' : datetime_convert('UTC', date_default_timezone_get(), $r[0]['howlong']))), + '$sexual' => self::sexpref_selector($r[0]['sexual']), + '$sexual_min' => self::sexpref_selector_min($r[0]['sexual']), + '$pronouns' => self::pronouns_selector($r[0]['pronouns']), + '$pronouns_min' => self::pronouns_selector($r[0]['pronouns']), + '$about' => array('about', t('Tell us about yourself'), $r[0]['about']), + '$homepage' => array('homepage', t('Homepage URL'), $r[0]['homepage']), + '$hometown' => array('hometown', t('Hometown'), $r[0]['hometown']), + '$politic' => array('politic', t('Political views'), $r[0]['politic']), + '$religion' => array('religion', t('Religious views'), $r[0]['religion']), + '$keywords' => array('keywords', t('Keywords used in directory listings'), $r[0]['keywords'], t('Example: fishing photography software')), + '$likes' => array('likes', t('Likes'), $r[0]['likes']), + '$dislikes' => array('dislikes', t('Dislikes'), $r[0]['dislikes']), + '$music' => array('music', t('Musical interests'), $r[0]['music']), + '$book' => array('book', t('Books, literature'), $r[0]['book']), + '$tv' => array('tv', t('Television'), $r[0]['tv']), + '$film' => array('film', t('Film/Dance/Culture/Entertainment'), $r[0]['film']), + '$interest' => array('interest', t('Hobbies/Interests'), $r[0]['interest']), + '$romance' => array('romance', t('Love/Romance'), $r[0]['romance']), + '$employ' => array('work', t('Work/Employment'), $r[0]['employment']), + '$education' => array('education', t('School/Education'), $r[0]['education']), + '$contact' => array('contact', t('Contact information and social networks'), $r[0]['contact']), + '$channels' => array('channels', t('My other channels'), $r[0]['channels']), + '$extra_fields' => $extra_fields, + '$comms' => t('Communications'), + '$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'), + )); + + $arr = array('profile' => $r[0], 'entry' => $o); + call_hooks('profile_edit', $arr); + + return $o; + } else { + $r = q( + "SELECT * FROM profile WHERE uid = %d", + local_channel() + ); + if ($r) { + $tpl = get_markup_template('profile_entry.tpl'); + foreach ($r as $rr) { + $profiles .= replace_macros($tpl, array( + '$photo' => $rr['thumb'], + '$id' => $rr['id'], + '$alt' => t('Profile Image'), + '$profile_name' => $rr['profile_name'], + '$visible' => (($rr['is_default']) + ? '' . translate_scope(map_scope(PermissionLimits::Get($channel['channel_id'], 'view_profile'))) . '' + : '' . t('Edit visibility') . '') + )); + } + + $tpl_header = get_markup_template('profile_listing_header.tpl'); + $o .= replace_macros($tpl_header, array( + '$header' => t('Edit Profiles'), + '$cr_new' => t('Create New'), + '$cr_new_link' => 'profiles/new?t=' . get_form_security_token("profile_new"), + '$profiles' => $profiles + )); + } + return $o; + } + } + + public static function profile_activity($changed, $value) + { + + if (!local_channel() || !is_array($changed) || !count($changed)) { + return; + } + + if (!get_pconfig(local_channel(), 'system', 'post_profilechange')) { + return; + } + + $self = App::get_channel(); + + if (!$self) { + return; + } + + $arr = []; + $uuid = new_uuid(); + $mid = z_root() . '/item/' . $uuid; + + $arr['uuid'] = $uuid; + $arr['mid'] = $arr['parent_mid'] = $mid; + $arr['uid'] = local_channel(); + $arr['aid'] = $self['channel_account_id']; + $arr['owner_xchan'] = $arr['author_xchan'] = $self['xchan_hash']; + + $arr['item_wall'] = 1; + $arr['item_origin'] = 1; + $arr['item_thread_top'] = 1; + $arr['verb'] = ACTIVITY_UPDATE; + $arr['obj_type'] = ACTIVITY_OBJ_PROFILE; + + $arr['plink'] = z_root() . '/channel/' . $self['channel_address'] . '/?f=&mid=' . urlencode($arr['mid']); + + $A = '[url=' . z_root() . '/channel/' . $self['channel_address'] . ']' . $self['channel_name'] . '[/url]'; -static function gender_text($current="",$suffix="") { - $o = ''; + $changes = ''; + $t = count($changed); + $z = 0; + foreach ($changed as $ch) { + if (strlen($changes)) { + if ($z == ($t - 1)) { + $changes .= t(' and '); + } else { + $changes .= t(', '); + } + } + $z++; + $changes .= $ch; + } - if(! get_config('system','profile_gender_textfield')) - return $o; + $prof = '[url=' . z_root() . '/profile/' . $self['channel_address'] . ']' . t('public profile') . '[/url]'; - $o .= ""; - return $o; -} + if ($t == 1 && strlen($value)) { + // if it's a url, the HTML quotes will mess it up, so link it and don't try and zidify it because we don't know what it points to. + $value = preg_replace_callback("/([^\]\='" . '"' . "]|^|\#\^)(https?\:\/\/[a-zA-Z0-9\pL\:\/\-\?\&\;\.\=\@\_\~\#\%\$\!\+\,]+)/ismu", 'red_zrl_callback', $value); + // take out the bookmark indicator + if (substr($value, 0, 2) === '#^') { + $value = str_replace('#^', '', $value); + } + + $message = sprintf(t('%1$s changed %2$s to “%3$s”'), $A, $changes, $value); + $message .= "\n\n" . sprintf(t('Visit %1$s\'s %2$s'), $A, $prof); + } else { + $message = sprintf(t('%1$s has an updated %2$s, changing %3$s.'), $A, $prof, $changes); + } + + $arr['body'] = $message; + + $arr['obj'] = [ + 'type' => ACTIVITY_OBJ_PROFILE, + 'summary' => bbcode($message), + 'source' => ['mediaType' => 'text/x-multicode', 'summary' => $message], + 'id' => $self['xchan_url'], + 'url' => z_root() . '/profile/' . $self['channel_address'] + ]; + $arr['allow_cid'] = $self['channel_allow_cid']; + $arr['allow_gid'] = $self['channel_allow_gid']; + $arr['deny_cid'] = $self['channel_deny_cid']; + $arr['deny_gid'] = $self['channel_deny_gid']; + + $res = item_store($arr); + $i = $res['item_id']; + + if ($i) { + // FIXME - limit delivery in notifier.php to those specificed in the perms argument + Run::Summon(['Notifier', 'activity', $i, 'PERMS_R_PROFILE']); + } + } + + public static function gender_selector($current = "", $suffix = "") + { + $o = ''; + $select = array('', t('Male'), t('Female'), t('Currently Male'), t('Currently Female'), t('Mostly Male'), t('Mostly Female'), t('Transgender'), t('Intersex'), t('Transsexual'), t('Hermaphrodite'), t('Neuter'), t('Non-specific'), t('Other'), t('Undecided')); + + call_hooks('gender_selector', $select); + + $o .= "'; + return $o; + } + + public static function gender_selector_min($current = "", $suffix = "") + { + $o = ''; + $select = array('', t('Male'), t('Female'), t('Other')); + + call_hooks('gender_selector_min', $select); + + $o .= "'; + return $o; + } + + public static function pronouns_selector($current = "", $suffix = "") + { + $o = ''; + $select = array('', t('He/Him'), t('She/Her'), t('They/Them')); + + call_hooks('pronouns_selector', $select); + + $o .= "'; + return $o; + } + public static function gender_text($current = "", $suffix = "") + { + $o = ''; -static function sexpref_selector($current="",$suffix="") { - $o = ''; - $select = array('', t('Males'), t('Females'), t('Gay'), t('Lesbian'), t('No Preference'), t('Bisexual'), t('Autosexual'), t('Abstinent'), t('Virgin'), t('Deviant'), t('Fetish'), t('Oodles'), t('Nonsexual')); + if (!get_config('system', 'profile_gender_textfield')) { + return $o; + } + + $o .= ""; + return $o; + } - call_hooks('sexpref_selector', $select); - - $o .= "'; - return $o; -} + public static function sexpref_selector($current = "", $suffix = "") + { + $o = ''; + $select = array('', t('Males'), t('Females'), t('Gay'), t('Lesbian'), t('No Preference'), t('Bisexual'), t('Autosexual'), t('Abstinent'), t('Virgin'), t('Deviant'), t('Fetish'), t('Oodles'), t('Nonsexual')); -static function sexpref_selector_min($current="",$suffix="") { - $o = ''; - $select = array('', t('Males'), t('Females'), t('Other')); + call_hooks('sexpref_selector', $select); - call_hooks('sexpref_selector_min', $select); - - $o .= "'; - return $o; -} + $o .= "'; + return $o; + } + public static function sexpref_selector_min($current = "", $suffix = "") + { + $o = ''; + $select = array('', t('Males'), t('Females'), t('Other')); -static function marital_selector($current="",$suffix="") { - $o = ''; - $select = array('', t('Single'), t('Lonely'), t('Available'), t('Unavailable'), t('Has crush'), t('Infatuated'), t('Dating'), t('Unfaithful'), t('Sex Addict'), t('Friends'), t('Friends/Benefits'), t('Casual'), t('Engaged'), t('Married'), t('Imaginarily married'), t('Partners'), t('Cohabiting'), t('Common law'), t('Happy'), t('Not looking'), t('Swinger'), t('Betrayed'), t('Separated'), t('Unstable'), t('Divorced'), t('Imaginarily divorced'), t('Widowed'), t('Uncertain'), t('It\'s complicated'), t('Don\'t care'), t('Ask me') ); + call_hooks('sexpref_selector_min', $select); - call_hooks('marital_selector', $select); - - $o .= "'; - return $o; -} - -static function marital_selector_min($current="",$suffix="") { - $o = ''; - $select = array('', t('Single'), t('Dating'), t('Cohabiting'), t('Married'), t('Separated'), t('Divorced'), t('Widowed'), t('It\'s complicated'), t('Other')); - - call_hooks('marital_selector_min', $select); - - $o .= "'; - return $o; -} + $o .= "'; + return $o; + } + public static function marital_selector($current = "", $suffix = "") + { + $o = ''; + $select = array('', t('Single'), t('Lonely'), t('Available'), t('Unavailable'), t('Has crush'), t('Infatuated'), t('Dating'), t('Unfaithful'), t('Sex Addict'), t('Friends'), t('Friends/Benefits'), t('Casual'), t('Engaged'), t('Married'), t('Imaginarily married'), t('Partners'), t('Cohabiting'), t('Common law'), t('Happy'), t('Not looking'), t('Swinger'), t('Betrayed'), t('Separated'), t('Unstable'), t('Divorced'), t('Imaginarily divorced'), t('Widowed'), t('Uncertain'), t('It\'s complicated'), t('Don\'t care'), t('Ask me')); - + call_hooks('marital_selector', $select); + + $o .= "'; + return $o; + } + + public static function marital_selector_min($current = "", $suffix = "") + { + $o = ''; + $select = array('', t('Single'), t('Dating'), t('Cohabiting'), t('Married'), t('Separated'), t('Divorced'), t('Widowed'), t('It\'s complicated'), t('Other')); + + call_hooks('marital_selector_min', $select); + + $o .= "'; + return $o; + } } diff --git a/Zotlabs/Module/Profperm.php b/Zotlabs/Module/Profperm.php index 51d186fc6..d4f1dc8d1 100644 --- a/Zotlabs/Module/Profperm.php +++ b/Zotlabs/Module/Profperm.php @@ -1,6 +1,6 @@ 2) && intval(argv(1)) && intval(argv(2))) { - $r = q("SELECT abook_id FROM abook WHERE abook_id = %d and abook_channel = %d limit 1", - intval(argv(2)), - intval(local_channel()) - ); - if($r) - $change = intval(argv(2)); - } - - - if((argc() > 1) && (intval(argv(1)))) { - $r = q("SELECT * FROM profile WHERE id = %d AND uid = %d AND is_default = 0 LIMIT 1", - intval(argv(1)), - intval(local_channel()) - ); - if(! $r) { - notice( t('Invalid profile identifier.') . EOL ); - return; - } - - $profile = $r[0]; - - $r = q("SELECT * FROM abook left join xchan on abook_xchan = xchan_hash WHERE abook_channel = %d AND abook_profile = '%s'", - intval(local_channel()), - dbesc($profile['profile_guid']) - ); - - $ingroup = []; - if($r) - foreach($r as $member) - $ingroup[] = $member['abook_id']; - - $members = $r; - - if($change) { - if(in_array($change,$ingroup)) { - q("UPDATE abook SET abook_profile = '' WHERE abook_id = %d AND abook_channel = %d", - intval($change), - intval(local_channel()) - ); - } - else { - q("UPDATE abook SET abook_profile = '%s' WHERE abook_id = %d AND abook_channel = %d", - dbesc($profile['profile_guid']), - intval($change), - intval(local_channel()) - ); - - } - - $r = q("SELECT * FROM abook left join xchan on abook_xchan = xchan_hash + public function init() + { + + if (!local_channel()) { + return; + } + + $channel = App::get_channel(); + $which = $channel['channel_address']; + + $profile = App::$argv[1]; + + Libprofile::load($which, $profile); + } + + + public function get() + { + + if (!local_channel()) { + notice(t('Permission denied') . EOL); + return; + } + + + if (argc() < 2) { + notice(t('Invalid profile identifier.') . EOL); + return; + } + + // Switch to text mod interface if we have more than 'n' contacts or group members + + $switchtotext = get_pconfig(local_channel(), 'system', 'groupedit_image_limit'); + if ($switchtotext === false) { + $switchtotext = get_config('system', 'groupedit_image_limit'); + } + if ($switchtotext === false) { + $switchtotext = 400; + } + + + if ((argc() > 2) && intval(argv(1)) && intval(argv(2))) { + $r = q( + "SELECT abook_id FROM abook WHERE abook_id = %d and abook_channel = %d limit 1", + intval(argv(2)), + intval(local_channel()) + ); + if ($r) { + $change = intval(argv(2)); + } + } + + + if ((argc() > 1) && (intval(argv(1)))) { + $r = q( + "SELECT * FROM profile WHERE id = %d AND uid = %d AND is_default = 0 LIMIT 1", + intval(argv(1)), + intval(local_channel()) + ); + if (!$r) { + notice(t('Invalid profile identifier.') . EOL); + return; + } + + $profile = $r[0]; + + $r = q( + "SELECT * FROM abook left join xchan on abook_xchan = xchan_hash WHERE abook_channel = %d AND abook_profile = '%s'", + intval(local_channel()), + dbesc($profile['profile_guid']) + ); + + $ingroup = []; + if ($r) { + foreach ($r as $member) { + $ingroup[] = $member['abook_id']; + } + } + + $members = $r; + + if ($change) { + if (in_array($change, $ingroup)) { + q( + "UPDATE abook SET abook_profile = '' WHERE abook_id = %d AND abook_channel = %d", + intval($change), + intval(local_channel()) + ); + } else { + q( + "UPDATE abook SET abook_profile = '%s' WHERE abook_id = %d AND abook_channel = %d", + dbesc($profile['profile_guid']), + intval($change), + intval(local_channel()) + ); + } + + $r = q( + "SELECT * FROM abook left join xchan on abook_xchan = xchan_hash WHERE abook_channel = %d AND abook_profile = '%s'", - intval(local_channel()), - dbesc($profile['profile_guid']) - ); - - $members = $r; - - $ingroup = []; - if(count($r)) - foreach($r as $member) - $ingroup[] = $member['abook_id']; - } - - $o .= '

      ' . t('Profile Visibility Editor') . '

      '; - - $o .= '

      ' . t('Profile') . ' \'' . $profile['profile_name'] . '\'

      '; - - $o .= '
      ' . t('Click on a contact to add or remove.') . '
      '; - - } - - $o .= '
      '; - if($change) - $o = ''; - - $o .= '
      '; - $o .= '

      ' . t('Visible To') . '

      '; - $o .= '
      '; - $o .= '
      '; - - $textmode = (($switchtotext && (count($members) > $switchtotext)) ? true : false); - - foreach($members as $member) { - if($member['xchan_url']) { - $member['click'] = 'profChangeMember(' . $profile['id'] . ',' . $member['abook_id'] . '); return false;'; - $o .= micropro($member,true,'mpprof', $textmode); - } - } - $o .= '
      '; - $o .= '
      '; - - $o .= '
      '; - $o .= '

      ' . t("All Connections") . '

      '; - $o .= '
      '; - $o .= '
      '; - - $r = abook_connections(local_channel()); - - if($r) { - $textmode = (($switchtotext && (count($r) > $switchtotext)) ? true : false); - foreach($r as $member) { - if(! in_array($member['abook_id'],$ingroup)) { - $member['click'] = 'profChangeMember(' . $profile['id'] . ',' . $member['abook_id'] . '); return false;'; - $o .= micropro($member,true,'mpprof',$textmode); - } - } - } - - $o .= '
      '; - - if($change) { - echo $o; - killme(); - } - $o .= '
      '; - return $o; - - } - - + intval(local_channel()), + dbesc($profile['profile_guid']) + ); + + $members = $r; + + $ingroup = []; + if (count($r)) { + foreach ($r as $member) { + $ingroup[] = $member['abook_id']; + } + } + } + + $o .= '

      ' . t('Profile Visibility Editor') . '

      '; + + $o .= '

      ' . t('Profile') . ' \'' . $profile['profile_name'] . '\'

      '; + + $o .= '
      ' . t('Click on a contact to add or remove.') . '
      '; + } + + $o .= '
      '; + if ($change) { + $o = ''; + } + + $o .= '
      '; + $o .= '

      ' . t('Visible To') . '

      '; + $o .= '
      '; + $o .= '
      '; + + $textmode = (($switchtotext && (count($members) > $switchtotext)) ? true : false); + + foreach ($members as $member) { + if ($member['xchan_url']) { + $member['click'] = 'profChangeMember(' . $profile['id'] . ',' . $member['abook_id'] . '); return false;'; + $o .= micropro($member, true, 'mpprof', $textmode); + } + } + $o .= '
      '; + $o .= '
      '; + + $o .= '
      '; + $o .= '

      ' . t("All Connections") . '

      '; + $o .= '
      '; + $o .= '
      '; + + $r = abook_connections(local_channel()); + + if ($r) { + $textmode = (($switchtotext && (count($r) > $switchtotext)) ? true : false); + foreach ($r as $member) { + if (!in_array($member['abook_id'], $ingroup)) { + $member['click'] = 'profChangeMember(' . $profile['id'] . ',' . $member['abook_id'] . '); return false;'; + $o .= micropro($member, true, 'mpprof', $textmode); + } + } + } + + $o .= '
      '; + + if ($change) { + echo $o; + killme(); + } + $o .= '
      '; + return $o; + } } diff --git a/Zotlabs/Module/Pubstream.php b/Zotlabs/Module/Pubstream.php index 6796db081..1cf8ba164 100644 --- a/Zotlabs/Module/Pubstream.php +++ b/Zotlabs/Module/Pubstream.php @@ -1,4 +1,5 @@ updating)) { - - $channel = App::get_channel(); + if (local_channel() && (!$this->updating)) { + $channel = App::get_channel(); - $channel_acl = array( - 'allow_cid' => $channel['channel_allow_cid'], - 'allow_gid' => $channel['channel_allow_gid'], - 'deny_cid' => $channel['channel_deny_cid'], - 'deny_gid' => $channel['channel_deny_gid'] - ); + $channel_acl = array( + 'allow_cid' => $channel['channel_allow_cid'], + 'allow_gid' => $channel['channel_allow_gid'], + 'deny_cid' => $channel['channel_deny_cid'], + 'deny_gid' => $channel['channel_deny_gid'] + ); - $x = array( - 'is_owner' => true, - 'allow_location' => ((intval(get_pconfig($channel['channel_id'],'system','use_browser_location'))) ? '1' : ''), - 'default_location' => $channel['channel_location'], - 'nickname' => $channel['channel_address'], - 'lockstate' => (($channel['channel_allow_cid'] || $channel['channel_allow_gid'] || $channel['channel_deny_cid'] || $channel['channel_deny_gid']) ? 'lock' : 'unlock'), - 'acl' => populate_acl($channel_acl,true,PermissionDescription::fromGlobalPermission('view_stream'), get_post_aclDialogDescription(), 'acl_dialog_post'), - 'permissions' => $channel_acl, - 'bang' => '', - 'visitor' => true, - 'profile_uid' => local_channel(), - 'return_path' => 'channel/' . $channel['channel_address'], - 'expanded' => true, - 'editor_autocomplete' => true, - 'bbco_autocomplete' => 'bbcode', - 'bbcode' => true, - 'jotnets' => true, - 'reset' => t('Reset form') - ); - - $o = '
      '; - $o .= status_editor($x); - $o .= '
      '; - } - - if(! $this->updating && !$this->loading) { + $x = array( + 'is_owner' => true, + 'allow_location' => ((intval(get_pconfig($channel['channel_id'], 'system', 'use_browser_location'))) ? '1' : ''), + 'default_location' => $channel['channel_location'], + 'nickname' => $channel['channel_address'], + 'lockstate' => (($channel['channel_allow_cid'] || $channel['channel_allow_gid'] || $channel['channel_deny_cid'] || $channel['channel_deny_gid']) ? 'lock' : 'unlock'), + 'acl' => populate_acl($channel_acl, true, PermissionDescription::fromGlobalPermission('view_stream'), get_post_aclDialogDescription(), 'acl_dialog_post'), + 'permissions' => $channel_acl, + 'bang' => '', + 'visitor' => true, + 'profile_uid' => local_channel(), + 'return_path' => 'channel/' . $channel['channel_address'], + 'expanded' => true, + 'editor_autocomplete' => true, + 'bbco_autocomplete' => 'bbcode', + 'bbcode' => true, + 'jotnets' => true, + 'reset' => t('Reset form') + ); - nav_set_selected(t('Public Stream')); + $o = '
      '; + $o .= status_editor($x); + $o .= '
      '; + } - if (! $mid) { - $_SESSION['loadtime_pubstream'] = datetime_convert(); - if (local_channel()) { - PConfig::Set(local_channel(),'system','loadtime_pubstream',$_SESSION['loadtime_pubstream']); - } - } - - $static = ((local_channel()) ? channel_manual_conv_update(local_channel()) : 1); - - $maxheight = get_config('system','home_divmore_height'); - if(! $maxheight) - $maxheight = 400; - - $o .= '
      ' . "\r\n"; - $o .= "\r\n"; - - // if we got a decoded hash we must encode it again before handing to javascript - $mid = gen_link_id($mid); + if (!$this->updating && !$this->loading) { + nav_set_selected(t('Public Stream')); - App::$page['htmlhead'] .= replace_macros(get_markup_template("build_query.tpl"),array( - '$baseurl' => z_root(), - '$pgtype' => 'pubstream', - '$uid' => ((local_channel()) ? local_channel() : '0'), - '$gid' => '0', - '$cid' => '0', - '$cmin' => '(-1)', - '$cmax' => '(-1)', - '$star' => '0', - '$liked' => '0', - '$conv' => '0', - '$spam' => '0', - '$fh' => '1', - '$dm' => '0', - '$nouveau' => '0', - '$wall' => '0', - '$draft' => '0', - '$list' => '0', - '$static' => $static, - '$page' => ((App::$pager['page'] != 1) ? App::$pager['page'] : 1), - '$search' => '', - '$xchan' => '', - '$order' => 'comment', - '$file' => '', - '$cats' => '', - '$tags' => (($hashtags) ? urlencode($hashtags) : ''), - '$dend' => '', - '$mid' => (($mid) ? urlencode($mid) : ''), - '$verb' => '', - '$net' => (($net) ? urlencode($net) : ''), - '$dbegin' => '' - )); - } - - if($this->updating && ! $this->loading) { - // only setup pagination on initial page view - $pager_sql = ''; - } - else { - $itemspage = ((local_channel()) ? get_pconfig(local_channel(),'system','itemspage', 20) : 20); - App::set_pager_itemspage($itemspage); - $pager_sql = sprintf(" LIMIT %d OFFSET %d ", intval(App::$pager['itemspage']), intval(App::$pager['start'])); - } - - require_once('include/channel.php'); - require_once('include/security.php'); - - if ($public_stream_mode === PUBLIC_STREAM_SITE) { - $uids = " and item_private = 0 and item_wall = 1 "; - } - else { - $sys = get_sys_channel(); - $uids = " and item_private = 0 and item_wall = 0 and item.uid = " . intval($sys['channel_id']) . " "; - $sql_extra = item_permissions_sql($sys['channel_id']); - App::$data['firehose'] = intval($sys['channel_id']); - } - - if(get_config('system','public_list_mode')) - $page_mode = 'list'; - else - $page_mode = 'client'; + if (!$mid) { + $_SESSION['loadtime_pubstream'] = datetime_convert(); + if (local_channel()) { + PConfig::Set(local_channel(), 'system', 'loadtime_pubstream', $_SESSION['loadtime_pubstream']); + } + } + + $static = ((local_channel()) ? channel_manual_conv_update(local_channel()) : 1); + + $maxheight = get_config('system', 'home_divmore_height'); + if (!$maxheight) { + $maxheight = 400; + } + + $o .= '
      ' . "\r\n"; + $o .= "\r\n"; + + // if we got a decoded hash we must encode it again before handing to javascript + $mid = gen_link_id($mid); + + App::$page['htmlhead'] .= replace_macros(get_markup_template("build_query.tpl"), array( + '$baseurl' => z_root(), + '$pgtype' => 'pubstream', + '$uid' => ((local_channel()) ? local_channel() : '0'), + '$gid' => '0', + '$cid' => '0', + '$cmin' => '(-1)', + '$cmax' => '(-1)', + '$star' => '0', + '$liked' => '0', + '$conv' => '0', + '$spam' => '0', + '$fh' => '1', + '$dm' => '0', + '$nouveau' => '0', + '$wall' => '0', + '$draft' => '0', + '$list' => '0', + '$static' => $static, + '$page' => ((App::$pager['page'] != 1) ? App::$pager['page'] : 1), + '$search' => '', + '$xchan' => '', + '$order' => 'comment', + '$file' => '', + '$cats' => '', + '$tags' => (($hashtags) ? urlencode($hashtags) : ''), + '$dend' => '', + '$mid' => (($mid) ? urlencode($mid) : ''), + '$verb' => '', + '$net' => (($net) ? urlencode($net) : ''), + '$dbegin' => '' + )); + } + + if ($this->updating && !$this->loading) { + // only setup pagination on initial page view + $pager_sql = ''; + } else { + $itemspage = ((local_channel()) ? get_pconfig(local_channel(), 'system', 'itemspage', 20) : 20); + App::set_pager_itemspage($itemspage); + $pager_sql = sprintf(" LIMIT %d OFFSET %d ", intval(App::$pager['itemspage']), intval(App::$pager['start'])); + } + + require_once('include/channel.php'); + require_once('include/security.php'); + + if ($public_stream_mode === PUBLIC_STREAM_SITE) { + $uids = " and item_private = 0 and item_wall = 1 "; + } else { + $sys = get_sys_channel(); + $uids = " and item_private = 0 and item_wall = 0 and item.uid = " . intval($sys['channel_id']) . " "; + $sql_extra = item_permissions_sql($sys['channel_id']); + App::$data['firehose'] = intval($sys['channel_id']); + } + + if (get_config('system', 'public_list_mode')) { + $page_mode = 'list'; + } else { + $page_mode = 'client'; + } - if(x($hashtags)) { - $sql_extra .= protect_sprintf(term_query('item', $hashtags, TERM_HASHTAG, TERM_COMMUNITYTAG)); - } + if (x($hashtags)) { + $sql_extra .= protect_sprintf(term_query('item', $hashtags, TERM_HASHTAG, TERM_COMMUNITYTAG)); + } - $net_query = (($net) ? " left join xchan on xchan_hash = author_xchan " : ''); - $net_query2 = (($net) ? " and xchan_network = '" . protect_sprintf(dbesc($net)) . "' " : ''); + $net_query = (($net) ? " left join xchan on xchan_hash = author_xchan " : ''); + $net_query2 = (($net) ? " and xchan_network = '" . protect_sprintf(dbesc($net)) . "' " : ''); - if (isset(App::$profile) && isset(App::$profile['profile_uid'])) { - $abook_uids = " and abook.abook_channel = " . intval(App::$profile['profile_uid']) . " "; - } - - $simple_update = ((isset($_SESSION['loadtime_pubstream']) && $_SESSION['loadtime_pubstream']) ? " AND item.changed > '" . datetime_convert('UTC','UTC',$_SESSION['loadtime_pubstream']) . "' " : ''); - - if ($this->loading) { - $simple_update = ''; - } + if (isset(App::$profile) && isset(App::$profile['profile_uid'])) { + $abook_uids = " and abook.abook_channel = " . intval(App::$profile['profile_uid']) . " "; + } - if($static && $simple_update) - $simple_update .= " and author_xchan = '" . protect_sprintf(get_observer_hash()) . "' "; + $simple_update = ((isset($_SESSION['loadtime_pubstream']) && $_SESSION['loadtime_pubstream']) ? " AND item.changed > '" . datetime_convert('UTC', 'UTC', $_SESSION['loadtime_pubstream']) . "' " : ''); - //logger('update: ' . $this->updating . ' load: ' . $this->loading); + if ($this->loading) { + $simple_update = ''; + } - if($this->updating) { - - $ordering = "commented"; - - if($this->loading) { - if($mid) { - $r = q("SELECT parent AS item_id FROM item + if ($static && $simple_update) { + $simple_update .= " and author_xchan = '" . protect_sprintf(get_observer_hash()) . "' "; + } + + //logger('update: ' . $this->updating . ' load: ' . $this->loading); + + if ($this->updating) { + $ordering = "commented"; + + if ($this->loading) { + if ($mid) { + $r = q( + "SELECT parent AS item_id FROM item left join abook on item.author_xchan = abook.abook_xchan $net_query WHERE mid like '%s' $uids $item_normal and (abook.abook_blocked = 0 or abook.abook_flags is null) $sql_extra $net_query2 LIMIT 1", - dbesc($mid . '%') - ); - } - else { - // Fetch a page full of parent items for this page - $r = q("SELECT item.id AS item_id FROM item + dbesc($mid . '%') + ); + } else { + // Fetch a page full of parent items for this page + $r = q("SELECT item.id AS item_id FROM item left join abook on ( item.author_xchan = abook.abook_xchan $abook_uids ) $net_query WHERE true $uids and item.item_thread_top = 1 $item_normal and (abook.abook_blocked = 0 or abook.abook_flags is null) $sql_extra $net_query2 - ORDER BY $ordering DESC $pager_sql " - ); - } - } - elseif($this->updating) { - if($mid) { - $r = q("SELECT parent AS item_id FROM item + ORDER BY $ordering DESC $pager_sql "); + } + } elseif ($this->updating) { + if ($mid) { + $r = q( + "SELECT parent AS item_id FROM item left join abook on item.author_xchan = abook.abook_xchan $net_query WHERE mid like '%s' $uids $item_normal_update $simple_update and (abook.abook_blocked = 0 or abook.abook_flags is null) $sql_extra $net_query2 LIMIT 1", - dbesc($mid . '%') - ); - - - } - else { - $r = q("SELECT parent AS item_id FROM item + dbesc($mid . '%') + ); + } else { + $r = q("SELECT parent AS item_id FROM item left join abook on item.author_xchan = abook.abook_xchan $net_query WHERE true $uids $item_normal_update $simple_update and (abook.abook_blocked = 0 or abook.abook_flags is null) - $sql_extra $net_query2" - ); - } - } + $sql_extra $net_query2"); + } + } - // Then fetch all the children of the parents that are on this page - $parents_str = ''; - $update_unseen = ''; - - if($r) { - - $parents_str = ids_to_querystr($r,'item_id'); - - $items = q("SELECT item.*, item.id AS item_id FROM item + // Then fetch all the children of the parents that are on this page + $parents_str = ''; + $update_unseen = ''; + + if ($r) { + $parents_str = ids_to_querystr($r, 'item_id'); + + $items = q( + "SELECT item.*, item.id AS item_id FROM item WHERE true $uids $item_normal AND item.parent IN ( %s ) $sql_extra ", - dbesc($parents_str) - ); - - // use effective_uid param of xchan_query to help sort out comment permission - // for sys_channel owned items. + dbesc($parents_str) + ); - xchan_query($items,true,(($sys) ? local_channel() : 0)); - $items = fetch_post_tags($items,true); - $items = conv_sort($items,$ordering); - } - else { - $items = []; - } - - } + // use effective_uid param of xchan_query to help sort out comment permission + // for sys_channel owned items. - if ($mid && local_channel()) { - $ids = ids_to_array($items,'item_id'); - $seen = PConfig::Get(local_channel(),'system','seen_items',[]); - if (! $seen) { - $seen = []; - } - $seen = array_merge($ids,$seen); - PConfig::Set(local_channel(),'system','seen_items',$seen); - } + xchan_query($items, true, (($sys) ? local_channel() : 0)); + $items = fetch_post_tags($items, true); + $items = conv_sort($items, $ordering); + } else { + $items = []; + } + } - // fake it - $mode = ('pubstream'); - - $o .= conversation($items,$mode,$this->updating,$page_mode); + if ($mid && local_channel()) { + $ids = ids_to_array($items, 'item_id'); + $seen = PConfig::Get(local_channel(), 'system', 'seen_items', []); + if (!$seen) { + $seen = []; + } + $seen = array_merge($ids, $seen); + PConfig::Set(local_channel(), 'system', 'seen_items', $seen); + } - if($mid) - $o .= '
      '; - - if(($items) && (! $this->updating)) - $o .= alt_pager(count($items)); + // fake it + $mode = ('pubstream'); - return $o; - - } + $o .= conversation($items, $mode, $this->updating, $page_mode); + + if ($mid) { + $o .= '
      '; + } + + if (($items) && (!$this->updating)) { + $o .= alt_pager(count($items)); + } + + return $o; + } } diff --git a/Zotlabs/Module/Q.php b/Zotlabs/Module/Q.php index f58f9afc5..3daedeaf0 100644 --- a/Zotlabs/Module/Q.php +++ b/Zotlabs/Module/Q.php @@ -1,32 +1,30 @@ false]; - function init() { + $h = argv(1); + if (!$h) { + json_return_and_die($ret); + } - $ret = [ 'success' => false ]; - - $h = argv(1); - if(! $h) { - json_return_and_die($ret); - } - - $r = q("select * from hubloc left join site on hubloc_url = site_url where hubloc_hash = '%s' and site_dead = 0", - dbesc($h) - ); - if($r) { - $ret['success'] = true; - $ret['results'] = ids_to_array($r,'hubloc_id_url'); - } - json_return_and_die($ret); - } - -} \ No newline at end of file + $r = q( + "select * from hubloc left join site on hubloc_url = site_url where hubloc_hash = '%s' and site_dead = 0", + dbesc($h) + ); + if ($r) { + $ret['success'] = true; + $ret['results'] = ids_to_array($r, 'hubloc_id_url'); + } + json_return_and_die($ret); + } +} diff --git a/Zotlabs/Module/Randprof.php b/Zotlabs/Module/Randprof.php index 94ec095cb..0f102b024 100644 --- a/Zotlabs/Module/Randprof.php +++ b/Zotlabs/Module/Randprof.php @@ -1,17 +1,20 @@ $item['mid']]); + $n['obj_type'] = ((array_path_exists('obj/type', $n)) ? $n['obj']['type'] : EMPTY_STR); + $n['tgt_type'] = 'Image'; - if($emoji) { + $n['target'] = [ + 'type' => 'Image', + 'name' => $emoji, + 'url' => z_root() . '/images/emoji/' . $emoji . '.png' + ]; - $i = q("select * from item where id = %d and uid = %d", - intval($postid), - intval(local_channel()) - ); + $x = item_store($n); - if(! $i) { - // try the global public stream - $i = q("select * from item where id = %d and uid = %d", - intval($postid), - intval($sys['channel_id']) - ); - // try the local public stream - if (! $i) { - $i = q("select * from item where id = %d and item_wall = 1 and item_private = 0", - intval($postid) - ); - } - - if($i && local_channel() && (! is_sys_channel(local_channel()))) { - $i = [ copy_of_pubitem($channel, $i[0]['mid']) ]; - $postid = (($i) ? $i[0]['id'] : 0); - } - } + retain_item($postid); - if(! $i) { - return; - } - - $item = array_shift($i); - - $n = [] ; - $n['aid'] = $channel['channel_account_id']; - $n['uid'] = $channel['channel_id']; - $n['item_origin'] = true; - $n['item_type'] = $item['item_type']; - $n['parent'] = $postid; - $n['parent_mid'] = $item['mid']; - $n['uuid'] = new_uuid(); - $n['mid'] = z_root() . '/item/' . $n['uuid']; - $n['verb'] = 'emojiReaction'; - $n['body'] = "\n\n" . '[img=32x32]' . z_root() . '/images/emoji/' . $emoji . '.png[/img]' . "\n\n"; - $n['author_xchan'] = $channel['channel_hash']; - - $n['obj'] = Activity::fetch_item( [ 'id' => $item['mid'] ] ); - $n['obj_type'] = ((array_path_exists('obj/type',$n)) ? $n['obj']['type'] : EMPTY_STR); - - $n['tgt_type'] = 'Image'; - - $n['target'] = [ - 'type' => 'Image', - 'name' => $emoji, - 'url' => z_root() . '/images/emoji/' . $emoji . '.png' - ]; - - $x = item_store($n); - - retain_item($postid); - - if($x['success']) { - $nid = $x['item_id']; - Run::Summon( [ 'Notifier', 'like', $nid ] ); - } - - } - - } - -} \ No newline at end of file + if ($x['success']) { + $nid = $x['item_id']; + Run::Summon(['Notifier', 'like', $nid]); + } + } + } +} diff --git a/Zotlabs/Module/Register.php b/Zotlabs/Module/Register.php index 9b835d45b..8e0ca2be5 100644 --- a/Zotlabs/Module/Register.php +++ b/Zotlabs/Module/Register.php @@ -1,4 +1,5 @@ 1) ? argv(1) : ''); - - // Provide a stored request for somebody desiring a connection - // when they first need to register someplace. Once they've - // created a channel, we'll try to revive the connection request - // and process it. - - if ($_REQUEST['connect']) { - $_SESSION['connect'] = $_REQUEST['connect']; - } - - switch ($cmd) { - case 'invite_check.json': - $result = check_account_invite($_REQUEST['invite_code']); - break; - case 'email_check.json': - $result = check_account_email($_REQUEST['email']); - break; - case 'password_check.json': - $result = check_account_password($_REQUEST['password1']); - break; - default: - break; - } - if ($result) { - json_return_and_die($result); - } - } - - - function post() { + public function init() + { - check_form_security_token_redirectOnErr('/register', 'register'); + $result = null; + $cmd = ((argc() > 1) ? argv(1) : ''); - $max_dailies = intval(get_config('system','max_daily_registrations')); - if ($max_dailies) { - $r = q("select count(account_id) as total from account where account_created > %s - INTERVAL %s", - db_utcnow(), db_quoteinterval('1 day') - ); - if ($r && intval($r[0]['total']) >= $max_dailies) { - notice( t('Maximum daily site registrations exceeded. Please try again tomorrow.') . EOL); - return; - } - } - - if (! (isset($_POST['tos']) && intval($_POST['tos']))) { - notice( t('Please indicate acceptance of the Terms of Service. Registration failed.') . EOL); - return; - } - - $policy = get_config('system','register_policy'); - - $email_verify = get_config('system','verify_email'); - - - switch ($policy) { - - case REGISTER_OPEN: - $flags = ACCOUNT_OK; - break; - - case REGISTER_APPROVE: - $flags = ACCOUNT_BLOCKED | ACCOUNT_PENDING; - break; - - default: - case REGISTER_CLOSED: - if (! is_site_admin()) { - notice( t('Permission denied.') . EOL ); - return; - } - $flags = ACCOUNT_BLOCKED; - break; - } - - if ($email_verify && $policy == REGISTER_OPEN) { - $flags = $flags | ACCOUNT_UNVERIFIED; - } - - - if ((! $_POST['password']) || ($_POST['password'] !== $_POST['password2'])) { - notice( t('Passwords do not match.') . EOL); - return; - } - - $arr = $_POST; - $arr['account_flags'] = $flags; - - $result = create_account($arr); - - if (! $result['success']) { - notice($result['message']); - return; - } + // Provide a stored request for somebody desiring a connection + // when they first need to register someplace. Once they've + // created a channel, we'll try to revive the connection request + // and process it. - require_once('include/security.php'); - - - if ($_REQUEST['name']) { - set_aconfig($result['account']['account_id'],'register','channel_name',$_REQUEST['name']); - } - if ($_REQUEST['nickname']) { - set_aconfig($result['account']['account_id'],'register','channel_address',$_REQUEST['nickname']); - } - if ($_REQUEST['permissions_role']) { - set_aconfig($result['account']['account_id'],'register','permissions_role',$_REQUEST['permissions_role']); - } + if ($_REQUEST['connect']) { + $_SESSION['connect'] = $_REQUEST['connect']; + } - // At this point the account has been created without error. Purge any error messages from prior failed registration - // attempts which haven't yet been delivered to the browser and start fresh. If you're willing to figure out why they - // weren't delivered to the browser please adopt zap issue 34. - - $_SESSION['sysmsg'] = []; - - $using_invites = intval(get_config('system','invitation_only')); - $num_invites = intval(get_config('system','number_invites')); - $invite_code = ((x($_POST,'invite_code')) ? notags(trim($_POST['invite_code'])) : ''); - - if ($using_invites && $invite_code && defined('INVITE_WORKING')) { - q("delete from register where hash = '%s'", dbesc($invite_code)); - // @FIXME - this also needs to be considered when using 'invites_remaining' in mod/invite.php - set_aconfig($result['account']['account_id'],'system','invites_remaining',$num_invites); - } - - if ($policy == REGISTER_OPEN ) { - if ($email_verify) { - $res = verify_email_address($result); - } - else { - $res = send_register_success_email($result['email'],$result['password']); - } - if ($res) { - if ($invite_code) { - info( t('Registration successful. Continue to create your first channel...') . EOL ) ; - } - else { - info( t('Registration successful. Please check your email for validation instructions.') . EOL ) ; - } - } - } - elseif ($policy == REGISTER_APPROVE) { - $res = send_reg_approval_email($result); - if ($res) { - info( t('Your registration is pending approval by the site owner.') . EOL ) ; - } - else { - notice( t('Your registration can not be processed.') . EOL); - } - goaway(z_root()); - } - - if ($email_verify) { - goaway(z_root() . '/email_validation/' . bin2hex($result['email'])); - } - - // fall through and authenticate if no approvals or verifications were required. - - authenticate_success($result['account'],null,true,false,true); - - $new_channel = false; - $next_page = 'new_channel'; - - if (get_config('system','auto_channel_create')) { - $new_channel = auto_channel_create($result['account']['account_id']); - if ($new_channel['success']) { - $channel_id = $new_channel['channel']['channel_id']; - change_channel($channel_id); - $next_page = '~'; - } - else { - $new_channel = false; - } - } - - $x = get_config('system','workflow_register_next'); - if ($x) { - $next_page = $x; - $_SESSION['workflow'] = true; - } - - unset($_SESSION['login_return_url']); - goaway(z_root() . '/' . $next_page); - - } - - - - function get() { - - $registration_is = EMPTY_STR; - $other_sites = false; - - if (intval(get_config('system','register_policy')) === REGISTER_CLOSED) { - notice( t('Registration on this website is disabled.') . EOL); - if (intval(get_config('system','directory_mode')) === DIRECTORY_MODE_STANDALONE) { - return EMPTY_STR; - } - else { - $other_sites = true; - } - } - - if (intval(get_config('system','register_policy')) == REGISTER_APPROVE) { - $registration_is = t('Registration on this website is by approval only.'); - $other_sites = true; - } - - $invitations = false; - - if (intval(get_config('system','invitation_only')) && defined('INVITE_WORKING')) { - $invitations = true; - $registration_is = t('Registration on this site is by invitation only.'); - $other_sites = true; - } - - $max_dailies = intval(get_config('system','max_daily_registrations')); - if ($max_dailies) { - $r = q("select count(account_id) as total from account where account_created > %s - INTERVAL %s", - db_utcnow(), db_quoteinterval('1 day') - ); - if ($r && $r[0]['total'] >= $max_dailies) { - logger('max daily registrations exceeded.'); - notice( t('This site has exceeded the number of allowed daily account registrations. Please try again tomorrow.') . EOL); - return; - } - } - - $privacy_role = ((x($_REQUEST,'permissions_role')) ? $_REQUEST['permissions_role'] : ""); - - $perm_roles = PermissionRoles::roles(); - - // Configurable terms of service link - - $tosurl = get_config('system','tos_url'); - if (! $tosurl) { - $tosurl = z_root() . '/help/TermsOfService'; - } - - $toslink = '' . t('Terms of Service') . ''; - - // Configurable whether to restrict age or not - default is based on international legal requirements - // This can be relaxed if you are on a restricted server that does not share with public servers - - if (get_config('system','no_age_restriction')) { - $label_tos = sprintf( t('I accept the %s for this website'), $toslink); - } - else { - $age = get_config('system','minimum_age'); - if (! $age) { - $age = 13; - } - $label_tos = sprintf( t('I am over %s years of age and accept the %s for this website'), $age, $toslink); - } - - $enable_tos = 1 - intval(get_config('system','no_termsofservice')); - - $email = [ 'email', t('Your email address'), ((x($_REQUEST,'email')) ? strip_tags(trim($_REQUEST['email'])) : ""), '' , '',' required ']; - $password = [ 'password', t('Choose a password'), '', '', '', ' required ' ]; - $password2 = [ 'password2', t('Please re-enter your password'), '', '', '', ' required ' ]; - $invite_code = [ 'invite_code', t('Please enter your invitation code'), ((x($_REQUEST,'invite_code')) ? strip_tags(trim($_REQUEST['invite_code'])) : "")]; - $name = [ 'name', t('Your Name'), ((x($_REQUEST,'name')) ? $_REQUEST['name'] : ''), t('Real names are preferred.') ]; - $nickhub = '@' . str_replace(array('http://','https://','/'), '', get_config('system','baseurl')); - $nickname = [ 'nickname', t('Choose a short nickname'), ((x($_REQUEST,'nickname')) ? $_REQUEST['nickname'] : ''), sprintf( t('Your nickname will be used to create an easy to remember channel address e.g. nickname%s'), $nickhub)]; - $role = ['permissions_role' , t('Channel role and privacy'), ($privacy_role) ? $privacy_role : 'social', t('Select a channel permission role for your usage needs and privacy requirements.'),$perm_roles]; - $tos = [ 'tos', $label_tos, '', '', [ t('no'), t('yes') ], ' required ' ]; + switch ($cmd) { + case 'invite_check.json': + $result = check_account_invite($_REQUEST['invite_code']); + break; + case 'email_check.json': + $result = check_account_email($_REQUEST['email']); + break; + case 'password_check.json': + $result = check_account_password($_REQUEST['password1']); + break; + default: + break; + } + if ($result) { + json_return_and_die($result); + } + } - $auto_create = (get_config('system','auto_channel_create') ? true : false); - $default_role = get_config('system','default_permissions_role'); - $email_verify = get_config('system','verify_email'); - - - $o = replace_macros(get_markup_template('register.tpl'), [ - '$form_security_token' => get_form_security_token("register"), - '$title' => t('Registration'), - '$reg_is' => $registration_is, - '$registertext' => bbcode(get_config('system','register_text')), - '$other_sites' => (($other_sites) ? t('Show affiliated sites - some of which may allow registration.') : EMPTY_STR), - '$invitations' => $invitations, - '$invite_code' => $invite_code, - '$auto_create' => $auto_create, - '$name' => $name, - '$role' => $role, - '$default_role' => $default_role, - '$nickname' => $nickname, - '$enable_tos' => $enable_tos, - '$tos' => $tos, - '$email' => $email, - '$pass1' => $password, - '$pass2' => $password2, - '$submit' => t('Register'), - '$verify_note' => (($email_verify) ? t('This site requires email verification. After completing this form, please check your email for further instructions.') : ''), - ] ); - - return $o; - - } - - + public function post() + { + + check_form_security_token_redirectOnErr('/register', 'register'); + + $max_dailies = intval(get_config('system', 'max_daily_registrations')); + if ($max_dailies) { + $r = q( + "select count(account_id) as total from account where account_created > %s - INTERVAL %s", + db_utcnow(), + db_quoteinterval('1 day') + ); + if ($r && intval($r[0]['total']) >= $max_dailies) { + notice(t('Maximum daily site registrations exceeded. Please try again tomorrow.') . EOL); + return; + } + } + + if (!(isset($_POST['tos']) && intval($_POST['tos']))) { + notice(t('Please indicate acceptance of the Terms of Service. Registration failed.') . EOL); + return; + } + + $policy = get_config('system', 'register_policy'); + + $email_verify = get_config('system', 'verify_email'); + + + switch ($policy) { + case REGISTER_OPEN: + $flags = ACCOUNT_OK; + break; + + case REGISTER_APPROVE: + $flags = ACCOUNT_BLOCKED | ACCOUNT_PENDING; + break; + + default: + case REGISTER_CLOSED: + if (!is_site_admin()) { + notice(t('Permission denied.') . EOL); + return; + } + $flags = ACCOUNT_BLOCKED; + break; + } + + if ($email_verify && $policy == REGISTER_OPEN) { + $flags = $flags | ACCOUNT_UNVERIFIED; + } + + + if ((!$_POST['password']) || ($_POST['password'] !== $_POST['password2'])) { + notice(t('Passwords do not match.') . EOL); + return; + } + + $arr = $_POST; + $arr['account_flags'] = $flags; + + $result = create_account($arr); + + if (!$result['success']) { + notice($result['message']); + return; + } + + require_once('include/security.php'); + + + if ($_REQUEST['name']) { + set_aconfig($result['account']['account_id'], 'register', 'channel_name', $_REQUEST['name']); + } + if ($_REQUEST['nickname']) { + set_aconfig($result['account']['account_id'], 'register', 'channel_address', $_REQUEST['nickname']); + } + if ($_REQUEST['permissions_role']) { + set_aconfig($result['account']['account_id'], 'register', 'permissions_role', $_REQUEST['permissions_role']); + } + + // At this point the account has been created without error. Purge any error messages from prior failed registration + // attempts which haven't yet been delivered to the browser and start fresh. If you're willing to figure out why they + // weren't delivered to the browser please adopt zap issue 34. + + $_SESSION['sysmsg'] = []; + + $using_invites = intval(get_config('system', 'invitation_only')); + $num_invites = intval(get_config('system', 'number_invites')); + $invite_code = ((x($_POST, 'invite_code')) ? notags(trim($_POST['invite_code'])) : ''); + + if ($using_invites && $invite_code && defined('INVITE_WORKING')) { + q("delete from register where hash = '%s'", dbesc($invite_code)); + // @FIXME - this also needs to be considered when using 'invites_remaining' in mod/invite.php + set_aconfig($result['account']['account_id'], 'system', 'invites_remaining', $num_invites); + } + + if ($policy == REGISTER_OPEN) { + if ($email_verify) { + $res = verify_email_address($result); + } else { + $res = send_register_success_email($result['email'], $result['password']); + } + if ($res) { + if ($invite_code) { + info(t('Registration successful. Continue to create your first channel...') . EOL); + } else { + info(t('Registration successful. Please check your email for validation instructions.') . EOL); + } + } + } elseif ($policy == REGISTER_APPROVE) { + $res = send_reg_approval_email($result); + if ($res) { + info(t('Your registration is pending approval by the site owner.') . EOL); + } else { + notice(t('Your registration can not be processed.') . EOL); + } + goaway(z_root()); + } + + if ($email_verify) { + goaway(z_root() . '/email_validation/' . bin2hex($result['email'])); + } + + // fall through and authenticate if no approvals or verifications were required. + + authenticate_success($result['account'], null, true, false, true); + + $new_channel = false; + $next_page = 'new_channel'; + + if (get_config('system', 'auto_channel_create')) { + $new_channel = auto_channel_create($result['account']['account_id']); + if ($new_channel['success']) { + $channel_id = $new_channel['channel']['channel_id']; + change_channel($channel_id); + $next_page = '~'; + } else { + $new_channel = false; + } + } + + $x = get_config('system', 'workflow_register_next'); + if ($x) { + $next_page = $x; + $_SESSION['workflow'] = true; + } + + unset($_SESSION['login_return_url']); + goaway(z_root() . '/' . $next_page); + } + + + public function get() + { + + $registration_is = EMPTY_STR; + $other_sites = false; + + if (intval(get_config('system', 'register_policy')) === REGISTER_CLOSED) { + notice(t('Registration on this website is disabled.') . EOL); + if (intval(get_config('system', 'directory_mode')) === DIRECTORY_MODE_STANDALONE) { + return EMPTY_STR; + } else { + $other_sites = true; + } + } + + if (intval(get_config('system', 'register_policy')) == REGISTER_APPROVE) { + $registration_is = t('Registration on this website is by approval only.'); + $other_sites = true; + } + + $invitations = false; + + if (intval(get_config('system', 'invitation_only')) && defined('INVITE_WORKING')) { + $invitations = true; + $registration_is = t('Registration on this site is by invitation only.'); + $other_sites = true; + } + + $max_dailies = intval(get_config('system', 'max_daily_registrations')); + if ($max_dailies) { + $r = q( + "select count(account_id) as total from account where account_created > %s - INTERVAL %s", + db_utcnow(), + db_quoteinterval('1 day') + ); + if ($r && $r[0]['total'] >= $max_dailies) { + logger('max daily registrations exceeded.'); + notice(t('This site has exceeded the number of allowed daily account registrations. Please try again tomorrow.') . EOL); + return; + } + } + + $privacy_role = ((x($_REQUEST, 'permissions_role')) ? $_REQUEST['permissions_role'] : ""); + + $perm_roles = PermissionRoles::roles(); + + // Configurable terms of service link + + $tosurl = get_config('system', 'tos_url'); + if (!$tosurl) { + $tosurl = z_root() . '/help/TermsOfService'; + } + + $toslink = '' . t('Terms of Service') . ''; + + // Configurable whether to restrict age or not - default is based on international legal requirements + // This can be relaxed if you are on a restricted server that does not share with public servers + + if (get_config('system', 'no_age_restriction')) { + $label_tos = sprintf(t('I accept the %s for this website'), $toslink); + } else { + $age = get_config('system', 'minimum_age'); + if (!$age) { + $age = 13; + } + $label_tos = sprintf(t('I am over %s years of age and accept the %s for this website'), $age, $toslink); + } + + $enable_tos = 1 - intval(get_config('system', 'no_termsofservice')); + + $email = ['email', t('Your email address'), ((x($_REQUEST, 'email')) ? strip_tags(trim($_REQUEST['email'])) : ""), '', '', ' required ']; + $password = ['password', t('Choose a password'), '', '', '', ' required ']; + $password2 = ['password2', t('Please re-enter your password'), '', '', '', ' required ']; + $invite_code = ['invite_code', t('Please enter your invitation code'), ((x($_REQUEST, 'invite_code')) ? strip_tags(trim($_REQUEST['invite_code'])) : "")]; + $name = ['name', t('Your Name'), ((x($_REQUEST, 'name')) ? $_REQUEST['name'] : ''), t('Real names are preferred.')]; + $nickhub = '@' . str_replace(array('http://', 'https://', '/'), '', get_config('system', 'baseurl')); + $nickname = ['nickname', t('Choose a short nickname'), ((x($_REQUEST, 'nickname')) ? $_REQUEST['nickname'] : ''), sprintf(t('Your nickname will be used to create an easy to remember channel address e.g. nickname%s'), $nickhub)]; + $role = ['permissions_role', t('Channel role and privacy'), ($privacy_role) ? $privacy_role : 'social', t('Select a channel permission role for your usage needs and privacy requirements.'), $perm_roles]; + $tos = ['tos', $label_tos, '', '', [t('no'), t('yes')], ' required ']; + + + $auto_create = (get_config('system', 'auto_channel_create') ? true : false); + $default_role = get_config('system', 'default_permissions_role'); + $email_verify = get_config('system', 'verify_email'); + + + $o = replace_macros(get_markup_template('register.tpl'), [ + '$form_security_token' => get_form_security_token("register"), + '$title' => t('Registration'), + '$reg_is' => $registration_is, + '$registertext' => bbcode(get_config('system', 'register_text')), + '$other_sites' => (($other_sites) ? t('Show affiliated sites - some of which may allow registration.') : EMPTY_STR), + '$invitations' => $invitations, + '$invite_code' => $invite_code, + '$auto_create' => $auto_create, + '$name' => $name, + '$role' => $role, + '$default_role' => $default_role, + '$nickname' => $nickname, + '$enable_tos' => $enable_tos, + '$tos' => $tos, + '$email' => $email, + '$pass1' => $password, + '$pass2' => $password2, + '$submit' => t('Register'), + '$verify_note' => (($email_verify) ? t('This site requires email verification. After completing this form, please check your email for further instructions.') : ''), + ]); + + return $o; + } } diff --git a/Zotlabs/Module/Regmod.php b/Zotlabs/Module/Regmod.php index 6fe89ca90..cada8e25c 100644 --- a/Zotlabs/Module/Regmod.php +++ b/Zotlabs/Module/Regmod.php @@ -1,41 +1,49 @@ NULL_DATE) { - $d1 = datetime_convert('UTC','UTC','now - 48 hours'); - if ($account['account_password_changed'] > d1) { - notice( t('Account removals are not allowed within 48 hours of changing the account password.') . EOL); - return; - } - } + if ($_SESSION['delegate']) { + return; + } - account_remove($account_id); - } - - function get() { - - if (! local_channel()) { - goaway(z_root()); - } - - $hash = random_string(); - - $_SESSION['remove_account_verify'] = $hash; + if ((!x($_POST, 'qxz_password')) || (!strlen(trim($_POST['qxz_password'])))) { + return; + } - $o .= replace_macros(get_markup_template('removeaccount.tpl'), [ - '$basedir' => z_root(), - '$hash' => $hash, - '$title' => t('Remove This Account'), - '$desc' => [ t('WARNING: '), t('This account and all its channels will be completely removed from this server. '), t('This action is permanent and can not be undone!') ], - '$passwd' => t('Please enter your password for verification:'), - '$submit' => t('Remove Account') - ]); - - return $o; - } - + if ((!x($_POST, 'verify')) || (!strlen(trim($_POST['verify'])))) { + return; + } + + if ($_POST['verify'] !== $_SESSION['remove_account_verify']) { + return; + } + + $account = App::get_account(); + $account_id = get_account_id(); + + if (!($account && $account_id)) { + return; + } + + $x = account_verify_password($account['account_email'], $_POST['qxz_password']); + if (!($x && $x['account'])) { + return; + } + + if ($account['account_password_changed'] > NULL_DATE) { + $d1 = datetime_convert('UTC', 'UTC', 'now - 48 hours'); + if ($account['account_password_changed'] > d1) { + notice(t('Account removals are not allowed within 48 hours of changing the account password.') . EOL); + return; + } + } + + account_remove($account_id); + } + + public function get() + { + + if (!local_channel()) { + goaway(z_root()); + } + + $hash = random_string(); + + $_SESSION['remove_account_verify'] = $hash; + + $o .= replace_macros(get_markup_template('removeaccount.tpl'), [ + '$basedir' => z_root(), + '$hash' => $hash, + '$title' => t('Remove This Account'), + '$desc' => [t('WARNING: '), t('This account and all its channels will be completely removed from this server. '), t('This action is permanent and can not be undone!')], + '$passwd' => t('Please enter your password for verification:'), + '$submit' => t('Remove Account') + ]); + + return $o; + } } diff --git a/Zotlabs/Module/Removeme.php b/Zotlabs/Module/Removeme.php index 11affb82a..5264415da 100644 --- a/Zotlabs/Module/Removeme.php +++ b/Zotlabs/Module/Removeme.php @@ -1,76 +1,79 @@ NULL_DATE) { - $d1 = datetime_convert('UTC','UTC','now - 48 hours'); - if($account['account_password_changed'] > $d1) { - notice( t('Channel removals are not allowed within 48 hours of changing the account password.') . EOL); - return; - } - } - - channel_remove(local_channel(),true,true); - } - - - function get() { - - if (! local_channel()) { - goaway(z_root()); - } - - $hash = random_string(); - - $_SESSION['remove_channel_verify'] = $hash; - - $o .= replace_macros(get_markup_template('removeme.tpl'), [ - '$basedir' => z_root(), - '$hash' => $hash, - '$title' => t('Remove This Channel'), - '$desc' => [ t('WARNING: '), t('This channel will be completely removed from this server. '), t('This action is permanent and can not be undone!') ], - '$passwd' => t('Please enter your password for verification:'), - '$submit' => t('Remove Channel') - ]); - - return $o; - } - + if (!local_channel()) { + return; + } + + if ($_SESSION['delegate']) { + return; + } + + if ((!x($_POST, 'qxz_password')) || (!strlen(trim($_POST['qxz_password'])))) { + return; + } + + if ((!x($_POST, 'verify')) || (!strlen(trim($_POST['verify'])))) { + return; + } + + if ($_POST['verify'] !== $_SESSION['remove_channel_verify']) { + return; + } + + $account = App::get_account(); + + if (!$account) { + return; + } + + $x = account_verify_password($account['account_email'], $_POST['qxz_password']); + if (!($x && $x['account'])) { + return; + } + + if ($account['account_password_changed'] > NULL_DATE) { + $d1 = datetime_convert('UTC', 'UTC', 'now - 48 hours'); + if ($account['account_password_changed'] > $d1) { + notice(t('Channel removals are not allowed within 48 hours of changing the account password.') . EOL); + return; + } + } + + channel_remove(local_channel(), true, true); + } + + + public function get() + { + + if (!local_channel()) { + goaway(z_root()); + } + + $hash = random_string(); + + $_SESSION['remove_channel_verify'] = $hash; + + $o .= replace_macros(get_markup_template('removeme.tpl'), [ + '$basedir' => z_root(), + '$hash' => $hash, + '$title' => t('Remove This Channel'), + '$desc' => [t('WARNING: '), t('This channel will be completely removed from this server. '), t('This action is permanent and can not be undone!')], + '$passwd' => t('Please enter your password for verification:'), + '$submit' => t('Remove Channel') + ]); + + return $o; + } } diff --git a/Zotlabs/Module/Rmagic.php b/Zotlabs/Module/Rmagic.php index a1697ce46..9d441c41b 100644 --- a/Zotlabs/Module/Rmagic.php +++ b/Zotlabs/Module/Rmagic.php @@ -1,88 +1,92 @@ $address ]; - call_hooks('reverse_magic_auth', $arr); - - // if they're still here... - notice( t('Authentication failed.') . EOL); - return; - } - else { - - // Presumed Red identity. Perform reverse magic auth - - if (strpos($address,'@') === false) { - notice('Invalid address.'); - return; - } - - $r = null; - if ($address) { - $r = q("select hubloc_url from hubloc where hubloc_addr = '%s' limit 1", - dbesc($address) - ); - } - if ($r) { - $url = $r[0]['hubloc_url']; - } - else { - $url = 'https://' . substr($address,strpos($address,'@')+1); - } - - if ($url) { - if ($_SESSION['return_url']) { - $dest = bin2hex(z_root() . '/' . str_replace('zid=','zid_=',$_SESSION['return_url'])); - } - else { - $dest = bin2hex(z_root() . '/' . str_replace([ 'rmagic', 'zid=' ] ,[ '', 'zid_='],App::$query_string)); - } - goaway($url . '/magic' . '?f=&owa=1&bdest=' . $dest); - } - } - } - - - function get() { - return replace_macros(get_markup_template('rmagic.tpl'), - [ - '$title' => t('Remote Authentication'), - '$address' => [ 'address', t('Enter your channel address (e.g. channel@example.com)'), '', '' ], - '$action' => 'rmagic', - '$method' => 'post', - '$submit' => t('Authenticate') - ] - ); - } + public function init() + { + + if (local_channel()) { + goaway(z_root()); + } + + $me = get_my_address(); + if ($me) { + $r = q( + "select hubloc_url from hubloc where hubloc_addr = '%s' limit 1", + dbesc($me) + ); + if ($r) { + if ($r[0]['hubloc_url'] === z_root()) { + goaway(z_root() . '/login'); + } + $dest = bin2hex(z_root() . '/' . str_replace(['rmagic', 'zid='], ['', 'zid_='], App::$query_string)); + goaway($r[0]['hubloc_url'] . '/magic' . '?f=&owa=1&bdest=' . $dest); + } + } + } + + public function post() + { + + $address = trim($_REQUEST['address']); + + if (strpos($address, '@') === false) { + $arr = ['address' => $address]; + call_hooks('reverse_magic_auth', $arr); + + // if they're still here... + notice(t('Authentication failed.') . EOL); + return; + } else { + // Presumed Red identity. Perform reverse magic auth + + if (strpos($address, '@') === false) { + notice('Invalid address.'); + return; + } + + $r = null; + if ($address) { + $r = q( + "select hubloc_url from hubloc where hubloc_addr = '%s' limit 1", + dbesc($address) + ); + } + if ($r) { + $url = $r[0]['hubloc_url']; + } else { + $url = 'https://' . substr($address, strpos($address, '@') + 1); + } + + if ($url) { + if ($_SESSION['return_url']) { + $dest = bin2hex(z_root() . '/' . str_replace('zid=', 'zid_=', $_SESSION['return_url'])); + } else { + $dest = bin2hex(z_root() . '/' . str_replace(['rmagic', 'zid='], ['', 'zid_='], App::$query_string)); + } + goaway($url . '/magic' . '?f=&owa=1&bdest=' . $dest); + } + } + } + + + public function get() + { + return replace_macros( + get_markup_template('rmagic.tpl'), + [ + '$title' => t('Remote Authentication'), + '$address' => ['address', t('Enter your channel address (e.g. channel@example.com)'), '', ''], + '$action' => 'rmagic', + '$method' => 'post', + '$submit' => t('Authenticate') + ] + ); + } } diff --git a/Zotlabs/Module/Rpost.php b/Zotlabs/Module/Rpost.php index c4d2471d1..9052c23f0 100644 --- a/Zotlabs/Module/Rpost.php +++ b/Zotlabs/Module/Rpost.php @@ -1,6 +1,6 @@ $arg) { - if ($key === 'req') - continue; - $url .= '&' . $key . '=' . $arg; - } - goaway($url); - } - } - - // The login procedure is going to bugger our $_REQUEST variables - // so save them in the session. - - if (array_key_exists('body',$_REQUEST)) { - $_SESSION['rpost'] = $_REQUEST; - } - return login(); - } + if (!local_channel()) { + if (remote_channel()) { + // redirect to your own site. + // We can only do this with a GET request so you'll need to keep the text short or risk getting truncated + // by the wretched beast called 'suhosin'. All the browsers now allow long GET requests, but suhosin + // blocks them. - nav_set_selected('Post'); + $url = Libzot::get_rpost_path(App::get_observer()); + // make sure we're not looping to our own hub + if (($url) && (!stristr($url, App::get_hostname()))) { + foreach ($_GET as $key => $arg) { + if ($key === 'req') { + continue; + } + $url .= '&' . $key . '=' . $arg; + } + goaway($url); + } + } - if (local_channel() && array_key_exists('userfile',$_FILES)) { - - $channel = App::get_channel(); - $observer = App::get_observer(); + // The login procedure is going to bugger our $_REQUEST variables + // so save them in the session. - $def_album = get_pconfig($channel['channel_id'],'system','photo_path'); - $def_attach = get_pconfig($channel['channel_id'],'system','attach_path'); - - $r = attach_store($channel, (($observer) ? $observer['xchan_hash'] : ''), '', [ - 'source' => 'editor', - 'visible' => 0, - 'album' => $def_album, - 'directory' => $def_attach, - 'flags' => 1, // indicates temporary permissions are created - 'allow_cid' => '<' . $channel['channel_hash'] . '>' - ]); + if (array_key_exists('body', $_REQUEST)) { + $_SESSION['rpost'] = $_REQUEST; + } + return login(); + } - if (! $r['success']) { - notice( $r['message'] . EOL); - } + nav_set_selected('Post'); - $s = EMPTY_STR; - - if (intval($r['data']['is_photo'])) { - $s .= "\n\n" . $r['body'] . "\n\n"; - } + if (local_channel() && array_key_exists('userfile', $_FILES)) { + $channel = App::get_channel(); + $observer = App::get_observer(); - $url = z_root() . '/cloud/' . $channel['channel_address'] . '/' . $r['data']['display_path']; + $def_album = get_pconfig($channel['channel_id'], 'system', 'photo_path'); + $def_attach = get_pconfig($channel['channel_id'], 'system', 'attach_path'); - if (strpos($r['data']['filetype'],'video') === 0) { - for ($n = 0; $n < 15; $n ++) { - $thumb = Linkinfo::get_video_poster($url); - if ($thumb) { - break; - } - sleep(1); - continue; - } + $r = attach_store($channel, (($observer) ? $observer['xchan_hash'] : ''), '', [ + 'source' => 'editor', + 'visible' => 0, + 'album' => $def_album, + 'directory' => $def_attach, + 'flags' => 1, // indicates temporary permissions are created + 'allow_cid' => '<' . $channel['channel_hash'] . '>' + ]); - if ($thumb) { - $s .= "\n\n" . '[zvideo poster=\'' . $thumb . '\']' . $url . '[/zvideo]' . "\n\n"; - } - else { - $s .= "\n\n" . '[zvideo]' . $url . '[/zvideo]' . "\n\n"; - } - } - if (strpos($r['data']['filetype'],'audio') === 0) { - $s .= "\n\n" . '[zaudio]' . $url . '[/zaudio]' . "\n\n"; - } - if ($r['data']['filetype'] === 'image/svg+xml') { - $x = @file_get_contents('store/' . $channel['channel_address'] . '/' . $r['data']['os_path']); - if ($x) { - $bb = svg2bb($x); - if ($bb) { - $s .= "\n\n" . $bb; - } - else { - logger('empty return from svgbb'); - } - } - else { - logger('unable to read svg data file: ' . 'store/' . $channel['channel_address'] . '/' . $r['data']['os_path']); - } - } - if ($r['data']['filetype'] === 'text/vnd.abc' && addon_is_installed('abc')) { - $x = @file_get_contents('store/' . $channel['channel_address'] . '/' . $r['data']['os_path']); - if ($x) { - $s .= "\n\n" . '[abc]' . $x . '[/abc]'; - } - else { - logger('unable to read ABC data file: ' . 'store/' . $channel['channel_address'] . '/' . $r['data']['os_path']); - } - } - if ($r['data']['filetype'] === 'text/calendar') { - $content = @file_get_contents('store/' . $channel['channel_address'] . '/' . $r['data']['os_path']); - if ($content) { - $ev = ical_to_ev($content); - if ($ev) { - $s .= "\n\n" . format_event_bbcode($ev[0]) . "\n\n"; - } - } - } + if (!$r['success']) { + notice($r['message'] . EOL); + } - $s .= "\n\n" . '[attachment]' . $r['data']['hash'] . ',' . $r['data']['revision'] . '[/attachment]' . "\n"; - $_REQUEST['body'] = ((array_key_exists('body',$_REQUEST)) ? $_REQUEST['body'] . $s : $s); - } + $s = EMPTY_STR; - // If we have saved rpost session variables, but nothing in the current $_REQUEST, recover the saved variables - - if ((! array_key_exists('body',$_REQUEST)) && (array_key_exists('rpost',$_SESSION))) { - $_REQUEST = $_SESSION['rpost']; - unset($_SESSION['rpost']); - } - - if (array_key_exists('channel',$_REQUEST)) { - $r = q("select channel_id from channel where channel_account_id = %d and channel_address = '%s' limit 1", - intval(get_account_id()), - dbesc($_REQUEST['channel']) - ); - if ($r) { - require_once('include/security.php'); - $change = change_channel($r[0]['channel_id']); - } - } - - if ($_REQUEST['remote_return']) { - $_SESSION['remote_return'] = $_REQUEST['remote_return']; - } - - if (argc() > 1 && argv(1) === 'return') { - if($_SESSION['remote_return']) { - goaway($_SESSION['remote_return']); - } - goaway(z_root() . '/stream'); - } - - $plaintext = true; - - if (array_key_exists('type', $_REQUEST) && $_REQUEST['type'] === 'html' && isset($_REQUEST['body'])) { - require_once('include/html2bbcode.php'); - $_REQUEST['body'] = html2bbcode($_REQUEST['body']); - } - - $channel = App::get_channel(); + if (intval($r['data']['is_photo'])) { + $s .= "\n\n" . $r['body'] . "\n\n"; + } - $acl = new AccessControl($channel); - - if (array_key_exists('to',$_REQUEST) && $_REQUEST['to']) { - $acl->set([ 'allow_cid' => '<' . $_REQUEST['to'] . '>', - 'allow_gid' => EMPTY_STR, - 'deny_cid' => EMPTY_STR, - 'deny_gid' => EMPTY_STR ] - ); - } + $url = z_root() . '/cloud/' . $channel['channel_address'] . '/' . $r['data']['display_path']; - $channel_acl = $acl->get(); - - if ($_REQUEST['url']) { - $x = z_fetch_url(z_root() . '/linkinfo?f=&url=' . urlencode($_REQUEST['url']) . '&oembed=1&zotobj=1'); - if ($x['success']) { - $_REQUEST['body'] = $_REQUEST['body'] . $x['body']; - } - } + if (strpos($r['data']['filetype'], 'video') === 0) { + for ($n = 0; $n < 15; $n++) { + $thumb = Linkinfo::get_video_poster($url); + if ($thumb) { + break; + } + sleep(1); + continue; + } - if ($_REQUEST['post_id']) { - $_REQUEST['body'] .= '[share=' . intval($_REQUEST['post_id']) . '][/share]'; - } - - $x = [ - 'is_owner' => true, - 'allow_location' => ((intval(get_pconfig($channel['channel_id'],'system','use_browser_location'))) ? '1' : ''), - 'default_location' => $channel['channel_location'], - 'nickname' => $channel['channel_address'], - 'lockstate' => (($acl->is_private()) ? 'lock' : 'unlock'), - 'acl' => populate_acl($channel_acl, true, PermissionDescription::fromGlobalPermission('view_stream'), get_post_aclDialogDescription(), 'acl_dialog_post'), - 'permissions' => $channel_acl, - 'bang' => '', - 'visitor' => true, - 'profile_uid' => local_channel(), - 'title' => $_REQUEST['title'], - 'body' => $_REQUEST['body'], - 'attachment' => $_REQUEST['attachment'], - 'source' => ((x($_REQUEST,'source')) ? strip_tags($_REQUEST['source']) : ''), - 'return_path' => 'rpost/return', - 'bbco_autocomplete' => 'bbcode', - 'editor_autocomplete' => true, - 'bbcode' => true, - 'jotnets' => true, - 'reset' => t('Reset form') - ]; - - $editor = status_editor($x); - - $o .= replace_macros(get_markup_template('edpost_head.tpl'), [ - '$title' => t('Edit post'), - '$cancel' => '', - '$editor' => $editor - ]); + if ($thumb) { + $s .= "\n\n" . '[zvideo poster=\'' . $thumb . '\']' . $url . '[/zvideo]' . "\n\n"; + } else { + $s .= "\n\n" . '[zvideo]' . $url . '[/zvideo]' . "\n\n"; + } + } + if (strpos($r['data']['filetype'], 'audio') === 0) { + $s .= "\n\n" . '[zaudio]' . $url . '[/zaudio]' . "\n\n"; + } + if ($r['data']['filetype'] === 'image/svg+xml') { + $x = @file_get_contents('store/' . $channel['channel_address'] . '/' . $r['data']['os_path']); + if ($x) { + $bb = svg2bb($x); + if ($bb) { + $s .= "\n\n" . $bb; + } else { + logger('empty return from svgbb'); + } + } else { + logger('unable to read svg data file: ' . 'store/' . $channel['channel_address'] . '/' . $r['data']['os_path']); + } + } + if ($r['data']['filetype'] === 'text/vnd.abc' && addon_is_installed('abc')) { + $x = @file_get_contents('store/' . $channel['channel_address'] . '/' . $r['data']['os_path']); + if ($x) { + $s .= "\n\n" . '[abc]' . $x . '[/abc]'; + } else { + logger('unable to read ABC data file: ' . 'store/' . $channel['channel_address'] . '/' . $r['data']['os_path']); + } + } + if ($r['data']['filetype'] === 'text/calendar') { + $content = @file_get_contents('store/' . $channel['channel_address'] . '/' . $r['data']['os_path']); + if ($content) { + $ev = ical_to_ev($content); + if ($ev) { + $s .= "\n\n" . format_event_bbcode($ev[0]) . "\n\n"; + } + } + } - return $o; - } + $s .= "\n\n" . '[attachment]' . $r['data']['hash'] . ',' . $r['data']['revision'] . '[/attachment]' . "\n"; + $_REQUEST['body'] = ((array_key_exists('body', $_REQUEST)) ? $_REQUEST['body'] . $s : $s); + } + + // If we have saved rpost session variables, but nothing in the current $_REQUEST, recover the saved variables + + if ((!array_key_exists('body', $_REQUEST)) && (array_key_exists('rpost', $_SESSION))) { + $_REQUEST = $_SESSION['rpost']; + unset($_SESSION['rpost']); + } + + if (array_key_exists('channel', $_REQUEST)) { + $r = q( + "select channel_id from channel where channel_account_id = %d and channel_address = '%s' limit 1", + intval(get_account_id()), + dbesc($_REQUEST['channel']) + ); + if ($r) { + require_once('include/security.php'); + $change = change_channel($r[0]['channel_id']); + } + } + + if ($_REQUEST['remote_return']) { + $_SESSION['remote_return'] = $_REQUEST['remote_return']; + } + + if (argc() > 1 && argv(1) === 'return') { + if ($_SESSION['remote_return']) { + goaway($_SESSION['remote_return']); + } + goaway(z_root() . '/stream'); + } + + $plaintext = true; + + if (array_key_exists('type', $_REQUEST) && $_REQUEST['type'] === 'html' && isset($_REQUEST['body'])) { + require_once('include/html2bbcode.php'); + $_REQUEST['body'] = html2bbcode($_REQUEST['body']); + } + + $channel = App::get_channel(); + + $acl = new AccessControl($channel); + + if (array_key_exists('to', $_REQUEST) && $_REQUEST['to']) { + $acl->set(['allow_cid' => '<' . $_REQUEST['to'] . '>', + 'allow_gid' => EMPTY_STR, + 'deny_cid' => EMPTY_STR, + 'deny_gid' => EMPTY_STR]); + } + + $channel_acl = $acl->get(); + + if ($_REQUEST['url']) { + $x = z_fetch_url(z_root() . '/linkinfo?f=&url=' . urlencode($_REQUEST['url']) . '&oembed=1&zotobj=1'); + if ($x['success']) { + $_REQUEST['body'] = $_REQUEST['body'] . $x['body']; + } + } + + if ($_REQUEST['post_id']) { + $_REQUEST['body'] .= '[share=' . intval($_REQUEST['post_id']) . '][/share]'; + } + + $x = [ + 'is_owner' => true, + 'allow_location' => ((intval(get_pconfig($channel['channel_id'], 'system', 'use_browser_location'))) ? '1' : ''), + 'default_location' => $channel['channel_location'], + 'nickname' => $channel['channel_address'], + 'lockstate' => (($acl->is_private()) ? 'lock' : 'unlock'), + 'acl' => populate_acl($channel_acl, true, PermissionDescription::fromGlobalPermission('view_stream'), get_post_aclDialogDescription(), 'acl_dialog_post'), + 'permissions' => $channel_acl, + 'bang' => '', + 'visitor' => true, + 'profile_uid' => local_channel(), + 'title' => $_REQUEST['title'], + 'body' => $_REQUEST['body'], + 'attachment' => $_REQUEST['attachment'], + 'source' => ((x($_REQUEST, 'source')) ? strip_tags($_REQUEST['source']) : ''), + 'return_path' => 'rpost/return', + 'bbco_autocomplete' => 'bbcode', + 'editor_autocomplete' => true, + 'bbcode' => true, + 'jotnets' => true, + 'reset' => t('Reset form') + ]; + + $editor = status_editor($x); + + $o .= replace_macros(get_markup_template('edpost_head.tpl'), [ + '$title' => t('Edit post'), + '$cancel' => '', + '$editor' => $editor + ]); + + return $o; + } } diff --git a/Zotlabs/Module/Safe.php b/Zotlabs/Module/Safe.php index 586e79275..d7ecd4b1f 100644 --- a/Zotlabs/Module/Safe.php +++ b/Zotlabs/Module/Safe.php @@ -4,20 +4,18 @@ namespace Zotlabs\Module; use Zotlabs\Web\Controller; -class Safe extends Controller { +class Safe extends Controller +{ - function init() { + public function init() + { - $x = get_safemode(); - if ($x) { - $_SESSION['safemode'] = 0; - } - else { - $_SESSION['safemode'] = 1; - } - goaway(z_root()); - } - - - -} \ No newline at end of file + $x = get_safemode(); + if ($x) { + $_SESSION['safemode'] = 0; + } else { + $_SESSION['safemode'] = 1; + } + goaway(z_root()); + } +} diff --git a/Zotlabs/Module/Search.php b/Zotlabs/Module/Search.php index 83200af07..5b42c2637 100644 --- a/Zotlabs/Module/Search.php +++ b/Zotlabs/Module/Search.php @@ -1,4 +1,5 @@ loading) { - $_SESSION['loadtime'] = datetime_convert(); - } - nav_set_selected('Search'); - - $format = (($_REQUEST['format']) ? $_REQUEST['format'] : ''); - if ($format !== '') { - $this->updating = $this->loading = 1; - } - - $observer = App::get_observer(); - $observer_hash = (($observer) ? $observer['xchan_hash'] : ''); - - $o = '' . "\r\n"; - $o .= '
      ' . "\r\n"; - $o .= '

      ' . t('Search') . '

      '; - - if (x(App::$data,'search')) { - $search = trim(App::$data['search']); - } - else { - $search = ((x($_GET,'search')) ? trim(escape_tags(rawurldecode($_GET['search']))) : ''); - } - $tag = false; - if (x($_GET,'tag')) { - $tag = true; - $search = ((x($_GET,'tag')) ? trim(escape_tags(rawurldecode($_GET['tag']))) : ''); - } - - $static = ((array_key_exists('static',$_REQUEST)) ? intval($_REQUEST['static']) : 0); - - $o .= search($search,'search-box','/search',((local_channel()) ? true : false)); - - // ActivityStreams object fetches from the navbar - - if (local_channel() && strpos($search,'https://') === 0 && (! $this->updating) && (! $this->loading)) { - logger('searching for ActivityPub'); - $channel = App::get_channel(); - $hash = EMPTY_STR; - $j = Activity::fetch($search,$channel); - if ($j) { - if (isset($j['type']) && ActivityStreams::is_an_actor($j['type'])) { - Activity::actor_store($j['id'],$j); - goaway(z_root() . '/directory' . '?f=1&navsearch=1&search=' . $search); - } - $AS = new ActivityStreams($j, null, true); - if ($AS->is_valid() && isset($AS->data['type'])) { - if (is_array($AS->obj)) { - // matches Collection and orderedCollection - if (isset($AS->obj['type']) && strpos($AS->obj['type'],'Collection') !== false) { - - // Collections are awkward to process because they can be huge. - // Our strategy is to limit a navbar search to 100 Collection items - // and only fetch the first 10 conversations in the foreground. - // We'll queue the rest, and then send you to a page where - // you can see something we've imported. - // In theory you'll start to see notifications as other conversations - // are fetched in the background while you're looking at the first ones. - - $max = intval(get_config('system','max_imported_search_collection',100)); - - if (intval($max)) { - $obj = new ASCollection($search, $channel, 0, $max); - $messages = $obj->get(); - // logger('received: ' . print_r($messages,true)); - $author = null; - if ($messages) { - logger('received ' . count($messages) . ' items from collection.', LOGGER_DEBUG); - $processed = 0; - foreach ($messages as $message) { - $processed ++; - // only process the first several items in the foreground and - // queue the remainder. - if ($processed > 10) { - - $fetch_url = ((is_string($message)) ? $message : EMPTY_STR); - $fetch_url = ((is_array($message) && array_key_exists('id',$message)) ? $message['id'] : $fetch_url); - - if (! $fetch_url) { - continue; - } - - $hash = new_uuid(); - Queue::insert( - [ - 'hash' => $hash, - 'account_id' => $channel['channel_account_id'], - 'channel_id' => $channel['channel_id'], - 'posturl' => $fetch_url, - 'notify' => EMPTY_STR, - 'msg' => EMPTY_STR, - 'driver' => 'asfetch' - ] - ); - continue; - } - - if (is_string($message)) { - $message = Activity::fetch($message,App::get_channel()); - } - $AS = new ActivityStreams($message,null,true); - if ($AS->is_valid() && is_array($AS->obj)) { - $item = Activity::decode_note($AS,true); - } - if ($item) { - if (! $author) { - $author = $item['author_xchan']; - } - Activity::store(App::get_channel(),get_observer_hash(),$AS,$item, true, true); - } - } - if ($hash) { - Run::Summon( [ 'Deliver', $hash ] ); - } - } - - // This will go to the right place most but not all of the time. - // It will go to a relevant place all of the time, so we'll use it. - - if ($author) { - goaway(z_root() . '/stream/?xchan=' . urlencode($author)); - } - goaway(z_root() . '/stream'); - } - } - else { - - // It wasn't a Collection object and wasn't an Actor object, - // so let's see if it decodes. The boolean flag enables html - // cache of the item - - $item = Activity::decode_note($AS,true); - if ($item) { - Activity::store(App::get_channel(),get_observer_hash(),$AS,$item, true, true); - goaway(z_root() . '/display/' . gen_link_id($item['mid'])); - } - } - } - } - } - } - - if (strpos($search,'#') === 0) { - $tag = true; - $search = substr($search,1); - } - if (strpos($search,'@') === 0) { - $search = substr($search,1); - goaway(z_root() . '/directory' . '?f=1&navsearch=1&search=' . $search); - } - if (strpos($search,'!') === 0) { - $search = substr($search,1); - goaway(z_root() . '/directory' . '?f=1&navsearch=1&search=' . $search); - } - if (strpos($search,'?') === 0) { - $search = substr($search,1); - goaway(z_root() . '/help' . '?f=1&navsearch=1&search=' . $search); - } - - // look for a naked webbie - if (strpos($search,'@') !== false && strpos($search,'http') !== 0) { - goaway(z_root() . '/directory' . '?f=1&navsearch=1&search=' . $search); - } - - if (! $search) { - return $o; - } - - if ($tag) { - $wildtag = str_replace('*','%',$search); - $sql_extra = sprintf(" AND item.id IN (select oid from term where otype = %d and ttype in ( %d , %d) and term like '%s') ", - intval(TERM_OBJ_POST), - intval(TERM_HASHTAG), - intval(TERM_COMMUNITYTAG), - dbesc(protect_sprintf($wildtag)) - ); - } - else { - $regstr = db_getfunc('REGEXP'); - $sql_extra = sprintf(" AND (item.title $regstr '%s' OR item.body $regstr '%s') ", dbesc(protect_sprintf(preg_quote($search))), dbesc(protect_sprintf(preg_quote($search)))); - } - - // Here is the way permissions work in the search module... - // Only public posts can be shown - // OR your own posts if you are a logged in member - // No items will be shown if the member has a blocked profile wall. - - - if ((! $this->updating) && (! $this->loading)) { - - $static = ((local_channel()) ? channel_manual_conv_update(local_channel()) : 0); + public function init() + { + if (x($_REQUEST, 'search')) { + App::$data['search'] = escape_tags($_REQUEST['search']); + } + } - // This is ugly, but we can't pass the profile_uid through the session to the ajax updater, - // because browser prefetching might change it on us. We have to deliver it with the page. - - $o .= '' . "\r\n"; - $o .= "\r\n"; - - App::$page['htmlhead'] .= replace_macros(get_markup_template("build_query.tpl"), [ - '$baseurl' => z_root(), - '$pgtype' => 'search', - '$uid' => ((App::$profile['profile_uid']) ? App::$profile['profile_uid'] : '0'), - '$gid' => '0', - '$cid' => '0', - '$cmin' => '(-1)', - '$cmax' => '(-1)', - '$star' => '0', - '$liked' => '0', - '$conv' => '0', - '$spam' => '0', - '$fh' => '0', - '$dm' => '0', - '$nouveau' => '0', - '$wall' => '0', - '$draft' => '0', - '$static' => $static, - '$list' => ((x($_REQUEST,'list')) ? intval($_REQUEST['list']) : 0), - '$page' => ((App::$pager['page'] != 1) ? App::$pager['page'] : 1), - '$search' => (($tag) ? urlencode('#') : '') . $search, - '$xchan' => '', - '$order' => '', - '$file' => '', - '$cats' => '', - '$tags' => '', - '$mid' => '', - '$verb' => '', - '$net' => '', - '$dend' => '', - '$dbegin' => '' - ]); - - - } - - $item_normal = item_normal_search(); - $pub_sql = item_permissions_sql(0,$observer_hash); - - $sys = get_sys_channel(); - - if (($this->updating) && ($this->loading)) { - $itemspage = get_pconfig(local_channel(),'system','itemspage'); - App::set_pager_itemspage(((intval($itemspage)) ? $itemspage : 20)); - $pager_sql = sprintf(" LIMIT %d OFFSET %d ", intval(App::$pager['itemspage']), intval(App::$pager['start'])); - - if ($this->loading) { - $r = null; + public function get() + { - // if logged in locally, first look in the items you own - // and if this returns zero results, resort to searching elsewhere on the site. - // Ideally these results would be merged but this can be difficult - // and results in lots of duplicated content and/or messed up pagination - - if (local_channel()) { - $r = q("SELECT mid, MAX(id) as item_id from item where uid = %d + if ((get_config('system', 'block_public')) || (get_config('system', 'block_public_search', 1))) { + if ((!local_channel()) && (!remote_channel())) { + notice(t('Public access denied.') . EOL); + return; + } + } + + if ($this->loading) { + $_SESSION['loadtime'] = datetime_convert(); + } + nav_set_selected('Search'); + + $format = (($_REQUEST['format']) ? $_REQUEST['format'] : ''); + if ($format !== '') { + $this->updating = $this->loading = 1; + } + + $observer = App::get_observer(); + $observer_hash = (($observer) ? $observer['xchan_hash'] : ''); + + $o = '' . "\r\n"; + $o .= '
      ' . "\r\n"; + $o .= '

      ' . t('Search') . '

      '; + + if (x(App::$data, 'search')) { + $search = trim(App::$data['search']); + } else { + $search = ((x($_GET, 'search')) ? trim(escape_tags(rawurldecode($_GET['search']))) : ''); + } + $tag = false; + if (x($_GET, 'tag')) { + $tag = true; + $search = ((x($_GET, 'tag')) ? trim(escape_tags(rawurldecode($_GET['tag']))) : ''); + } + + $static = ((array_key_exists('static', $_REQUEST)) ? intval($_REQUEST['static']) : 0); + + $o .= search($search, 'search-box', '/search', ((local_channel()) ? true : false)); + + // ActivityStreams object fetches from the navbar + + if (local_channel() && strpos($search, 'https://') === 0 && (!$this->updating) && (!$this->loading)) { + logger('searching for ActivityPub'); + $channel = App::get_channel(); + $hash = EMPTY_STR; + $j = Activity::fetch($search, $channel); + if ($j) { + if (isset($j['type']) && ActivityStreams::is_an_actor($j['type'])) { + Activity::actor_store($j['id'], $j); + goaway(z_root() . '/directory' . '?f=1&navsearch=1&search=' . $search); + } + $AS = new ActivityStreams($j, null, true); + if ($AS->is_valid() && isset($AS->data['type'])) { + if (is_array($AS->obj)) { + // matches Collection and orderedCollection + if (isset($AS->obj['type']) && strpos($AS->obj['type'], 'Collection') !== false) { + // Collections are awkward to process because they can be huge. + // Our strategy is to limit a navbar search to 100 Collection items + // and only fetch the first 10 conversations in the foreground. + // We'll queue the rest, and then send you to a page where + // you can see something we've imported. + // In theory you'll start to see notifications as other conversations + // are fetched in the background while you're looking at the first ones. + + $max = intval(get_config('system', 'max_imported_search_collection', 100)); + + if (intval($max)) { + $obj = new ASCollection($search, $channel, 0, $max); + $messages = $obj->get(); + // logger('received: ' . print_r($messages,true)); + $author = null; + if ($messages) { + logger('received ' . count($messages) . ' items from collection.', LOGGER_DEBUG); + $processed = 0; + foreach ($messages as $message) { + $processed++; + // only process the first several items in the foreground and + // queue the remainder. + if ($processed > 10) { + $fetch_url = ((is_string($message)) ? $message : EMPTY_STR); + $fetch_url = ((is_array($message) && array_key_exists('id', $message)) ? $message['id'] : $fetch_url); + + if (!$fetch_url) { + continue; + } + + $hash = new_uuid(); + Queue::insert( + [ + 'hash' => $hash, + 'account_id' => $channel['channel_account_id'], + 'channel_id' => $channel['channel_id'], + 'posturl' => $fetch_url, + 'notify' => EMPTY_STR, + 'msg' => EMPTY_STR, + 'driver' => 'asfetch' + ] + ); + continue; + } + + if (is_string($message)) { + $message = Activity::fetch($message, App::get_channel()); + } + $AS = new ActivityStreams($message, null, true); + if ($AS->is_valid() && is_array($AS->obj)) { + $item = Activity::decode_note($AS, true); + } + if ($item) { + if (!$author) { + $author = $item['author_xchan']; + } + Activity::store(App::get_channel(), get_observer_hash(), $AS, $item, true, true); + } + } + if ($hash) { + Run::Summon(['Deliver', $hash]); + } + } + + // This will go to the right place most but not all of the time. + // It will go to a relevant place all of the time, so we'll use it. + + if ($author) { + goaway(z_root() . '/stream/?xchan=' . urlencode($author)); + } + goaway(z_root() . '/stream'); + } + } else { + // It wasn't a Collection object and wasn't an Actor object, + // so let's see if it decodes. The boolean flag enables html + // cache of the item + + $item = Activity::decode_note($AS, true); + if ($item) { + Activity::store(App::get_channel(), get_observer_hash(), $AS, $item, true, true); + goaway(z_root() . '/display/' . gen_link_id($item['mid'])); + } + } + } + } + } + } + + if (strpos($search, '#') === 0) { + $tag = true; + $search = substr($search, 1); + } + if (strpos($search, '@') === 0) { + $search = substr($search, 1); + goaway(z_root() . '/directory' . '?f=1&navsearch=1&search=' . $search); + } + if (strpos($search, '!') === 0) { + $search = substr($search, 1); + goaway(z_root() . '/directory' . '?f=1&navsearch=1&search=' . $search); + } + if (strpos($search, '?') === 0) { + $search = substr($search, 1); + goaway(z_root() . '/help' . '?f=1&navsearch=1&search=' . $search); + } + + // look for a naked webbie + if (strpos($search, '@') !== false && strpos($search, 'http') !== 0) { + goaway(z_root() . '/directory' . '?f=1&navsearch=1&search=' . $search); + } + + if (!$search) { + return $o; + } + + if ($tag) { + $wildtag = str_replace('*', '%', $search); + $sql_extra = sprintf( + " AND item.id IN (select oid from term where otype = %d and ttype in ( %d , %d) and term like '%s') ", + intval(TERM_OBJ_POST), + intval(TERM_HASHTAG), + intval(TERM_COMMUNITYTAG), + dbesc(protect_sprintf($wildtag)) + ); + } else { + $regstr = db_getfunc('REGEXP'); + $sql_extra = sprintf(" AND (item.title $regstr '%s' OR item.body $regstr '%s') ", dbesc(protect_sprintf(preg_quote($search))), dbesc(protect_sprintf(preg_quote($search)))); + } + + // Here is the way permissions work in the search module... + // Only public posts can be shown + // OR your own posts if you are a logged in member + // No items will be shown if the member has a blocked profile wall. + + + if ((!$this->updating) && (!$this->loading)) { + $static = ((local_channel()) ? channel_manual_conv_update(local_channel()) : 0); + + + // This is ugly, but we can't pass the profile_uid through the session to the ajax updater, + // because browser prefetching might change it on us. We have to deliver it with the page. + + $o .= '' . "\r\n"; + $o .= "\r\n"; + + App::$page['htmlhead'] .= replace_macros(get_markup_template("build_query.tpl"), [ + '$baseurl' => z_root(), + '$pgtype' => 'search', + '$uid' => ((App::$profile['profile_uid']) ? App::$profile['profile_uid'] : '0'), + '$gid' => '0', + '$cid' => '0', + '$cmin' => '(-1)', + '$cmax' => '(-1)', + '$star' => '0', + '$liked' => '0', + '$conv' => '0', + '$spam' => '0', + '$fh' => '0', + '$dm' => '0', + '$nouveau' => '0', + '$wall' => '0', + '$draft' => '0', + '$static' => $static, + '$list' => ((x($_REQUEST, 'list')) ? intval($_REQUEST['list']) : 0), + '$page' => ((App::$pager['page'] != 1) ? App::$pager['page'] : 1), + '$search' => (($tag) ? urlencode('#') : '') . $search, + '$xchan' => '', + '$order' => '', + '$file' => '', + '$cats' => '', + '$tags' => '', + '$mid' => '', + '$verb' => '', + '$net' => '', + '$dend' => '', + '$dbegin' => '' + ]); + } + + $item_normal = item_normal_search(); + $pub_sql = item_permissions_sql(0, $observer_hash); + + $sys = get_sys_channel(); + + if (($this->updating) && ($this->loading)) { + $itemspage = get_pconfig(local_channel(), 'system', 'itemspage'); + App::set_pager_itemspage(((intval($itemspage)) ? $itemspage : 20)); + $pager_sql = sprintf(" LIMIT %d OFFSET %d ", intval(App::$pager['itemspage']), intval(App::$pager['start'])); + + if ($this->loading) { + $r = null; + + // if logged in locally, first look in the items you own + // and if this returns zero results, resort to searching elsewhere on the site. + // Ideally these results would be merged but this can be difficult + // and results in lots of duplicated content and/or messed up pagination + + if (local_channel()) { + $r = q( + "SELECT mid, MAX(id) as item_id from item where uid = %d $item_normal $sql_extra group by mid, created order by created desc $pager_sql ", - intval(local_channel()) - ); - } - if (! $r) { - $r = q("SELECT mid, MAX(id) as item_id from item WHERE true $pub_sql + intval(local_channel()) + ); + } + if (!$r) { + $r = q("SELECT mid, MAX(id) as item_id from item WHERE true $pub_sql $item_normal $sql_extra - group by mid, created order by created desc $pager_sql" - ); - } - if ($r) { - $str = ids_to_querystr($r,'item_id'); - $r = q("select *, id as item_id from item where id in ( " . $str . ") order by created desc "); - } - } - else { - $r = []; - } - - } - - if ($r) { - xchan_query($r); - $items = fetch_post_tags($r,true); - } else { - $items = []; - } + group by mid, created order by created desc $pager_sql"); + } + if ($r) { + $str = ids_to_querystr($r, 'item_id'); + $r = q("select *, id as item_id from item where id in ( " . $str . ") order by created desc "); + } + } else { + $r = []; + } + } - if ($format == 'json') { - $result = []; - require_once('include/conversation.php'); - foreach ($items as $item) { - $item['html'] = zidify_links(bbcode($item['body'])); - $x = encode_item($item); - $x['html'] = prepare_text($item['body'],$item['mimetype']); - $result[] = $x; - } - json_return_and_die(array('success' => true,'messages' => $result)); - } - - if ($tag) - $o .= '

      ' . sprintf( t('Items tagged with: %s'), $search) . '

      '; - else - $o .= '

      ' . sprintf( t('Search results for: %s'), $search) . '

      '; - - $o .= conversation($items,'search',$this->updating,'client'); - - $o .= '
      '; - - return $o; - } - + if ($r) { + xchan_query($r); + $items = fetch_post_tags($r, true); + } else { + $items = []; + } + + if ($format == 'json') { + $result = []; + require_once('include/conversation.php'); + foreach ($items as $item) { + $item['html'] = zidify_links(bbcode($item['body'])); + $x = encode_item($item); + $x['html'] = prepare_text($item['body'], $item['mimetype']); + $result[] = $x; + } + json_return_and_die(array('success' => true, 'messages' => $result)); + } + + if ($tag) { + $o .= '

      ' . sprintf(t('Items tagged with: %s'), $search) . '

      '; + } else { + $o .= '

      ' . sprintf(t('Search results for: %s'), $search) . '

      '; + } + + $o .= conversation($items, 'search', $this->updating, 'client'); + + $o .= '
      '; + + return $o; + } } diff --git a/Zotlabs/Module/Search_ac.php b/Zotlabs/Module/Search_ac.php index c548d6e47..37c050313 100644 --- a/Zotlabs/Module/Search_ac.php +++ b/Zotlabs/Module/Search_ac.php @@ -1,100 +1,101 @@ $g['xchan_photo_s'], - 'name' => '@' . $g['xchan_name'], - 'id' => $g['abook_id'], - 'link' => $g['xchan_url'], - 'label' => '', - 'nick' => '', - ]; - } - } - } + intval(local_channel()) + ); - if ($do_tags) { - $r = q("select distinct term, tid, url from term + if ($r) { + foreach ($r as $g) { + $results[] = [ + 'photo' => $g['xchan_photo_s'], + 'name' => '@' . $g['xchan_name'], + 'id' => $g['abook_id'], + 'link' => $g['xchan_url'], + 'label' => '', + 'nick' => '', + ]; + } + } + } + + if ($do_tags) { + $r = q( + "select distinct term, tid, url from term where ttype in ( %d, %d ) $tag_sql_extra group by term order by term asc", - intval(TERM_HASHTAG), - intval(TERM_COMMUNITYTAG) - ); - - if ($r) { - foreach ($r as $g) { - $results[] = [ - 'photo' => z_root() . '/images/hashtag.png', - 'name' => '#' . $g['term'], - 'id' => $g['tid'], - 'link' => $g['url'], - 'label' => '', - 'nick' => '', - ]; - } - } - } + intval(TERM_HASHTAG), + intval(TERM_COMMUNITYTAG) + ); - json_return_and_die( [ - 'start' => $start, - 'count' => $count, - 'items' => $results, - ]); - - } - + if ($r) { + foreach ($r as $g) { + $results[] = [ + 'photo' => z_root() . '/images/hashtag.png', + 'name' => '#' . $g['term'], + 'id' => $g['tid'], + 'link' => $g['url'], + 'label' => '', + 'nick' => '', + ]; + } + } + } + + json_return_and_die([ + 'start' => $start, + 'count' => $count, + 'items' => $results, + ]); + } } diff --git a/Zotlabs/Module/Secrets.php b/Zotlabs/Module/Secrets.php index b6f77270a..e720d81fd 100644 --- a/Zotlabs/Module/Secrets.php +++ b/Zotlabs/Module/Secrets.php @@ -6,23 +6,24 @@ use Zotlabs\Lib\Apps; use Zotlabs\Lib\Libsync; use Zotlabs\Web\Controller; -class Secrets extends Controller { +class Secrets extends Controller +{ - function get() { + public function get() + { $desc = t('This app allows you to protect messages with a secret passphrase. This only works across selected platforms.'); $text = ''; - if(! ( local_channel() && Apps::system_app_installed(local_channel(),'Secrets'))) { + if (!(local_channel() && Apps::system_app_installed(local_channel(), 'Secrets'))) { return $text; } - $desc = t('This app is installed. A button to encrypt content may be found in the post editor.'); + $desc = t('This app is installed. A button to encrypt content may be found in the post editor.'); - $text = ''; + $text = ''; - return $text; - - } + return $text; + } } diff --git a/Zotlabs/Module/Service_limits.php b/Zotlabs/Module/Service_limits.php index 2a1f78054..05ca54322 100644 --- a/Zotlabs/Module/Service_limits.php +++ b/Zotlabs/Module/Service_limits.php @@ -1,28 +1,30 @@ sm = new SubModule(); - } - - - function post() { - - if (! local_channel()) { - return; - } - - if ($_SESSION['delegate']) { - return; - } - - // logger('mod_settings: ' . print_r($_REQUEST,true)); - - if (argc() > 1) { - if ($this->sm->call('post') !== false) { - return; - } - } - - goaway(z_root() . '/settings' ); - } - - - - function get() { - - nav_set_selected('Settings'); - - if ((! local_channel()) || ($_SESSION['delegate'])) { - notice( t('Permission denied.') . EOL ); - return login(); - } - - - $channel = App::get_channel(); - if ($channel) { - head_set_icon($channel['xchan_photo_s']); - } - - $o = $this->sm->call('get'); - if ($o !== false) { - return $o; - } + if ($_SESSION['delegate']) { + return; + } - $o = EMPTY_STR; - } + App::$profile_uid = local_channel(); + + // default is channel settings in the absence of other arguments + + if (argc() == 1) { + // We are setting these values - don't use the argc(), argv() functions here + App::$argc = 2; + App::$argv[] = 'channel'; + } + + $this->sm = new SubModule(); + } + + + public function post() + { + + if (!local_channel()) { + return; + } + + if ($_SESSION['delegate']) { + return; + } + + // logger('mod_settings: ' . print_r($_REQUEST,true)); + + if (argc() > 1) { + if ($this->sm->call('post') !== false) { + return; + } + } + + goaway(z_root() . '/settings'); + } + + + public function get() + { + + nav_set_selected('Settings'); + + if ((!local_channel()) || ($_SESSION['delegate'])) { + notice(t('Permission denied.') . EOL); + return login(); + } + + + $channel = App::get_channel(); + if ($channel) { + head_set_icon($channel['xchan_photo_s']); + } + + $o = $this->sm->call('get'); + if ($o !== false) { + return $o; + } + + $o = EMPTY_STR; + } } - - diff --git a/Zotlabs/Module/Settings/Account.php b/Zotlabs/Module/Settings/Account.php index 29418c621..59999f0a1 100644 --- a/Zotlabs/Module/Settings/Account.php +++ b/Zotlabs/Module/Settings/Account.php @@ -2,112 +2,121 @@ namespace Zotlabs\Module\Settings; -class Account { +use App; - function post() { - check_form_security_token_redirectOnErr('/settings/account', 'settings_account'); - - call_hooks('account_settings_post', $_POST); - - $errs = []; - - $email = ((x($_POST,'email')) ? trim(notags($_POST['email'])) : ''); +class Account +{ - $account = \App::get_account(); - if($email != $account['account_email']) { - if(! validate_email($email)) - $errs[] = t('Not valid email.'); - $adm = trim(get_config('system','admin_email')); - if(($adm) && (strcasecmp($email,$adm) == 0)) { - $errs[] = t('Protected email address. Cannot change to that email.'); - $email = \App::$account['account_email']; - } - if(! $errs) { - $r = q("update account set account_email = '%s' where account_id = %d", - dbesc($email), - intval($account['account_id']) - ); - if(! $r) - $errs[] = t('System failure storing new email. Please try again.'); - } - } - - if($errs) { - foreach($errs as $err) - notice($err . EOL); - $errs = []; - } - - - if((x($_POST,'npassword')) || (x($_POST,'confirm'))) { - - $origpass = trim($_POST['origpass']); - - require_once('include/auth.php'); - if(! account_verify_password($email,$origpass)) { - $errs[] = t('Password verification failed.'); - } - - $newpass = trim($_POST['npassword']); - $confirm = trim($_POST['confirm']); - - if($newpass != $confirm ) { - $errs[] = t('Passwords do not match. Password unchanged.'); - } - - if((! x($newpass)) || (! x($confirm))) { - $errs[] = t('Empty passwords are not allowed. Password unchanged.'); - } - - if(! $errs) { - $salt = random_string(32); - $password_encoded = hash('whirlpool', $salt . $newpass); - $r = q("update account set account_salt = '%s', account_password = '%s', account_password_changed = '%s' + public function post() + { + check_form_security_token_redirectOnErr('/settings/account', 'settings_account'); + + call_hooks('account_settings_post', $_POST); + + $errs = []; + + $email = ((x($_POST, 'email')) ? trim(notags($_POST['email'])) : ''); + + $account = App::get_account(); + if ($email != $account['account_email']) { + if (!validate_email($email)) { + $errs[] = t('Not valid email.'); + } + $adm = trim(get_config('system', 'admin_email')); + if (($adm) && (strcasecmp($email, $adm) == 0)) { + $errs[] = t('Protected email address. Cannot change to that email.'); + $email = App::$account['account_email']; + } + if (!$errs) { + $r = q( + "update account set account_email = '%s' where account_id = %d", + dbesc($email), + intval($account['account_id']) + ); + if (!$r) { + $errs[] = t('System failure storing new email. Please try again.'); + } + } + } + + if ($errs) { + foreach ($errs as $err) { + notice($err . EOL); + } + $errs = []; + } + + + if ((x($_POST, 'npassword')) || (x($_POST, 'confirm'))) { + $origpass = trim($_POST['origpass']); + + require_once('include/auth.php'); + if (!account_verify_password($email, $origpass)) { + $errs[] = t('Password verification failed.'); + } + + $newpass = trim($_POST['npassword']); + $confirm = trim($_POST['confirm']); + + if ($newpass != $confirm) { + $errs[] = t('Passwords do not match. Password unchanged.'); + } + + if ((!x($newpass)) || (!x($confirm))) { + $errs[] = t('Empty passwords are not allowed. Password unchanged.'); + } + + if (!$errs) { + $salt = random_string(32); + $password_encoded = hash('whirlpool', $salt . $newpass); + $r = q( + "update account set account_salt = '%s', account_password = '%s', account_password_changed = '%s' where account_id = %d", - dbesc($salt), - dbesc($password_encoded), - dbesc(datetime_convert()), - intval(get_account_id()) - ); - if($r) - info( t('Password changed.') . EOL); - else - $errs[] = t('Password update failed. Please try again.'); - } - } - - - if($errs) { - foreach($errs as $err) - notice($err . EOL); - } - goaway(z_root() . '/settings/account' ); - } - - - - function get() { - $account_settings = ""; - - call_hooks('account_settings', $account_settings); - - $email = \App::$account['account_email']; + dbesc($salt), + dbesc($password_encoded), + dbesc(datetime_convert()), + intval(get_account_id()) + ); + if ($r) { + info(t('Password changed.') . EOL); + } else { + $errs[] = t('Password update failed. Please try again.'); + } + } + } - $tpl = get_markup_template("settings_account.tpl"); - $o .= replace_macros($tpl, array( - '$form_security_token' => get_form_security_token("settings_account"), - '$title' => t('Account Settings'), - '$origpass' => array('origpass', t('Current Password'), ' ',''), - '$password1'=> array('npassword', t('Enter New Password'), '', ''), - '$password2'=> array('confirm', t('Confirm New Password'), '', t('Leave password fields blank unless changing')), - '$submit' => t('Submit'), - '$email' => array('email', t('Email Address:'), $email, ''), - '$removeme' => t('Remove Account'), - '$removeaccount' => t('Remove this account including all its channels'), - '$account_settings' => $account_settings - )); - return $o; - } + if ($errs) { + foreach ($errs as $err) { + notice($err . EOL); + } + } + goaway(z_root() . '/settings/account'); + } + + public function get() + { + $account_settings = ""; + + call_hooks('account_settings', $account_settings); + + $email = App::$account['account_email']; + + + $tpl = get_markup_template("settings_account.tpl"); + $o .= replace_macros($tpl, array( + '$form_security_token' => get_form_security_token("settings_account"), + '$title' => t('Account Settings'), + '$origpass' => array('origpass', t('Current Password'), ' ', ''), + '$password1' => array('npassword', t('Enter New Password'), '', ''), + '$password2' => array('confirm', t('Confirm New Password'), '', t('Leave password fields blank unless changing')), + '$submit' => t('Submit'), + '$email' => array('email', t('Email Address:'), $email, ''), + '$removeme' => t('Remove Account'), + '$removeaccount' => t('Remove this account including all its channels'), + '$account_settings' => $account_settings + )); + return $o; + } } diff --git a/Zotlabs/Module/Settings/Channel.php b/Zotlabs/Module/Settings/Channel.php index 5cdc15418..8c9d83145 100644 --- a/Zotlabs/Module/Settings/Channel.php +++ b/Zotlabs/Module/Settings/Channel.php @@ -13,723 +13,756 @@ use Zotlabs\Access\AccessControl; use Zotlabs\Daemon\Run; use Zotlabs\Lib\Permcat; -class Channel { +class Channel +{ - function post() { + public function post() + { - $channel = App::get_channel(); + $channel = App::get_channel(); - check_form_security_token_redirectOnErr('/settings', 'settings'); - - call_hooks('settings_post', $_POST); - - $set_perms = ''; - - $role = ((x($_POST,'permissions_role')) ? notags(trim($_POST['permissions_role'])) : ''); - $oldrole = get_pconfig(local_channel(),'system','permissions_role'); + check_form_security_token_redirectOnErr('/settings', 'settings'); - $forbidden_roles = [ 'collection', 'collection_restricted' ]; - if (in_array($role,$forbidden_roles) || in_array($oldrole,$forbidden_roles)) { - $role = $oldrole; - } + call_hooks('settings_post', $_POST); - if(($role != $oldrole) || ($role === 'custom')) { - - if($role === 'custom') { - $hide_presence = (((x($_POST,'hide_presence')) && (intval($_POST['hide_presence']) == 1)) ? 1: 0); - $def_group = ((x($_POST,'group-selection')) ? notags(trim($_POST['group-selection'])) : ''); - $r = q("update channel set channel_default_group = '%s' where channel_id = %d", - dbesc($def_group), - intval(local_channel()) - ); - - $global_perms = Permissions::Perms(); - - foreach($global_perms as $k => $v) { - PermissionLimits::Set(local_channel(),$k,intval($_POST[$k])); - } - $acl = new AccessControl($channel); - $acl->set_from_array($_POST); - $x = $acl->get(); - - $r = q("update channel set channel_allow_cid = '%s', channel_allow_gid = '%s', + $set_perms = ''; + + $role = ((x($_POST, 'permissions_role')) ? notags(trim($_POST['permissions_role'])) : ''); + $oldrole = get_pconfig(local_channel(), 'system', 'permissions_role'); + + $forbidden_roles = ['collection', 'collection_restricted']; + if (in_array($role, $forbidden_roles) || in_array($oldrole, $forbidden_roles)) { + $role = $oldrole; + } + + if (($role != $oldrole) || ($role === 'custom')) { + if ($role === 'custom') { + $hide_presence = (((x($_POST, 'hide_presence')) && (intval($_POST['hide_presence']) == 1)) ? 1 : 0); + $def_group = ((x($_POST, 'group-selection')) ? notags(trim($_POST['group-selection'])) : ''); + $r = q( + "update channel set channel_default_group = '%s' where channel_id = %d", + dbesc($def_group), + intval(local_channel()) + ); + + $global_perms = Permissions::Perms(); + + foreach ($global_perms as $k => $v) { + PermissionLimits::Set(local_channel(), $k, intval($_POST[$k])); + } + $acl = new AccessControl($channel); + $acl->set_from_array($_POST); + $x = $acl->get(); + + $r = q( + "update channel set channel_allow_cid = '%s', channel_allow_gid = '%s', channel_deny_cid = '%s', channel_deny_gid = '%s' where channel_id = %d", - dbesc($x['allow_cid']), - dbesc($x['allow_gid']), - dbesc($x['deny_cid']), - dbesc($x['deny_gid']), - intval(local_channel()) - ); - } - else { - $role_permissions = PermissionRoles::role_perms($_POST['permissions_role']); - if(! $role_permissions) { - notice('Permissions category could not be found.'); - return; - } - $hide_presence = 1 - (intval($role_permissions['online'])); - if($role_permissions['default_collection']) { - $r = q("select hash from pgrp where uid = %d and gname = '%s' limit 1", - intval(local_channel()), - dbesc( t('Friends') ) - ); - if(! $r) { - - AccessList::add(local_channel(), t('Friends')); - AccessList::member_add(local_channel(),t('Friends'),$channel['channel_hash']); - $r = q("select hash from pgrp where uid = %d and gname = '%s' limit 1", - intval(local_channel()), - dbesc( t('Friends') ) - ); - } - if($r) { - q("update channel set channel_default_group = '%s', channel_allow_gid = '%s', channel_allow_cid = '', channel_deny_gid = '', channel_deny_cid = '' where channel_id = %d", - dbesc($r[0]['hash']), - dbesc('<' . $r[0]['hash'] . '>'), - intval(local_channel()) - ); - } - else { - notice( sprintf('Default access list \'%s\' not found. Please create and re-submit permission change.', t('Friends')) . EOL); - return; - } - } - // no default permissions - else { - q("update channel set channel_default_group = '', channel_allow_gid = '', channel_allow_cid = '', channel_deny_gid = '', + dbesc($x['allow_cid']), + dbesc($x['allow_gid']), + dbesc($x['deny_cid']), + dbesc($x['deny_gid']), + intval(local_channel()) + ); + } else { + $role_permissions = PermissionRoles::role_perms($_POST['permissions_role']); + if (!$role_permissions) { + notice('Permissions category could not be found.'); + return; + } + $hide_presence = 1 - (intval($role_permissions['online'])); + if ($role_permissions['default_collection']) { + $r = q( + "select hash from pgrp where uid = %d and gname = '%s' limit 1", + intval(local_channel()), + dbesc(t('Friends')) + ); + if (!$r) { + AccessList::add(local_channel(), t('Friends')); + AccessList::member_add(local_channel(), t('Friends'), $channel['channel_hash']); + $r = q( + "select hash from pgrp where uid = %d and gname = '%s' limit 1", + intval(local_channel()), + dbesc(t('Friends')) + ); + } + if ($r) { + q( + "update channel set channel_default_group = '%s', channel_allow_gid = '%s', channel_allow_cid = '', channel_deny_gid = '', channel_deny_cid = '' where channel_id = %d", + dbesc($r[0]['hash']), + dbesc('<' . $r[0]['hash'] . '>'), + intval(local_channel()) + ); + } else { + notice(sprintf('Default access list \'%s\' not found. Please create and re-submit permission change.', t('Friends')) . EOL); + return; + } + } // no default permissions + else { + q( + "update channel set channel_default_group = '', channel_allow_gid = '', channel_allow_cid = '', channel_deny_gid = '', channel_deny_cid = '' where channel_id = %d", - intval(local_channel()) - ); - } + intval(local_channel()) + ); + } - if($role_permissions['perms_connect']) { - $x = Permissions::FilledPerms($role_permissions['perms_connect']); - $str = Permissions::serialise($x); - set_abconfig(local_channel(),$channel['channel_hash'],'system','my_perms',$str); + if ($role_permissions['perms_connect']) { + $x = Permissions::FilledPerms($role_permissions['perms_connect']); + $str = Permissions::serialise($x); + set_abconfig(local_channel(), $channel['channel_hash'], 'system', 'my_perms', $str); - $autoperms = intval($role_permissions['perms_auto']); - } + $autoperms = intval($role_permissions['perms_auto']); + } - if($role_permissions['limits']) { - foreach($role_permissions['limits'] as $k => $v) { - PermissionLimits::Set(local_channel(),$k,$v); - } - } - if(array_key_exists('directory_publish',$role_permissions)) { - $publish = intval($role_permissions['directory_publish']); - } - } - - set_pconfig(local_channel(),'system','hide_online_status',$hide_presence); - set_pconfig(local_channel(),'system','permissions_role',$role); - } + if ($role_permissions['limits']) { + foreach ($role_permissions['limits'] as $k => $v) { + PermissionLimits::Set(local_channel(), $k, $v); + } + } + if (array_key_exists('directory_publish', $role_permissions)) { + $publish = intval($role_permissions['directory_publish']); + } + } - // The post_comments permission is critical to privacy so we always allow you to set it, no matter what - // permission role is in place. - - $post_comments = array_key_exists('post_comments',$_POST) ? intval($_POST['post_comments']) : PERMS_SPECIFIC; - PermissionLimits::Set(local_channel(),'post_comments',$post_comments); + set_pconfig(local_channel(), 'system', 'hide_online_status', $hide_presence); + set_pconfig(local_channel(), 'system', 'permissions_role', $role); + } - $post_mail = array_key_exists('post_mail',$_POST) ? intval($_POST['post_mail']) : PERMS_SPECIFIC; - PermissionLimits::Set(local_channel(),'post_mail',$post_mail); + // The post_comments permission is critical to privacy so we always allow you to set it, no matter what + // permission role is in place. + + $post_comments = array_key_exists('post_comments', $_POST) ? intval($_POST['post_comments']) : PERMS_SPECIFIC; + PermissionLimits::Set(local_channel(), 'post_comments', $post_comments); + + $post_mail = array_key_exists('post_mail', $_POST) ? intval($_POST['post_mail']) : PERMS_SPECIFIC; + PermissionLimits::Set(local_channel(), 'post_mail', $post_mail); + $publish = (((x($_POST, 'profile_in_directory')) && (intval($_POST['profile_in_directory']) == 1)) ? 1 : 0); + $username = ((x($_POST, 'username')) ? escape_tags(trim($_POST['username'])) : ''); + $timezone = ((x($_POST, 'timezone_select')) ? notags(trim($_POST['timezone_select'])) : ''); + $defloc = ((x($_POST, 'defloc')) ? notags(trim($_POST['defloc'])) : ''); + $openid = ((x($_POST, 'openid_url')) ? notags(trim($_POST['openid_url'])) : ''); + $maxreq = ((x($_POST, 'maxreq')) ? intval($_POST['maxreq']) : 0); + $expire = ((x($_POST, 'expire')) ? intval($_POST['expire']) : 0); + $evdays = ((x($_POST, 'evdays')) ? intval($_POST['evdays']) : 3); + $photo_path = ((x($_POST, 'photo_path')) ? escape_tags(trim($_POST['photo_path'])) : ''); + $attach_path = ((x($_POST, 'attach_path')) ? escape_tags(trim($_POST['attach_path'])) : ''); + $noindex = ((x($_POST, 'noindex')) ? intval($_POST['noindex']) : 0); + $channel_menu = ((x($_POST['channel_menu'])) ? htmlspecialchars_decode(trim($_POST['channel_menu']), ENT_QUOTES) : ''); - $publish = (((x($_POST,'profile_in_directory')) && (intval($_POST['profile_in_directory']) == 1)) ? 1: 0); - $username = ((x($_POST,'username')) ? escape_tags(trim($_POST['username'])) : ''); - $timezone = ((x($_POST,'timezone_select')) ? notags(trim($_POST['timezone_select'])) : ''); - $defloc = ((x($_POST,'defloc')) ? notags(trim($_POST['defloc'])) : ''); - $openid = ((x($_POST,'openid_url')) ? notags(trim($_POST['openid_url'])) : ''); - $maxreq = ((x($_POST,'maxreq')) ? intval($_POST['maxreq']) : 0); - $expire = ((x($_POST,'expire')) ? intval($_POST['expire']) : 0); - $evdays = ((x($_POST,'evdays')) ? intval($_POST['evdays']) : 3); - $photo_path = ((x($_POST,'photo_path')) ? escape_tags(trim($_POST['photo_path'])) : ''); - $attach_path = ((x($_POST,'attach_path')) ? escape_tags(trim($_POST['attach_path'])) : ''); - $noindex = ((x($_POST,'noindex')) ? intval($_POST['noindex']) : 0); - $channel_menu = ((x($_POST['channel_menu'])) ? htmlspecialchars_decode(trim($_POST['channel_menu']),ENT_QUOTES) : ''); - - $expire_items = ((x($_POST,'expire_items')) ? intval($_POST['expire_items']) : 0); - $expire_starred = ((x($_POST,'expire_starred')) ? intval($_POST['expire_starred']) : 0); - $expire_photos = ((x($_POST,'expire_photos'))? intval($_POST['expire_photos']) : 0); - $expire_network_only = ((x($_POST,'expire_network_only'))? intval($_POST['expire_network_only']) : 0); - - $allow_location = (((x($_POST,'allow_location')) && (intval($_POST['allow_location']) == 1)) ? 1: 0); - - $blocktags = (((x($_POST,'blocktags')) && (intval($_POST['blocktags']) == 1)) ? 0: 1); // this setting is inverted! - $unkmail = (((x($_POST,'unkmail')) && (intval($_POST['unkmail']) == 1)) ? 1: 0); - $cntunkmail = ((x($_POST,'cntunkmail')) ? intval($_POST['cntunkmail']) : 0); - $suggestme = ((x($_POST,'suggestme')) ? intval($_POST['suggestme']) : 0); -// $anymention = ((x($_POST,'anymention')) ? intval($_POST['anymention']) : 0); - $hyperdrive = ((x($_POST,'hyperdrive')) ? intval($_POST['hyperdrive']) : 0); - $activitypub = ((x($_POST,'activitypub')) ? intval($_POST['activitypub']) : 0); - $tag_username = ((x($_POST,'tag_username')) ? intval($_POST['tag_username']) : 0); - $post_newfriend = (($_POST['post_newfriend'] == 1) ? 1: 0); - $post_joingroup = (($_POST['post_joingroup'] == 1) ? 1: 0); - $post_profilechange = (($_POST['post_profilechange'] == 1) ? 1: 0); - $adult = (($_POST['adult'] == 1) ? 1 : 0); - $defpermcat = ((x($_POST,'defpermcat')) ? notags(trim($_POST['defpermcat'])) : 'default'); + $expire_items = ((x($_POST, 'expire_items')) ? intval($_POST['expire_items']) : 0); + $expire_starred = ((x($_POST, 'expire_starred')) ? intval($_POST['expire_starred']) : 0); + $expire_photos = ((x($_POST, 'expire_photos')) ? intval($_POST['expire_photos']) : 0); + $expire_network_only = ((x($_POST, 'expire_network_only')) ? intval($_POST['expire_network_only']) : 0); - $hide_friends = 1 - intval($_POST['hide_friends']); + $allow_location = (((x($_POST, 'allow_location')) && (intval($_POST['allow_location']) == 1)) ? 1 : 0); - $cal_first_day = (((x($_POST,'first_day')) && intval($_POST['first_day']) >= 0 && intval($_POST['first_day']) < 7) ? intval($_POST['first_day']) : 0); - $mailhost = ((array_key_exists('mailhost',$_POST)) ? notags(trim($_POST['mailhost'])) : ''); - $profile_assign = ((x($_POST,'profile_assign')) ? notags(trim($_POST['profile_assign'])) : ''); - $permit_all_mentions = (($_POST['permit_all_mentions'] == 1) ? 1: 0); - $close_comment_days = (($_POST['close_comments']) ? intval($_POST['close_comments']) : 0); - if ($close_comment_days) { - set_pconfig(local_channel(),'system','close_comments', $close_comment_days . ' days'); - } - else { - set_pconfig(local_channel(),'system','close_comments', EMPTY_STR); - } - // allow a permission change to over-ride the autoperms setting from the form + $blocktags = (((x($_POST, 'blocktags')) && (intval($_POST['blocktags']) == 1)) ? 0 : 1); // this setting is inverted! + $unkmail = (((x($_POST, 'unkmail')) && (intval($_POST['unkmail']) == 1)) ? 1 : 0); + $cntunkmail = ((x($_POST, 'cntunkmail')) ? intval($_POST['cntunkmail']) : 0); + $suggestme = ((x($_POST, 'suggestme')) ? intval($_POST['suggestme']) : 0); +// $anymention = ((x($_POST,'anymention')) ? intval($_POST['anymention']) : 0); + $hyperdrive = ((x($_POST, 'hyperdrive')) ? intval($_POST['hyperdrive']) : 0); + $activitypub = ((x($_POST, 'activitypub')) ? intval($_POST['activitypub']) : 0); + $tag_username = ((x($_POST, 'tag_username')) ? intval($_POST['tag_username']) : 0); + $post_newfriend = (($_POST['post_newfriend'] == 1) ? 1 : 0); + $post_joingroup = (($_POST['post_joingroup'] == 1) ? 1 : 0); + $post_profilechange = (($_POST['post_profilechange'] == 1) ? 1 : 0); + $adult = (($_POST['adult'] == 1) ? 1 : 0); + $defpermcat = ((x($_POST, 'defpermcat')) ? notags(trim($_POST['defpermcat'])) : 'default'); - if(! isset($autoperms)) { - $autoperms = ((x($_POST,'autoperms')) ? intval($_POST['autoperms']) : 0); - } + $hide_friends = 1 - intval($_POST['hide_friends']); + + $cal_first_day = (((x($_POST, 'first_day')) && intval($_POST['first_day']) >= 0 && intval($_POST['first_day']) < 7) ? intval($_POST['first_day']) : 0); + $mailhost = ((array_key_exists('mailhost', $_POST)) ? notags(trim($_POST['mailhost'])) : ''); + $profile_assign = ((x($_POST, 'profile_assign')) ? notags(trim($_POST['profile_assign'])) : ''); + $permit_all_mentions = (($_POST['permit_all_mentions'] == 1) ? 1 : 0); + $close_comment_days = (($_POST['close_comments']) ? intval($_POST['close_comments']) : 0); + if ($close_comment_days) { + set_pconfig(local_channel(), 'system', 'close_comments', $close_comment_days . ' days'); + } else { + set_pconfig(local_channel(), 'system', 'close_comments', EMPTY_STR); + } + // allow a permission change to over-ride the autoperms setting from the form + + if (!isset($autoperms)) { + $autoperms = ((x($_POST, 'autoperms')) ? intval($_POST['autoperms']) : 0); + } - $pageflags = $channel['channel_pageflags']; - $existing_adult = (($pageflags & PAGE_ADULT) ? 1 : 0); - if($adult != $existing_adult) - $pageflags = ($pageflags ^ PAGE_ADULT); - - - $notify = 0; - - if(x($_POST,'notify1')) - $notify += intval($_POST['notify1']); - if(x($_POST,'notify2')) - $notify += intval($_POST['notify2']); - if(x($_POST,'notify3')) - $notify += intval($_POST['notify3']); - if(x($_POST,'notify4')) - $notify += intval($_POST['notify4']); - if(x($_POST,'notify5')) - $notify += intval($_POST['notify5']); - if(x($_POST,'notify6')) - $notify += intval($_POST['notify6']); - if(x($_POST,'notify7')) - $notify += intval($_POST['notify7']); - if(x($_POST,'notify8')) - $notify += intval($_POST['notify8']); - if(x($_POST,'notify10')) - $notify += intval($_POST['notify10']); - - - $vnotify = 0; - - if(x($_POST,'vnotify1')) - $vnotify += intval($_POST['vnotify1']); - if(x($_POST,'vnotify2')) - $vnotify += intval($_POST['vnotify2']); - if(x($_POST,'vnotify3')) - $vnotify += intval($_POST['vnotify3']); - if(x($_POST,'vnotify4')) - $vnotify += intval($_POST['vnotify4']); - if(x($_POST,'vnotify5')) - $vnotify += intval($_POST['vnotify5']); - if(x($_POST,'vnotify6')) - $vnotify += intval($_POST['vnotify6']); - if(x($_POST,'vnotify7')) - $vnotify += intval($_POST['vnotify7']); - if(x($_POST,'vnotify8')) - $vnotify += intval($_POST['vnotify8']); - if(x($_POST,'vnotify9')) - $vnotify += intval($_POST['vnotify9']); - if(x($_POST,'vnotify10')) - $vnotify += intval($_POST['vnotify10']); - if(x($_POST,'vnotify11') && is_site_admin()) - $vnotify += intval($_POST['vnotify11']); - if(x($_POST,'vnotify12')) - $vnotify += intval($_POST['vnotify12']); - if(x($_POST,'vnotify13')) - $vnotify += intval($_POST['vnotify13']); - if(x($_POST,'vnotify14')) - $vnotify += intval($_POST['vnotify14']); - if(x($_POST,'vnotify15')) - $vnotify += intval($_POST['vnotify15']); - if(x($_POST,'vnotify16')) - $vnotify += intval($_POST['vnotify16']); - - $always_show_in_notices = x($_POST,'always_show_in_notices') ? 1 : 0; - - $err = ''; - - $name_change = false; - - if($username != $channel['channel_name']) { - $name_change = true; - require_once('include/channel.php'); - $err = validate_channelname($username); - if($err) { - notice($err); - return; - } - } - - if($timezone != $channel['channel_timezone']) { - if(strlen($timezone)) - date_default_timezone_set($timezone); - } + $pageflags = $channel['channel_pageflags']; + $existing_adult = (($pageflags & PAGE_ADULT) ? 1 : 0); + if ($adult != $existing_adult) { + $pageflags = ($pageflags ^ PAGE_ADULT); + } - $followed_tags = $_POST['followed_tags']; - $ntags = []; - if ($followed_tags) { - $tags = explode(',', $followed_tags); - foreach ($tags as $t) { - $t = trim($t); - if ($t) { - $ntags[] = $t; - } - } - } + $notify = 0; - set_pconfig(local_channel(),'system','followed_tags',($ntags) ? $ntags : EMPTY_STR); - set_pconfig(local_channel(),'system','use_browser_location',$allow_location); - set_pconfig(local_channel(),'system','suggestme', $suggestme); - set_pconfig(local_channel(),'system','post_newfriend', $post_newfriend); - set_pconfig(local_channel(),'system','post_joingroup', $post_joingroup); - set_pconfig(local_channel(),'system','post_profilechange', $post_profilechange); - set_pconfig(local_channel(),'system','blocktags',$blocktags); - set_pconfig(local_channel(),'system','channel_menu',$channel_menu); - set_pconfig(local_channel(),'system','vnotify',$vnotify); - set_pconfig(local_channel(),'system','always_show_in_notices',$always_show_in_notices); - set_pconfig(local_channel(),'system','evdays',$evdays); - set_pconfig(local_channel(),'system','photo_path',$photo_path); - set_pconfig(local_channel(),'system','attach_path',$attach_path); - set_pconfig(local_channel(),'system','cal_first_day',$cal_first_day); - set_pconfig(local_channel(),'system','default_permcat',$defpermcat); - set_pconfig(local_channel(),'system','email_notify_host',$mailhost); - set_pconfig(local_channel(),'system','profile_assign',$profile_assign); -// set_pconfig(local_channel(),'system','anymention',$anymention); - set_pconfig(local_channel(),'system','hyperdrive',$hyperdrive); - set_pconfig(local_channel(),'system','activitypub',$activitypub); - set_pconfig(local_channel(),'system','autoperms',$autoperms); - set_pconfig(local_channel(),'system','tag_username',$tag_username); - set_pconfig(local_channel(),'system','permit_all_mentions',$permit_all_mentions); - set_pconfig(local_channel(),'system','noindex',$noindex); - - - $r = q("update channel set channel_name = '%s', channel_pageflags = %d, channel_timezone = '%s', channel_location = '%s', channel_notifyflags = %d, channel_max_anon_mail = %d, channel_max_friend_req = %d, channel_expire_days = %d $set_perms where channel_id = %d", - dbesc($username), - intval($pageflags), - dbesc($timezone), - dbesc($defloc), - intval($notify), - intval($unkmail), - intval($maxreq), - intval($expire), - intval(local_channel()) - ); - if($r) - info( t('Settings updated.') . EOL); - - - $r = q("UPDATE profile SET publish = %d, hide_friends = %d WHERE is_default = 1 AND uid = %d", - intval($publish), - intval($hide_friends), - intval(local_channel()) - ); - $r = q("UPDATE xchan SET xchan_hidden = %d WHERE xchan_hash = '%s'", - intval(1 - $publish), - intval($channel['channel_hash']) - ); - - if ($name_change) { - // catch xchans for all protocols by matching the url - $r = q("update xchan set xchan_name = '%s', xchan_name_date = '%s' where xchan_url = '%s'", - dbesc($username), - dbesc(datetime_convert()), - dbesc(z_root() . '/channel/' . $channel['channel_address']) - ); - $r = q("update profile set fullname = '%s' where uid = %d and is_default = 1", - dbesc($username), - intval($channel['channel_id']) - ); - if (is_sys_channel($channel['channel_id'])) { - set_config('system','sitename', $username); - } - } - - Run::Summon( [ 'Directory', local_channel() ] ); - - Libsync::build_sync_packet(); - - - if($email_changed && App::$config['system']['register_policy'] == REGISTER_VERIFY) { - - // FIXME - set to un-verified, blocked and redirect to logout - // Q: Why? Are we verifying people or email addresses? - // A: the policy is to verify email addresses - } - - goaway(z_root() . '/settings' ); - return; // NOTREACHED - } - - function get() { - - require_once('include/acl_selectors.php'); - require_once('include/permissions.php'); + if (x($_POST, 'notify1')) { + $notify += intval($_POST['notify1']); + } + if (x($_POST, 'notify2')) { + $notify += intval($_POST['notify2']); + } + if (x($_POST, 'notify3')) { + $notify += intval($_POST['notify3']); + } + if (x($_POST, 'notify4')) { + $notify += intval($_POST['notify4']); + } + if (x($_POST, 'notify5')) { + $notify += intval($_POST['notify5']); + } + if (x($_POST, 'notify6')) { + $notify += intval($_POST['notify6']); + } + if (x($_POST, 'notify7')) { + $notify += intval($_POST['notify7']); + } + if (x($_POST, 'notify8')) { + $notify += intval($_POST['notify8']); + } + if (x($_POST, 'notify10')) { + $notify += intval($_POST['notify10']); + } - $yes_no = [ t('No'), t('Yes') ]; - - - $p = q("SELECT * FROM profile WHERE is_default = 1 AND uid = %d LIMIT 1", - intval(local_channel()) - ); - if(count($p)) - $profile = $p[0]; - - load_pconfig(local_channel(),'expire'); - - $channel = App::get_channel(); - - $global_perms = Permissions::Perms(); + $vnotify = 0; - $permiss = []; - - $perm_opts = [ - [ t('Restricted - from connections only'), PERMS_SPECIFIC ], - [ t('Semi-public - from anybody that can be identified'), PERMS_AUTHED ], - [ t('Public - from anybody on the internet'), PERMS_PUBLIC ] - ]; - - $limits = PermissionLimits::Get(local_channel()); - $anon_comments = get_config('system','anonymous_comments'); - - foreach($global_perms as $k => $perm) { - $options = []; - $can_be_public = ((strstr($k,'view') || ($k === 'post_comments' && $anon_comments)) ? true : false); - foreach($perm_opts as $opt) { - if($opt[1] == PERMS_PUBLIC && (! $can_be_public)) - continue; - $options[$opt[1]] = $opt[0]; - } - if($k === 'post_comments') { - $comment_perms = [ $k, $perm, $limits[$k],'',$options ]; - } - elseif ($k === 'post_mail') { - $mail_perms = [ $k, $perm, $limits[$k],'',$options ]; - } - else { - $permiss[] = array($k,$perm,$limits[$k],'',$options); - } - } - - // logger('permiss: ' . print_r($permiss,true)); - - $username = $channel['channel_name']; - $nickname = $channel['channel_address']; - $timezone = $channel['channel_timezone']; - $notify = $channel['channel_notifyflags']; - $defloc = $channel['channel_location']; - - $maxreq = $channel['channel_max_friend_req']; - $expire = $channel['channel_expire_days']; - $adult_flag = intval($channel['channel_pageflags'] & PAGE_ADULT); - $sys_expire = get_config('system','default_expire_days'); - - $hide_presence = intval(get_pconfig(local_channel(), 'system','hide_online_status')); + if (x($_POST, 'vnotify1')) { + $vnotify += intval($_POST['vnotify1']); + } + if (x($_POST, 'vnotify2')) { + $vnotify += intval($_POST['vnotify2']); + } + if (x($_POST, 'vnotify3')) { + $vnotify += intval($_POST['vnotify3']); + } + if (x($_POST, 'vnotify4')) { + $vnotify += intval($_POST['vnotify4']); + } + if (x($_POST, 'vnotify5')) { + $vnotify += intval($_POST['vnotify5']); + } + if (x($_POST, 'vnotify6')) { + $vnotify += intval($_POST['vnotify6']); + } + if (x($_POST, 'vnotify7')) { + $vnotify += intval($_POST['vnotify7']); + } + if (x($_POST, 'vnotify8')) { + $vnotify += intval($_POST['vnotify8']); + } + if (x($_POST, 'vnotify9')) { + $vnotify += intval($_POST['vnotify9']); + } + if (x($_POST, 'vnotify10')) { + $vnotify += intval($_POST['vnotify10']); + } + if (x($_POST, 'vnotify11') && is_site_admin()) { + $vnotify += intval($_POST['vnotify11']); + } + if (x($_POST, 'vnotify12')) { + $vnotify += intval($_POST['vnotify12']); + } + if (x($_POST, 'vnotify13')) { + $vnotify += intval($_POST['vnotify13']); + } + if (x($_POST, 'vnotify14')) { + $vnotify += intval($_POST['vnotify14']); + } + if (x($_POST, 'vnotify15')) { + $vnotify += intval($_POST['vnotify15']); + } + if (x($_POST, 'vnotify16')) { + $vnotify += intval($_POST['vnotify16']); + } - $expire_items = get_pconfig(local_channel(), 'expire','items'); - $expire_items = (($expire_items===false)? '1' : $expire_items); // default if not set: 1 - - $expire_notes = get_pconfig(local_channel(), 'expire','notes'); - $expire_notes = (($expire_notes===false)? '1' : $expire_notes); // default if not set: 1 - - $expire_starred = get_pconfig(local_channel(), 'expire','starred'); - $expire_starred = (($expire_starred===false)? '1' : $expire_starred); // default if not set: 1 + $always_show_in_notices = x($_POST, 'always_show_in_notices') ? 1 : 0; - - $expire_photos = get_pconfig(local_channel(), 'expire','photos'); - $expire_photos = (($expire_photos===false)? '0' : $expire_photos); // default if not set: 0 - - $expire_network_only = get_pconfig(local_channel(), 'expire','network_only'); - $expire_network_only = (($expire_network_only===false)? '0' : $expire_network_only); // default if not set: 0 - - - $suggestme = get_pconfig(local_channel(), 'system','suggestme'); - $suggestme = (($suggestme===false)? '0': $suggestme); // default if not set: 0 - - $post_newfriend = get_pconfig(local_channel(), 'system','post_newfriend'); - $post_newfriend = (($post_newfriend===false)? '0': $post_newfriend); // default if not set: 0 - - $post_joingroup = get_pconfig(local_channel(), 'system','post_joingroup'); - $post_joingroup = (($post_joingroup===false)? '0': $post_joingroup); // default if not set: 0 - - $post_profilechange = get_pconfig(local_channel(), 'system','post_profilechange'); - $post_profilechange = (($post_profilechange===false)? '0': $post_profilechange); // default if not set: 0 - - $blocktags = get_pconfig(local_channel(),'system','blocktags'); - $blocktags = (($blocktags===false) ? '0' : $blocktags); - - $timezone = date_default_timezone_get(); - - $opt_tpl = get_markup_template("field_checkbox.tpl"); - if(get_config('system','publish_all')) { - $profile_in_dir = ''; - } - else { - $profile_in_dir = replace_macros($opt_tpl,array( - '$field' => array('profile_in_directory', t('Publish your profile in the network directory'), $profile['publish'], '', $yes_no), - )); - } + $err = ''; - $suggestme = replace_macros($opt_tpl,array( - '$field' => array('suggestme', t('Allow us to suggest you as a potential friend to new members?'), $suggestme, '', $yes_no), - - )); - - $subdir = ((strlen(App::get_path())) ? '
      ' . t('or') . ' ' . z_root() . '/channel/' . $nickname : ''); + $name_change = false; - $webbie = $nickname . '@' . App::get_hostname(); - $intl_nickname = unpunify($nickname) . '@' . unpunify(App::get_hostname()); - - $prof_addr = replace_macros(get_markup_template('channel_settings_header.tpl'),array( - '$desc' => t('Your channel address is'), - '$nickname' => (($intl_nickname === $webbie) ? $webbie : $intl_nickname . ' (' . $webbie . ')'), - '$compat' => t('Friends using compatible applications can use this address to connect with you.'), - '$subdir' => $subdir, - '$davdesc' => t('Your files/photos are accessible as a network drive at'), - '$davpath' => z_root() . '/dav/' . $nickname, - '$windows' => t('(Windows)'), - '$other' => t('(other platforms)'), - '$or' => t('or'), - '$davspath' => 'davs://' . App::get_hostname() . '/dav/' . $nickname, - '$basepath' => App::get_hostname() - )); + if ($username != $channel['channel_name']) { + $name_change = true; + require_once('include/channel.php'); + $err = validate_channelname($username); + if ($err) { + notice($err); + return; + } + } + + if ($timezone != $channel['channel_timezone']) { + if (strlen($timezone)) { + date_default_timezone_set($timezone); + } + } - $pcat = new Permcat(local_channel()); - $pcatlist = $pcat->listing(); - $permcats = []; - if($pcatlist) { - foreach($pcatlist as $pc) { - $permcats[$pc['name']] = $pc['localname']; - } - } + $followed_tags = $_POST['followed_tags']; + $ntags = []; + if ($followed_tags) { + $tags = explode(',', $followed_tags); + foreach ($tags as $t) { + $t = trim($t); + if ($t) { + $ntags[] = $t; + } + } + } - $default_permcat = get_pconfig(local_channel(),'system','default_permcat','default'); - - - $acl = new AccessControl($channel); - $perm_defaults = $acl->get(); - - $group_select = AccessList::select(local_channel(),$channel['channel_default_group']); - - require_once('include/menu.php'); - $m1 = menu_list(local_channel()); - $menu = false; - if($m1) { - $menu = []; - $current = get_pconfig(local_channel(),'system','channel_menu'); - $menu[] = array('name' => '', 'selected' => ((! $current) ? true : false)); - foreach($m1 as $m) { - $menu[] = array('name' => htmlspecialchars($m['menu_name'],ENT_COMPAT,'UTF-8'), 'selected' => (($m['menu_name'] === $current) ? ' selected="selected" ' : false)); - } - } - - $evdays = get_pconfig(local_channel(),'system','evdays'); - if(! $evdays) - $evdays = 3; - - $permissions_role = get_pconfig(local_channel(),'system','permissions_role'); - if(! $permissions_role) - $permissions_role = 'custom'; - - if(in_array($permissions_role,['forum','repository'])) { - $autoperms = replace_macros(get_markup_template('field_checkbox.tpl'), [ - '$field' => [ 'autoperms',t('Automatic membership approval'), ((get_pconfig(local_channel(),'system','autoperms',0)) ? 1 : 0), t('If enabled, connection requests will be approved without your interaction'), $yes_no ]]); - } - else { - $autoperms = ''; - } - - $hyperdrive = [ 'hyperdrive', t('Friend-of-friend conversations'), ((get_pconfig(local_channel(),'system','hyperdrive',true)) ? 1 : 0), t('Import public third-party conversations in which your connections participate.'), $yes_no ]; - - if (get_config('system','activitypub', ACTIVITYPUB_ENABLED)) { - $apconfig = true; - $activitypub = replace_macros(get_markup_template('field_checkbox.tpl'), [ '$field' => [ 'activitypub', t('Enable ActivityPub protocol'), ((get_pconfig(local_channel(),'system','activitypub', ACTIVITYPUB_ENABLED)) ? 1 : 0), t('ActivityPub is an emerging internet standard for social communications'), $yes_no ]]); - } - else { - $apconfig = false; - $activitypub = '' . EOL; - } - - $permissions_set = (($permissions_role != 'custom') ? true : false); - - $perm_roles = PermissionRoles::roles(); - // Don't permit changing to a collection (@TODO unless there is a mechanism to select the channel_parent) - unset($perm_roles['Collection']); - - - $vnotify = get_pconfig(local_channel(),'system','vnotify'); - $always_show_in_notices = get_pconfig(local_channel(),'system','always_show_in_notices'); - if($vnotify === false) - $vnotify = (-1); - - $plugin = [ 'basic' => '', 'security' => '', 'notify' => '', 'misc' => '' ]; - call_hooks('channel_settings',$plugin); - - $public_stream_mode = intval(get_config('system','public_stream_mode', PUBLIC_STREAM_NONE)); - - $ft = get_pconfig(local_channel(),'system','followed_tags',''); - if ($ft && is_array($ft)) { - $followed = implode(',', $ft); - } - else { - $followed = EMPTY_STR; - } + set_pconfig(local_channel(), 'system', 'followed_tags', ($ntags) ? $ntags : EMPTY_STR); + set_pconfig(local_channel(), 'system', 'use_browser_location', $allow_location); + set_pconfig(local_channel(), 'system', 'suggestme', $suggestme); + set_pconfig(local_channel(), 'system', 'post_newfriend', $post_newfriend); + set_pconfig(local_channel(), 'system', 'post_joingroup', $post_joingroup); + set_pconfig(local_channel(), 'system', 'post_profilechange', $post_profilechange); + set_pconfig(local_channel(), 'system', 'blocktags', $blocktags); + set_pconfig(local_channel(), 'system', 'channel_menu', $channel_menu); + set_pconfig(local_channel(), 'system', 'vnotify', $vnotify); + set_pconfig(local_channel(), 'system', 'always_show_in_notices', $always_show_in_notices); + set_pconfig(local_channel(), 'system', 'evdays', $evdays); + set_pconfig(local_channel(), 'system', 'photo_path', $photo_path); + set_pconfig(local_channel(), 'system', 'attach_path', $attach_path); + set_pconfig(local_channel(), 'system', 'cal_first_day', $cal_first_day); + set_pconfig(local_channel(), 'system', 'default_permcat', $defpermcat); + set_pconfig(local_channel(), 'system', 'email_notify_host', $mailhost); + set_pconfig(local_channel(), 'system', 'profile_assign', $profile_assign); +// set_pconfig(local_channel(),'system','anymention',$anymention); + set_pconfig(local_channel(), 'system', 'hyperdrive', $hyperdrive); + set_pconfig(local_channel(), 'system', 'activitypub', $activitypub); + set_pconfig(local_channel(), 'system', 'autoperms', $autoperms); + set_pconfig(local_channel(), 'system', 'tag_username', $tag_username); + set_pconfig(local_channel(), 'system', 'permit_all_mentions', $permit_all_mentions); + set_pconfig(local_channel(), 'system', 'noindex', $noindex); + $r = q( + "update channel set channel_name = '%s', channel_pageflags = %d, channel_timezone = '%s', channel_location = '%s', channel_notifyflags = %d, channel_max_anon_mail = %d, channel_max_friend_req = %d, channel_expire_days = %d $set_perms where channel_id = %d", + dbesc($username), + intval($pageflags), + dbesc($timezone), + dbesc($defloc), + intval($notify), + intval($unkmail), + intval($maxreq), + intval($expire), + intval(local_channel()) + ); + if ($r) { + info(t('Settings updated.') . EOL); + } - $o .= replace_macros(get_markup_template('settings.tpl'), [ - '$ptitle' => t('Channel Settings'), - '$submit' => t('Submit'), - '$baseurl' => z_root(), - '$uid' => local_channel(), - '$form_security_token' => get_form_security_token("settings"), - '$nickname_block' => $prof_addr, - '$h_basic' => t('Basic Settings'), - '$username' => array('username', t('Full name'), $username,''), - '$email' => array('email', t('Email Address'), $email, ''), - '$timezone' => array('timezone_select' , t('Your timezone'), $timezone, t('This is important for showing the correct time on shared events'), get_timezones()), - '$defloc' => array('defloc', t('Default post location'), $defloc, t('Optional geographical location to display on your posts')), - '$allowloc' => array('allow_location', t('Obtain post location from your web browser or device'), ((get_pconfig(local_channel(),'system','use_browser_location')) ? 1 : ''), '', $yes_no), - - '$adult' => array('adult', t('Adult content'), $adult_flag, t('Enable to indicate if this channel frequently or regularly publishes adult content. (Please also tag any adult material and/or nudity with #NSFW)'), $yes_no), - - '$h_prv' => t('Security and Privacy'), - '$permissions_set' => $permissions_set, - '$perms_set_msg' => t('Your permissions are already configured. Click to view/adjust'), - - '$hide_presence' => array('hide_presence', t('Hide my online presence'),$hide_presence, t('Prevents displaying in your profile that you are online'), $yes_no), - '$hidefriends' => array('hide_friends', t('Allow others to view your friends and connections'), 1 - intval($profile['hide_friends']), '', $yes_no ), - '$permiss_arr' => $permiss, - '$comment_perms' => $comment_perms, - '$mail_perms' => $mail_perms, - '$noindex' => [ 'noindex', t('Forbid indexing of your channel content by search engines'), get_pconfig($channel['channel_id'],'system','noindex'), '', $yes_no], - '$close_comments' => [ 'close_comments', t('Disable acceptance of comments on my posts after this many days'), ((intval(get_pconfig(local_channel(),'system','close_comments'))) ? intval(get_pconfig(local_channel(),'system','close_comments')) : EMPTY_STR), t('Leave unset or enter 0 to allow comments indefinitely') ], - '$blocktags' => array('blocktags',t('Allow others to tag your posts'), 1-$blocktags, t('Often used by the community to retro-actively flag inappropriate content'), $yes_no), - - '$lbl_p2macro' => t('Channel Permission Limits'), - - '$expire' => array('expire',t('Expire conversations you have not participated in after this many days'),$expire, t('0 or blank to use the website limit.') . ' ' . ((intval($sys_expire)) ? sprintf( t('This website expires after %d days.'),intval($sys_expire)) : t('This website does not provide an expiration policy.')) . ' ' . t('The website limit takes precedence if lower than your limit.')), - '$maxreq' => array('maxreq', t('Maximum Friend Requests/Day:'), intval($channel['channel_max_friend_req']) , t('May reduce spam activity')), - '$permissions' => t('Default Access List'), - '$permdesc' => t("(click to open/close)"), - '$aclselect' => populate_acl($perm_defaults, false, PermissionDescription::fromDescription(t('Use my default audience setting for the type of object published'))), - '$profseltxt' => t('Profile to assign new connections'), - '$profselect' => ((feature_enabled(local_channel(),'multi_profiles')) ? contact_profile_assign(get_pconfig(local_channel(),'system','profile_assign','')) : ''), - '$allow_cid' => acl2json($perm_defaults['allow_cid']), - '$allow_gid' => acl2json($perm_defaults['allow_gid']), - '$deny_cid' => acl2json($perm_defaults['deny_cid']), - '$deny_gid' => acl2json($perm_defaults['deny_gid']), - '$suggestme' => $suggestme, - '$group_select' => $group_select, - '$can_change_role' => ((in_array($permissions_role, [ 'collection', 'collection_restricted'] )) ? false : true), - '$permissions_role' => $permissions_role, - '$role' => array('permissions_role' , t('Channel type and privacy'), $permissions_role, '', $perm_roles, ' onchange="update_role_text(); return false;"'), - '$defpermcat' => [ 'defpermcat', t('Default Permissions Group'), $default_permcat, '', $permcats ], - '$permcat_enable' => feature_enabled(local_channel(),'permcats'), - '$profile_in_dir' => $profile_in_dir, - '$hide_friends' => $hide_friends, - '$hide_wall' => $hide_wall, - '$unkmail' => $unkmail, - '$cntunkmail' => array('cntunkmail', t('Maximum direct messages per day from unknown people:'), intval($channel['channel_max_anon_mail']) ,t("Useful to reduce spamming if you allow direct messages from unknown people")), - - '$autoperms' => $autoperms, -// '$anymention' => $anymention, - '$hyperdrive' => $hyperdrive, - '$activitypub' => $activitypub, - '$apconfig' => $apconfig, - '$close' => t('Close'), - '$h_not' => t('Notifications'), - '$activity_options' => t('By default post a status message when:'), - '$post_newfriend' => array('post_newfriend', t('accepting a friend request'), $post_newfriend, '', $yes_no), - '$post_joingroup' => array('post_joingroup', t('joining a forum/community'), $post_joingroup, '', $yes_no), - '$post_profilechange' => array('post_profilechange', t('making an interesting profile change'), $post_profilechange, '', $yes_no), - '$lbl_not' => t('Send a notification email when:'), - '$notify1' => array('notify1', t('You receive a connection request'), ($notify & NOTIFY_INTRO), NOTIFY_INTRO, '', $yes_no), -// '$notify2' => array('notify2', t('Your connections are confirmed'), ($notify & NOTIFY_CONFIRM), NOTIFY_CONFIRM, '', $yes_no), - '$notify3' => array('notify3', t('Someone writes on your profile wall'), ($notify & NOTIFY_WALL), NOTIFY_WALL, '', $yes_no), - '$notify4' => array('notify4', t('Someone writes a followup comment'), ($notify & NOTIFY_COMMENT), NOTIFY_COMMENT, '', $yes_no), - '$notify10' => array('notify10', t('Someone shares a followed conversation'), ($notify & NOTIFY_RESHARE), NOTIFY_RESHARE, '', $yes_no), - '$notify5' => array('notify5', t('You receive a direct (private) message'), ($notify & NOTIFY_MAIL), NOTIFY_MAIL, '', $yes_no), -// '$notify6' => array('notify6', t('You receive a friend suggestion'), ($notify & NOTIFY_SUGGEST), NOTIFY_SUGGEST, '', $yes_no), - '$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:'), - - '$vnotify1' => array('vnotify1', t('Unseen stream activity'), ($vnotify & VNOTIFY_NETWORK), VNOTIFY_NETWORK, '', $yes_no), - '$vnotify2' => array('vnotify2', t('Unseen channel activity'), ($vnotify & VNOTIFY_CHANNEL), VNOTIFY_CHANNEL, '', $yes_no), - '$vnotify3' => array('vnotify3', t('Unseen direct messages'), ($vnotify & VNOTIFY_MAIL), VNOTIFY_MAIL, t('Recommended'), $yes_no), - '$vnotify4' => array('vnotify4', t('Upcoming events'), ($vnotify & VNOTIFY_EVENT), VNOTIFY_EVENT, '', $yes_no), - '$vnotify5' => array('vnotify5', t('Events today'), ($vnotify & VNOTIFY_EVENTTODAY), VNOTIFY_EVENTTODAY, '', $yes_no), - '$vnotify6' => array('vnotify6', t('Upcoming birthdays'), ($vnotify & VNOTIFY_BIRTHDAY), VNOTIFY_BIRTHDAY, t('Not available in all themes'), $yes_no), - '$vnotify7' => array('vnotify7', t('System (personal) notifications'), ($vnotify & VNOTIFY_SYSTEM), VNOTIFY_SYSTEM, '', $yes_no), - '$vnotify8' => array('vnotify8', t('System info messages'), ($vnotify & VNOTIFY_INFO), VNOTIFY_INFO, t('Recommended'), $yes_no), - '$vnotify9' => array('vnotify9', t('System critical alerts'), ($vnotify & VNOTIFY_ALERT), VNOTIFY_ALERT, t('Recommended'), $yes_no), - '$vnotify10' => array('vnotify10', t('New connections'), ($vnotify & VNOTIFY_INTRO), VNOTIFY_INTRO, t('Recommended'), $yes_no), - '$vnotify11' => ((is_site_admin()) ? array('vnotify11', t('System Registrations'), ($vnotify & VNOTIFY_REGISTER), VNOTIFY_REGISTER, '', $yes_no) : []), -// '$vnotify12' => array('vnotify12', t('Unseen shared files'), ($vnotify & VNOTIFY_FILES), VNOTIFY_FILES, '', $yes_no), - '$vnotify13' => (($public_stream_mode) ? [ 'vnotify13', t('Unseen public stream activity'), ($vnotify & VNOTIFY_PUBS), VNOTIFY_PUBS, '', $yes_no] : []), - '$vnotify14' => array('vnotify14', t('Unseen likes and dislikes'), ($vnotify & VNOTIFY_LIKE), VNOTIFY_LIKE, '', $yes_no), - '$vnotify15' => array('vnotify15', t('Unseen forum posts'), ($vnotify & VNOTIFY_FORUMS), VNOTIFY_FORUMS, '', $yes_no), - '$vnotify16' => ((is_site_admin()) ? array('vnotify16', t('Reported content'), ($vnotify & VNOTIFY_REPORTS), VNOTIFY_REPORTS, '', $yes_no) : [] ), - '$desktop_notifications_info' => t('Desktop notifications are unavailable because the required browser permission has not been granted'), - '$desktop_notifications_request' => t('Grant permission'), - '$mailhost' => [ 'mailhost', t('Email notifications sent from (hostname)'), get_pconfig(local_channel(),'system','email_notify_host',App::get_hostname()), sprintf( t('If your channel is mirrored to multiple locations, set this to your preferred location. This will prevent duplicate email notifications. Example: %s'),App::get_hostname()) ], - '$always_show_in_notices' => array('always_show_in_notices', t('Show new wall posts, private messages and connections under Notices'), $always_show_in_notices, 1, '', $yes_no), - '$permit_all_mentions' => [ 'permit_all_mentions', t('Accept messages from strangers which mention me'), get_pconfig(local_channel(),'system','permit_all_mentions'), t('This setting bypasses normal permissions'), $yes_no ], - '$followed_tags' => [ 'followed_tags', t('Accept messages from strangers which include any of the following hashtags'), $followed, t('comma separated, do not include the #') ], - '$evdays' => array('evdays', t('Notify me of events this many days in advance'), $evdays, t('Must be greater than 0')), - '$basic_addon' => $plugin['basic'], - '$sec_addon' => $plugin['security'], - '$notify_addon' => $plugin['notify'], - '$misc_addon' => $plugin['misc'], - '$lbl_time' => t('Date and time'), - '$miscdoc' => t('This section is reserved for use by optional addons and apps to provide additional settings.'), - '$h_advn' => t('Advanced Account/Page Type Settings'), - '$h_descadvn' => t('Change the behaviour of this account for special situations'), - '$pagetype' => $pagetype, - '$lbl_misc' => t('Miscellaneous'), - '$photo_path' => array('photo_path', t('Default photo upload folder name'), get_pconfig(local_channel(),'system','photo_path'), t('%Y - current year, %m - current month')), - '$attach_path' => array('attach_path', t('Default file upload folder name'), get_pconfig(local_channel(),'system','attach_path'), t('%Y - current year, %m - current month')), - '$menus' => $menu, - '$menu_desc' => t('Personal menu to display in your channel pages'), - '$removeme' => t('Remove Channel'), - '$removechannel' => t('Remove this channel.'), - '$tag_username' => [ 'tag_username', t('Mentions should display'), intval(get_pconfig(local_channel(),'system','tag_username',get_config('system','tag_username',false))), t('Changes to this setting are applied to new posts/comments only. It is not retroactive.'), - [ - 0 => t('the channel display name [example: @Barbara Jenkins]'), - 1 => t('the channel nickname [example: @barbara1976]'), - 2 => t('combined [example: @Barbara Jenkins (barbara1976)]'), - 127 => t('no preference, use the system default'), - ]], - - '$cal_first_day' => array('first_day', t('Calendar week begins on'), intval(get_pconfig(local_channel(),'system','cal_first_day')), t('This varies by country/culture'), - [ 0 => t('Sunday'), - 1 => t('Monday'), - 2 => t('Tuesday'), - 3 => t('Wednesday'), - 4 => t('Thursday'), - 5 => t('Friday'), - 6 => t('Saturday') - ]), - ]); - - call_hooks('settings_form',$o); - return $o; - } + $r = q( + "UPDATE profile SET publish = %d, hide_friends = %d WHERE is_default = 1 AND uid = %d", + intval($publish), + intval($hide_friends), + intval(local_channel()) + ); + $r = q( + "UPDATE xchan SET xchan_hidden = %d WHERE xchan_hash = '%s'", + intval(1 - $publish), + intval($channel['channel_hash']) + ); + + if ($name_change) { + // catch xchans for all protocols by matching the url + $r = q( + "update xchan set xchan_name = '%s', xchan_name_date = '%s' where xchan_url = '%s'", + dbesc($username), + dbesc(datetime_convert()), + dbesc(z_root() . '/channel/' . $channel['channel_address']) + ); + $r = q( + "update profile set fullname = '%s' where uid = %d and is_default = 1", + dbesc($username), + intval($channel['channel_id']) + ); + if (is_sys_channel($channel['channel_id'])) { + set_config('system', 'sitename', $username); + } + } + + Run::Summon(['Directory', local_channel()]); + + Libsync::build_sync_packet(); + + + if ($email_changed && App::$config['system']['register_policy'] == REGISTER_VERIFY) { + // FIXME - set to un-verified, blocked and redirect to logout + // Q: Why? Are we verifying people or email addresses? + // A: the policy is to verify email addresses + } + + goaway(z_root() . '/settings'); + return; // NOTREACHED + } + + public function get() + { + + require_once('include/acl_selectors.php'); + require_once('include/permissions.php'); + + + $yes_no = [t('No'), t('Yes')]; + + + $p = q( + "SELECT * FROM profile WHERE is_default = 1 AND uid = %d LIMIT 1", + intval(local_channel()) + ); + if (count($p)) { + $profile = $p[0]; + } + + load_pconfig(local_channel(), 'expire'); + + $channel = App::get_channel(); + + $global_perms = Permissions::Perms(); + + $permiss = []; + + $perm_opts = [ + [t('Restricted - from connections only'), PERMS_SPECIFIC], + [t('Semi-public - from anybody that can be identified'), PERMS_AUTHED], + [t('Public - from anybody on the internet'), PERMS_PUBLIC] + ]; + + $limits = PermissionLimits::Get(local_channel()); + $anon_comments = get_config('system', 'anonymous_comments'); + + foreach ($global_perms as $k => $perm) { + $options = []; + $can_be_public = ((strstr($k, 'view') || ($k === 'post_comments' && $anon_comments)) ? true : false); + foreach ($perm_opts as $opt) { + if ($opt[1] == PERMS_PUBLIC && (!$can_be_public)) { + continue; + } + $options[$opt[1]] = $opt[0]; + } + if ($k === 'post_comments') { + $comment_perms = [$k, $perm, $limits[$k], '', $options]; + } elseif ($k === 'post_mail') { + $mail_perms = [$k, $perm, $limits[$k], '', $options]; + } else { + $permiss[] = array($k, $perm, $limits[$k], '', $options); + } + } + + // logger('permiss: ' . print_r($permiss,true)); + + $username = $channel['channel_name']; + $nickname = $channel['channel_address']; + $timezone = $channel['channel_timezone']; + $notify = $channel['channel_notifyflags']; + $defloc = $channel['channel_location']; + + $maxreq = $channel['channel_max_friend_req']; + $expire = $channel['channel_expire_days']; + $adult_flag = intval($channel['channel_pageflags'] & PAGE_ADULT); + $sys_expire = get_config('system', 'default_expire_days'); + + $hide_presence = intval(get_pconfig(local_channel(), 'system', 'hide_online_status')); + + $expire_items = get_pconfig(local_channel(), 'expire', 'items'); + $expire_items = (($expire_items === false) ? '1' : $expire_items); // default if not set: 1 + + $expire_notes = get_pconfig(local_channel(), 'expire', 'notes'); + $expire_notes = (($expire_notes === false) ? '1' : $expire_notes); // default if not set: 1 + + $expire_starred = get_pconfig(local_channel(), 'expire', 'starred'); + $expire_starred = (($expire_starred === false) ? '1' : $expire_starred); // default if not set: 1 + + + $expire_photos = get_pconfig(local_channel(), 'expire', 'photos'); + $expire_photos = (($expire_photos === false) ? '0' : $expire_photos); // default if not set: 0 + + $expire_network_only = get_pconfig(local_channel(), 'expire', 'network_only'); + $expire_network_only = (($expire_network_only === false) ? '0' : $expire_network_only); // default if not set: 0 + + + $suggestme = get_pconfig(local_channel(), 'system', 'suggestme'); + $suggestme = (($suggestme === false) ? '0' : $suggestme); // default if not set: 0 + + $post_newfriend = get_pconfig(local_channel(), 'system', 'post_newfriend'); + $post_newfriend = (($post_newfriend === false) ? '0' : $post_newfriend); // default if not set: 0 + + $post_joingroup = get_pconfig(local_channel(), 'system', 'post_joingroup'); + $post_joingroup = (($post_joingroup === false) ? '0' : $post_joingroup); // default if not set: 0 + + $post_profilechange = get_pconfig(local_channel(), 'system', 'post_profilechange'); + $post_profilechange = (($post_profilechange === false) ? '0' : $post_profilechange); // default if not set: 0 + + $blocktags = get_pconfig(local_channel(), 'system', 'blocktags'); + $blocktags = (($blocktags === false) ? '0' : $blocktags); + + $timezone = date_default_timezone_get(); + + $opt_tpl = get_markup_template("field_checkbox.tpl"); + if (get_config('system', 'publish_all')) { + $profile_in_dir = ''; + } else { + $profile_in_dir = replace_macros($opt_tpl, array( + '$field' => array('profile_in_directory', t('Publish your profile in the network directory'), $profile['publish'], '', $yes_no), + )); + } + + $suggestme = replace_macros($opt_tpl, array( + '$field' => array('suggestme', t('Allow us to suggest you as a potential friend to new members?'), $suggestme, '', $yes_no), + + )); + + $subdir = ((strlen(App::get_path())) ? '
      ' . t('or') . ' ' . z_root() . '/channel/' . $nickname : ''); + + $webbie = $nickname . '@' . App::get_hostname(); + $intl_nickname = unpunify($nickname) . '@' . unpunify(App::get_hostname()); + + $prof_addr = replace_macros(get_markup_template('channel_settings_header.tpl'), array( + '$desc' => t('Your channel address is'), + '$nickname' => (($intl_nickname === $webbie) ? $webbie : $intl_nickname . ' (' . $webbie . ')'), + '$compat' => t('Friends using compatible applications can use this address to connect with you.'), + '$subdir' => $subdir, + '$davdesc' => t('Your files/photos are accessible as a network drive at'), + '$davpath' => z_root() . '/dav/' . $nickname, + '$windows' => t('(Windows)'), + '$other' => t('(other platforms)'), + '$or' => t('or'), + '$davspath' => 'davs://' . App::get_hostname() . '/dav/' . $nickname, + '$basepath' => App::get_hostname() + )); + + + $pcat = new Permcat(local_channel()); + $pcatlist = $pcat->listing(); + $permcats = []; + if ($pcatlist) { + foreach ($pcatlist as $pc) { + $permcats[$pc['name']] = $pc['localname']; + } + } + + $default_permcat = get_pconfig(local_channel(), 'system', 'default_permcat', 'default'); + + + $acl = new AccessControl($channel); + $perm_defaults = $acl->get(); + + $group_select = AccessList::select(local_channel(), $channel['channel_default_group']); + + require_once('include/menu.php'); + $m1 = menu_list(local_channel()); + $menu = false; + if ($m1) { + $menu = []; + $current = get_pconfig(local_channel(), 'system', 'channel_menu'); + $menu[] = array('name' => '', 'selected' => ((!$current) ? true : false)); + foreach ($m1 as $m) { + $menu[] = array('name' => htmlspecialchars($m['menu_name'], ENT_COMPAT, 'UTF-8'), 'selected' => (($m['menu_name'] === $current) ? ' selected="selected" ' : false)); + } + } + + $evdays = get_pconfig(local_channel(), 'system', 'evdays'); + if (!$evdays) { + $evdays = 3; + } + + $permissions_role = get_pconfig(local_channel(), 'system', 'permissions_role'); + if (!$permissions_role) { + $permissions_role = 'custom'; + } + + if (in_array($permissions_role, ['forum', 'repository'])) { + $autoperms = replace_macros(get_markup_template('field_checkbox.tpl'), [ + '$field' => ['autoperms', t('Automatic membership approval'), ((get_pconfig(local_channel(), 'system', 'autoperms', 0)) ? 1 : 0), t('If enabled, connection requests will be approved without your interaction'), $yes_no]]); + } else { + $autoperms = ''; + } + + $hyperdrive = ['hyperdrive', t('Friend-of-friend conversations'), ((get_pconfig(local_channel(), 'system', 'hyperdrive', true)) ? 1 : 0), t('Import public third-party conversations in which your connections participate.'), $yes_no]; + + if (get_config('system', 'activitypub', ACTIVITYPUB_ENABLED)) { + $apconfig = true; + $activitypub = replace_macros(get_markup_template('field_checkbox.tpl'), ['$field' => ['activitypub', t('Enable ActivityPub protocol'), ((get_pconfig(local_channel(), 'system', 'activitypub', ACTIVITYPUB_ENABLED)) ? 1 : 0), t('ActivityPub is an emerging internet standard for social communications'), $yes_no]]); + } else { + $apconfig = false; + $activitypub = '' . EOL; + } + + $permissions_set = (($permissions_role != 'custom') ? true : false); + + $perm_roles = PermissionRoles::roles(); + // Don't permit changing to a collection (@TODO unless there is a mechanism to select the channel_parent) + unset($perm_roles['Collection']); + + + $vnotify = get_pconfig(local_channel(), 'system', 'vnotify'); + $always_show_in_notices = get_pconfig(local_channel(), 'system', 'always_show_in_notices'); + if ($vnotify === false) { + $vnotify = (-1); + } + + $plugin = ['basic' => '', 'security' => '', 'notify' => '', 'misc' => '']; + call_hooks('channel_settings', $plugin); + + $public_stream_mode = intval(get_config('system', 'public_stream_mode', PUBLIC_STREAM_NONE)); + + $ft = get_pconfig(local_channel(), 'system', 'followed_tags', ''); + if ($ft && is_array($ft)) { + $followed = implode(',', $ft); + } else { + $followed = EMPTY_STR; + } + + + $o .= replace_macros(get_markup_template('settings.tpl'), [ + '$ptitle' => t('Channel Settings'), + '$submit' => t('Submit'), + '$baseurl' => z_root(), + '$uid' => local_channel(), + '$form_security_token' => get_form_security_token("settings"), + '$nickname_block' => $prof_addr, + '$h_basic' => t('Basic Settings'), + '$username' => array('username', t('Full name'), $username, ''), + '$email' => array('email', t('Email Address'), $email, ''), + '$timezone' => array('timezone_select', t('Your timezone'), $timezone, t('This is important for showing the correct time on shared events'), get_timezones()), + '$defloc' => array('defloc', t('Default post location'), $defloc, t('Optional geographical location to display on your posts')), + '$allowloc' => array('allow_location', t('Obtain post location from your web browser or device'), ((get_pconfig(local_channel(), 'system', 'use_browser_location')) ? 1 : ''), '', $yes_no), + + '$adult' => array('adult', t('Adult content'), $adult_flag, t('Enable to indicate if this channel frequently or regularly publishes adult content. (Please also tag any adult material and/or nudity with #NSFW)'), $yes_no), + + '$h_prv' => t('Security and Privacy'), + '$permissions_set' => $permissions_set, + '$perms_set_msg' => t('Your permissions are already configured. Click to view/adjust'), + + '$hide_presence' => array('hide_presence', t('Hide my online presence'), $hide_presence, t('Prevents displaying in your profile that you are online'), $yes_no), + '$hidefriends' => array('hide_friends', t('Allow others to view your friends and connections'), 1 - intval($profile['hide_friends']), '', $yes_no), + '$permiss_arr' => $permiss, + '$comment_perms' => $comment_perms, + '$mail_perms' => $mail_perms, + '$noindex' => ['noindex', t('Forbid indexing of your channel content by search engines'), get_pconfig($channel['channel_id'], 'system', 'noindex'), '', $yes_no], + '$close_comments' => ['close_comments', t('Disable acceptance of comments on my posts after this many days'), ((intval(get_pconfig(local_channel(), 'system', 'close_comments'))) ? intval(get_pconfig(local_channel(), 'system', 'close_comments')) : EMPTY_STR), t('Leave unset or enter 0 to allow comments indefinitely')], + '$blocktags' => array('blocktags', t('Allow others to tag your posts'), 1 - $blocktags, t('Often used by the community to retro-actively flag inappropriate content'), $yes_no), + + '$lbl_p2macro' => t('Channel Permission Limits'), + + '$expire' => array('expire', t('Expire conversations you have not participated in after this many days'), $expire, t('0 or blank to use the website limit.') . ' ' . ((intval($sys_expire)) ? sprintf(t('This website expires after %d days.'), intval($sys_expire)) : t('This website does not provide an expiration policy.')) . ' ' . t('The website limit takes precedence if lower than your limit.')), + '$maxreq' => array('maxreq', t('Maximum Friend Requests/Day:'), intval($channel['channel_max_friend_req']), t('May reduce spam activity')), + '$permissions' => t('Default Access List'), + '$permdesc' => t("(click to open/close)"), + '$aclselect' => populate_acl($perm_defaults, false, PermissionDescription::fromDescription(t('Use my default audience setting for the type of object published'))), + '$profseltxt' => t('Profile to assign new connections'), + '$profselect' => ((feature_enabled(local_channel(), 'multi_profiles')) ? contact_profile_assign(get_pconfig(local_channel(), 'system', 'profile_assign', '')) : ''), + + '$allow_cid' => acl2json($perm_defaults['allow_cid']), + '$allow_gid' => acl2json($perm_defaults['allow_gid']), + '$deny_cid' => acl2json($perm_defaults['deny_cid']), + '$deny_gid' => acl2json($perm_defaults['deny_gid']), + '$suggestme' => $suggestme, + '$group_select' => $group_select, + '$can_change_role' => ((in_array($permissions_role, ['collection', 'collection_restricted'])) ? false : true), + '$permissions_role' => $permissions_role, + '$role' => array('permissions_role', t('Channel type and privacy'), $permissions_role, '', $perm_roles, ' onchange="update_role_text(); return false;"'), + '$defpermcat' => ['defpermcat', t('Default Permissions Group'), $default_permcat, '', $permcats], + '$permcat_enable' => feature_enabled(local_channel(), 'permcats'), + '$profile_in_dir' => $profile_in_dir, + '$hide_friends' => $hide_friends, + '$hide_wall' => $hide_wall, + '$unkmail' => $unkmail, + '$cntunkmail' => array('cntunkmail', t('Maximum direct messages per day from unknown people:'), intval($channel['channel_max_anon_mail']), t("Useful to reduce spamming if you allow direct messages from unknown people")), + + '$autoperms' => $autoperms, +// '$anymention' => $anymention, + '$hyperdrive' => $hyperdrive, + '$activitypub' => $activitypub, + '$apconfig' => $apconfig, + '$close' => t('Close'), + '$h_not' => t('Notifications'), + '$activity_options' => t('By default post a status message when:'), + '$post_newfriend' => array('post_newfriend', t('accepting a friend request'), $post_newfriend, '', $yes_no), + '$post_joingroup' => array('post_joingroup', t('joining a forum/community'), $post_joingroup, '', $yes_no), + '$post_profilechange' => array('post_profilechange', t('making an interesting profile change'), $post_profilechange, '', $yes_no), + '$lbl_not' => t('Send a notification email when:'), + '$notify1' => array('notify1', t('You receive a connection request'), ($notify & NOTIFY_INTRO), NOTIFY_INTRO, '', $yes_no), +// '$notify2' => array('notify2', t('Your connections are confirmed'), ($notify & NOTIFY_CONFIRM), NOTIFY_CONFIRM, '', $yes_no), + '$notify3' => array('notify3', t('Someone writes on your profile wall'), ($notify & NOTIFY_WALL), NOTIFY_WALL, '', $yes_no), + '$notify4' => array('notify4', t('Someone writes a followup comment'), ($notify & NOTIFY_COMMENT), NOTIFY_COMMENT, '', $yes_no), + '$notify10' => array('notify10', t('Someone shares a followed conversation'), ($notify & NOTIFY_RESHARE), NOTIFY_RESHARE, '', $yes_no), + '$notify5' => array('notify5', t('You receive a direct (private) message'), ($notify & NOTIFY_MAIL), NOTIFY_MAIL, '', $yes_no), +// '$notify6' => array('notify6', t('You receive a friend suggestion'), ($notify & NOTIFY_SUGGEST), NOTIFY_SUGGEST, '', $yes_no), + '$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:'), + + '$vnotify1' => array('vnotify1', t('Unseen stream activity'), ($vnotify & VNOTIFY_NETWORK), VNOTIFY_NETWORK, '', $yes_no), + '$vnotify2' => array('vnotify2', t('Unseen channel activity'), ($vnotify & VNOTIFY_CHANNEL), VNOTIFY_CHANNEL, '', $yes_no), + '$vnotify3' => array('vnotify3', t('Unseen direct messages'), ($vnotify & VNOTIFY_MAIL), VNOTIFY_MAIL, t('Recommended'), $yes_no), + '$vnotify4' => array('vnotify4', t('Upcoming events'), ($vnotify & VNOTIFY_EVENT), VNOTIFY_EVENT, '', $yes_no), + '$vnotify5' => array('vnotify5', t('Events today'), ($vnotify & VNOTIFY_EVENTTODAY), VNOTIFY_EVENTTODAY, '', $yes_no), + '$vnotify6' => array('vnotify6', t('Upcoming birthdays'), ($vnotify & VNOTIFY_BIRTHDAY), VNOTIFY_BIRTHDAY, t('Not available in all themes'), $yes_no), + '$vnotify7' => array('vnotify7', t('System (personal) notifications'), ($vnotify & VNOTIFY_SYSTEM), VNOTIFY_SYSTEM, '', $yes_no), + '$vnotify8' => array('vnotify8', t('System info messages'), ($vnotify & VNOTIFY_INFO), VNOTIFY_INFO, t('Recommended'), $yes_no), + '$vnotify9' => array('vnotify9', t('System critical alerts'), ($vnotify & VNOTIFY_ALERT), VNOTIFY_ALERT, t('Recommended'), $yes_no), + '$vnotify10' => array('vnotify10', t('New connections'), ($vnotify & VNOTIFY_INTRO), VNOTIFY_INTRO, t('Recommended'), $yes_no), + '$vnotify11' => ((is_site_admin()) ? array('vnotify11', t('System Registrations'), ($vnotify & VNOTIFY_REGISTER), VNOTIFY_REGISTER, '', $yes_no) : []), +// '$vnotify12' => array('vnotify12', t('Unseen shared files'), ($vnotify & VNOTIFY_FILES), VNOTIFY_FILES, '', $yes_no), + '$vnotify13' => (($public_stream_mode) ? ['vnotify13', t('Unseen public stream activity'), ($vnotify & VNOTIFY_PUBS), VNOTIFY_PUBS, '', $yes_no] : []), + '$vnotify14' => array('vnotify14', t('Unseen likes and dislikes'), ($vnotify & VNOTIFY_LIKE), VNOTIFY_LIKE, '', $yes_no), + '$vnotify15' => array('vnotify15', t('Unseen forum posts'), ($vnotify & VNOTIFY_FORUMS), VNOTIFY_FORUMS, '', $yes_no), + '$vnotify16' => ((is_site_admin()) ? array('vnotify16', t('Reported content'), ($vnotify & VNOTIFY_REPORTS), VNOTIFY_REPORTS, '', $yes_no) : []), + '$desktop_notifications_info' => t('Desktop notifications are unavailable because the required browser permission has not been granted'), + '$desktop_notifications_request' => t('Grant permission'), + '$mailhost' => ['mailhost', t('Email notifications sent from (hostname)'), get_pconfig(local_channel(), 'system', 'email_notify_host', App::get_hostname()), sprintf(t('If your channel is mirrored to multiple locations, set this to your preferred location. This will prevent duplicate email notifications. Example: %s'), App::get_hostname())], + '$always_show_in_notices' => array('always_show_in_notices', t('Show new wall posts, private messages and connections under Notices'), $always_show_in_notices, 1, '', $yes_no), + '$permit_all_mentions' => ['permit_all_mentions', t('Accept messages from strangers which mention you'), get_pconfig(local_channel(), 'system', 'permit_all_mentions'), t('This setting bypasses normal permissions'), $yes_no], + '$followed_tags' => ['followed_tags', t('Accept messages from strangers which include any of the following hashtags'), $followed, t('comma separated, do not include the #')], + '$evdays' => array('evdays', t('Notify me of events this many days in advance'), $evdays, t('Must be greater than 0')), + '$basic_addon' => $plugin['basic'], + '$sec_addon' => $plugin['security'], + '$notify_addon' => $plugin['notify'], + '$misc_addon' => $plugin['misc'], + '$lbl_time' => t('Date and time'), + '$miscdoc' => t('This section is reserved for use by optional addons and apps to provide additional settings.'), + '$h_advn' => t('Advanced Account/Page Type Settings'), + '$h_descadvn' => t('Change the behaviour of this account for special situations'), + '$pagetype' => $pagetype, + '$lbl_misc' => t('Miscellaneous'), + '$photo_path' => array('photo_path', t('Default photo upload folder name'), get_pconfig(local_channel(), 'system', 'photo_path'), t('%Y - current year, %m - current month')), + '$attach_path' => array('attach_path', t('Default file upload folder name'), get_pconfig(local_channel(), 'system', 'attach_path'), t('%Y - current year, %m - current month')), + '$menus' => $menu, + '$menu_desc' => t('Personal menu to display in your channel pages'), + '$removeme' => t('Remove Channel'), + '$removechannel' => t('Remove this channel.'), + '$tag_username' => ['tag_username', t('Mentions should display'), intval(get_pconfig(local_channel(), 'system', 'tag_username', get_config('system', 'tag_username', false))), t('Changes to this setting are applied to new posts/comments only. It is not retroactive.'), + [ + 0 => t('the channel display name [example: @Barbara Jenkins]'), + 1 => t('the channel nickname [example: @barbara1976]'), + 2 => t('combined [example: @Barbara Jenkins (barbara1976)]'), + 127 => t('no preference, use the system default'), + ]], + + '$cal_first_day' => array('first_day', t('Calendar week begins on'), intval(get_pconfig(local_channel(), 'system', 'cal_first_day')), t('This varies by country/culture'), + [0 => t('Sunday'), + 1 => t('Monday'), + 2 => t('Tuesday'), + 3 => t('Wednesday'), + 4 => t('Thursday'), + 5 => t('Friday'), + 6 => t('Saturday') + ]), + ]); + + call_hooks('settings_form', $o); + return $o; + } } diff --git a/Zotlabs/Module/Settings/Display.php b/Zotlabs/Module/Settings/Display.php index 47c7f9b4a..e11677564 100644 --- a/Zotlabs/Module/Settings/Display.php +++ b/Zotlabs/Module/Settings/Display.php @@ -5,229 +5,240 @@ namespace Zotlabs\Module\Settings; use App; use Zotlabs\Lib\Libsync; +class Display +{ -class Display { + /* + * DISPLAY SETTINGS + */ - /* - * DISPLAY SETTINGS - */ + public function post() + { + check_form_security_token_redirectOnErr('/settings/display', 'settings_display'); - function post() { - check_form_security_token_redirectOnErr('/settings/display', 'settings_display'); + $themespec = explode(':', App::$channel['channel_theme']); + $existing_theme = $themespec[0]; + $existing_schema = $themespec[1]; - $themespec = explode(':', App::$channel['channel_theme']); - $existing_theme = $themespec[0]; - $existing_schema = $themespec[1]; + $theme = ((x($_POST, 'theme')) ? notags(trim($_POST['theme'])) : $existing_theme); - $theme = ((x($_POST,'theme')) ? notags(trim($_POST['theme'])) : $existing_theme); - - if(! $theme) - $theme = 'redbasic'; + if (!$theme) { + $theme = 'redbasic'; + } - $preload_images = ((x($_POST,'preload_images')) ? intval($_POST['preload_images']) : 0); - $channel_menu = ((x($_POST,'channel_menu')) ? intval($_POST['channel_menu']) : 0); - $user_scalable = ((x($_POST,'user_scalable')) ? intval($_POST['user_scalable']) : 0); - $nosmile = ((x($_POST,'nosmile')) ? intval($_POST['nosmile']) : 0); - $indentpx = ((x($_POST,'indentpx')) ? intval($_POST['indentpx']) : 0); + $preload_images = ((x($_POST, 'preload_images')) ? intval($_POST['preload_images']) : 0); + $channel_menu = ((x($_POST, 'channel_menu')) ? intval($_POST['channel_menu']) : 0); + $user_scalable = ((x($_POST, 'user_scalable')) ? intval($_POST['user_scalable']) : 0); + $nosmile = ((x($_POST, 'nosmile')) ? intval($_POST['nosmile']) : 0); + $indentpx = ((x($_POST, 'indentpx')) ? intval($_POST['indentpx']) : 0); - $channel_divmore_height = ((x($_POST,'channel_divmore_height')) ? intval($_POST['channel_divmore_height']) : 400); - if($channel_divmore_height < 50) - $channel_divmore_height = 50; - $stream_divmore_height = ((x($_POST,'stream_divmore_height')) ? intval($_POST['stream_divmore_height']) : 400); - if($stream_divmore_height < 50) - $stream_divmore_height = 50; + $channel_divmore_height = ((x($_POST, 'channel_divmore_height')) ? intval($_POST['channel_divmore_height']) : 400); + if ($channel_divmore_height < 50) { + $channel_divmore_height = 50; + } + $stream_divmore_height = ((x($_POST, 'stream_divmore_height')) ? intval($_POST['stream_divmore_height']) : 400); + if ($stream_divmore_height < 50) { + $stream_divmore_height = 50; + } - $browser_update = ((x($_POST,'browser_update')) ? intval($_POST['browser_update']) : 0); - $browser_update = $browser_update * 1000; - if($browser_update < 15000) - $browser_update = 15000; + $browser_update = ((x($_POST, 'browser_update')) ? intval($_POST['browser_update']) : 0); + $browser_update = $browser_update * 1000; + if ($browser_update < 15000) { + $browser_update = 15000; + } - $itemspage = ((x($_POST,'itemspage')) ? intval($_POST['itemspage']) : 20); - if($itemspage > 100) - $itemspage = 100; + $itemspage = ((x($_POST, 'itemspage')) ? intval($_POST['itemspage']) : 20); + if ($itemspage > 100) { + $itemspage = 100; + } - if ($indentpx < 0) { - $indentpx = 0; - } - if ($indentpx > 20) { - $indentpx = 20; - } + if ($indentpx < 0) { + $indentpx = 0; + } + if ($indentpx > 20) { + $indentpx = 20; + } - set_pconfig(local_channel(),'system','preload_images',$preload_images); - set_pconfig(local_channel(),'system','user_scalable',$user_scalable); - set_pconfig(local_channel(),'system','update_interval', $browser_update); - set_pconfig(local_channel(),'system','itemspage', $itemspage); - set_pconfig(local_channel(),'system','no_smilies',1-intval($nosmile)); - set_pconfig(local_channel(),'system','channel_divmore_height', $channel_divmore_height); - set_pconfig(local_channel(),'system','stream_divmore_height', $stream_divmore_height); - set_pconfig(local_channel(),'system','channel_menu', $channel_menu); - set_pconfig(local_channel(),'system','thread_indent_px',$indentpx); + set_pconfig(local_channel(), 'system', 'preload_images', $preload_images); + set_pconfig(local_channel(), 'system', 'user_scalable', $user_scalable); + set_pconfig(local_channel(), 'system', 'update_interval', $browser_update); + set_pconfig(local_channel(), 'system', 'itemspage', $itemspage); + set_pconfig(local_channel(), 'system', 'no_smilies', 1 - intval($nosmile)); + set_pconfig(local_channel(), 'system', 'channel_divmore_height', $channel_divmore_height); + set_pconfig(local_channel(), 'system', 'stream_divmore_height', $stream_divmore_height); + set_pconfig(local_channel(), 'system', 'channel_menu', $channel_menu); + set_pconfig(local_channel(), 'system', 'thread_indent_px', $indentpx); - $newschema = ''; - if($theme){ - // call theme_post only if theme has not been changed - if( ($themeconfigfile = $this->get_theme_config_file($theme)) != null){ - require_once($themeconfigfile); - if(class_exists('\\Zotlabs\\Theme\\' . ucfirst($theme) . 'Config')) { - $clsname = '\\Zotlabs\\Theme\\' . ucfirst($theme) . 'Config'; - $theme_config = new $clsname(); - $schemas = $theme_config->get_schemas(); - if(array_key_exists($_POST['schema'],$schemas)) - $newschema = $_POST['schema']; - if($newschema === '---') - $newschema = ''; - $theme_config->post(); - } - } - } + $newschema = ''; + if ($theme) { + // call theme_post only if theme has not been changed + if (($themeconfigfile = $this->get_theme_config_file($theme)) != null) { + require_once($themeconfigfile); + if (class_exists('\\Zotlabs\\Theme\\' . ucfirst($theme) . 'Config')) { + $clsname = '\\Zotlabs\\Theme\\' . ucfirst($theme) . 'Config'; + $theme_config = new $clsname(); + $schemas = $theme_config->get_schemas(); + if (array_key_exists($_POST['schema'], $schemas)) { + $newschema = $_POST['schema']; + } + if ($newschema === '---') { + $newschema = ''; + } + $theme_config->post(); + } + } + } - logger('theme: ' . $theme . (($newschema) ? ':' . $newschema : '')); + logger('theme: ' . $theme . (($newschema) ? ':' . $newschema : '')); - $_SESSION['theme'] = $theme . (($newschema) ? ':' . $newschema : ''); + $_SESSION['theme'] = $theme . (($newschema) ? ':' . $newschema : ''); - $r = q("UPDATE channel SET channel_theme = '%s' WHERE channel_id = %d", - dbesc($theme . (($newschema) ? ':' . $newschema : '')), - intval(local_channel()) - ); + $r = q( + "UPDATE channel SET channel_theme = '%s' WHERE channel_id = %d", + dbesc($theme . (($newschema) ? ':' . $newschema : '')), + intval(local_channel()) + ); - call_hooks('display_settings_post', $_POST); - Libsync::build_sync_packet(); - goaway(z_root() . '/settings/display' ); - return; // NOTREACHED - } + call_hooks('display_settings_post', $_POST); + Libsync::build_sync_packet(); + goaway(z_root() . '/settings/display'); + return; // NOTREACHED + } - function get() { + public function get() + { - $yes_no = array(t('No'),t('Yes')); + $yes_no = array(t('No'), t('Yes')); - $default_theme = get_config('system','theme'); - if(! $default_theme) - $default_theme = 'redbasic'; + $default_theme = get_config('system', 'theme'); + if (!$default_theme) { + $default_theme = 'redbasic'; + } - $themespec = explode(':', App::$channel['channel_theme']); - $existing_theme = $themespec[0]; - $existing_schema = $themespec[1]; + $themespec = explode(':', App::$channel['channel_theme']); + $existing_theme = $themespec[0]; + $existing_schema = $themespec[1]; - $theme = (($existing_theme) ? $existing_theme : $default_theme); + $theme = (($existing_theme) ? $existing_theme : $default_theme); - $allowed_themes_str = get_config('system','allowed_themes'); - $allowed_themes_raw = explode(',',$allowed_themes_str); - $allowed_themes = []; - if(count($allowed_themes_raw)) - foreach($allowed_themes_raw as $x) - if(strlen(trim($x)) && is_dir("view/theme/$x")) - $allowed_themes[] = trim($x); + $allowed_themes_str = get_config('system', 'allowed_themes'); + $allowed_themes_raw = explode(',', $allowed_themes_str); + $allowed_themes = []; + if (count($allowed_themes_raw)) { + foreach ($allowed_themes_raw as $x) { + if (strlen(trim($x)) && is_dir("view/theme/$x")) { + $allowed_themes[] = trim($x); + } + } + } - $themes = []; - $files = glob('view/theme/*'); - if($allowed_themes) { - foreach($allowed_themes as $th) { - $f = $th; + $themes = []; + $files = glob('view/theme/*'); + if ($allowed_themes) { + foreach ($allowed_themes as $th) { + $f = $th; - $info = get_theme_info($th); - $compatible = check_plugin_versions($info); - if(! $compatible) { - $themes[$f] = sprintf(t('%s - (Incompatible)'), $f); - continue; - } + $info = get_theme_info($th); + $compatible = check_plugin_versions($info); + if (!$compatible) { + $themes[$f] = sprintf(t('%s - (Incompatible)'), $f); + continue; + } - $is_experimental = file_exists('view/theme/' . $th . '/experimental'); - $unsupported = file_exists('view/theme/' . $th . '/unsupported'); - $is_library = file_exists('view/theme/'. $th . '/library'); + $is_experimental = file_exists('view/theme/' . $th . '/experimental'); + $unsupported = file_exists('view/theme/' . $th . '/unsupported'); + $is_library = file_exists('view/theme/' . $th . '/library'); - if (!$is_experimental or ($is_experimental && (get_config('experimentals','exp_themes')==1 or get_config('experimentals','exp_themes')===false))){ - $theme_name = (($is_experimental) ? sprintf(t('%s - (Experimental)'), $f) : $f); - if (! $is_library) { - $themes[$f] = $theme_name; - } - } - } - } + if (!$is_experimental or ($is_experimental && (get_config('experimentals', 'exp_themes') == 1 or get_config('experimentals', 'exp_themes') === false))) { + $theme_name = (($is_experimental) ? sprintf(t('%s - (Experimental)'), $f) : $f); + if (!$is_library) { + $themes[$f] = $theme_name; + } + } + } + } - $theme_selected = ((array_key_exists('theme',$_SESSION) && $_SESSION['theme']) ? $_SESSION['theme'] : $theme); + $theme_selected = ((array_key_exists('theme', $_SESSION) && $_SESSION['theme']) ? $_SESSION['theme'] : $theme); - if (strpos($theme_selected, ':')) { - $theme_selected = explode(':', $theme_selected)[0]; - } + if (strpos($theme_selected, ':')) { + $theme_selected = explode(':', $theme_selected)[0]; + } - $preload_images = get_pconfig(local_channel(),'system','preload_images'); + $preload_images = get_pconfig(local_channel(), 'system', 'preload_images'); - $user_scalable = get_pconfig(local_channel(),'system','user_scalable', '0'); + $user_scalable = get_pconfig(local_channel(), 'system', 'user_scalable', '0'); - $browser_update = intval(get_pconfig(local_channel(), 'system','update_interval',30000)); // default if not set: 30 seconds - $browser_update = (($browser_update < 15000) ? 15 : $browser_update / 1000); // minimum 15 seconds + $browser_update = intval(get_pconfig(local_channel(), 'system', 'update_interval', 30000)); // default if not set: 30 seconds + $browser_update = (($browser_update < 15000) ? 15 : $browser_update / 1000); // minimum 15 seconds - $itemspage = intval(get_pconfig(local_channel(), 'system','itemspage')); - $itemspage = (($itemspage > 0 && $itemspage < 101) ? $itemspage : 20); // default if not set: 20 items + $itemspage = intval(get_pconfig(local_channel(), 'system', 'itemspage')); + $itemspage = (($itemspage > 0 && $itemspage < 101) ? $itemspage : 20); // default if not set: 20 items - $nosmile = get_pconfig(local_channel(),'system','no_smilies'); - $nosmile = (($nosmile===false)? '0': $nosmile); // default if not set: 0 + $nosmile = get_pconfig(local_channel(), 'system', 'no_smilies'); + $nosmile = (($nosmile === false) ? '0' : $nosmile); // default if not set: 0 - $theme_config = ""; - if(($themeconfigfile = $this->get_theme_config_file($theme)) != null){ - require_once($themeconfigfile); - if(class_exists('\\Zotlabs\\Theme\\' . ucfirst($theme) . 'Config')) { - $clsname = '\\Zotlabs\\Theme\\' . ucfirst($theme) . 'Config'; - $thm_config = new $clsname(); - $schemas = $thm_config->get_schemas(); - $theme_config = $thm_config->get(); - } - } + $theme_config = ""; + if (($themeconfigfile = $this->get_theme_config_file($theme)) != null) { + require_once($themeconfigfile); + if (class_exists('\\Zotlabs\\Theme\\' . ucfirst($theme) . 'Config')) { + $clsname = '\\Zotlabs\\Theme\\' . ucfirst($theme) . 'Config'; + $thm_config = new $clsname(); + $schemas = $thm_config->get_schemas(); + $theme_config = $thm_config->get(); + } + } - // logger('schemas: ' . print_r($schemas,true)); + // logger('schemas: ' . print_r($schemas,true)); - $tpl = get_markup_template("settings_display.tpl"); - $o = replace_macros($tpl, array( - '$ptitle' => t('Display Settings'), - '$d_tset' => t('Theme Settings'), - '$d_ctset' => t('Custom Theme Settings'), - '$d_cset' => t('Content Settings'), - '$form_security_token' => get_form_security_token("settings_display"), - '$submit' => t('Submit'), - '$baseurl' => z_root(), - '$uid' => local_channel(), + $tpl = get_markup_template("settings_display.tpl"); + $o = replace_macros($tpl, array( + '$ptitle' => t('Display Settings'), + '$d_tset' => t('Theme Settings'), + '$d_ctset' => t('Custom Theme Settings'), + '$d_cset' => t('Content Settings'), + '$form_security_token' => get_form_security_token("settings_display"), + '$submit' => t('Submit'), + '$baseurl' => z_root(), + '$uid' => local_channel(), - '$theme' => (($themes) ? array('theme', t('Display Theme:'), $theme_selected, '', $themes, 'preview') : false), - '$schema' => array('schema', t('Select scheme'), $existing_schema, '' , $schemas), + '$theme' => (($themes) ? array('theme', t('Display Theme:'), $theme_selected, '', $themes, 'preview') : false), + '$schema' => array('schema', t('Select scheme'), $existing_schema, '', $schemas), - '$preload_images' => array('preload_images', t("Preload images before rendering the page"), $preload_images, t("The subjective page load time will be longer but the page will be ready when displayed"), $yes_no), - '$user_scalable' => array('user_scalable', t("Enable user zoom on mobile devices"), $user_scalable, '', $yes_no), - '$ajaxint' => array('browser_update', t("Update notifications every xx seconds"), $browser_update, t('Minimum of 15 seconds, no maximum')), - '$itemspage' => array('itemspage', t("Maximum number of conversations to load at any time:"), $itemspage, t('Maximum of 100 items')), - '$nosmile' => array('nosmile', t("Show emoticons (smilies) as images"), 1-intval($nosmile), '', $yes_no), - '$channel_menu' => [ 'channel_menu', t('Provide channel menu in navigation bar'), get_pconfig(local_channel(),'system','channel_menu',get_config('system','channel_menu',0)), t('Default: channel menu located in app menu'),$yes_no ], - '$layout_editor' => t('System Page Layout Editor - (advanced)'), - '$theme_config' => $theme_config, - '$expert' => feature_enabled(local_channel(),'advanced_theming'), - '$channel_divmore_height' => array('channel_divmore_height', t('Channel page max height of content (in pixels)'), ((get_pconfig(local_channel(),'system','channel_divmore_height')) ? get_pconfig(local_channel(),'system','channel_divmore_height') : 400), t('click to expand content exceeding this height')), - '$stream_divmore_height' => array('stream_divmore_height', t('Stream page max height of content (in pixels)'), ((get_pconfig(local_channel(),'system','stream_divmore_height')) ? get_pconfig(local_channel(),'system','stream_divmore_height') : 400) , t('click to expand content exceeding this height')), - '$indentpx' => [ 'indentpx', t('Indent threaded comments this many pixels from the parent'), intval(get_pconfig(local_channel(),'system','thread_indent_px', get_config('system','thread_indent_px',0))), t('0-20') ], + '$preload_images' => array('preload_images', t("Preload images before rendering the page"), $preload_images, t("The subjective page load time will be longer but the page will be ready when displayed"), $yes_no), + '$user_scalable' => array('user_scalable', t("Enable user zoom on mobile devices"), $user_scalable, '', $yes_no), + '$ajaxint' => array('browser_update', t("Update notifications every xx seconds"), $browser_update, t('Minimum of 15 seconds, no maximum')), + '$itemspage' => array('itemspage', t("Maximum number of conversations to load at any time:"), $itemspage, t('Maximum of 100 items')), + '$nosmile' => array('nosmile', t("Show emoticons (smilies) as images"), 1 - intval($nosmile), '', $yes_no), + '$channel_menu' => ['channel_menu', t('Provide channel menu in navigation bar'), get_pconfig(local_channel(), 'system', 'channel_menu', get_config('system', 'channel_menu', 0)), t('Default: channel menu located in app menu'), $yes_no], + '$layout_editor' => t('System Page Layout Editor - (advanced)'), + '$theme_config' => $theme_config, + '$expert' => feature_enabled(local_channel(), 'advanced_theming'), + '$channel_divmore_height' => array('channel_divmore_height', t('Channel page max height of content (in pixels)'), ((get_pconfig(local_channel(), 'system', 'channel_divmore_height')) ? get_pconfig(local_channel(), 'system', 'channel_divmore_height') : 400), t('click to expand content exceeding this height')), + '$stream_divmore_height' => array('stream_divmore_height', t('Stream page max height of content (in pixels)'), ((get_pconfig(local_channel(), 'system', 'stream_divmore_height')) ? get_pconfig(local_channel(), 'system', 'stream_divmore_height') : 400), t('click to expand content exceeding this height')), + '$indentpx' => ['indentpx', t('Indent threaded comments this many pixels from the parent'), intval(get_pconfig(local_channel(), 'system', 'thread_indent_px', get_config('system', 'thread_indent_px', 0))), t('0-20')], - )); + )); - call_hooks('display_settings',$o); - return $o; - } - - - function get_theme_config_file($theme){ - - $base_theme = App::$theme_info['extends']; - - if (file_exists("view/theme/$theme/php/config.php")){ - return "view/theme/$theme/php/config.php"; - } - if (file_exists("view/theme/$base_theme/php/config.php")){ - return "view/theme/$base_theme/php/config.php"; - } - return null; - } + call_hooks('display_settings', $o); + return $o; + } + public function get_theme_config_file($theme) + { + $base_theme = App::$theme_info['extends']; + if (file_exists("view/theme/$theme/php/config.php")) { + return "view/theme/$theme/php/config.php"; + } + if (file_exists("view/theme/$base_theme/php/config.php")) { + return "view/theme/$base_theme/php/config.php"; + } + return null; + } } diff --git a/Zotlabs/Module/Settings/Featured.php b/Zotlabs/Module/Settings/Featured.php index 83148d648..6405b0113 100644 --- a/Zotlabs/Module/Settings/Featured.php +++ b/Zotlabs/Module/Settings/Featured.php @@ -4,83 +4,84 @@ namespace Zotlabs\Module\Settings; use Zotlabs\Lib\Libsync; +class Featured +{ -class Featured { - - function post() { - check_form_security_token_redirectOnErr('/settings/featured', 'settings_featured'); - - call_hooks('feature_settings_post', $_POST); - - if($_POST['affinity_slider-submit']) { - $cmax = intval($_POST['affinity_cmax']); - if($cmax < 0 || $cmax > 99) - $cmax = 99; - $cmin = intval($_POST['affinity_cmin']); - if($cmin < 0 || $cmin > 99) - $cmin = 0; - set_pconfig(local_channel(),'affinity','cmin',$cmin); - set_pconfig(local_channel(),'affinity','cmax',$cmax); + public function post() + { + check_form_security_token_redirectOnErr('/settings/featured', 'settings_featured'); - info( t('Affinity Slider settings updated.') . EOL); + call_hooks('feature_settings_post', $_POST); - } - - Libsync::build_sync_packet(); - return; - } + if ($_POST['affinity_slider-submit']) { + $cmax = intval($_POST['affinity_cmax']); + if ($cmax < 0 || $cmax > 99) { + $cmax = 99; + } + $cmin = intval($_POST['affinity_cmin']); + if ($cmin < 0 || $cmin > 99) { + $cmin = 0; + } + set_pconfig(local_channel(), 'affinity', 'cmin', $cmin); + set_pconfig(local_channel(), 'affinity', 'cmax', $cmax); - function get() { - $settings_addons = ""; - - $o = ''; - - $r = q("SELECT * FROM hook WHERE hook = 'feature_settings' "); - if(! $r) - $settings_addons = t('No feature settings configured'); - - if(feature_enabled(local_channel(),'affinity')) { - - $cmax = intval(get_pconfig(local_channel(),'affinity','cmax')); - $cmax = (($cmax) ? $cmax : 99); - $setting_fields .= replace_macros(get_markup_template('field_input.tpl'), array( - '$field' => array('affinity_cmax', t('Default maximum affinity level'), $cmax, t('0-99 default 99')) - )); - $cmin = intval(get_pconfig(local_channel(),'affinity','cmin')); - $cmin = (($cmin) ? $cmin : 0); - $setting_fields .= replace_macros(get_markup_template('field_input.tpl'), array( - '$field' => array('affinity_cmin', t('Default minimum affinity level'), $cmin, t('0-99 - default 0')) - )); + info(t('Affinity Slider settings updated.') . EOL); + } - $settings_addons .= replace_macros(get_markup_template('generic_addon_settings.tpl'), array( - '$addon' => array('affinity_slider', '' . t('Affinity Slider Settings'), '', t('Submit')), - '$content' => $setting_fields - )); - } + Libsync::build_sync_packet(); + return; + } - call_hooks('feature_settings', $settings_addons); - - $this->sortpanels($settings_addons); + public function get() + { + $settings_addons = ""; - - $tpl = get_markup_template("settings_addons.tpl"); - $o .= replace_macros($tpl, array( - '$form_security_token' => get_form_security_token("settings_featured"), - '$title' => t('Addon Settings'), - '$descrip' => t('Please save/submit changes to any panel before opening another.'), - '$settings_addons' => $settings_addons - )); - return $o; - } + $o = ''; - function sortpanels(&$s) { - $a = explode('
      ',$s); - if($a) { - usort($a,'featured_sort'); - $s = implode('
      ',$a); - } - } + $r = q("SELECT * FROM hook WHERE hook = 'feature_settings' "); + if (!$r) { + $settings_addons = t('No feature settings configured'); + } + if (feature_enabled(local_channel(), 'affinity')) { + $cmax = intval(get_pconfig(local_channel(), 'affinity', 'cmax')); + $cmax = (($cmax) ? $cmax : 99); + $setting_fields .= replace_macros(get_markup_template('field_input.tpl'), array( + '$field' => array('affinity_cmax', t('Default maximum affinity level'), $cmax, t('0-99 default 99')) + )); + $cmin = intval(get_pconfig(local_channel(), 'affinity', 'cmin')); + $cmin = (($cmin) ? $cmin : 0); + $setting_fields .= replace_macros(get_markup_template('field_input.tpl'), array( + '$field' => array('affinity_cmin', t('Default minimum affinity level'), $cmin, t('0-99 - default 0')) + )); + + $settings_addons .= replace_macros(get_markup_template('generic_addon_settings.tpl'), array( + '$addon' => array('affinity_slider', '' . t('Affinity Slider Settings'), '', t('Submit')), + '$content' => $setting_fields + )); + } + + call_hooks('feature_settings', $settings_addons); + + $this->sortpanels($settings_addons); + + + $tpl = get_markup_template("settings_addons.tpl"); + $o .= replace_macros($tpl, array( + '$form_security_token' => get_form_security_token("settings_featured"), + '$title' => t('Addon Settings'), + '$descrip' => t('Please save/submit changes to any panel before opening another.'), + '$settings_addons' => $settings_addons + )); + return $o; + } + + public function sortpanels(&$s) + { + $a = explode('
      ', $s); + if ($a) { + usort($a, 'featured_sort'); + $s = implode('
      ', $a); + } + } } - - diff --git a/Zotlabs/Module/Settings/Features.php b/Zotlabs/Module/Settings/Features.php index aa46e92e4..a2d88e0ca 100644 --- a/Zotlabs/Module/Settings/Features.php +++ b/Zotlabs/Module/Settings/Features.php @@ -4,62 +4,65 @@ namespace Zotlabs\Module\Settings; use Zotlabs\Lib\Libsync; -class Features { +class Features +{ - function post() { - check_form_security_token_redirectOnErr('/settings/features', 'settings_features'); - - $features = get_features(false); + public function post() + { + check_form_security_token_redirectOnErr('/settings/features', 'settings_features'); - foreach($features as $fname => $fdata) { - foreach(array_slice($fdata,1) as $f) { - $k = $f[0]; - if(array_key_exists("feature_$k",$_POST)) - set_pconfig(local_channel(),'feature',$k, (string) $_POST["feature_$k"]); - else - set_pconfig(local_channel(),'feature', $k, ''); - } - } - Libsync::build_sync_packet(); - return; - } + $features = get_features(false); - function get() { - - $arr = []; - $harr = []; + foreach ($features as $fname => $fdata) { + foreach (array_slice($fdata, 1) as $f) { + $k = $f[0]; + if (array_key_exists("feature_$k", $_POST)) { + set_pconfig(local_channel(), 'feature', $k, (string)$_POST["feature_$k"]); + } else { + set_pconfig(local_channel(), 'feature', $k, ''); + } + } + } + Libsync::build_sync_packet(); + return; + } + + public function get() + { + + $arr = []; + $harr = []; - $all_features_raw = get_features(false); + $all_features_raw = get_features(false); - foreach($all_features_raw as $fname => $fdata) { - foreach(array_slice($fdata,1) as $f) { - $harr[$f[0]] = ((intval(feature_enabled(local_channel(),$f[0]))) ? "1" : ''); - } - } + foreach ($all_features_raw as $fname => $fdata) { + foreach (array_slice($fdata, 1) as $f) { + $harr[$f[0]] = ((intval(feature_enabled(local_channel(), $f[0]))) ? "1" : ''); + } + } - $features = get_features(true); + $features = get_features(true); - foreach($features as $fname => $fdata) { - $arr[$fname] = []; - $arr[$fname][0] = $fdata[0]; - foreach(array_slice($fdata,1) as $f) { - $arr[$fname][1][] = array('feature_' . $f[0],$f[1],((intval(feature_enabled(local_channel(),$f[0]))) ? "1" : ''),$f[2],array(t('Off'),t('On'))); - unset($harr[$f[0]]); - } - } - - $tpl = get_markup_template("settings_features.tpl"); - $o .= replace_macros($tpl, array( - '$form_security_token' => get_form_security_token("settings_features"), - '$title' => t('Additional Features'), - '$features' => $arr, - '$hiddens' => $harr, - '$baseurl' => z_root(), - '$submit' => t('Submit'), - )); - - return $o; - } + foreach ($features as $fname => $fdata) { + $arr[$fname] = []; + $arr[$fname][0] = $fdata[0]; + foreach (array_slice($fdata, 1) as $f) { + $arr[$fname][1][] = array('feature_' . $f[0], $f[1], ((intval(feature_enabled(local_channel(), $f[0]))) ? "1" : ''), $f[2], array(t('Off'), t('On'))); + unset($harr[$f[0]]); + } + } + $tpl = get_markup_template("settings_features.tpl"); + $o .= replace_macros($tpl, array( + '$form_security_token' => get_form_security_token("settings_features"), + '$title' => t('Additional Features'), + '$features' => $arr, + '$hiddens' => $harr, + '$baseurl' => z_root(), + '$submit' => t('Submit'), + )); + + return $o; + } } diff --git a/Zotlabs/Module/Settings/Network.php b/Zotlabs/Module/Settings/Network.php index 14a118f2c..8c902df9c 100644 --- a/Zotlabs/Module/Settings/Network.php +++ b/Zotlabs/Module/Settings/Network.php @@ -2,127 +2,129 @@ namespace Zotlabs\Module\Settings; +class Network +{ -class Network { + public function post() + { + check_form_security_token_redirectOnErr('/settings/network', 'settings_network'); - function post() { - check_form_security_token_redirectOnErr('/settings/network', 'settings_network'); - - $features = self::get_features(); + $features = self::get_features(); - foreach($features as $f) { - $k = $f[0]; - if(array_key_exists("feature_$k",$_POST)) - set_pconfig(local_channel(),'feature',$k, (string) $_POST["feature_$k"]); - else - set_pconfig(local_channel(),'feature', $k, ''); - } - - build_sync_packet(); - return; - } + foreach ($features as $f) { + $k = $f[0]; + if (array_key_exists("feature_$k", $_POST)) { + set_pconfig(local_channel(), 'feature', $k, (string)$_POST["feature_$k"]); + } else { + set_pconfig(local_channel(), 'feature', $k, ''); + } + } - function get() { - - $features = self::get_features(); + build_sync_packet(); + return; + } - foreach($features as $f) { - $arr[] = array('feature_' . $f[0],$f[1],((intval(feature_enabled(local_channel(),$f[0]))) ? "1" : ''),$f[2],array(t('Off'),t('On'))); - } + public function get() + { - $tpl = get_markup_template("settings_module.tpl"); + $features = self::get_features(); - $o .= replace_macros($tpl, array( - '$action_url' => 'settings/network', - '$form_security_token' => get_form_security_token("settings_network"), - '$title' => t('Activity Settings'), - '$features' => $arr, - '$baseurl' => z_root(), - '$submit' => t('Submit'), - )); - - return $o; - } + foreach ($features as $f) { + $arr[] = array('feature_' . $f[0], $f[1], ((intval(feature_enabled(local_channel(), $f[0]))) ? "1" : ''), $f[2], array(t('Off'), t('On'))); + } - function get_features() { - $arr = [ + $tpl = get_markup_template("settings_module.tpl"); - [ - 'archives', - t('Search by Date'), - t('Ability to select posts by date ranges'), - false, - get_config('feature_lock','archives') - ], + $o .= replace_macros($tpl, array( + '$action_url' => 'settings/network', + '$form_security_token' => get_form_security_token("settings_network"), + '$title' => t('Activity Settings'), + '$features' => $arr, + '$baseurl' => z_root(), + '$submit' => t('Submit'), + )); - [ - 'savedsearch', - t('Saved Searches'), - t('Save search terms for re-use'), - false, - get_config('feature_lock','savedsearch') - ], + return $o; + } - [ - 'order_tab', - t('Alternate Stream Order'), - t('Ability to order the stream by last post date, last comment date or unthreaded activities'), - false, - get_config('feature_lock','order_tab') - ], + public function get_features() + { + $arr = [ - [ - 'name_tab', - t('Contact Filter'), - t('Ability to display only posts of a selected contact'), - false, - get_config('feature_lock','name_tab') - ], + [ + 'archives', + t('Search by Date'), + t('Ability to select posts by date ranges'), + false, + get_config('feature_lock', 'archives') + ], - [ - 'forums_tab', - t('Forum Filter'), - t('Ability to display only posts of a specific forum'), - false, - get_config('feature_lock','forums_tab') - ], + [ + 'savedsearch', + t('Saved Searches'), + t('Save search terms for re-use'), + false, + get_config('feature_lock', 'savedsearch') + ], - [ - 'personal_tab', - t('Personal Posts Filter'), - t('Ability to display only posts that you\'ve interacted on'), - false, - get_config('feature_lock','personal_tab') - ], + [ + 'order_tab', + t('Alternate Stream Order'), + t('Ability to order the stream by last post date, last comment date or unthreaded activities'), + false, + get_config('feature_lock', 'order_tab') + ], - [ - 'affinity', - t('Affinity Tool'), - t('Filter stream activity by depth of relationships'), - false, - get_config('feature_lock','affinity') - ], + [ + 'name_tab', + t('Contact Filter'), + t('Ability to display only posts of a selected contact'), + false, + get_config('feature_lock', 'name_tab') + ], - [ - 'suggest', - t('Suggest Channels'), - t('Show friend and connection suggestions'), - false, - get_config('feature_lock','suggest') - ], + [ + 'forums_tab', + t('Forum Filter'), + t('Ability to display only posts of a specific forum'), + false, + get_config('feature_lock', 'forums_tab') + ], - [ - 'connfilter', - t('Connection Filtering'), - t('Filter incoming posts from connections based on keywords/content'), - false, - get_config('feature_lock','connfilter') - ] + [ + 'personal_tab', + t('Personal Posts Filter'), + t('Ability to display only posts that you\'ve interacted on'), + false, + get_config('feature_lock', 'personal_tab') + ], - ]; + [ + 'affinity', + t('Affinity Tool'), + t('Filter stream activity by depth of relationships'), + false, + get_config('feature_lock', 'affinity') + ], - return $arr; + [ + 'suggest', + t('Suggest Channels'), + t('Show friend and connection suggestions'), + false, + get_config('feature_lock', 'suggest') + ], - } + [ + 'connfilter', + t('Connection Filtering'), + t('Filter incoming posts from connections based on keywords/content'), + false, + get_config('feature_lock', 'connfilter') + ] + ]; + + return $arr; + } } diff --git a/Zotlabs/Module/Settings/Oauth.php b/Zotlabs/Module/Settings/Oauth.php index d6576c6de..62434dcf2 100644 --- a/Zotlabs/Module/Settings/Oauth.php +++ b/Zotlabs/Module/Settings/Oauth.php @@ -2,46 +2,49 @@ namespace Zotlabs\Module\Settings; - -class Oauth { +class Oauth +{ - function post() { - - if(x($_POST,'remove')){ - check_form_security_token_redirectOnErr('/settings/oauth', 'settings_oauth'); - - $key = $_POST['remove']; - q("DELETE FROM tokens WHERE id='%s' AND uid=%d", - dbesc($key), - local_channel()); - goaway(z_root()."/settings/oauth/"); - return; - } - - if((argc() > 2) && (argv(2) === 'edit' || argv(2) === 'add') && x($_POST,'submit')) { - - check_form_security_token_redirectOnErr('/settings/oauth', 'settings_oauth'); - - $name = ((x($_POST,'name')) ? escape_tags($_POST['name']) : ''); - $key = ((x($_POST,'key')) ? escape_tags($_POST['key']) : ''); - $secret = ((x($_POST,'secret')) ? escape_tags($_POST['secret']) : ''); - $redirect = ((x($_POST,'redirect')) ? escape_tags($_POST['redirect']) : ''); - $icon = ((x($_POST,'icon')) ? escape_tags($_POST['icon']) : ''); - $oauth2 = ((x($_POST,'oauth2')) ? intval($_POST['oauth2']) : 0); - $ok = true; - if($name == '') { - $ok = false; - notice( t('Name is required') . EOL); - } - if($key == '' || $secret == '') { - $ok = false; - notice( t('Key and Secret are required') . EOL); - } - - if($ok) { - if ($_POST['submit']==t("Update")){ - $r = q("UPDATE clients SET + public function post() + { + + if (x($_POST, 'remove')) { + check_form_security_token_redirectOnErr('/settings/oauth', 'settings_oauth'); + + $key = $_POST['remove']; + q( + "DELETE FROM tokens WHERE id='%s' AND uid=%d", + dbesc($key), + local_channel() + ); + goaway(z_root() . "/settings/oauth/"); + return; + } + + if ((argc() > 2) && (argv(2) === 'edit' || argv(2) === 'add') && x($_POST, 'submit')) { + check_form_security_token_redirectOnErr('/settings/oauth', 'settings_oauth'); + + $name = ((x($_POST, 'name')) ? escape_tags($_POST['name']) : ''); + $key = ((x($_POST, 'key')) ? escape_tags($_POST['key']) : ''); + $secret = ((x($_POST, 'secret')) ? escape_tags($_POST['secret']) : ''); + $redirect = ((x($_POST, 'redirect')) ? escape_tags($_POST['redirect']) : ''); + $icon = ((x($_POST, 'icon')) ? escape_tags($_POST['icon']) : ''); + $oauth2 = ((x($_POST, 'oauth2')) ? intval($_POST['oauth2']) : 0); + $ok = true; + if ($name == '') { + $ok = false; + notice(t('Name is required') . EOL); + } + if ($key == '' || $secret == '') { + $ok = false; + notice(t('Key and Secret are required') . EOL); + } + + if ($ok) { + if ($_POST['submit'] == t("Update")) { + $r = q( + "UPDATE clients SET client_id='%s', pw='%s', clname='%s', @@ -49,113 +52,121 @@ class Oauth { icon='%s', uid=%d WHERE client_id='%s'", - dbesc($key), - dbesc($secret), - dbesc($name), - dbesc($redirect), - dbesc($icon), - intval(local_channel()), - dbesc($key)); - } else { - $r = q("INSERT INTO clients (client_id, pw, clname, redirect_uri, icon, uid) + dbesc($key), + dbesc($secret), + dbesc($name), + dbesc($redirect), + dbesc($icon), + intval(local_channel()), + dbesc($key) + ); + } else { + $r = q( + "INSERT INTO clients (client_id, pw, clname, redirect_uri, icon, uid) VALUES ('%s','%s','%s','%s','%s',%d)", - dbesc($key), - dbesc($secret), - dbesc($name), - dbesc($redirect), - dbesc($icon), - intval(local_channel()) - ); - $r = q("INSERT INTO xperm (xp_client, xp_channel, xp_perm) VALUES ('%s', %d, '%s') ", - dbesc($key), - intval(local_channel()), - dbesc('all') - ); - } - } - goaway(z_root()."/settings/oauth/"); - return; - } - } + dbesc($key), + dbesc($secret), + dbesc($name), + dbesc($redirect), + dbesc($icon), + intval(local_channel()) + ); + $r = q( + "INSERT INTO xperm (xp_client, xp_channel, xp_perm) VALUES ('%s', %d, '%s') ", + dbesc($key), + intval(local_channel()), + dbesc('all') + ); + } + } + goaway(z_root() . "/settings/oauth/"); + return; + } + } - function get() { - - if((argc() > 2) && (argv(2) === 'add')) { - $tpl = get_markup_template("settings_oauth_edit.tpl"); - $o .= replace_macros($tpl, array( - '$form_security_token' => get_form_security_token("settings_oauth"), - '$title' => t('Add application'), - '$submit' => t('Submit'), - '$cancel' => t('Cancel'), - '$name' => array('name', t('Name'), '', t('Name of application')), - '$key' => array('key', t('Consumer Key'), random_string(16), t('Automatically generated - change if desired. Max length 20')), - '$secret' => array('secret', t('Consumer Secret'), random_string(16), t('Automatically generated - change if desired. Max length 20')), - '$redirect' => array('redirect', t('Redirect'), '', t('Redirect URI - leave blank unless your application specifically requires this')), - '$icon' => array('icon', t('Icon url'), '', t('Optional')), - )); - return $o; - } - - if((argc() > 3) && (argv(2) === 'edit')) { - $r = q("SELECT * FROM clients WHERE client_id='%s' AND uid=%d", - dbesc(argv(3)), - local_channel()); - - if (!count($r)){ - notice(t('Application not found.')); - return; - } - $app = $r[0]; - - $tpl = get_markup_template("settings_oauth_edit.tpl"); - $o .= replace_macros($tpl, array( - '$form_security_token' => get_form_security_token("settings_oauth"), - '$title' => t('Add application'), - '$submit' => t('Update'), - '$cancel' => t('Cancel'), - '$name' => array('name', t('Name'), $app['clname'] , ''), - '$key' => array('key', t('Consumer Key'), $app['client_id'], ''), - '$secret' => array('secret', t('Consumer Secret'), $app['pw'], ''), - '$redirect' => array('redirect', t('Redirect'), $app['redirect_uri'], ''), - '$icon' => array('icon', t('Icon url'), $app['icon'], ''), - )); - return $o; - } - - if((argc() > 3) && (argv(2) === 'delete')) { - check_form_security_token_redirectOnErr('/settings/oauth', 'settings_oauth', 't'); - - $r = q("DELETE FROM clients WHERE client_id='%s' AND uid=%d", - dbesc(argv(3)), - local_channel()); - goaway(z_root()."/settings/oauth/"); - return; - } - - - $r = q("SELECT clients.*, tokens.id as oauth_token, (clients.uid=%d) AS my + public function get() + { + + if ((argc() > 2) && (argv(2) === 'add')) { + $tpl = get_markup_template("settings_oauth_edit.tpl"); + $o .= replace_macros($tpl, array( + '$form_security_token' => get_form_security_token("settings_oauth"), + '$title' => t('Add application'), + '$submit' => t('Submit'), + '$cancel' => t('Cancel'), + '$name' => array('name', t('Name'), '', t('Name of application')), + '$key' => array('key', t('Consumer Key'), random_string(16), t('Automatically generated - change if desired. Max length 20')), + '$secret' => array('secret', t('Consumer Secret'), random_string(16), t('Automatically generated - change if desired. Max length 20')), + '$redirect' => array('redirect', t('Redirect'), '', t('Redirect URI - leave blank unless your application specifically requires this')), + '$icon' => array('icon', t('Icon url'), '', t('Optional')), + )); + return $o; + } + + if ((argc() > 3) && (argv(2) === 'edit')) { + $r = q( + "SELECT * FROM clients WHERE client_id='%s' AND uid=%d", + dbesc(argv(3)), + local_channel() + ); + + if (!count($r)) { + notice(t('Application not found.')); + return; + } + $app = $r[0]; + + $tpl = get_markup_template("settings_oauth_edit.tpl"); + $o .= replace_macros($tpl, array( + '$form_security_token' => get_form_security_token("settings_oauth"), + '$title' => t('Add application'), + '$submit' => t('Update'), + '$cancel' => t('Cancel'), + '$name' => array('name', t('Name'), $app['clname'], ''), + '$key' => array('key', t('Consumer Key'), $app['client_id'], ''), + '$secret' => array('secret', t('Consumer Secret'), $app['pw'], ''), + '$redirect' => array('redirect', t('Redirect'), $app['redirect_uri'], ''), + '$icon' => array('icon', t('Icon url'), $app['icon'], ''), + )); + return $o; + } + + if ((argc() > 3) && (argv(2) === 'delete')) { + check_form_security_token_redirectOnErr('/settings/oauth', 'settings_oauth', 't'); + + $r = q( + "DELETE FROM clients WHERE client_id='%s' AND uid=%d", + dbesc(argv(3)), + local_channel() + ); + goaway(z_root() . "/settings/oauth/"); + return; + } + + + $r = q( + "SELECT clients.*, tokens.id as oauth_token, (clients.uid=%d) AS my FROM clients LEFT JOIN tokens ON clients.client_id=tokens.client_id WHERE clients.uid IN (%d,0)", - local_channel(), - local_channel()); - - - $tpl = get_markup_template("settings_oauth.tpl"); - $o .= replace_macros($tpl, array( - '$form_security_token' => get_form_security_token("settings_oauth"), - '$baseurl' => z_root(), - '$title' => t('Connected Apps'), - '$add' => t('Add application'), - '$edit' => t('Edit'), - '$delete' => t('Delete'), - '$consumerkey' => t('Client key starts with'), - '$noname' => t('No name'), - '$remove' => t('Remove authorization'), - '$apps' => $r, - )); - return $o; - - } + local_channel(), + local_channel() + ); -} \ No newline at end of file + + $tpl = get_markup_template("settings_oauth.tpl"); + $o .= replace_macros($tpl, array( + '$form_security_token' => get_form_security_token("settings_oauth"), + '$baseurl' => z_root(), + '$title' => t('Connected Apps'), + '$add' => t('Add application'), + '$edit' => t('Edit'), + '$delete' => t('Delete'), + '$consumerkey' => t('Client key starts with'), + '$noname' => t('No name'), + '$remove' => t('Remove authorization'), + '$apps' => $r, + )); + return $o; + } +} diff --git a/Zotlabs/Module/Settings/Oauth2.php b/Zotlabs/Module/Settings/Oauth2.php index 55dd6a548..a934fe7f7 100644 --- a/Zotlabs/Module/Settings/Oauth2.php +++ b/Zotlabs/Module/Settings/Oauth2.php @@ -4,53 +4,57 @@ namespace Zotlabs\Module\Settings; use Zotlabs\Lib\Apps; - -class Oauth2 { +class Oauth2 +{ - function post() { - - if(x($_POST,'remove')){ - check_form_security_token_redirectOnErr('/settings/oauth2', 'settings_oauth2'); - $name = ((x($_POST,'name')) ? escape_tags(trim($_POST['name'])) : ''); - logger("REMOVE! ".$name." uid: ".local_channel()); - $key = $_POST['remove']; - q("DELETE FROM oauth_authorization_codes WHERE client_id='%s' AND user_id=%d", - dbesc($name), - intval(local_channel()) - ); - q("DELETE FROM oauth_access_tokens WHERE client_id='%s' AND user_id=%d", - dbesc($name), - intval(local_channel()) - ); - q("DELETE FROM oauth_refresh_tokens WHERE client_id='%s' AND user_id=%d", - dbesc($name), - intval(local_channel()) - ); - goaway(z_root()."/settings/oauth2/"); - return; - } - - if((argc() > 2) && (argv(2) === 'edit' || argv(2) === 'add') && x($_POST,'submit')) { - - check_form_security_token_redirectOnErr('/settings/oauth2', 'settings_oauth2'); - - $name = ((x($_POST,'name')) ? escape_tags(trim($_POST['name'])) : ''); - $clid = ((x($_POST,'clid')) ? escape_tags(trim($_POST['clid'])) : ''); - $secret = ((x($_POST,'secret')) ? escape_tags(trim($_POST['secret'])) : ''); - $redirect = ((x($_POST,'redirect')) ? escape_tags(trim($_POST['redirect'])) : ''); - $grant = ((x($_POST,'grant')) ? escape_tags(trim($_POST['grant'])) : ''); - $scope = ((x($_POST,'scope')) ? escape_tags(trim($_POST['scope'])) : ''); -logger('redirect: ' . $redirect); - $ok = true; - if($clid == '' || $secret == '') { - $ok = false; - notice( t('ID and Secret are required') . EOL); - } - - if($ok) { - if ($_POST['submit']==t("Update")){ - $r = q("UPDATE oauth_clients SET + public function post() + { + + if (x($_POST, 'remove')) { + check_form_security_token_redirectOnErr('/settings/oauth2', 'settings_oauth2'); + $name = ((x($_POST, 'name')) ? escape_tags(trim($_POST['name'])) : ''); + logger("REMOVE! " . $name . " uid: " . local_channel()); + $key = $_POST['remove']; + q( + "DELETE FROM oauth_authorization_codes WHERE client_id='%s' AND user_id=%d", + dbesc($name), + intval(local_channel()) + ); + q( + "DELETE FROM oauth_access_tokens WHERE client_id='%s' AND user_id=%d", + dbesc($name), + intval(local_channel()) + ); + q( + "DELETE FROM oauth_refresh_tokens WHERE client_id='%s' AND user_id=%d", + dbesc($name), + intval(local_channel()) + ); + goaway(z_root() . "/settings/oauth2/"); + return; + } + + if ((argc() > 2) && (argv(2) === 'edit' || argv(2) === 'add') && x($_POST, 'submit')) { + check_form_security_token_redirectOnErr('/settings/oauth2', 'settings_oauth2'); + + $name = ((x($_POST, 'name')) ? escape_tags(trim($_POST['name'])) : ''); + $clid = ((x($_POST, 'clid')) ? escape_tags(trim($_POST['clid'])) : ''); + $secret = ((x($_POST, 'secret')) ? escape_tags(trim($_POST['secret'])) : ''); + $redirect = ((x($_POST, 'redirect')) ? escape_tags(trim($_POST['redirect'])) : ''); + $grant = ((x($_POST, 'grant')) ? escape_tags(trim($_POST['grant'])) : ''); + $scope = ((x($_POST, 'scope')) ? escape_tags(trim($_POST['scope'])) : ''); + logger('redirect: ' . $redirect); + $ok = true; + if ($clid == '' || $secret == '') { + $ok = false; + notice(t('ID and Secret are required') . EOL); + } + + if ($ok) { + if ($_POST['submit'] == t("Update")) { + $r = q( + "UPDATE oauth_clients SET client_name = '%s', client_id = '%s', client_secret = '%s', @@ -59,149 +63,158 @@ logger('redirect: ' . $redirect); scope = '%s', user_id = %d WHERE client_id='%s' and user_id = %s", - dbesc($name), - dbesc($clid), - dbesc($secret), - dbesc($redirect), - dbesc($grant), - dbesc($scope), - intval(local_channel()), - dbesc($clid), - intval(local_channel())); - } else { - $r = q("INSERT INTO oauth_clients (client_name, client_id, client_secret, redirect_uri, grant_types, scope, user_id) + dbesc($name), + dbesc($clid), + dbesc($secret), + dbesc($redirect), + dbesc($grant), + dbesc($scope), + intval(local_channel()), + dbesc($clid), + intval(local_channel()) + ); + } else { + $r = q( + "INSERT INTO oauth_clients (client_name, client_id, client_secret, redirect_uri, grant_types, scope, user_id) VALUES ('%s','%s','%s','%s','%s','%s',%d)", - dbesc($name), - dbesc($clid), - dbesc($secret), - dbesc($redirect), - dbesc($grant), - dbesc($scope), - intval(local_channel()) - ); - $r = q("INSERT INTO xperm (xp_client, xp_channel, xp_perm) VALUES ('%s', %d, '%s') ", - dbesc($name), - intval(local_channel()), - dbesc('all') - ); - } - } - goaway(z_root()."/settings/oauth2/"); - return; - } - } + dbesc($name), + dbesc($clid), + dbesc($secret), + dbesc($redirect), + dbesc($grant), + dbesc($scope), + intval(local_channel()) + ); + $r = q( + "INSERT INTO xperm (xp_client, xp_channel, xp_perm) VALUES ('%s', %d, '%s') ", + dbesc($name), + intval(local_channel()), + dbesc('all') + ); + } + } + goaway(z_root() . "/settings/oauth2/"); + return; + } + } - function get() { + public function get() + { - if(! Apps::system_app_installed(local_channel(),'Clients')) { - return; - } + if (!Apps::system_app_installed(local_channel(), 'Clients')) { + return; + } - if((argc() > 2) && (argv(2) === 'add')) { - $tpl = get_markup_template("settings_oauth2_edit.tpl"); - $o .= replace_macros($tpl, array( - '$form_security_token' => get_form_security_token("settings_oauth2"), - '$title' => t('Add OAuth2 application'), - '$submit' => t('Submit'), - '$cancel' => t('Cancel'), - '$name' => array('name', t('Name'), '', t('Name of application')), - '$clid' => array('clid', t('Consumer ID'), random_string(16), t('Automatically generated - change if desired. Max length 20')), - '$secret' => array('secret', t('Consumer Secret'), random_string(16), t('Automatically generated - change if desired. Max length 20')), - '$redirect' => array('redirect', t('Redirect'), '', t('Redirect URI - leave blank unless your application specifically requires this')), - '$grant' => array('grant', t('Grant Types'), '', t('leave blank unless your application specifically requires this')), - '$scope' => array('scope', t('Authorization scope'), '', t('leave blank unless your application specifically requires this')), - )); - return $o; - } - - if((argc() > 3) && (argv(2) === 'edit')) { - $r = q("SELECT * FROM oauth_clients WHERE client_id='%s' AND user_id= %d", - dbesc(argv(3)), - intval(local_channel()) - ); - - if (! $r){ - notice(t('OAuth2 Application not found.')); - return; - } + if ((argc() > 2) && (argv(2) === 'add')) { + $tpl = get_markup_template("settings_oauth2_edit.tpl"); + $o .= replace_macros($tpl, array( + '$form_security_token' => get_form_security_token("settings_oauth2"), + '$title' => t('Add OAuth2 application'), + '$submit' => t('Submit'), + '$cancel' => t('Cancel'), + '$name' => array('name', t('Name'), '', t('Name of application')), + '$clid' => array('clid', t('Consumer ID'), random_string(16), t('Automatically generated - change if desired. Max length 20')), + '$secret' => array('secret', t('Consumer Secret'), random_string(16), t('Automatically generated - change if desired. Max length 20')), + '$redirect' => array('redirect', t('Redirect'), '', t('Redirect URI - leave blank unless your application specifically requires this')), + '$grant' => array('grant', t('Grant Types'), '', t('leave blank unless your application specifically requires this')), + '$scope' => array('scope', t('Authorization scope'), '', t('leave blank unless your application specifically requires this')), + )); + return $o; + } - $app = $r[0]; - - $tpl = get_markup_template("settings_oauth2_edit.tpl"); - $o .= replace_macros($tpl, array( - '$form_security_token' => get_form_security_token("settings_oauth2"), - '$title' => t('Add application'), - '$submit' => t('Update'), - '$cancel' => t('Cancel'), - '$name' => array('name', t('Name'), $app['client_name'], t('Name of application')), - '$clid' => array('clid', t('Consumer ID'), $app['client_id'], t('Automatically generated - change if desired. Max length 20')), - '$secret' => array('secret', t('Consumer Secret'), $app['client_secret'], t('Automatically generated - change if desired. Max length 20')), - '$redirect' => array('redirect', t('Redirect'), $app['redirect_uri'], t('Redirect URI - leave blank unless your application specifically requires this')), - '$grant' => array('grant', t('Grant Types'), $app['grant_types'], t('leave blank unless your application specifically requires this')), - '$scope' => array('scope', t('Authorization scope'), $app['scope'], t('leave blank unless your application specifically requires this')), - )); - return $o; - } - - if((argc() > 3) && (argv(2) === 'delete')) { - check_form_security_token_redirectOnErr('/settings/oauth2', 'settings_oauth2', 't'); - - $r = q("DELETE FROM oauth_clients WHERE client_id = '%s' AND user_id = %d", - dbesc(argv(3)), - intval(local_channel()) - ); - $r = q("DELETE FROM oauth_access_tokens WHERE client_id = '%s' AND user_id = %d", - dbesc(argv(3)), - intval(local_channel()) - ); - $r = q("DELETE FROM oauth_authorization_codes WHERE client_id = '%s' AND user_id = %d", - dbesc(argv(3)), - intval(local_channel()) - ); - $r = q("DELETE FROM oauth_refresh_tokens WHERE client_id = '%s' AND user_id = %d", - dbesc(argv(3)), - intval(local_channel()) - ); - goaway(z_root()."/settings/oauth2/"); - return; - } - + if ((argc() > 3) && (argv(2) === 'edit')) { + $r = q( + "SELECT * FROM oauth_clients WHERE client_id='%s' AND user_id= %d", + dbesc(argv(3)), + intval(local_channel()) + ); - $r = q("SELECT * FROM oauth_clients WHERE user_id = %d ", - intval(local_channel()) - ); + if (!$r) { + notice(t('OAuth2 Application not found.')); + return; + } - $c = q("select client_id, access_token from oauth_access_tokens where user_id = %d", - intval(local_channel()) - ); - if ($r && $c) { - foreach($c as $cv) { - for($x = 0; $x < count($r); $x ++) { - if($r[$x]['client_id'] === $cv['client_id']) { - if(! array_key_exists('tokens',$r[$x])) { - $r[$x]['tokens'] = []; - } - $r[$x]['tokens'][] = $cv['access_token']; - } - } - } - } + $app = $r[0]; - $tpl = get_markup_template("settings_oauth2.tpl"); - $o .= replace_macros($tpl, array( - '$form_security_token' => get_form_security_token("settings_oauth2"), - '$baseurl' => z_root(), - '$title' => t('Connected OAuth2 Apps'), - '$add' => t('Add application'), - '$edit' => t('Edit'), - '$delete' => t('Delete'), - '$consumerkey' => t('Client key starts with'), - '$noname' => t('No name'), - '$remove' => t('Remove authorization'), - '$apps' => $r, - )); - return $o; - - } + $tpl = get_markup_template("settings_oauth2_edit.tpl"); + $o .= replace_macros($tpl, array( + '$form_security_token' => get_form_security_token("settings_oauth2"), + '$title' => t('Add application'), + '$submit' => t('Update'), + '$cancel' => t('Cancel'), + '$name' => array('name', t('Name'), $app['client_name'], t('Name of application')), + '$clid' => array('clid', t('Consumer ID'), $app['client_id'], t('Automatically generated - change if desired. Max length 20')), + '$secret' => array('secret', t('Consumer Secret'), $app['client_secret'], t('Automatically generated - change if desired. Max length 20')), + '$redirect' => array('redirect', t('Redirect'), $app['redirect_uri'], t('Redirect URI - leave blank unless your application specifically requires this')), + '$grant' => array('grant', t('Grant Types'), $app['grant_types'], t('leave blank unless your application specifically requires this')), + '$scope' => array('scope', t('Authorization scope'), $app['scope'], t('leave blank unless your application specifically requires this')), + )); + return $o; + } + if ((argc() > 3) && (argv(2) === 'delete')) { + check_form_security_token_redirectOnErr('/settings/oauth2', 'settings_oauth2', 't'); + + $r = q( + "DELETE FROM oauth_clients WHERE client_id = '%s' AND user_id = %d", + dbesc(argv(3)), + intval(local_channel()) + ); + $r = q( + "DELETE FROM oauth_access_tokens WHERE client_id = '%s' AND user_id = %d", + dbesc(argv(3)), + intval(local_channel()) + ); + $r = q( + "DELETE FROM oauth_authorization_codes WHERE client_id = '%s' AND user_id = %d", + dbesc(argv(3)), + intval(local_channel()) + ); + $r = q( + "DELETE FROM oauth_refresh_tokens WHERE client_id = '%s' AND user_id = %d", + dbesc(argv(3)), + intval(local_channel()) + ); + goaway(z_root() . "/settings/oauth2/"); + return; + } + + + $r = q( + "SELECT * FROM oauth_clients WHERE user_id = %d ", + intval(local_channel()) + ); + + $c = q( + "select client_id, access_token from oauth_access_tokens where user_id = %d", + intval(local_channel()) + ); + if ($r && $c) { + foreach ($c as $cv) { + for ($x = 0; $x < count($r); $x++) { + if ($r[$x]['client_id'] === $cv['client_id']) { + if (!array_key_exists('tokens', $r[$x])) { + $r[$x]['tokens'] = []; + } + $r[$x]['tokens'][] = $cv['access_token']; + } + } + } + } + + $tpl = get_markup_template("settings_oauth2.tpl"); + $o .= replace_macros($tpl, array( + '$form_security_token' => get_form_security_token("settings_oauth2"), + '$baseurl' => z_root(), + '$title' => t('Connected OAuth2 Apps'), + '$add' => t('Add application'), + '$edit' => t('Edit'), + '$delete' => t('Delete'), + '$consumerkey' => t('Client key starts with'), + '$noname' => t('No name'), + '$remove' => t('Remove authorization'), + '$apps' => $r, + )); + return $o; + } } diff --git a/Zotlabs/Module/Settings/Permcats.php b/Zotlabs/Module/Settings/Permcats.php index 1f3e23b53..a4294db31 100644 --- a/Zotlabs/Module/Settings/Permcats.php +++ b/Zotlabs/Module/Settings/Permcats.php @@ -2,120 +2,130 @@ namespace Zotlabs\Module\Settings; +use App; +use Zotlabs\Access\PermissionLimits; +use Zotlabs\Access\Permissions; use Zotlabs\Lib\Libsync; +use Zotlabs\Lib\Permcat; + +class Permcats +{ + + public function post() + { + + if (!local_channel()) { + return; + } + + $channel = App::get_channel(); + + check_form_security_token_redirectOnErr('/settings/permcats', 'settings_permcats'); -class Permcats { + $all_perms = Permissions::Perms(); - function post() { - - if(! local_channel()) - return; - - $channel = \App::get_channel(); - - check_form_security_token_redirectOnErr('/settings/permcats', 'settings_permcats'); + $name = escape_tags(trim($_POST['name'])); + if (!$name) { + notice(t('Permission Name is required.') . EOL); + return; + } - $all_perms = \Zotlabs\Access\Permissions::Perms(); + $pcarr = []; - $name = escape_tags(trim($_POST['name'])); - if(! $name) { - notice( t('Permission Name is required.') . EOL); - return; - } + if ($all_perms) { + foreach ($all_perms as $perm => $desc) { + if (array_key_exists('perms_' . $perm, $_POST)) { + $pcarr[] = $perm; + } + } + } + + Permcat::update(local_channel(), $name, $pcarr); + + Libsync::build_sync_packet(); + + info(t('Permission category saved.') . EOL); + + return; + } - $pcarr = []; + public function get() + { - if($all_perms) { - foreach($all_perms as $perm => $desc) { - if(array_key_exists('perms_' . $perm, $_POST)) { - $pcarr[] = $perm; - } - } - } - - \Zotlabs\Lib\Permcat::update(local_channel(),$name,$pcarr); + if (!local_channel()) { + return; + } - Libsync::build_sync_packet(); - - info( t('Permission category saved.') . EOL); - - return; - } - - - function get() { - - if(! local_channel()) - return; - - $channel = \App::get_channel(); + $channel = App::get_channel(); - if(argc() > 2) - $name = hex2bin(argv(2)); + if (argc() > 2) { + $name = hex2bin(argv(2)); + } - if(argc() > 3 && argv(3) === 'drop') { - \Zotlabs\Lib\Permcat::delete(local_channel(),$name); - Libsync::build_sync_packet(); - json_return_and_die([ 'success' => true ]); - } + if (argc() > 3 && argv(3) === 'drop') { + Permcat::delete(local_channel(), $name); + Libsync::build_sync_packet(); + json_return_and_die(['success' => true]); + } - $desc = t('Use this form to create permission rules for various classes of people or connections.'); + $desc = t('Use this form to create permission rules for various classes of people or connections.'); - $existing = []; + $existing = []; - $pcat = new \Zotlabs\Lib\Permcat(local_channel()); - $pcatlist = $pcat->listing(); - $permcats = []; - if($pcatlist) { - foreach($pcatlist as $pc) { - if(($pc['name']) && ($name) && ($pc['name'] == $name)) - $existing = $pc['perms']; - if(! $pc['system']) - $permcats[bin2hex($pc['name'])] = $pc['localname']; - } - } + $pcat = new Permcat(local_channel()); + $pcatlist = $pcat->listing(); + $permcats = []; + if ($pcatlist) { + foreach ($pcatlist as $pc) { + if (($pc['name']) && ($name) && ($pc['name'] == $name)) { + $existing = $pc['perms']; + } + if (!$pc['system']) { + $permcats[bin2hex($pc['name'])] = $pc['localname']; + } + } + } - $global_perms = \Zotlabs\Access\Permissions::Perms(); + $global_perms = Permissions::Perms(); - foreach($global_perms as $k => $v) { - $thisperm = \Zotlabs\Lib\Permcat::find_permcat($existing,$k); - $checkinherited = \Zotlabs\Access\PermissionLimits::Get(local_channel(),$k); + foreach ($global_perms as $k => $v) { + $thisperm = Permcat::find_permcat($existing, $k); + $checkinherited = PermissionLimits::Get(local_channel(), $k); - if($existing[$k]) - $thisperm = "1"; + if ($existing[$k]) { + $thisperm = "1"; + } - $perms[] = array('perms_' . $k, $v, '',$thisperm, 1, (($checkinherited & PERMS_SPECIFIC) ? '' : '1'), '', $checkinherited); - } + $perms[] = array('perms_' . $k, $v, '', $thisperm, 1, (($checkinherited & PERMS_SPECIFIC) ? '' : '1'), '', $checkinherited); + } - - $tpl = get_markup_template("settings_permcats.tpl"); - $o .= replace_macros($tpl, array( - '$form_security_token' => get_form_security_token("settings_permcats"), - '$title' => t('Permission Categories'), - '$desc' => $desc, - '$desc2' => $desc2, - '$tokens' => $t, - '$permcats' => $permcats, - '$atoken' => $atoken, - '$url1' => z_root() . '/channel/' . $channel['channel_address'], - '$url2' => z_root() . '/photos/' . $channel['channel_address'], - '$name' => array('name', t('Permission Name') . ' *', (($name) ? $name : ''), ''), - '$me' => t('My Settings'), - '$perms' => $perms, - '$inherited' => t('inherited'), - '$notself' => 0, - '$self' => 1, - '$permlbl' => t('Individual Permissions'), - '$permnote' => t('Some permissions may be inherited from your channel\'s privacy settings, which have higher priority than individual settings. You can not change those settings here.'), - '$submit' => t('Submit') - )); - return $o; - } - + $tpl = get_markup_template("settings_permcats.tpl"); + $o .= replace_macros($tpl, array( + '$form_security_token' => get_form_security_token("settings_permcats"), + '$title' => t('Permission Categories'), + '$desc' => $desc, + '$desc2' => $desc2, + '$tokens' => $t, + '$permcats' => $permcats, + '$atoken' => $atoken, + '$url1' => z_root() . '/channel/' . $channel['channel_address'], + '$url2' => z_root() . '/photos/' . $channel['channel_address'], + '$name' => array('name', t('Permission Name') . ' *', (($name) ? $name : ''), ''), + '$me' => t('My Settings'), + '$perms' => $perms, + '$inherited' => t('inherited'), + '$notself' => 0, + '$self' => 1, + '$permlbl' => t('Individual Permissions'), + '$permnote' => t('Some permissions may be inherited from your channel\'s privacy settings, which have higher priority than individual settings. You can not change those settings here.'), + '$submit' => t('Submit') + )); + return $o; + } } diff --git a/Zotlabs/Module/Settings/Tokens.php b/Zotlabs/Module/Settings/Tokens.php index 9ce89c20a..64b74fb84 100644 --- a/Zotlabs/Module/Settings/Tokens.php +++ b/Zotlabs/Module/Settings/Tokens.php @@ -10,292 +10,309 @@ use Zotlabs\Lib\Libsync; require_once('include/security.php'); -class Tokens { +class Tokens +{ - function post() { + public function post() + { - $channel = App::get_channel(); + $channel = App::get_channel(); - check_form_security_token_redirectOnErr('/settings/tokens', 'settings_tokens'); - $token_errs = 0; - if(array_key_exists('token',$_POST)) { - $atoken_id = (($_POST['atoken_id']) ? intval($_POST['atoken_id']) : 0); - if (! $atoken_id) { - $atoken_guid = new_uuid(); - } - $name = trim(escape_tags($_POST['name'])); - $token = trim($_POST['token']); - if((! $name) || (! $token)) - $token_errs ++; - if(trim($_POST['expires'])) - $expires = datetime_convert(date_default_timezone_get(),'UTC',$_POST['expires']); - else - $expires = NULL_DATE; - $max_atokens = service_class_fetch(local_channel(),'access_tokens'); - if($max_atokens) { - $r = q("select count(atoken_id) as total where atoken_uid = %d", - intval(local_channel()) - ); - if($r && intval($r[0]['total']) >= $max_tokens) { - notice( sprintf( t('This channel is limited to %d tokens'), $max_tokens) . EOL); - return; - } - } - } - if($token_errs) { - notice( t('Name and Password are required.') . EOL); - return; - } + check_form_security_token_redirectOnErr('/settings/tokens', 'settings_tokens'); + $token_errs = 0; + if (array_key_exists('token', $_POST)) { + $atoken_id = (($_POST['atoken_id']) ? intval($_POST['atoken_id']) : 0); + if (!$atoken_id) { + $atoken_guid = new_uuid(); + } + $name = trim(escape_tags($_POST['name'])); + $token = trim($_POST['token']); + if ((!$name) || (!$token)) { + $token_errs++; + } + if (trim($_POST['expires'])) { + $expires = datetime_convert(date_default_timezone_get(), 'UTC', $_POST['expires']); + } else { + $expires = NULL_DATE; + } + $max_atokens = service_class_fetch(local_channel(), 'access_tokens'); + if ($max_atokens) { + $r = q( + "select count(atoken_id) as total where atoken_uid = %d", + intval(local_channel()) + ); + if ($r && intval($r[0]['total']) >= $max_tokens) { + notice(sprintf(t('This channel is limited to %d tokens'), $max_tokens) . EOL); + return; + } + } + } + if ($token_errs) { + notice(t('Name and Password are required.') . EOL); + return; + } - $old_atok = q("select * from atoken where atoken_uid = %d and atoken_name = '%s'", - intval($channel['channel_id']), - dbesc($name) - ); - if ($old_atok) { - $old_atok = array_shift($old_atok); - $old_xchan = atoken_xchan($old_atok); - } - - if($atoken_id) { - $r = q("update atoken set atoken_name = '%s', atoken_token = '%s', atoken_expires = '%s' + $old_atok = q( + "select * from atoken where atoken_uid = %d and atoken_name = '%s'", + intval($channel['channel_id']), + dbesc($name) + ); + if ($old_atok) { + $old_atok = array_shift($old_atok); + $old_xchan = atoken_xchan($old_atok); + } + + if ($atoken_id) { + $r = q( + "update atoken set atoken_name = '%s', atoken_token = '%s', atoken_expires = '%s' where atoken_id = %d and atoken_uid = %d", - dbesc($name), - dbesc($token), - dbesc($expires), - intval($atoken_id), - intval($channel['channel_id']) - ); - } - else { - $r = q("insert into atoken ( atoken_guid, atoken_aid, atoken_uid, atoken_name, atoken_token, atoken_expires ) + dbesc($name), + dbesc($token), + dbesc($expires), + intval($atoken_id), + intval($channel['channel_id']) + ); + } else { + $r = q( + "insert into atoken ( atoken_guid, atoken_aid, atoken_uid, atoken_name, atoken_token, atoken_expires ) values ( '%s', %d, %d, '%s', '%s', '%s' ) ", - dbesc($atoken_guid), - intval($channel['channel_account_id']), - intval($channel['channel_id']), - dbesc($name), - dbesc($token), - dbesc($expires) - ); - } - $atok = q("select * from atoken where atoken_uid = %d and atoken_name = '%s'", - intval($channel['channel_id']), - dbesc($name) - ); - if ($atok) { - $xchan = atoken_xchan($atok[0]); - atoken_create_xchan($xchan); - $atoken_xchan = $xchan['xchan_hash']; - if ($old_atok && $old_xchan) { - $r = q("update xchan set xchan_name = '%s' where xchan_hash = '%s'", - dbesc($xchan['xchan_name']), - dbesc($old_xchan['xchan_hash']) - ); - } - } + dbesc($atoken_guid), + intval($channel['channel_account_id']), + intval($channel['channel_id']), + dbesc($name), + dbesc($token), + dbesc($expires) + ); + } + $atok = q( + "select * from atoken where atoken_uid = %d and atoken_name = '%s'", + intval($channel['channel_id']), + dbesc($name) + ); + if ($atok) { + $xchan = atoken_xchan($atok[0]); + atoken_create_xchan($xchan); + $atoken_xchan = $xchan['xchan_hash']; + if ($old_atok && $old_xchan) { + $r = q( + "update xchan set xchan_name = '%s' where xchan_hash = '%s'", + dbesc($xchan['xchan_name']), + dbesc($old_xchan['xchan_hash']) + ); + } + } - $all_perms = Permissions::Perms(); + $all_perms = Permissions::Perms(); - $p = EMPTY_STR; + $p = EMPTY_STR; - if($all_perms) { - foreach($all_perms as $perm => $desc) { - if(array_key_exists('perms_' . $perm, $_POST)) { - if($p) - $p .= ','; - $p .= $perm; - } - } - set_abconfig(local_channel(),$atoken_xchan,'system','my_perms',$p); - if ($old_atok) { + if ($all_perms) { + foreach ($all_perms as $perm => $desc) { + if (array_key_exists('perms_' . $perm, $_POST)) { + if ($p) { + $p .= ','; + } + $p .= $perm; + } + } + set_abconfig(local_channel(), $atoken_xchan, 'system', 'my_perms', $p); + if ($old_atok) { + } + } + if (!$atoken_id) { + // If this is a new token, create a new abook record - } - } - - if (! $atoken_id) { - - // If this is a new token, create a new abook record + $closeness = get_pconfig($uid, 'system', 'new_abook_closeness', 80); + $profile_assign = get_pconfig($uid, 'system', 'profile_assign', ''); - $closeness = get_pconfig($uid,'system','new_abook_closeness',80); - $profile_assign = get_pconfig($uid,'system','profile_assign',''); + $r = abook_store_lowlevel( + [ + 'abook_account' => $channel['channel_account_id'], + 'abook_channel' => $channel['channel_id'], + 'abook_closeness' => intval($closeness), + 'abook_xchan' => $atoken_xchan, + 'abook_profile' => $profile_assign, + 'abook_feed' => 0, + 'abook_created' => datetime_convert(), + 'abook_updated' => datetime_convert(), + 'abook_instance' => z_root() + ] + ); - $r = abook_store_lowlevel( - [ - 'abook_account' => $channel['channel_account_id'], - 'abook_channel' => $channel['channel_id'], - 'abook_closeness' => intval($closeness), - 'abook_xchan' => $atoken_xchan, - 'abook_profile' => $profile_assign, - 'abook_feed' => 0, - 'abook_created' => datetime_convert(), - 'abook_updated' => datetime_convert(), - 'abook_instance' => z_root() - ] - ); + if (!$r) { + logger('abook creation failed'); + } - if (! $r) { - logger('abook creation failed'); - } + /** If there is a default group for this channel, add this connection to it */ - /** If there is a default group for this channel, add this connection to it */ + if ($channel['channel_default_group']) { + $g = AccessList::rec_byhash($uid, $channel['channel_default_group']); + if ($g) { + AccessList::member_add($uid, '', $atoken_xchan, $g['id']); + } + } - if ($channel['channel_default_group']) { - $g = AccessList::rec_byhash($uid,$channel['channel_default_group']); - if ($g) { - AccessList::member_add($uid,'',$atoken_xchan,$g['id']); - } - } - - $r = q("SELECT abook.*, xchan.* + $r = q( + "SELECT abook.*, xchan.* FROM abook left join xchan on abook_xchan = xchan_hash WHERE abook_channel = %d and abook_xchan = '%s' LIMIT 1", - intval($channel['channel_id']), - dbesc($atoken_xchan) - ); + intval($channel['channel_id']), + dbesc($atoken_xchan) + ); - if (! $r) { - logger('abook or xchan record not saved correctly'); - return; - } + if (!$r) { + logger('abook or xchan record not saved correctly'); + return; + } - $clone = array_shift($r); - - unset($clone['abook_id']); - unset($clone['abook_account']); - unset($clone['abook_channel']); - - $abconfig = load_abconfig($channel['channel_id'],$clone['abook_xchan']); - if ($abconfig) { - $clone['abconfig'] = $abconfig; - } - - Libsync::build_sync_packet($channel['channel_id'], - [ 'abook' => [ $clone ], 'atoken' => $atok ], - true); - } + $clone = array_shift($r); - info( t('Token saved.') . EOL); - return; - } - + unset($clone['abook_id']); + unset($clone['abook_account']); + unset($clone['abook_channel']); - function get() { + $abconfig = load_abconfig($channel['channel_id'], $clone['abook_xchan']); + if ($abconfig) { + $clone['abconfig'] = $abconfig; + } - $channel = App::get_channel(); + Libsync::build_sync_packet( + $channel['channel_id'], + ['abook' => [$clone], 'atoken' => $atok], + true + ); + } - $atoken = null; - $atoken_xchan = ''; + info(t('Token saved.') . EOL); + return; + } - if(argc() > 2) { - $id = argv(2); - $atoken = q("select * from atoken where atoken_id = %d and atoken_uid = %d", - intval($id), - intval(local_channel()) - ); + public function get() + { - if($atoken) { - $atoken = $atoken[0]; - $atoken_xchan = substr($channel['channel_hash'],0,16) . '.' . $atoken['atoken_guid']; - } + $channel = App::get_channel(); - if($atoken && argc() > 3 && argv(3) === 'drop') { - $atoken['deleted'] = true; - - $r = q("SELECT abook.*, xchan.* + $atoken = null; + $atoken_xchan = ''; + + if (argc() > 2) { + $id = argv(2); + + $atoken = q( + "select * from atoken where atoken_id = %d and atoken_uid = %d", + intval($id), + intval(local_channel()) + ); + + if ($atoken) { + $atoken = $atoken[0]; + $atoken_xchan = substr($channel['channel_hash'], 0, 16) . '.' . $atoken['atoken_guid']; + } + + if ($atoken && argc() > 3 && argv(3) === 'drop') { + $atoken['deleted'] = true; + + $r = q( + "SELECT abook.*, xchan.* FROM abook left join xchan on abook_xchan = xchan_hash WHERE abook_channel = %d and abook_xchan = '%s' LIMIT 1", - intval($channel['channel_id']), - dbesc($atoken_xchan) - ); - if (! $r) { - return; - } + intval($channel['channel_id']), + dbesc($atoken_xchan) + ); + if (!$r) { + return; + } - $clone = array_shift($r); - - unset($clone['abook_id']); - unset($clone['abook_account']); - unset($clone['abook_channel']); + $clone = array_shift($r); - $clone['entry_deleted'] = true; - - $abconfig = load_abconfig($channel['channel_id'],$clone['abook_xchan']); - if ($abconfig) { - $clone['abconfig'] = $abconfig; - } + unset($clone['abook_id']); + unset($clone['abook_account']); + unset($clone['abook_channel']); - atoken_delete($id); - Libsync::build_sync_packet($channel['channel_id'], - [ 'abook' => [ $clone ], 'atoken' => [ $atoken ] ], - true); + $clone['entry_deleted'] = true; - $atoken = null; - $atoken_xchan = ''; - } - } + $abconfig = load_abconfig($channel['channel_id'], $clone['abook_xchan']); + if ($abconfig) { + $clone['abconfig'] = $abconfig; + } - $t = q("select * from atoken where atoken_uid = %d", - intval(local_channel()) - ); + atoken_delete($id); + Libsync::build_sync_packet( + $channel['channel_id'], + ['abook' => [$clone], 'atoken' => [$atoken]], + true + ); - $desc = t('Use this form to create temporary access identifiers to share things with non-members. These identities may be used in Access Control Lists and visitors may login using these credentials to access private content.'); + $atoken = null; + $atoken_xchan = ''; + } + } - $desc2 = t('You may also provide dropbox style access links to friends and associates by adding the Login Password to any specific site URL as shown. Examples:'); + $t = q( + "select * from atoken where atoken_uid = %d", + intval(local_channel()) + ); + + $desc = t('Use this form to create temporary access identifiers to share things with non-members. These identities may be used in Access Control Lists and visitors may login using these credentials to access private content.'); + + $desc2 = t('You may also provide dropbox style access links to friends and associates by adding the Login Password to any specific site URL as shown. Examples:'); - $global_perms = Permissions::Perms(); - $existing = get_all_perms(local_channel(),(($atoken_xchan) ? $atoken_xchan : EMPTY_STR)); + $global_perms = Permissions::Perms(); + $existing = get_all_perms(local_channel(), (($atoken_xchan) ? $atoken_xchan : EMPTY_STR)); - $theirs = get_abconfig(local_channel(),$atoken_xchan,'system','their_perms',EMPTY_STR); + $theirs = get_abconfig(local_channel(), $atoken_xchan, 'system', 'their_perms', EMPTY_STR); - $their_perms = Permissions::FilledPerms(explode(',',$theirs)); - foreach($global_perms as $k => $v) { - if(! array_key_exists($k,$their_perms)) - $their_perms[$k] = 1; - } + $their_perms = Permissions::FilledPerms(explode(',', $theirs)); + foreach ($global_perms as $k => $v) { + if (!array_key_exists($k, $their_perms)) { + $their_perms[$k] = 1; + } + } - $my_perms = explode(',',get_abconfig(local_channel(),$atoken_xchan,'system','my_perms',EMPTY_STR)); + $my_perms = explode(',', get_abconfig(local_channel(), $atoken_xchan, 'system', 'my_perms', EMPTY_STR)); - foreach($global_perms as $k => $v) { - $thisperm = ((in_array($k,$my_perms)) ? 1 : 0); - - $checkinherited = PermissionLimits::Get(local_channel(),$k); - - // For auto permissions (when $self is true) we don't want to look at existing - // permissions because they are enabled for the channel owner - if((! $self) && ($existing[$k])) - $thisperm = "1"; + foreach ($global_perms as $k => $v) { + $thisperm = ((in_array($k, $my_perms)) ? 1 : 0); - $perms[] = array('perms_' . $k, $v, ((array_key_exists($k,$their_perms)) ? intval($their_perms[$k]) : ''),$thisperm, 1, (($checkinherited & PERMS_SPECIFIC) ? '' : '1'), '', $checkinherited); - } + $checkinherited = PermissionLimits::Get(local_channel(), $k); + + // For auto permissions (when $self is true) we don't want to look at existing + // permissions because they are enabled for the channel owner + if ((!$self) && ($existing[$k])) { + $thisperm = "1"; + } + + $perms[] = array('perms_' . $k, $v, ((array_key_exists($k, $their_perms)) ? intval($their_perms[$k]) : ''), $thisperm, 1, (($checkinherited & PERMS_SPECIFIC) ? '' : '1'), '', $checkinherited); + } - $tpl = get_markup_template("settings_tokens.tpl"); - $o .= replace_macros($tpl, array( - '$form_security_token' => get_form_security_token("settings_tokens"), - '$title' => t('Guest Access Tokens'), - '$desc' => $desc, - '$desc2' => $desc2, - '$tokens' => $t, - '$atoken' => $atoken, - '$atoken_xchan' => $atoken_chan, - '$url1' => z_root() . '/channel/' . $channel['channel_address'], - '$url2' => z_root() . '/photos/' . $channel['channel_address'], - '$name' => array('name', t('Login Name') . ' *', (($atoken) ? $atoken['atoken_name'] : ''),''), - '$token'=> array('token', t('Login Password') . ' *',(($atoken) ? $atoken['atoken_token'] : new_token()), ''), - '$expires'=> array('expires', t('Expires (yyyy-mm-dd)'), (($atoken['atoken_expires'] && $atoken['atoken_expires'] > NULL_DATE) ? datetime_convert('UTC',date_default_timezone_get(),$atoken['atoken_expires']) : ''), ''), - '$them' => t('Their Settings'), - '$me' => t('My Settings'), - '$perms' => $perms, - '$inherited' => t('inherited'), - '$notself' => 1, - '$self' => 0, - '$permlbl' => t('Individual Permissions'), - '$permnote' => t('Some permissions may be inherited from your channel\'s privacy settings, which have higher priority than individual settings. You can not change those settings here.'), - '$submit' => t('Submit') - )); - return $o; - } - + $tpl = get_markup_template("settings_tokens.tpl"); + $o .= replace_macros($tpl, array( + '$form_security_token' => get_form_security_token("settings_tokens"), + '$title' => t('Guest Access Tokens'), + '$desc' => $desc, + '$desc2' => $desc2, + '$tokens' => $t, + '$atoken' => $atoken, + '$atoken_xchan' => $atoken_chan, + '$url1' => z_root() . '/channel/' . $channel['channel_address'], + '$url2' => z_root() . '/photos/' . $channel['channel_address'], + '$name' => array('name', t('Login Name') . ' *', (($atoken) ? $atoken['atoken_name'] : ''), ''), + '$token' => array('token', t('Login Password') . ' *', (($atoken) ? $atoken['atoken_token'] : new_token()), ''), + '$expires' => array('expires', t('Expires (yyyy-mm-dd)'), (($atoken['atoken_expires'] && $atoken['atoken_expires'] > NULL_DATE) ? datetime_convert('UTC', date_default_timezone_get(), $atoken['atoken_expires']) : ''), ''), + '$them' => t('Their Settings'), + '$me' => t('My Settings'), + '$perms' => $perms, + '$inherited' => t('inherited'), + '$notself' => 1, + '$self' => 0, + '$permlbl' => t('Individual Permissions'), + '$permnote' => t('Some permissions may be inherited from your channel\'s privacy settings, which have higher priority than individual settings. You can not change those settings here.'), + '$submit' => t('Submit') + )); + return $o; + } } diff --git a/Zotlabs/Module/Setup.php b/Zotlabs/Module/Setup.php index b54eac66f..71bf40056 100644 --- a/Zotlabs/Module/Setup.php +++ b/Zotlabs/Module/Setup.php @@ -12,6 +12,7 @@ namespace Zotlabs\Module; use App; use DBA; +use PDO; use Zotlabs\Lib\System; use Zotlabs\Web\Controller; @@ -19,869 +20,881 @@ use Zotlabs\Web\Controller; * @brief Initialisation for the setup module. * */ +class Setup extends Controller +{ -class Setup extends Controller { + private static $install_wizard_pass = 1; - private static $install_wizard_pass = 1; + /** + * {@inheritDoc} + * @see \\Zotlabs\\Web\\Controller::init() + */ - /** - * {@inheritDoc} - * @see \\Zotlabs\\Web\\Controller::init() - */ + public function init() + { - function init() { + // Ensure that if somebody hasn't read the install documentation and doesn't have all + // the required modules or has a totally borked shared hosting provider and they can't + // figure out what the hell is going on - that we at least spit out an error message which + // we can inquire about when they write to tell us that our software doesn't work. - // Ensure that if somebody hasn't read the install documentation and doesn't have all - // the required modules or has a totally borked shared hosting provider and they can't - // figure out what the hell is going on - that we at least spit out an error message which - // we can inquire about when they write to tell us that our software doesn't work. + // The worst thing we can do at this point is throw a white screen of death and rely on + // them knowing about servers and php modules and logfiles enough so that we can guess + // at the source of the problem. As ugly as it may be, we need to throw a technically worded + // PHP error message in their face. Once installation is complete application errors will + // throw a white screen because these error messages divulge information which can + // potentially be useful to hackers. - // The worst thing we can do at this point is throw a white screen of death and rely on - // them knowing about servers and php modules and logfiles enough so that we can guess - // at the source of the problem. As ugly as it may be, we need to throw a technically worded - // PHP error message in their face. Once installation is complete application errors will - // throw a white screen because these error messages divulge information which can - // potentially be useful to hackers. + // We can only set E_WARNING once the code has been thoroughly cleansed of referencing + // undeclared or unset variables which were once legal or accepted in PHP. - // We can only set E_WARNING once the code has been thoroughly cleansed of referencing - // undeclared or unset variables which were once legal or accepted in PHP. - - error_reporting(E_ERROR | E_PARSE ); - ini_set('log_errors', '0'); - ini_set('display_errors', '1'); + error_reporting(E_ERROR | E_PARSE); + ini_set('log_errors', '0'); + ini_set('display_errors', '1'); - // $baseurl/setup/testrewrite to test if rewrite in .htaccess is working - - if (argc() == 2 && argv(1) == "testrewrite") { - echo 'ok'; - killme(); - } + // $baseurl/setup/testrewrite to test if rewrite in .htaccess is working - if (x($_POST, 'pass')) { - $this->install_wizard_pass = intval($_POST['pass']); - } - else { - $this->install_wizard_pass = 1; - } - } - - /** - * @brief Handle the actions of the different setup steps. - * - */ - function post() { - - switch ($this->install_wizard_pass) { - case 1: - case 2: - return; - // implied break; - case 3: - $urlpath = App::get_path(); - $dbhost = trim($_POST['dbhost']); - $dbport = intval(trim($_POST['dbport'])); - $dbuser = trim($_POST['dbuser']); - $dbpass = trim($_POST['dbpass']); - $dbdata = trim($_POST['dbdata']); - $dbtype = intval(trim($_POST['dbtype'])); - $servertype = intval(trim($_POST['servertype'])); - $phpath = trim($_POST['phpath']); - $adminmail = trim($_POST['adminmail']); - $siteurl = trim($_POST['siteurl']); - - // $siteurl should not have a trailing slash - - $siteurl = rtrim($siteurl,'/'); - - require_once('include/dba/dba_driver.php'); - - $db = DBA::dba_factory($dbhost, $dbport, $dbuser, $dbpass, $dbdata, $dbtype, true); - - if (! DBA::$dba->connected) { - echo 'Database Connect failed: ' . DBA::$dba->error; - killme(); - } - return; - // implied break; - case 4: - $urlpath = App::get_path(); - $dbhost = trim($_POST['dbhost']); - $dbport = intval(trim($_POST['dbport'])); - $dbuser = trim($_POST['dbuser']); - $dbpass = trim($_POST['dbpass']); - $dbdata = trim($_POST['dbdata']); - $dbtype = intval(trim($_POST['dbtype'])); - $servertype = intval(trim($_POST['servertype'])); - $phpath = trim($_POST['phpath']); - $timezone = trim($_POST['timezone']); - $adminmail = trim($_POST['adminmail']); - $siteurl = trim($_POST['siteurl']); - - if ($siteurl != z_root()) { - $test = z_fetch_url($siteurl . '/setup/testrewrite'); - if ((! $test['success']) || ($test['body'] !== 'ok')) { - App::$data['url_fail'] = true; - App::$data['url_error'] = $test['error']; - return; - } - } - - if (! DBA::$dba->connected) { - // connect to db - $db = DBA::dba_factory($dbhost, $dbport, $dbuser, $dbpass, $dbdata, $dbtype, true); - } - - if (! DBA::$dba->connected) { - echo 'CRITICAL: DB not connected.'; - killme(); - } - - $txt = replace_macros(get_intltext_template('htconfig.tpl'), [ - '$dbhost' => $dbhost, - '$dbport' => $dbport, - '$dbuser' => $dbuser, - '$dbpass' => $dbpass, - '$dbdata' => $dbdata, - '$dbtype' => $dbtype, - '$servertype' => '', - '$server_role' => 'pro', - '$timezone' => $timezone, - '$platform' => ucfirst(PLATFORM_NAME), - '$siteurl' => $siteurl, - '$site_id' => random_string(), - '$phpath' => $phpath, - '$adminmail' => $adminmail - ]); - - $result = file_put_contents('.htconfig.php', $txt); - if (! $result) { - App::$data['txt'] = $txt; - } - - $errors = $this->load_database($db); - - if ($errors) { - App::$data['db_failed'] = $errors; - } - else { - App::$data['db_installed'] = true; - } - - return; - // implied break; - default: - break; - } - } - - /** - * @brief Get output for the setup page. - * - * Depending on the state we are currently in it returns different content. - * - * @return string parsed HTML output - */ - - function get() { - - $o = EMPTY_STR; - $wizard_status = EMPTY_STR; - $db_return_text = EMPTY_STR; - $install_title = t('$Projectname Server - Setup'); - - if (x(App::$data, 'db_conn_failed')) { - $this->install_wizard_pass = 2; - $wizard_status = t('Could not connect to database.'); - } - if (x(App::$data, 'url_fail')) { - $this->install_wizard_pass = 3; - $wizard_status = t('Could not connect to specified site URL. Possible SSL certificate or DNS issue.'); - if (App::$data['url_error']) { - $wizard_status .= ' ' . App::$data['url_error']; - } - } - - if (x(App::$data, 'db_create_failed')) { - $this->install_wizard_pass = 2; - $wizard_status = t('Could not create table.'); - } - if (x(App::$data, 'db_installed')) { - $pass = t('Installation succeeded!'); - $icon = 'check'; - $txt = t('Your site database has been installed.') . EOL; - $db_return_text .= $txt; - } - if (x(App::$data, 'db_failed')) { - $pass = t('Database install failed!'); - $icon = 'exclamation-triangle'; - $txt = t('You may need to import the file "install/schema_xxx.sql" manually using a database client.') . EOL; - $txt .= t('Please see the file "install/INSTALL.txt".') . EOL . '
      ' ; - $txt .= '
      ' . App::$data['db_failed'] . '
      ' . EOL ; - $db_return_text .= $txt; - } - if (DBA::$dba && DBA::$dba->connected) { - $r = q("SELECT COUNT(*) as total FROM account"); - if ($r && count($r) && $r[0]['total']) { - return replace_macros(get_markup_template('install.tpl'), [ - '$title' => $install_title, - '$pass' => '', - '$status' => t('Permission denied.'), - '$text' => '', - ]); - } - } - - if (x(App::$data, 'txt') && strlen(App::$data['txt'])) { - $db_return_text .= $this->manual_config($a); - } - - if ($db_return_text !== EMPTY_STR) { - $tpl = get_markup_template('install.tpl'); - return replace_macros(get_markup_template('install.tpl'), [ - '$title' => $install_title, - '$icon' => $icon, - '$pass' => $pass, - '$text' => $db_return_text, - '$what_next' => $this->what_next() - ]); - } - - switch ($this->install_wizard_pass) { - - case 1: { - // System check - - $checks = []; - - $this->check_funcs($checks); - - $this->check_htconfig($checks); - - $this->check_store($checks); - - $this->check_smarty3($checks); - - $this->check_keys($checks); - - if (x($_POST, 'phpath')) { - $phpath = notags(trim($_POST['phpath'])); - } - - $this->check_php($phpath, $checks); - - $this->check_phpconfig($checks); - - $this->check_htaccess($checks); - - $checkspassed = array_reduce($checks, "self::check_passed", true); - - $o .= replace_macros(get_markup_template('install_checks.tpl'), [ - '$title' => $install_title, - '$pass' => t('System check'), - '$checks' => $checks, - '$passed' => $checkspassed, - '$see_install' => t('Please see the file "install/INSTALL.txt".'), - '$next' => t('Next'), - '$reload' => t('Check again'), - '$phpath' => $phpath, - '$baseurl' => z_root(), - ]); - return $o; - break; - } - - case 2: { - // Database config - - $dbhost = ((x($_POST,'dbhost')) ? trim($_POST['dbhost']) : '127.0.0.1'); - $dbuser = ((x($_POST,'dbuser')) ? trim($_POST['dbuser']): EMPTY_STR); - $dbport = ((x($_POST,'dbport')) ? intval(trim($_POST['dbport'])) : 0); - $dbpass = ((x($_POST,'dbpass')) ? trim($_POST['dbpass']): EMPTY_STR); - $dbdata = ((x($_POST,'dbdata')) ? trim($_POST['dbdata']): EMPTY_STR); - $dbtype = ((x($_POST,'dbtype')) ? intval(trim($_POST['dbtype'])) : 0); - $phpath = ((x($_POST,'phpath')) ? trim($_POST['phpath']): EMPTY_STR); - $adminmail = ((x($_POST,'adminmail')) ? trim($_POST['adminmail']): EMPTY_STR); - $siteurl = ((x($_POST,'siteurl')) ? trim($_POST['siteurl']): EMPTY_STR); - - $servertype = EMPTY_STR; - - $o .= replace_macros(get_markup_template('install_db.tpl'), [ - '$title' => $install_title, - '$pass' => t('Database connection'), - '$info_01' => t('In order to install this software we need to know how to connect to your database.'), - '$info_02' => t('Please contact your hosting provider or site administrator if you have questions about these settings.'), - '$info_03' => t('The database you specify below should already exist. If it does not, please create it before continuing.'), - - '$status' => $wizard_status, - - '$dbhost' => array('dbhost', t('Database Server Name'), $dbhost, t('Default is 127.0.0.1')), - '$dbport' => array('dbport', t('Database Port'), $dbport, t('Communication port number - use 0 for default')), - '$dbuser' => array('dbuser', t('Database Login Name'), $dbuser, ''), - '$dbpass' => array('dbpass', t('Database Login Password'), $dbpass, ''), - '$dbdata' => array('dbdata', t('Database Name'), $dbdata, ''), - '$dbtype' => array('dbtype', t('Database Type'), $dbtype, '', array( 0=>'MySQL', 1=>'PostgreSQL' )), - - '$adminmail' => array('adminmail', t('Site administrator email address'), $adminmail, t('Required. Your account email address must match this in order to use the web admin panel.')), - '$siteurl' => array('siteurl', t('Website URL'), z_root(), t('Required. Please use SSL (https) URL if available.')), - '$lbl_10' => t('Please select a default timezone for your website'), - '$baseurl' => z_root(), - '$phpath' => $phpath, - '$submit' => t('Submit'), - ]); - - return $o; - break; - } - case 3: { - // Site settings - $dbhost = ((x($_POST,'dbhost')) ? trim($_POST['dbhost']) : '127.0.0.1'); - $dbuser = ((x($_POST,'dbuser')) ? trim($_POST['dbuser']): EMPTY_STR); - $dbport = ((x($_POST,'dbport')) ? intval(trim($_POST['dbport'])) : 0); - $dbpass = ((x($_POST,'dbpass')) ? trim($_POST['dbpass']): EMPTY_STR); - $dbdata = ((x($_POST,'dbdata')) ? trim($_POST['dbdata']): EMPTY_STR); - $dbtype = ((x($_POST,'dbtype')) ? intval(trim($_POST['dbtype'])) : 0); - $phpath = ((x($_POST,'phpath')) ? trim($_POST['phpath']): EMPTY_STR); - - $servertype = EMPTY_STR; - - $adminmail = ((x($_POST,'adminmail')) ? trim($_POST['adminmail']): EMPTY_STR); - $siteurl = ((x($_POST,'siteurl')) ? trim($_POST['siteurl']): EMPTY_STR); - $timezone = ((x($_POST,'timezone')) ? ($_POST['timezone']) : 'America/Los_Angeles'); - - $o .= replace_macros(get_markup_template('install_settings.tpl'), [ - '$title' => $install_title, - '$pass' => t('Site settings'), - '$status' => $wizard_status, - - '$dbhost' => $dbhost, - '$dbport' => $dbport, - '$dbuser' => $dbuser, - '$dbpass' => $dbpass, - '$dbdata' => $dbdata, - '$phpath' => $phpath, - '$dbtype' => $dbtype, - '$servertype' => $servertype, - - '$adminmail' => [ 'adminmail', t('Site administrator email address'), $adminmail, t('Required. Your account email address must match this in order to use the web admin panel.') ], - - '$siteurl' => [ 'siteurl', t('Website URL'), z_root(), t('Required. Please use SSL (https) URL if available.')], - - '$timezone' => [ 'timezone', t('Please select a default timezone for your website'), $timezone, '', get_timezones() ], - - '$baseurl' => z_root(), - - '$submit' => t('Submit'), - ]); - return $o; - break; - } - } - } - - /** - * @brief Add a check result to the array for output. - * - * @param[in,out] array &$checks array passed to template - * @param string $title a title for the check - * @param boolean $status - * @param boolean $required - * @param string $help optional help string - */ - function check_add(&$checks, $title, $status, $required, $help = '') { - $checks[] = [ - 'title' => $title, - 'status' => $status, - 'required' => $required, - 'help' => $help - ]; - } - - /** - * @brief Checks the PHP environment. - * - * @param[in,out] string &$phpath - * @param[out] array &$checks - */ - function check_php(&$phpath, &$checks) { - $help = ''; - - if (version_compare(PHP_VERSION, '7.1') < 0) { - $help .= t('PHP version 7.1 or greater is required.'); - $this->check_add($checks, t('PHP version'), false, true, $help); - } - - if (strlen($phpath)) { - $passed = file_exists($phpath); - } - elseif (function_exists('shell_exec')) { - if (is_windows()) { - $phpath = trim(shell_exec('where php')); - } - else { - $phpath = trim(shell_exec('which php')); - } - $passed = strlen($phpath); - } - - if (!$passed) { - $help .= t('Could not find a command line version of PHP in the web server PATH.'). EOL; - $help .= t('If you do not have a command line version of PHP installed on server, you will not be able to run background tasks - including message delivery.') . EOL; - $help .= EOL; - - $help .= replace_macros(get_markup_template('field_input.tpl'), [ - '$field' => [ 'phpath', t('PHP executable path'), $phpath, t('Enter full path to php executable. You can leave this blank to continue the installation.')], - ]); - $phpath = ''; - } - - $this->check_add($checks, t('Command line PHP') . ($passed ? " ($phpath)" : EMPTY_STR), $passed, false, $help); - - if ($passed) { - $str = autoname(8); - $cmd = "$phpath install/testargs.php $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.'); - } - - $this->check_add($checks, t('PHP register_argc_argv'), $passed, true, $help); - } - } - - /** - * @brief Some PHP configuration checks. - * - * @todo Change how we display such informational text. Add more description - * how to change them. - * - * @param[out] array &$checks - */ - function check_phpconfig(&$checks) { - - $help = ''; - $mem_warning = EMPTY_STR; - - $result = self::getPhpiniUploadLimits(); - if($result['post_max_size'] < (2 * 1024 * 1024) || $result['max_upload_filesize'] < (2 * 1024 * 1024)) { - $mem_warning = '' . t('This is not sufficient to upload larger images or files. You should be able to upload at least 2MB (2097152 bytes) at once.') . ''; + if (argc() == 2 && argv(1) == "testrewrite") { + echo 'ok'; + killme(); } - $help = sprintf(t('Your max allowed total upload size is set to %s. Maximum size of one file to upload is set to %s. You are allowed to upload up to %d files at once.'), - userReadableSize($result['post_max_size']), - userReadableSize($result['max_upload_filesize']), - $result['max_file_uploads'] - ); + if (x($_POST, 'pass')) { + $this->install_wizard_pass = intval($_POST['pass']); + } else { + $this->install_wizard_pass = 1; + } + } - $help .= (($mem_warning) ? $mem_warning : EMPTY_STR); - $help .= '

      ' . t('You can adjust these settings in the server php.ini file.'); + /** + * @brief Handle the actions of the different setup steps. + * + */ + public function post() + { - $this->check_add($checks, t('PHP upload limits'), true, false, $help); - } + switch ($this->install_wizard_pass) { + case 1: + case 2: + return; + // implied break; + case 3: + $urlpath = App::get_path(); + $dbhost = trim($_POST['dbhost']); + $dbport = intval(trim($_POST['dbport'])); + $dbuser = trim($_POST['dbuser']); + $dbpass = trim($_POST['dbpass']); + $dbdata = trim($_POST['dbdata']); + $dbtype = intval(trim($_POST['dbtype'])); + $servertype = intval(trim($_POST['servertype'])); + $phpath = trim($_POST['phpath']); + $adminmail = trim($_POST['adminmail']); + $siteurl = trim($_POST['siteurl']); - /** - * @brief Check if the openssl implementation can generate keys. - * - * @param[out] array $checks - */ - function check_keys(&$checks) { - $help = ''; - $res = false; + // $siteurl should not have a trailing slash - if (function_exists('openssl_pkey_new')) { - $res = openssl_pkey_new(array( - 'digest_alg' => 'sha1', - 'private_key_bits' => 4096, - 'encrypt_key' => false) - ); - } + $siteurl = rtrim($siteurl, '/'); - // Get private key + require_once('include/dba/dba_driver.php'); - if (! $res) { - $help .= t('Error: the "openssl_pkey_new" function on this system is not able to generate encryption keys'). EOL; - $help .= t('If running under Windows, please see "http://www.php.net/manual/en/openssl.installation.php".'); - } + $db = DBA::dba_factory($dbhost, $dbport, $dbuser, $dbpass, $dbdata, $dbtype, true); - $this->check_add($checks, t('Generate encryption keys'), $res, true, $help); - } + if (!DBA::$dba->connected) { + echo 'Database Connect failed: ' . DBA::$dba->error; + killme(); + } + return; + // implied break; + case 4: + $urlpath = App::get_path(); + $dbhost = trim($_POST['dbhost']); + $dbport = intval(trim($_POST['dbport'])); + $dbuser = trim($_POST['dbuser']); + $dbpass = trim($_POST['dbpass']); + $dbdata = trim($_POST['dbdata']); + $dbtype = intval(trim($_POST['dbtype'])); + $servertype = intval(trim($_POST['servertype'])); + $phpath = trim($_POST['phpath']); + $timezone = trim($_POST['timezone']); + $adminmail = trim($_POST['adminmail']); + $siteurl = trim($_POST['siteurl']); - /** - * @brief Check for some PHP functions and modules. - * - * @param[in,out] array &$checks - */ - function check_funcs(&$checks) { - $ck_funcs = []; + if ($siteurl != z_root()) { + $test = z_fetch_url($siteurl . '/setup/testrewrite'); + if ((!$test['success']) || ($test['body'] !== 'ok')) { + App::$data['url_fail'] = true; + App::$data['url_error'] = $test['error']; + return; + } + } - $disabled = explode(',',ini_get('disable_functions')); - if ($disabled) { - array_walk($disabled,'array_trim'); - } + if (!DBA::$dba->connected) { + // connect to db + $db = DBA::dba_factory($dbhost, $dbport, $dbuser, $dbpass, $dbdata, $dbtype, true); + } - // add check metadata, the real check is done bit later and return values set + if (!DBA::$dba->connected) { + echo 'CRITICAL: DB not connected.'; + killme(); + } - $this->check_add($ck_funcs, t('libCurl PHP module'), true, true); - $this->check_add($ck_funcs, t('GD graphics PHP module'), true, true); - $this->check_add($ck_funcs, t('OpenSSL PHP module'), true, true); - $this->check_add($ck_funcs, t('PDO database PHP module'), true, true); - $this->check_add($ck_funcs, t('mb_string PHP module'), true, true); - $this->check_add($ck_funcs, t('xml PHP module'), true, true); - $this->check_add($ck_funcs, t('zip PHP module'), true, true); + $txt = replace_macros(get_intltext_template('htconfig.tpl'), [ + '$dbhost' => $dbhost, + '$dbport' => $dbport, + '$dbuser' => $dbuser, + '$dbpass' => $dbpass, + '$dbdata' => $dbdata, + '$dbtype' => $dbtype, + '$servertype' => '', + '$server_role' => 'pro', + '$timezone' => $timezone, + '$platform' => ucfirst(PLATFORM_NAME), + '$siteurl' => $siteurl, + '$site_id' => random_string(), + '$phpath' => $phpath, + '$adminmail' => $adminmail + ]); - if (function_exists('apache_get_modules')){ - if (! in_array('mod_rewrite', apache_get_modules())) { - $this->check_add($ck_funcs, t('Apache mod_rewrite module'), false, true, t('Error: Apache webserver mod-rewrite module is required but not installed.')); - } - else { - $this->check_add($ck_funcs, t('Apache mod_rewrite module'), true, true); - } - } - 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('shell_exec'), true, true); - } + $result = file_put_contents('.htconfig.php', $txt); + if (!$result) { + App::$data['txt'] = $txt; + } - if (! function_exists('curl_init')) { - $ck_funcs[0]['status'] = false; - $ck_funcs[0]['help'] = t('Error: libCURL PHP module required but not installed.'); - } - if ((! function_exists('imagecreatefromjpeg')) && (! class_exists('\\Imagick'))) { - $ck_funcs[1]['status'] = false; - $ck_funcs[1]['help'] = t('Error: GD PHP module with JPEG support or ImageMagick graphics library required but not installed.'); - } - if (! function_exists('openssl_public_encrypt')) { - $ck_funcs[2]['status'] = false; - $ck_funcs[2]['help'] = t('Error: openssl PHP module required but not installed.'); - } - if (class_exists('\\PDO')) { - $x = \PDO::getAvailableDrivers(); - if ((! in_array('mysql',$x)) && (! in_array('pgsql',$x))) { - $ck_funcs[3]['status'] = false; - $ck_funcs[3]['help'] = t('Error: PDO database PHP module missing a driver for either mysql or pgsql.'); - } - } - if (! class_exists('\\PDO')) { - $ck_funcs[3]['status'] = false; - $ck_funcs[3]['help'] = t('Error: PDO database PHP module required but not installed.'); - } - if (! function_exists('mb_strlen')) { - $ck_funcs[4]['status'] = false; - $ck_funcs[4]['help'] = t('Error: mb_string PHP module required but not installed.'); - } - if (! extension_loaded('xml')) { - $ck_funcs[5]['status'] = false; - $ck_funcs[5]['help'] = t('Error: xml PHP module required for DAV but not installed.'); - } - if (! extension_loaded('zip')) { - $ck_funcs[6]['status'] = false; - $ck_funcs[6]['help'] = t('Error: zip PHP module required but not installed.'); - } + $errors = $this->load_database($db); - $checks = array_merge($checks, $ck_funcs); - } + if ($errors) { + App::$data['db_failed'] = $errors; + } else { + App::$data['db_installed'] = true; + } - /** - * @brief Check for .htconfig requirements. - * - * @param[out] array &$checks - */ - function check_htconfig(&$checks) { - $status = true; - $help = EMPTY_STR; + return; + // implied break; + default: + break; + } + } - $fname = '.htconfig.php'; + /** + * @brief Get output for the setup page. + * + * Depending on the state we are currently in it returns different content. + * + * @return string parsed HTML output + */ - if((file_exists($fname) && is_writable($fname)) || - (! (file_exists($fname) && is_writable('.')))) { - $this->check_add($checks, t('.htconfig.php is writable'), $status, true, $help); - return; - } + public function get() + { - $status = false; - $help .= t('The web installer needs to be able to create a file called ".htconfig.php" in the top folder of your web server and it is unable to do so.') .EOL; - $help .= t('This is most often a permission setting, as the web server may not be able to write files in your folder - even if you can.').EOL; - $help .= t('Please see install/INSTALL.txt for additional information.'); + $o = EMPTY_STR; + $wizard_status = EMPTY_STR; + $db_return_text = EMPTY_STR; + $install_title = t('$Projectname Server - Setup'); - $this->check_add($checks, t('.htconfig.php is writable'), $status, true, $help); - } + if (x(App::$data, 'db_conn_failed')) { + $this->install_wizard_pass = 2; + $wizard_status = t('Could not connect to database.'); + } + if (x(App::$data, 'url_fail')) { + $this->install_wizard_pass = 3; + $wizard_status = t('Could not connect to specified site URL. Possible SSL certificate or DNS issue.'); + if (App::$data['url_error']) { + $wizard_status .= ' ' . App::$data['url_error']; + } + } - /** - * @brief Checks for our templating engine Smarty3 requirements. - * - * @param[out] array &$checks - */ - function check_smarty3(&$checks) { - $status = true; - $help = ''; + if (x(App::$data, 'db_create_failed')) { + $this->install_wizard_pass = 2; + $wizard_status = t('Could not create table.'); + } + if (x(App::$data, 'db_installed')) { + $pass = t('Installation succeeded!'); + $icon = 'check'; + $txt = t('Your site database has been installed.') . EOL; + $db_return_text .= $txt; + } + if (x(App::$data, 'db_failed')) { + $pass = t('Database install failed!'); + $icon = 'exclamation-triangle'; + $txt = t('You may need to import the file "install/schema_xxx.sql" manually using a database client.') . EOL; + $txt .= t('Please see the file "install/INSTALL.txt".') . EOL . '
      '; + $txt .= '
      ' . App::$data['db_failed'] . '
      ' . EOL; + $db_return_text .= $txt; + } + if (DBA::$dba && DBA::$dba->connected) { + $r = q("SELECT COUNT(*) as total FROM account"); + if ($r && count($r) && $r[0]['total']) { + return replace_macros(get_markup_template('install.tpl'), [ + '$title' => $install_title, + '$pass' => '', + '$status' => t('Permission denied.'), + '$text' => '', + ]); + } + } - @os_mkdir(TEMPLATE_BUILD_PATH, STORAGE_DEFAULT_PERMISSIONS, true); + if (x(App::$data, 'txt') && strlen(App::$data['txt'])) { + $db_return_text .= $this->manual_config($a); + } - if (! is_writable(TEMPLATE_BUILD_PATH) ) { - $status = false; - $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; - } + if ($db_return_text !== EMPTY_STR) { + $tpl = get_markup_template('install.tpl'); + return replace_macros(get_markup_template('install.tpl'), [ + '$title' => $install_title, + '$icon' => $icon, + '$pass' => $pass, + '$text' => $db_return_text, + '$what_next' => $this->what_next() + ]); + } - $this->check_add($checks, sprintf( t('%s is writable'), TEMPLATE_BUILD_PATH), $status, true, $help); - } + switch ($this->install_wizard_pass) { + case 1: + { + // System check - /** - * @brief Check for store directory. - * - * @param[out] array &$checks - */ - function check_store(&$checks) { - $status = true; - $help = ''; + $checks = []; - @os_mkdir('store', STORAGE_DEFAULT_PERMISSIONS, true); - - 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 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; - } + $this->check_funcs($checks); - $this->check_add($checks, t('store is writable'), $status, true, $help); - } + $this->check_htconfig($checks); - /** - * @brief Check URL rewrite und SSL certificate. - * - * @param[out] array &$checks - */ - function check_htaccess(&$checks) { - $status = true; - $help = ''; - $ssl_error = false; + $this->check_store($checks); - $url = z_root() . '/setup/testrewrite'; + $this->check_smarty3($checks); - if (function_exists('curl_init')) { - $test = z_fetch_url($url); - if (! $test['success']) { - if (strstr($url,'https://')) { - $test = z_fetch_url($url,false,0, [ 'novalidate' => true ]); - if ($test['success']) { - $ssl_error = true; - } - } - else { - $test = z_fetch_url(str_replace('http://','https://',$url),false,0, [ 'novalidate' => true ]); - if ($test['success']) { - $ssl_error = true; - } - } + $this->check_keys($checks); - if ($ssl_error) { - $help .= t('SSL certificate cannot be validated. Fix certificate or disable https access to this site.') . EOL; - $help .= t('If you have https access to your website or allow connections to TCP port 443 (the https: port), you MUST use a browser-valid certificate. You MUST NOT use self-signed certificates!') . EOL; - $help .= t('This restriction is incorporated because public posts from you may for example contain references to images on your own hub.') . EOL; - $help .= t('If your certificate is not recognized, members of other sites (who may themselves have valid certificates) will get a warning message on their own site complaining about security issues.') . EOL; - $help .= t('This can cause usability issues elsewhere (not just on your own site) so we must insist on this requirement.') .EOL; - $help .= t('Providers are available that issue free certificates which are browser-valid.'). EOL; + if (x($_POST, 'phpath')) { + $phpath = notags(trim($_POST['phpath'])); + } - $help .= t('If you are confident that the certificate is valid and signed by a trusted authority, check to see if you have failed to install an intermediate cert. These are not normally required by browsers, but are required for server-to-server communications.') . EOL; + $this->check_php($phpath, $checks); - $this->check_add($checks, t('SSL certificate validation'), false, true, $help); - } - } + $this->check_phpconfig($checks); - if ((! $test['success']) || ($test['body'] !== 'ok')) { - $status = false; - $help = t('Url rewrite in .htaccess is not working. Check your server configuration.' . 'Test: ' . var_export($test,true)); - } + $this->check_htaccess($checks); - $this->check_add($checks, t('Url rewrite is working'), $status, true, $help); - } - else { - // cannot check modrewrite if libcurl is not installed - } - } + $checkspassed = array_reduce($checks, "self::check_passed", true); - /** - * @brief - * - * @param App &$a - * @return string with paresed HTML - */ - function manual_config(&$a) { - $data = htmlspecialchars(App::$data['txt'], ENT_COMPAT, 'UTF-8'); - $o = t('The database configuration file ".htconfig.php" could not be written. Please use the enclosed text to create a configuration file in your web server root.'); - $o .= ""; + $o .= replace_macros(get_markup_template('install_checks.tpl'), [ + '$title' => $install_title, + '$pass' => t('System check'), + '$checks' => $checks, + '$passed' => $checkspassed, + '$see_install' => t('Please see the file "install/INSTALL.txt".'), + '$next' => t('Next'), + '$reload' => t('Check again'), + '$phpath' => $phpath, + '$baseurl' => z_root(), + ]); + return $o; + break; + } - return $o; - } + case 2: + { + // Database config - function load_database_rem($v, $i) { - $l = trim($i); - if (strlen($l) > 1 && ($l[0] === '-' || ($l[0] === '/' && $l[1] === '*'))) { - return $v; - } - else { - return $v . "\n" . $i; - } - } + $dbhost = ((x($_POST, 'dbhost')) ? trim($_POST['dbhost']) : '127.0.0.1'); + $dbuser = ((x($_POST, 'dbuser')) ? trim($_POST['dbuser']) : EMPTY_STR); + $dbport = ((x($_POST, 'dbport')) ? intval(trim($_POST['dbport'])) : 0); + $dbpass = ((x($_POST, 'dbpass')) ? trim($_POST['dbpass']) : EMPTY_STR); + $dbdata = ((x($_POST, 'dbdata')) ? trim($_POST['dbdata']) : EMPTY_STR); + $dbtype = ((x($_POST, 'dbtype')) ? intval(trim($_POST['dbtype'])) : 0); + $phpath = ((x($_POST, 'phpath')) ? trim($_POST['phpath']) : EMPTY_STR); + $adminmail = ((x($_POST, 'adminmail')) ? trim($_POST['adminmail']) : EMPTY_STR); + $siteurl = ((x($_POST, 'siteurl')) ? trim($_POST['siteurl']) : EMPTY_STR); + + $servertype = EMPTY_STR; + + $o .= replace_macros(get_markup_template('install_db.tpl'), [ + '$title' => $install_title, + '$pass' => t('Database connection'), + '$info_01' => t('In order to install this software we need to know how to connect to your database.'), + '$info_02' => t('Please contact your hosting provider or site administrator if you have questions about these settings.'), + '$info_03' => t('The database you specify below should already exist. If it does not, please create it before continuing.'), + + '$status' => $wizard_status, + + '$dbhost' => array('dbhost', t('Database Server Name'), $dbhost, t('Default is 127.0.0.1')), + '$dbport' => array('dbport', t('Database Port'), $dbport, t('Communication port number - use 0 for default')), + '$dbuser' => array('dbuser', t('Database Login Name'), $dbuser, ''), + '$dbpass' => array('dbpass', t('Database Login Password'), $dbpass, ''), + '$dbdata' => array('dbdata', t('Database Name'), $dbdata, ''), + '$dbtype' => array('dbtype', t('Database Type'), $dbtype, '', array(0 => 'MySQL', 1 => 'PostgreSQL')), + + '$adminmail' => array('adminmail', t('Site administrator email address'), $adminmail, t('Required. Your account email address must match this in order to use the web admin panel.')), + '$siteurl' => array('siteurl', t('Website URL'), z_root(), t('Required. Please use SSL (https) URL if available.')), + '$lbl_10' => t('Please select a default timezone for your website'), + '$baseurl' => z_root(), + '$phpath' => $phpath, + '$submit' => t('Submit'), + ]); + + return $o; + break; + } + case 3: + { + // Site settings + $dbhost = ((x($_POST, 'dbhost')) ? trim($_POST['dbhost']) : '127.0.0.1'); + $dbuser = ((x($_POST, 'dbuser')) ? trim($_POST['dbuser']) : EMPTY_STR); + $dbport = ((x($_POST, 'dbport')) ? intval(trim($_POST['dbport'])) : 0); + $dbpass = ((x($_POST, 'dbpass')) ? trim($_POST['dbpass']) : EMPTY_STR); + $dbdata = ((x($_POST, 'dbdata')) ? trim($_POST['dbdata']) : EMPTY_STR); + $dbtype = ((x($_POST, 'dbtype')) ? intval(trim($_POST['dbtype'])) : 0); + $phpath = ((x($_POST, 'phpath')) ? trim($_POST['phpath']) : EMPTY_STR); + + $servertype = EMPTY_STR; + + $adminmail = ((x($_POST, 'adminmail')) ? trim($_POST['adminmail']) : EMPTY_STR); + $siteurl = ((x($_POST, 'siteurl')) ? trim($_POST['siteurl']) : EMPTY_STR); + $timezone = ((x($_POST, 'timezone')) ? ($_POST['timezone']) : 'America/Los_Angeles'); + + $o .= replace_macros(get_markup_template('install_settings.tpl'), [ + '$title' => $install_title, + '$pass' => t('Site settings'), + '$status' => $wizard_status, + + '$dbhost' => $dbhost, + '$dbport' => $dbport, + '$dbuser' => $dbuser, + '$dbpass' => $dbpass, + '$dbdata' => $dbdata, + '$phpath' => $phpath, + '$dbtype' => $dbtype, + '$servertype' => $servertype, + + '$adminmail' => ['adminmail', t('Site administrator email address'), $adminmail, t('Required. Your account email address must match this in order to use the web admin panel.')], + + '$siteurl' => ['siteurl', t('Website URL'), z_root(), t('Required. Please use SSL (https) URL if available.')], + + '$timezone' => ['timezone', t('Please select a default timezone for your website'), $timezone, '', get_timezones()], + + '$baseurl' => z_root(), + + '$submit' => t('Submit'), + ]); + return $o; + break; + } + } + } + + /** + * @brief Add a check result to the array for output. + * + * @param[in,out] array &$checks array passed to template + * @param string $title a title for the check + * @param bool $status + * @param bool $required + * @param string $help optional help string + */ + public function check_add(&$checks, $title, $status, $required, $help = '') + { + $checks[] = [ + 'title' => $title, + 'status' => $status, + 'required' => $required, + 'help' => $help + ]; + } + + /** + * @brief Checks the PHP environment. + * + * @param[in,out] string &$phpath + * @param[out] array &$checks + */ + public function check_php(&$phpath, &$checks) + { + $help = ''; + + if (version_compare(PHP_VERSION, '7.1') < 0) { + $help .= t('PHP version 7.1 or greater is required.'); + $this->check_add($checks, t('PHP version'), false, true, $help); + } + + if (strlen($phpath)) { + $passed = file_exists($phpath); + } elseif (function_exists('shell_exec')) { + if (is_windows()) { + $phpath = trim(shell_exec('where php')); + } else { + $phpath = trim(shell_exec('which php')); + } + $passed = strlen($phpath); + } + + if (!$passed) { + $help .= t('Could not find a command line version of PHP in the web server PATH.') . EOL; + $help .= t('If you do not have a command line version of PHP installed on server, you will not be able to run background tasks - including message delivery.') . EOL; + $help .= EOL; + + $help .= replace_macros(get_markup_template('field_input.tpl'), [ + '$field' => ['phpath', t('PHP executable path'), $phpath, t('Enter full path to php executable. You can leave this blank to continue the installation.')], + ]); + $phpath = ''; + } + + $this->check_add($checks, t('Command line PHP') . ($passed ? " ($phpath)" : EMPTY_STR), $passed, false, $help); + + if ($passed) { + $str = autoname(8); + $cmd = "$phpath install/testargs.php $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.'); + } + + $this->check_add($checks, t('PHP register_argc_argv'), $passed, true, $help); + } + } + + /** + * @brief Some PHP configuration checks. + * + * @param[out] array &$checks + * @todo Change how we display such informational text. Add more description + * how to change them. + * + */ + public function check_phpconfig(&$checks) + { + + $help = ''; + $mem_warning = EMPTY_STR; + + $result = self::getPhpiniUploadLimits(); + if ($result['post_max_size'] < (2 * 1024 * 1024) || $result['max_upload_filesize'] < (2 * 1024 * 1024)) { + $mem_warning = '' . t('This is not sufficient to upload larger images or files. You should be able to upload at least 2MB (2097152 bytes) at once.') . ''; + } + + $help = sprintf( + t('Your max allowed total upload size is set to %s. Maximum size of one file to upload is set to %s. You are allowed to upload up to %d files at once.'), + userReadableSize($result['post_max_size']), + userReadableSize($result['max_upload_filesize']), + $result['max_file_uploads'] + ); + + $help .= (($mem_warning) ? $mem_warning : EMPTY_STR); + $help .= '

      ' . t('You can adjust these settings in the server php.ini file.'); + + $this->check_add($checks, t('PHP upload limits'), true, false, $help); + } + + /** + * @brief Check if the openssl implementation can generate keys. + * + * @param[out] array $checks + */ + public function check_keys(&$checks) + { + $help = ''; + $res = false; + + if (function_exists('openssl_pkey_new')) { + $res = openssl_pkey_new(array( + 'digest_alg' => 'sha1', + 'private_key_bits' => 4096, + 'encrypt_key' => false)); + } + + // Get private key + + if (!$res) { + $help .= t('Error: the "openssl_pkey_new" function on this system is not able to generate encryption keys') . EOL; + $help .= t('If running under Windows, please see "http://www.php.net/manual/en/openssl.installation.php".'); + } + + $this->check_add($checks, t('Generate encryption keys'), $res, true, $help); + } + + /** + * @brief Check for some PHP functions and modules. + * + * @param[in,out] array &$checks + */ + public function check_funcs(&$checks) + { + $ck_funcs = []; + + $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); + $this->check_add($ck_funcs, t('OpenSSL PHP module'), true, true); + $this->check_add($ck_funcs, t('PDO database PHP module'), true, true); + $this->check_add($ck_funcs, t('mb_string PHP module'), true, true); + $this->check_add($ck_funcs, t('xml PHP module'), true, true); + $this->check_add($ck_funcs, t('zip PHP module'), true, true); + + if (function_exists('apache_get_modules')) { + if (!in_array('mod_rewrite', apache_get_modules())) { + $this->check_add($ck_funcs, t('Apache mod_rewrite module'), false, true, t('Error: Apache webserver mod-rewrite module is required but not installed.')); + } else { + $this->check_add($ck_funcs, t('Apache mod_rewrite module'), true, true); + } + } + 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('shell_exec'), true, true); + } + + if (!function_exists('curl_init')) { + $ck_funcs[0]['status'] = false; + $ck_funcs[0]['help'] = t('Error: libCURL PHP module required but not installed.'); + } + if ((!function_exists('imagecreatefromjpeg')) && (!class_exists('\\Imagick'))) { + $ck_funcs[1]['status'] = false; + $ck_funcs[1]['help'] = t('Error: GD PHP module with JPEG support or ImageMagick graphics library required but not installed.'); + } + if (!function_exists('openssl_public_encrypt')) { + $ck_funcs[2]['status'] = false; + $ck_funcs[2]['help'] = t('Error: openssl PHP module required but not installed.'); + } + if (class_exists('\\PDO')) { + $x = PDO::getAvailableDrivers(); + if ((!in_array('mysql', $x)) && (!in_array('pgsql', $x))) { + $ck_funcs[3]['status'] = false; + $ck_funcs[3]['help'] = t('Error: PDO database PHP module missing a driver for either mysql or pgsql.'); + } + } + if (!class_exists('\\PDO')) { + $ck_funcs[3]['status'] = false; + $ck_funcs[3]['help'] = t('Error: PDO database PHP module required but not installed.'); + } + if (!function_exists('mb_strlen')) { + $ck_funcs[4]['status'] = false; + $ck_funcs[4]['help'] = t('Error: mb_string PHP module required but not installed.'); + } + if (!extension_loaded('xml')) { + $ck_funcs[5]['status'] = false; + $ck_funcs[5]['help'] = t('Error: xml PHP module required for DAV but not installed.'); + } + if (!extension_loaded('zip')) { + $ck_funcs[6]['status'] = false; + $ck_funcs[6]['help'] = t('Error: zip PHP module required but not installed.'); + } + + $checks = array_merge($checks, $ck_funcs); + } + + /** + * @brief Check for .htconfig requirements. + * + * @param[out] array &$checks + */ + public function check_htconfig(&$checks) + { + $status = true; + $help = EMPTY_STR; + + $fname = '.htconfig.php'; + + if ( + (file_exists($fname) && is_writable($fname)) || + (!(file_exists($fname) && is_writable('.'))) + ) { + $this->check_add($checks, t('.htconfig.php is writable'), $status, true, $help); + return; + } + + $status = false; + $help .= t('The web installer needs to be able to create a file called ".htconfig.php" in the top folder of your web server and it is unable to do so.') . EOL; + $help .= t('This is most often a permission setting, as the web server may not be able to write files in your folder - even if you can.') . EOL; + $help .= t('Please see install/INSTALL.txt for additional information.'); + + $this->check_add($checks, t('.htconfig.php is writable'), $status, true, $help); + } + + /** + * @brief Checks for our templating engine Smarty3 requirements. + * + * @param[out] array &$checks + */ + public function check_smarty3(&$checks) + { + $status = true; + $help = ''; + + @os_mkdir(TEMPLATE_BUILD_PATH, STORAGE_DEFAULT_PERMISSIONS, true); + + if (!is_writable(TEMPLATE_BUILD_PATH)) { + $status = false; + $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; + } + + $this->check_add($checks, sprintf(t('%s is writable'), TEMPLATE_BUILD_PATH), $status, true, $help); + } + + /** + * @brief Check for store directory. + * + * @param[out] array &$checks + */ + public function check_store(&$checks) + { + $status = true; + $help = ''; + + @os_mkdir('store', STORAGE_DEFAULT_PERMISSIONS, true); + + 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 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; + } + + $this->check_add($checks, t('store is writable'), $status, true, $help); + } + + /** + * @brief Check URL rewrite und SSL certificate. + * + * @param[out] array &$checks + */ + public function check_htaccess(&$checks) + { + $status = true; + $help = ''; + $ssl_error = false; + + $url = z_root() . '/setup/testrewrite'; + + if (function_exists('curl_init')) { + $test = z_fetch_url($url); + if (!$test['success']) { + if (strstr($url, 'https://')) { + $test = z_fetch_url($url, false, 0, ['novalidate' => true]); + if ($test['success']) { + $ssl_error = true; + } + } else { + $test = z_fetch_url(str_replace('http://', 'https://', $url), false, 0, ['novalidate' => true]); + if ($test['success']) { + $ssl_error = true; + } + } + + if ($ssl_error) { + $help .= t('SSL certificate cannot be validated. Fix certificate or disable https access to this site.') . EOL; + $help .= t('If you have https access to your website or allow connections to TCP port 443 (the https: port), you MUST use a browser-valid certificate. You MUST NOT use self-signed certificates!') . EOL; + $help .= t('This restriction is incorporated because public posts from you may for example contain references to images on your own hub.') . EOL; + $help .= t('If your certificate is not recognized, members of other sites (who may themselves have valid certificates) will get a warning message on their own site complaining about security issues.') . EOL; + $help .= t('This can cause usability issues elsewhere (not just on your own site) so we must insist on this requirement.') . EOL; + $help .= t('Providers are available that issue free certificates which are browser-valid.') . EOL; + + $help .= t('If you are confident that the certificate is valid and signed by a trusted authority, check to see if you have failed to install an intermediate cert. These are not normally required by browsers, but are required for server-to-server communications.') . EOL; + + $this->check_add($checks, t('SSL certificate validation'), false, true, $help); + } + } + + if ((!$test['success']) || ($test['body'] !== 'ok')) { + $status = false; + $help = t('Url rewrite in .htaccess is not working. Check your server configuration.' . 'Test: ' . var_export($test, true)); + } + + $this->check_add($checks, t('Url rewrite is working'), $status, true, $help); + } else { + // cannot check modrewrite if libcurl is not installed + } + } + + /** + * @brief + * + * @param App &$a + * @return string with paresed HTML + */ + public function manual_config(&$a) + { + $data = htmlspecialchars(App::$data['txt'], ENT_COMPAT, 'UTF-8'); + $o = t('The database configuration file ".htconfig.php" could not be written. Please use the enclosed text to create a configuration file in your web server root.'); + $o .= ""; + + return $o; + } + + public function load_database_rem($v, $i) + { + $l = trim($i); + if (strlen($l) > 1 && ($l[0] === '-' || ($l[0] === '/' && $l[1] === '*'))) { + return $v; + } else { + return $v . "\n" . $i; + } + } - function load_database($db) { - $str = file_get_contents(DBA::$dba->get_install_script()); - $arr = explode(';', $str); - $errors = false; - foreach ($arr as $a) { - if (strlen(trim($a))) { - $r = dbq(trim($a)); - if (! $r) { - $errors .= t('Errors encountered creating database tables.') . $a . EOL; - } - } - } + public function load_database($db) + { + $str = file_get_contents(DBA::$dba->get_install_script()); + $arr = explode(';', $str); + $errors = false; + foreach ($arr as $a) { + if (strlen(trim($a))) { + $r = dbq(trim($a)); + if (!$r) { + $errors .= t('Errors encountered creating database tables.') . $a . EOL; + } + } + } - return $errors; - } + return $errors; + } - /** - * @brief - * - * @return string with parsed HTML - */ - function what_next() { - // install the standard theme - set_config('system', 'allowed_themes', 'redbasic'); + /** + * @brief + * + * @return string with parsed HTML + */ + public function what_next() + { + // install the standard theme + set_config('system', 'allowed_themes', 'redbasic'); - // if imagick converter is installed, use it - if (@is_executable('/usr/bin/convert')) { - set_config('system','imagick_convert_path','/usr/bin/convert'); - } + // if imagick converter is installed, use it + if (@is_executable('/usr/bin/convert')) { + set_config('system', 'imagick_convert_path', '/usr/bin/convert'); + } - // Set a lenient list of ciphers if using openssl. Other ssl engines - // (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 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 - // to make the content available rather than tell folks that there's a - // weird SSL error which they can't do anything about. This does not affect - // the SSL server, but is only a client negotiation to find something workable. - // Hence it will not make your system susceptible to POODL or other nasties. + // Set a lenient list of ciphers if using openssl. Other ssl engines + // (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 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 + // to make the content available rather than tell folks that there's a + // weird SSL error which they can't do anything about. This does not affect + // the SSL server, but is only a client negotiation to find something workable. + // Hence it will not make your system susceptible to POODL or other nasties. - $x = curl_version(); - if(stristr($x['ssl_version'],'openssl')) - set_config('system','curl_ssl_ciphers','ALL:!eNULL'); + $x = curl_version(); + if (stristr($x['ssl_version'], 'openssl')) { + set_config('system', 'curl_ssl_ciphers', 'ALL:!eNULL'); + } - // Create a system channel - - create_sys_channel(); + // Create a system channel - $register_link = '' . t('registration page') . '' ; - - return - t('

      What next?

      ') - . '
      ' - . t('IMPORTANT: You will need to [manually] setup a scheduled task for the poller.') - . EOL - . t('Please see the file "install/INSTALL.txt" for more information and instructions.') - . '
      ' - . sprintf( t('Go to your new hub %s and register as new member. Please use the same email address that you entered for the administrator email as this will allow your new account to enter the site admin panel.'), $register_link ) - .'
      '; - } + create_sys_channel(); - /** - * @brief - * - * @param unknown $v - * @param array $c - * @return array - */ - static private function check_passed($v, $c) { - if ($c['required']) { - $v = $v && $c['status']; - } - return $v; - } + $register_link = '' . t('registration page') . ''; - /** - * @brief Get some upload related limits from php.ini. - * - * This function returns values from php.ini like \b post_max_size, - * \b max_file_uploads, \b upload_max_filesize. - * - * @return array associative array - * * \e int \b post_max_size the maximum size of a complete POST in bytes - * * \e int \b upload_max_filesize the maximum size of one file in bytes - * * \e int \b max_file_uploads maximum number of files in one POST - * * \e int \b max_upload_filesize min(post_max_size, upload_max_filesize) - */ + return + t('

      What next?

      ') + . '
      ' + . t('IMPORTANT: You will need to [manually] setup a scheduled task for the poller.') + . EOL + . t('Please see the file "install/INSTALL.txt" for more information and instructions.') + . '
      ' + . sprintf(t('Go to your new hub %s and register as new member. Please use the same email address that you entered for the administrator email as this will allow your new account to enter the site admin panel.'), $register_link) + . '
      '; + } - static private function getPhpiniUploadLimits() { - $ret = []; + /** + * @brief + * + * @param unknown $v + * @param array $c + * @return array + */ + private static function check_passed($v, $c) + { + if ($c['required']) { + $v = $v && $c['status']; + } + return $v; + } - // max size of the complete POST - $ret['post_max_size'] = self::phpiniSizeToBytes(ini_get('post_max_size')); - // max size of one file - $ret['upload_max_filesize'] = self::phpiniSizeToBytes(ini_get('upload_max_filesize')); - // catch a configuration error where post_max_size < upload_max_filesize - $ret['max_upload_filesize'] = min( - $ret['post_max_size'], - $ret['upload_max_filesize'] - ); - // maximum number of files in one POST - $ret['max_file_uploads'] = intval(ini_get('max_file_uploads')); + /** + * @brief Get some upload related limits from php.ini. + * + * This function returns values from php.ini like \b post_max_size, + * \b max_file_uploads, \b upload_max_filesize. + * + * @return array associative array + * * \e int \b post_max_size the maximum size of a complete POST in bytes + * * \e int \b upload_max_filesize the maximum size of one file in bytes + * * \e int \b max_file_uploads maximum number of files in one POST + * * \e int \b max_upload_filesize min(post_max_size, upload_max_filesize) + */ - return $ret; - } + private static function getPhpiniUploadLimits() + { + $ret = []; - /** - * @brief Parses php_ini size settings to bytes. - * - * This function parses common size setting from php.ini files to bytes. - * e.g. post_max_size = 8M ==> 8388608 - * - * \note This method does not recognise other human readable formats like - * 8MB, etc. - * - * @todo Make this function more universal useable. MB, T, etc. - * - * @param string $val value from php.ini e.g. 2M, 8M - * @return int size in bytes - */ - static private function phpiniSizeToBytes($val) { - $val = trim($val); - $unit = strtolower($val[strlen($val)-1]); - // strip off any non-numeric portion - $val = intval($val); - switch($unit) { - case 'g': - $val *= 1024; - case 'm': - $val *= 1024; - case 'k': - $val *= 1024; - default: - break; - } + // max size of the complete POST + $ret['post_max_size'] = self::phpiniSizeToBytes(ini_get('post_max_size')); + // max size of one file + $ret['upload_max_filesize'] = self::phpiniSizeToBytes(ini_get('upload_max_filesize')); + // catch a configuration error where post_max_size < upload_max_filesize + $ret['max_upload_filesize'] = min( + $ret['post_max_size'], + $ret['upload_max_filesize'] + ); + // maximum number of files in one POST + $ret['max_file_uploads'] = intval(ini_get('max_file_uploads')); - return (int)$val; - } + return $ret; + } + /** + * @brief Parses php_ini size settings to bytes. + * + * This function parses common size setting from php.ini files to bytes. + * e.g. post_max_size = 8M ==> 8388608 + * + * \note This method does not recognise other human readable formats like + * 8MB, etc. + * + * @param string $val value from php.ini e.g. 2M, 8M + * @return int size in bytes + * @todo Make this function more universal useable. MB, T, etc. + * + */ + private static function phpiniSizeToBytes($val) + { + $val = trim($val); + $unit = strtolower($val[strlen($val) - 1]); + // strip off any non-numeric portion + $val = intval($val); + switch ($unit) { + case 'g': + $val *= 1024; + case 'm': + $val *= 1024; + case 'k': + $val *= 1024; + default: + break; + } + + return (int)$val; + } } diff --git a/Zotlabs/Module/Share.php b/Zotlabs/Module/Share.php index 5c6cb7d3b..fcadc26d9 100644 --- a/Zotlabs/Module/Share.php +++ b/Zotlabs/Module/Share.php @@ -1,136 +1,150 @@ 1) ? intval(argv(1)) : 0); - - if(! $post_id) - killme(); - - if(! local_channel()) { - killme(); - } + public function init() + { - $observer = App::get_observer(); + $post_id = ((argc() > 1) ? intval(argv(1)) : 0); - $channel = App::get_channel(); + if (!$post_id) { + killme(); + } + + if (!local_channel()) { + killme(); + } + + $observer = App::get_observer(); + + $channel = App::get_channel(); - $r = q("SELECT * from item left join xchan on author_xchan = xchan_hash WHERE id = %d LIMIT 1", - intval($post_id) - ); - if(! $r) - killme(); + $r = q( + "SELECT * from item left join xchan on author_xchan = xchan_hash WHERE id = %d LIMIT 1", + intval($post_id) + ); + if (!$r) { + killme(); + } - if(($r[0]['item_private']) && ($r[0]['xchan_network'] !== 'rss')) - killme(); - - $sql_extra = item_permissions_sql($r[0]['uid']); - - $r = q("select * from item where id = %d $sql_extra", - intval($post_id) - ); - if(! $r) - killme(); - - /** @FIXME we only share bbcode */ - - if (! in_array($r[0]['mimetype'], [ 'text/bbcode', 'text/x-multicode' ])) { - killme(); - } - - - xchan_query($r); - - $arr = []; + if (($r[0]['item_private']) && ($r[0]['xchan_network'] !== 'rss')) { + killme(); + } - $item = $r[0]; + $sql_extra = item_permissions_sql($r[0]['uid']); - $owner_uid = $r[0]['uid']; - $owner_aid = $r[0]['aid']; + $r = q( + "select * from item where id = %d $sql_extra", + intval($post_id) + ); + if (!$r) { + killme(); + } - $can_comment = false; - if((array_key_exists('owner',$item)) && intval($item['owner']['abook_self'])) - $can_comment = perm_is_allowed($item['uid'],$observer['xchan_hash'],'post_comments'); - else - $can_comment = can_comment_on_post($observer['xchan_hash'],$item); + /** @FIXME we only share bbcode */ - if(! $can_comment) { - notice( t('Permission denied') . EOL); - killme(); - } + if (!in_array($r[0]['mimetype'], ['text/bbcode', 'text/x-multicode'])) { + killme(); + } - $r = q("select * from xchan where xchan_hash = '%s' limit 1", - dbesc($item['owner_xchan']) - ); - if($r) - $thread_owner = $r[0]; - else - killme(); - - $r = q("select * from xchan where xchan_hash = '%s' limit 1", - dbesc($item['author_xchan']) - ); - if($r) - $item_author = $r[0]; - else - killme(); - + xchan_query($r); - $arr['aid'] = $owner_aid; - $arr['uid'] = $owner_uid; + $arr = []; - $arr['item_origin'] = 1; - $arr['item_wall'] = $item['item_wall']; - $arr['uuid'] = new_uuid(); - $arr['mid'] = z_root() . '/item/' . $arr['uuid']; - $arr['mid'] = str_replace('/item/','/activity/',$arr['mid']); - $arr['parent_mid'] = $item['mid']; + $item = $r[0]; - $mention = '@[zrl=' . $item['author']['xchan_url'] . ']' . $item['author']['xchan_name'] . '[/zrl]'; - $arr['body'] = sprintf( t('🔁 Repeated %1$s\'s %2$s'), $mention, $item['obj_type']); + $owner_uid = $r[0]['uid']; + $owner_aid = $r[0]['aid']; - $arr['author_xchan'] = $channel['channel_hash']; - $arr['owner_xchan'] = $item['author_xchan']; - $arr['obj'] = $item['obj']; - $arr['obj_type'] = $item['obj_type']; - $arr['verb'] = 'Announce'; + $can_comment = false; + if ((array_key_exists('owner', $item)) && intval($item['owner']['abook_self'])) { + $can_comment = perm_is_allowed($item['uid'], $observer['xchan_hash'], 'post_comments'); + } else { + $can_comment = can_comment_on_post($observer['xchan_hash'], $item); + } - $post = item_store($arr); + if (!$can_comment) { + notice(t('Permission denied') . EOL); + killme(); + } - $post_id = $post['item_id']; + $r = q( + "select * from xchan where xchan_hash = '%s' limit 1", + dbesc($item['owner_xchan']) + ); - $arr['id'] = $post_id; - - call_hooks('post_local_end', $arr); + if ($r) { + $thread_owner = $r[0]; + } else { + killme(); + } - info( t('Post repeated') . EOL); + $r = q( + "select * from xchan where xchan_hash = '%s' limit 1", + dbesc($item['author_xchan']) + ); + if ($r) { + $item_author = $r[0]; + } else { + killme(); + } - $r = q("select * from item where id = %d", - intval($post_id) - ); - if($r) { - xchan_query($r); - $sync_item = fetch_post_tags($r); - Libsync::build_sync_packet($channel['channel_id'], [ 'item' => [ encode_item($sync_item[0],true) ] ]); - } - Run::Summon([ 'Notifier','like',$post_id ]); - - killme(); - - } - + $arr['aid'] = $owner_aid; + $arr['uid'] = $owner_uid; + + $arr['item_origin'] = 1; + $arr['item_wall'] = $item['item_wall']; + $arr['uuid'] = new_uuid(); + $arr['mid'] = z_root() . '/item/' . $arr['uuid']; + $arr['mid'] = str_replace('/item/', '/activity/', $arr['mid']); + $arr['parent_mid'] = $item['mid']; + + $mention = '@[zrl=' . $item['author']['xchan_url'] . ']' . $item['author']['xchan_name'] . '[/zrl]'; + $arr['body'] = sprintf(t('🔁 Repeated %1$s\'s %2$s'), $mention, $item['obj_type']); + + $arr['author_xchan'] = $channel['channel_hash']; + $arr['owner_xchan'] = $item['author_xchan']; + $arr['obj'] = $item['obj']; + $arr['obj_type'] = $item['obj_type']; + $arr['verb'] = 'Announce'; + + $post = item_store($arr); + + $post_id = $post['item_id']; + + $arr['id'] = $post_id; + + call_hooks('post_local_end', $arr); + + info(t('Post repeated') . EOL); + + $r = q( + "select * from item where id = %d", + intval($post_id) + ); + if ($r) { + xchan_query($r); + $sync_item = fetch_post_tags($r); + Libsync::build_sync_packet($channel['channel_id'], ['item' => [encode_item($sync_item[0], true)]]); + } + + Run::Summon(['Notifier', 'like', $post_id]); + + killme(); + } } diff --git a/Zotlabs/Module/Sharedwithme.php b/Zotlabs/Module/Sharedwithme.php index ea996ac7e..6fac0ad42 100644 --- a/Zotlabs/Module/Sharedwithme.php +++ b/Zotlabs/Module/Sharedwithme.php @@ -1,6 +1,6 @@ 2) && (argv(2) === 'drop')) { - - $id = intval(argv(1)); - - q("DELETE FROM item WHERE id = %d AND uid = %d", - intval($id), - intval(local_channel()) - ); - - goaway(z_root() . '/sharedwithme'); - } - - //drop all files - localuser - if((argc() > 1) && (argv(1) === 'dropall')) { - - q("DELETE FROM item WHERE verb = '%s' AND obj_type = '%s' AND uid = %d", - dbesc(ACTIVITY_POST), - dbesc(ACTIVITY_OBJ_FILE), - intval(local_channel()) - ); - - goaway(z_root() . '/sharedwithme'); - } - - //list files - $r = q("SELECT id, uid, obj, item_unseen FROM item WHERE verb = '%s' AND obj_type = '%s' AND uid = %d AND owner_xchan != '%s'", - dbesc(ACTIVITY_POST), - dbesc(ACTIVITY_OBJ_FILE), - intval(local_channel()), - dbesc($channel['channel_hash']) - ); - - $items =[]; - $ids = ''; - - if($r) { - - foreach($r as $rr) { - $object = json_decode($rr['obj'],true); - - $item = []; - $item['id'] = $rr['id']; - $item['objfiletype'] = $object['filetype']; - $item['objfiletypeclass'] = getIconFromType($object['filetype']); - $item['objurl'] = rawurldecode(get_rel_link($object['link'],'alternate')) . '?f=&zid=' . $channel['xchan_addr']; - $item['objfilename'] = $object['filename']; - $item['objfilesize'] = userReadableSize($object['filesize']); - $item['objedited'] = $object['edited']; - $item['unseen'] = $rr['item_unseen']; - - $items[] = $item; - - if($item['unseen'] > 0) { - $ids .= " '" . $rr['id'] . "',"; - } - - } - - } - - if($ids) { - - //remove trailing , - $ids = rtrim($ids, ","); - - q("UPDATE item SET item_unseen = 0 WHERE id IN ( $ids ) AND uid = %d", - intval(local_channel()) - ); - - } - - $o = ''; - - $o .= replace_macros(get_markup_template('sharedwithme.tpl'), array( - '$header' => t('Files: shared with me'), - '$name' => t('Name'), - '$label_new' => t('NEW'), - '$size' => t('Size'), - '$lastmod' => t('Last Modified'), - '$dropall' => t('Remove all files'), - '$drop' => t('Remove this file'), - '$items' => $items - )); - - return $o; - - } - - + $channel = App::get_channel(); + + $is_owner = (local_channel() && (local_channel() == $channel['channel_id'])); + + //check for updated items and remove them + require_once('include/sharedwithme.php'); + apply_updates(); + + //drop single file - localuser + if ((argc() > 2) && (argv(2) === 'drop')) { + $id = intval(argv(1)); + + q( + "DELETE FROM item WHERE id = %d AND uid = %d", + intval($id), + intval(local_channel()) + ); + + goaway(z_root() . '/sharedwithme'); + } + + //drop all files - localuser + if ((argc() > 1) && (argv(1) === 'dropall')) { + q( + "DELETE FROM item WHERE verb = '%s' AND obj_type = '%s' AND uid = %d", + dbesc(ACTIVITY_POST), + dbesc(ACTIVITY_OBJ_FILE), + intval(local_channel()) + ); + + goaway(z_root() . '/sharedwithme'); + } + + //list files + $r = q( + "SELECT id, uid, obj, item_unseen FROM item WHERE verb = '%s' AND obj_type = '%s' AND uid = %d AND owner_xchan != '%s'", + dbesc(ACTIVITY_POST), + dbesc(ACTIVITY_OBJ_FILE), + intval(local_channel()), + dbesc($channel['channel_hash']) + ); + + $items = []; + $ids = ''; + + if ($r) { + foreach ($r as $rr) { + $object = json_decode($rr['obj'], true); + + $item = []; + $item['id'] = $rr['id']; + $item['objfiletype'] = $object['filetype']; + $item['objfiletypeclass'] = getIconFromType($object['filetype']); + $item['objurl'] = rawurldecode(get_rel_link($object['link'], 'alternate')) . '?f=&zid=' . $channel['xchan_addr']; + $item['objfilename'] = $object['filename']; + $item['objfilesize'] = userReadableSize($object['filesize']); + $item['objedited'] = $object['edited']; + $item['unseen'] = $rr['item_unseen']; + + $items[] = $item; + + if ($item['unseen'] > 0) { + $ids .= " '" . $rr['id'] . "',"; + } + } + } + + if ($ids) { + //remove trailing , + $ids = rtrim($ids, ","); + + q( + "UPDATE item SET item_unseen = 0 WHERE id IN ( $ids ) AND uid = %d", + intval(local_channel()) + ); + } + + $o = ''; + + $o .= replace_macros(get_markup_template('sharedwithme.tpl'), array( + '$header' => t('Files: shared with me'), + '$name' => t('Name'), + '$label_new' => t('NEW'), + '$size' => t('Size'), + '$lastmod' => t('Last Modified'), + '$dropall' => t('Remove all files'), + '$drop' => t('Remove this file'), + '$items' => $items + )); + + return $o; + } } diff --git a/Zotlabs/Module/Siteinfo.php b/Zotlabs/Module/Siteinfo.php index 70d3bfa54..061f7df2f 100644 --- a/Zotlabs/Module/Siteinfo.php +++ b/Zotlabs/Module/Siteinfo.php @@ -1,62 +1,64 @@ t('About this site'), - '$url' => z_root(), - '$sitenametxt' => t('Site Name'), - '$sitename' => 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 Nomad'), - '$transport_link' => 'https://zotlabs.com', + call_hooks('federated_transports', $federated); - '$ebs' => System::ebs(), - '$additional_text' => t('Protocols:'), - '$additional_fed' => $federated, - '$prj_version' => ((get_config('system','hidden_version_siteinfo')) ? '' : sprintf( t('Version %s'), System::get_project_version())), - '$prj_linktxt' => t('Project homepage'), - '$prj_srctxt' => t('Developer homepage'), - '$prj_link' => System::get_project_link(), - '$prj_src' => System::get_project_srclink(), - '$prj_icon' => System::get_site_icon(), - ] - ); + $siteinfo = replace_macros( + get_markup_template('siteinfo.tpl'), + [ + '$title' => t('About this site'), + '$url' => z_root(), + '$sitenametxt' => t('Site Name'), + '$sitename' => 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 Nomad'), + '$transport_link' => 'https://zotlabs.com', - call_hooks('about_hook', $siteinfo); + '$ebs' => System::ebs(), + '$additional_text' => t('Protocols:'), + '$additional_fed' => $federated, + '$prj_version' => ((get_config('system', 'hidden_version_siteinfo')) ? '' : sprintf(t('Version %s'), System::get_project_version())), + '$prj_linktxt' => t('Project homepage'), + '$prj_srctxt' => t('Developer homepage'), + '$prj_link' => System::get_project_link(), + '$prj_src' => System::get_project_srclink(), + '$prj_icon' => System::get_site_icon(), + ] + ); - return $siteinfo; + call_hooks('about_hook', $siteinfo); - } - - + return $siteinfo; + } } diff --git a/Zotlabs/Module/Sites.php b/Zotlabs/Module/Sites.php index f644f2024..d687984b6 100644 --- a/Zotlabs/Module/Sites.php +++ b/Zotlabs/Module/Sites.php @@ -1,145 +1,148 @@ $rr['site_url'], - 'name' => $sitename, - 'access' => $access, - 'register' => $register_link, - 'sellpage' => $rr['site_sellpage'], - 'location_label' => t('Location'), - 'location' => $rr['site_location'], - 'project' => $rr['site_project'], - 'version' => $rr['site_version'], - 'photo' => $logo, - 'about' => bbcode($about), - 'hash' => substr(hash('sha256', $rr['site_url']), 0, 16), - 'network_label' => t('Project'), - 'network' => $rr['site_project'], - 'version_label' => t('Version'), - 'version' => $rr['site_version'], - 'private' => $disabled, - 'connect' => (($disabled) ? '' : $register_link), - 'connect_label' => $register, - 'access' => (($access === 'private') ? '' : $access), - 'access_label' => t('Access type'), - ]; - } - } + $j[] = [ + 'profile_link' => $rr['site_url'], + 'name' => $sitename, + 'access' => $access, + 'register' => $register_link, + 'sellpage' => $rr['site_sellpage'], + 'location_label' => t('Location'), + 'location' => $rr['site_location'], + 'project' => $rr['site_project'], + 'version' => $rr['site_version'], + 'photo' => $logo, + 'about' => bbcode($about), + 'hash' => substr(hash('sha256', $rr['site_url']), 0, 16), + 'network_label' => t('Project'), + 'network' => $rr['site_project'], + 'version_label' => t('Version'), + 'version' => $rr['site_version'], + 'private' => $disabled, + 'connect' => (($disabled) ? '' : $register_link), + 'connect_label' => $register, + 'access' => (($access === 'private') ? '' : $access), + 'access_label' => t('Access type'), + ]; + } + } - $o = replace_macros(get_markup_template('sitentry_header.tpl'), [ - '$dirlbl' => t('Affiliated Sites'), - '$desc' => $desc, - '$entries' => $j, - ]); + $o = replace_macros(get_markup_template('sitentry_header.tpl'), [ + '$dirlbl' => t('Affiliated Sites'), + '$desc' => $desc, + '$entries' => $j, + ]); + return $o; + } + public function sort_sites($a) + { + $ret = []; + if ($a) { + foreach ($a as $e) { + $projectname = explode(' ', $e['project']); + $ret[$projectname[0]][] = $e; + } + } + $projects = array_keys($ret); + rsort($projects); - return $o; - } + $newret = []; + foreach ($projects as $p) { + $newret[$p] = $ret[$p]; + } - function sort_sites($a) { - $ret = []; - if($a) { - foreach($a as $e) { - $projectname = explode(' ',$e['project']); - $ret[$projectname[0]][] = $e; - } - } - $projects = array_keys($ret); - rsort($projects); - - $newret = []; - foreach($projects as $p) { - $newret[$p] = $ret[$p]; - } + return $newret; + } - return $newret; - } - - function sort_versions($a,$b) { - return version_compare($b['version'],$a['version']); - } + public function sort_versions($a, $b) + { + return version_compare($b['version'], $a['version']); + } } diff --git a/Zotlabs/Module/Smilies.php b/Zotlabs/Module/Smilies.php index f9233cd40..523efe1f4 100644 --- a/Zotlabs/Module/Smilies.php +++ b/Zotlabs/Module/Smilies.php @@ -1,21 +1,24 @@ $tmp['texts'][$i], 'icon' => $tmp['icons'][$i]); - } - json_return_and_die($results); - } - else { - return smilies('',true); - } - } - + public function get() + { + if (App::$argv[1] === "json") { + $tmp = list_smilies(); + $results = []; + for ($i = 0; $i < count($tmp['texts']); $i++) { + $results[] = array('text' => $tmp['texts'][$i], 'icon' => $tmp['icons'][$i]); + } + json_return_and_die($results); + } else { + return smilies('', true); + } + } } diff --git a/Zotlabs/Module/Sources.php b/Zotlabs/Module/Sources.php index cae4b0bcd..c82cad12c 100644 --- a/Zotlabs/Module/Sources.php +++ b/Zotlabs/Module/Sources.php @@ -1,191 +1,199 @@ t('Channel Sources'), - '$desc' => t('Manage remote sources of content for your channel.'), - '$new' => t('New Source'), - '$sources' => $r - ]); - return $o; - } - - if (argc() == 2 && argv(1) === 'new') { - // TODO add the words 'or RSS feed' and corresponding code to manage feeds and frequency - - $o = replace_macros(get_markup_template('sources_new.tpl'), [ - '$title' => t('New Source'), - '$desc' => t('Import all or selected content from the following channel into this channel and distribute it according to your channel settings.'), - '$words' => [ 'words', t('Only import content with these words (one per line)'),'',t('Leave blank to import all public content')], - '$name' => [ 'name', t('Channel Name'), '', '', '', 'autocomplete="off"'], - '$tags' => [ 'tags', t('Add the following categories to posts imported from this source (comma separated)'),'',t('Optional')], - '$resend' => [ 'resend', t('Resend posts with this channel as author'), 0, t('Copyrights may apply'), [ t('No'), t('Yes') ]], - '$submit' => t('Submit') - ]); - return $o; - - } - - if (argc() == 2 && intval(argv(1))) { - // edit source - $r = q("select source.*, xchan.* from source left join xchan on src_xchan = xchan_hash where src_id = %d and src_channel_id = %d limit 1", - intval(argv(1)), - intval(local_channel()) - ); - if ($r) { - $x = q("select abook_id from abook where abook_xchan = '%s' and abook_channel = %d limit 1", - dbesc($r[0]['src_xchan']), - intval(local_channel()) - ); - } - if (! $r) { - notice( t('Source not found.') . EOL); - return ''; - } - - $r[0]['src_patt'] = htmlspecialchars($r[0]['src_patt'], ENT_QUOTES,'UTF-8'); - - $o = replace_macros(get_markup_template('sources_edit.tpl'), array( - '$title' => t('Edit Source'), - '$drop' => t('Delete Source'), - '$id' => $r[0]['src_id'], - '$desc' => t('Import all or selected content from the following channel into this channel and distribute it according to your channel settings.'), - '$words' => array( 'words', t('Only import content with these words (one per line)'),$r[0]['src_patt'],t('Leave blank to import all public content')), - '$xchan' => $r[0]['src_xchan'], - '$abook' => $x[0]['abook_id'], - '$tags' => array('tags', t('Add the following categories to posts imported from this source (comma separated)'),$r[0]['src_tag'],t('Optional')), - '$resend' => [ 'resend', t('Resend posts with this channel as author'), get_abconfig(local_channel(), $r[0]['xchan_hash'],'system','rself'), t('Copyrights may apply'), [ t('No'), t('Yes') ]], - '$name' => array( 'name', t('Channel Name'), $r[0]['xchan_name'], ''), - '$submit' => t('Submit') - )); - return $o; - - } - - if(argc() == 3 && intval(argv(1)) && argv(2) === 'drop') { - $r = q("select * from source where src_id = %d and src_channel_id = %d limit 1", - intval(argv(1)), - intval(local_channel()) - ); - if(! $r) { - notice( t('Source not found.') . EOL); - return ''; - } - $r = q("delete from source where src_id = %d and src_channel_id = %d", - intval(argv(1)), - intval(local_channel()) - ); - if($r) - info( t('Source removed') . EOL); - else - notice( t('Unable to remove source.') . EOL); - - goaway(z_root() . '/sources'); - - } - - // shouldn't get here. - - } + public function get() + { + + if (!local_channel()) { + notice(t('Permission denied.') . EOL); + return EMPTY_STR; + } + + if (!feature_enabled(local_channel(), 'channel_sources')) { + return EMPTY_STR; + } + + // list sources + if (argc() == 1) { + $r = q( + "select source.*, xchan.* from source left join xchan on src_xchan = xchan_hash where src_channel_id = %d", + intval(local_channel()) + ); + if ($r) { + for ($x = 0; $x < count($r); $x++) { + if ($r[$x]['src_xchan'] == '*') { + $r[$x]['xchan_name'] = t('*'); + } + $r[$x]['src_patt'] = htmlspecialchars($r[$x]['src_patt'], ENT_COMPAT, 'UTF-8'); + } + } + $o = replace_macros(get_markup_template('sources_list.tpl'), [ + '$title' => t('Channel Sources'), + '$desc' => t('Manage remote sources of content for your channel.'), + '$new' => t('New Source'), + '$sources' => $r + ]); + return $o; + } + + if (argc() == 2 && argv(1) === 'new') { + // TODO add the words 'or RSS feed' and corresponding code to manage feeds and frequency + + $o = replace_macros(get_markup_template('sources_new.tpl'), [ + '$title' => t('New Source'), + '$desc' => t('Import all or selected content from the following channel into this channel and distribute it according to your channel settings.'), + '$words' => ['words', t('Only import content with these words (one per line)'), '', t('Leave blank to import all public content')], + '$name' => ['name', t('Channel Name'), '', '', '', 'autocomplete="off"'], + '$tags' => ['tags', t('Add the following categories to posts imported from this source (comma separated)'), '', t('Optional')], + '$resend' => ['resend', t('Resend posts with this channel as author'), 0, t('Copyrights may apply'), [t('No'), t('Yes')]], + '$submit' => t('Submit') + ]); + return $o; + } + + if (argc() == 2 && intval(argv(1))) { + // edit source + $r = q( + "select source.*, xchan.* from source left join xchan on src_xchan = xchan_hash where src_id = %d and src_channel_id = %d limit 1", + intval(argv(1)), + intval(local_channel()) + ); + if ($r) { + $x = q( + "select abook_id from abook where abook_xchan = '%s' and abook_channel = %d limit 1", + dbesc($r[0]['src_xchan']), + intval(local_channel()) + ); + } + if (!$r) { + notice(t('Source not found.') . EOL); + return ''; + } + + $r[0]['src_patt'] = htmlspecialchars($r[0]['src_patt'], ENT_QUOTES, 'UTF-8'); + + $o = replace_macros(get_markup_template('sources_edit.tpl'), array( + '$title' => t('Edit Source'), + '$drop' => t('Delete Source'), + '$id' => $r[0]['src_id'], + '$desc' => t('Import all or selected content from the following channel into this channel and distribute it according to your channel settings.'), + '$words' => array('words', t('Only import content with these words (one per line)'), $r[0]['src_patt'], t('Leave blank to import all public content')), + '$xchan' => $r[0]['src_xchan'], + '$abook' => $x[0]['abook_id'], + '$tags' => array('tags', t('Add the following categories to posts imported from this source (comma separated)'), $r[0]['src_tag'], t('Optional')), + '$resend' => ['resend', t('Resend posts with this channel as author'), get_abconfig(local_channel(), $r[0]['xchan_hash'], 'system', 'rself'), t('Copyrights may apply'), [t('No'), t('Yes')]], + + '$name' => array('name', t('Channel Name'), $r[0]['xchan_name'], ''), + '$submit' => t('Submit') + )); + return $o; + } + + if (argc() == 3 && intval(argv(1)) && argv(2) === 'drop') { + $r = q( + "select * from source where src_id = %d and src_channel_id = %d limit 1", + intval(argv(1)), + intval(local_channel()) + ); + if (!$r) { + notice(t('Source not found.') . EOL); + return ''; + } + $r = q( + "delete from source where src_id = %d and src_channel_id = %d", + intval(argv(1)), + intval(local_channel()) + ); + if ($r) { + info(t('Source removed') . EOL); + } else { + notice(t('Unable to remove source.') . EOL); + } + + goaway(z_root() . '/sources'); + } + + // shouldn't get here. + } } diff --git a/Zotlabs/Module/Sslify.php b/Zotlabs/Module/Sslify.php index 2891f3691..c27ec888d 100644 --- a/Zotlabs/Module/Sslify.php +++ b/Zotlabs/Module/Sslify.php @@ -1,25 +1,29 @@ 1) - $message_id = intval(argv(1)); - if(! $message_id) - killme(); - - $r = q("SELECT item_starred FROM item WHERE uid = %d AND id = %d LIMIT 1", - intval(local_channel()), - intval($message_id) - ); - if(! count($r)) - killme(); - - $item_starred = (intval($r[0]['item_starred']) ? 0 : 1); - - $r = q("UPDATE item SET item_starred = %d WHERE uid = %d and id = %d", - intval($item_starred), - intval(local_channel()), - intval($message_id) - ); + if (!local_channel()) { + killme(); + } + $message_id = ((argc() > 1) ? intval(argv(1)) : 0); + if (!$message_id) { + killme(); + } - $r = q("select * from item where id = %d", - intval($message_id) - ); - if($r) { - xchan_query($r); - $sync_item = fetch_post_tags($r); - Libsync::build_sync_packet(local_channel(),[ - 'item' => [ - encode_item($sync_item[0],true) - ] - ]); - } + $r = q( + "SELECT * FROM item WHERE id = %d + and item_type in (0,6,7) and item_deleted = 0 and item_unpublished = 0 + and item_delayed = 0 and item_pending_remove = 0 and item_blocked = 0 LIMIT 1", + intval($message_id) + ); - header('Content-type: application/json'); - echo json_encode(array('result' => $item_starred)); - killme(); - } - + // if interacting with a pubstream item, + // create a copy of the parent in your stream. + + + if ($r) { + if (! is_sys_channel(local_channel())) { + $r = [ copy_of_pubitem(App::get_channel(), $r[0]['mid']) ]; + } + } + + if (! $r) { + killme(); + } + + // reset $message_id to the fetched copy of message if applicable + $message_id = $r[0]['id']; + $item_starred = (intval($r[0]['item_starred']) ? 0 : 1); + + $r = q( + "UPDATE item SET item_starred = %d WHERE uid = %d and id = %d", + intval($item_starred), + intval(local_channel()), + intval($message_id) + ); + + $r = q( + "select * from item where id = %d", + intval($message_id) + ); + if ($r) { + xchan_query($r); + $sync_item = fetch_post_tags($r); + Libsync::build_sync_packet(local_channel(), [ + 'item' => [ + encode_item($sync_item[0], true) + ] + ]); + } + + header('Content-type: application/json'); + echo json_encode(array('result' => $item_starred)); + killme(); + } } diff --git a/Zotlabs/Module/Stream.php b/Zotlabs/Module/Stream.php index 3cb0c38c4..02f3db8b5 100644 --- a/Zotlabs/Module/Stream.php +++ b/Zotlabs/Module/Stream.php @@ -1,4 +1,5 @@ loading) { - $_SESSION['loadtime_stream'] = datetime_convert(); - PConfig::Set(local_channel(),'system','loadtime_stream',$_SESSION['loadtime_stream']); - // stream is a superset of channel when it comes to notifications - $_SESSION['loadtime_channel'] = datetime_convert(); - PConfig::Set(local_channel(),'system','loadtime_channel',$_SESSION['loadtime_channel']); - } - - $arr = [ 'query' => App::$query_string ]; - call_hooks('stream_content_init', $arr); - - $channel = ((isset(App::$data['channel'])) ? App::$data['channel'] : null); - - // if called from liveUpdate() we will not have called Stream->init() on this request and $channel will not be set - - if (! $channel) { - $channel = App::get_channel(); - } + // setup identity information for page + $channel = App::get_channel(); + App::$profile_uid = local_channel(); + App::$data['channel'] = $channel; + head_set_icon($channel['xchan_photo_s']); + } - $item_normal = item_normal(); - $item_normal_update = item_normal_update(); - - $datequery = $datequery2 = ''; - - $group = 0; - - $nouveau = false; - - $datequery = ((x($_GET,'dend') && is_a_date_arg($_GET['dend'])) ? notags($_GET['dend']) : ''); - $datequery2 = ((x($_GET,'dbegin') && is_a_date_arg($_GET['dbegin'])) ? notags($_GET['dbegin']) : ''); - $static = ((x($_GET,'static')) ? intval($_GET['static']) : 0); - $gid = ((x($_GET,'gid')) ? $_REQUEST['gid'] : 0); - $category = ((x($_REQUEST,'cat')) ? $_REQUEST['cat'] : ''); - $hashtags = ((x($_REQUEST,'tag')) ? $_REQUEST['tag'] : ''); - $verb = ((x($_REQUEST,'verb')) ? $_REQUEST['verb'] : ''); - $dm = ((x($_REQUEST,'dm')) ? $_REQUEST['dm'] : 0); + public function get() + { - $c_order = get_pconfig(local_channel(), 'mod_stream', 'order', 0); - switch ($c_order) { - case 0: - $order = 'comment'; - break; - case 1: - $order = 'post'; - break; - case 2: - $nouveau = true; - break; - } + if (!local_channel()) { + $_SESSION['return_url'] = App::$query_string; + return login(false); + } - $search = (isset($_GET['search']) ? $_GET['search'] : ''); - if ($search) { - $_GET['netsearch'] = escape_tags($search); - if (strpos($search,'@') === 0) { - $r = q("select abook_id from abook left join xchan on abook_xchan = xchan_hash where xchan_name = '%s' and abook_channel = %d limit 1", - dbesc(substr($search,1)), - intval(local_channel()) - ); - if ($r) { - $_GET['cid'] = $r[0]['abook_id']; - $search = $_GET['search'] = ''; - } - } - elseif (strpos($search,'#') === 0) { - $hashtags = substr($search,1); - $search = $_GET['search'] = ''; - } - } - - if ($datequery) { - $order = 'post'; - } - - // filter by collection (e.g. group) + $o = ''; - $vg = false; - - if ($gid) { - if (strpos($gid,':') === 0) { - $g = substr($gid,1); - switch ($g) { - case '1': - $r = [[ 'hash' => 'connections:' . $channel['channel_hash'] ]]; - $vg = t('Connections'); - break; - case '2': - $r = [[ 'hash' => 'zot:' . $channel['channel_hash'] ]]; - $vg = t('Nomad'); - break; - case '3': - $r = [[ 'hash' => 'activitypub:' . $channel['channel_hash'] ]]; - $vg = t('ActivityPub'); - break; - default: - break; - } - } - else { - $r = q("SELECT * FROM pgrp WHERE id = %d AND uid = %d LIMIT 1", - intval($gid), - intval(local_channel()) - ); - if (! $r) { - if ($this->updating) { - killme(); - } - notice( t('Access list not found') . EOL ); - goaway(z_root() . '/stream'); - } - } + if ($this->loading) { + $_SESSION['loadtime_stream'] = datetime_convert(); + PConfig::Set(local_channel(), 'system', 'loadtime_stream', $_SESSION['loadtime_stream']); + // stream is a superset of channel when it comes to notifications + $_SESSION['loadtime_channel'] = datetime_convert(); + PConfig::Set(local_channel(), 'system', 'loadtime_channel', $_SESSION['loadtime_channel']); + } + + $arr = ['query' => App::$query_string]; + call_hooks('stream_content_init', $arr); + + $channel = ((isset(App::$data['channel'])) ? App::$data['channel'] : null); + + // if called from liveUpdate() we will not have called Stream->init() on this request and $channel will not be set + + if (!$channel) { + $channel = App::get_channel(); + } + $item_normal = item_normal(); + $item_normal_update = item_normal_update(); - $group = $gid; - $group_hash = $r[0]['hash']; + $datequery = $datequery2 = ''; - } - - $default_cmin = ((Apps::system_app_installed(local_channel(),'Friend Zoom')) ? get_pconfig(local_channel(),'affinity','cmin',0) : (-1)); - $default_cmax = ((Apps::system_app_installed(local_channel(),'Friend Zoom')) ? get_pconfig(local_channel(),'affinity','cmax',99) : (-1)); + $group = 0; - $cid = ((x($_GET,'cid')) ? intval($_GET['cid']) : 0); - $draft = ((x($_GET,'draft')) ? intval($_GET['draft']) : 0); - $star = ((x($_GET,'star')) ? intval($_GET['star']) : 0); - $liked = ((x($_GET,'liked')) ? intval($_GET['liked']) : 0); - $conv = ((x($_GET,'conv')) ? intval($_GET['conv']) : 0); - $spam = ((x($_GET,'spam')) ? intval($_GET['spam']) : 0); - $cmin = ((array_key_exists('cmin',$_GET)) ? intval($_GET['cmin']) : $default_cmin); - $cmax = ((array_key_exists('cmax',$_GET)) ? intval($_GET['cmax']) : $default_cmax); - $file = ((x($_GET,'file')) ? $_GET['file'] : ''); - $xchan = ((x($_GET,'xchan')) ? $_GET['xchan'] : ''); - $net = ((x($_GET,'net')) ? $_GET['net'] : ''); - $pf = ((x($_GET,'pf')) ? $_GET['pf'] : ''); - - $deftag = ''; - + $nouveau = false; - - if (x($_GET,'search') || $file || (!$pf && $cid)) { - $nouveau = true; - } + $datequery = ((x($_GET, 'dend') && is_a_date_arg($_GET['dend'])) ? notags($_GET['dend']) : ''); + $datequery2 = ((x($_GET, 'dbegin') && is_a_date_arg($_GET['dbegin'])) ? notags($_GET['dbegin']) : ''); + $static = ((x($_GET, 'static')) ? intval($_GET['static']) : 0); + $gid = ((x($_GET, 'gid')) ? $_REQUEST['gid'] : 0); + $category = ((x($_REQUEST, 'cat')) ? $_REQUEST['cat'] : ''); + $hashtags = ((x($_REQUEST, 'tag')) ? $_REQUEST['tag'] : ''); + $verb = ((x($_REQUEST, 'verb')) ? $_REQUEST['verb'] : ''); + $dm = ((x($_REQUEST, 'dm')) ? $_REQUEST['dm'] : 0); - if ($cid) { - $cid_r = q("SELECT abook.abook_xchan, xchan.xchan_addr, xchan.xchan_name, xchan.xchan_url, xchan.xchan_photo_s, xchan.xchan_type from abook left join xchan on abook_xchan = xchan_hash where abook_id = %d and abook_channel = %d and abook_blocked = 0 limit 1", - intval($cid), - intval(local_channel()) - ); + $c_order = get_pconfig(local_channel(), 'mod_stream', 'order', 0); + switch ($c_order) { + case 0: + $order = 'comment'; + break; + case 1: + $order = 'post'; + break; + case 2: + $nouveau = true; + break; + } - if (! $cid_r) { - if ($this->updating) { - killme(); - } - notice( t('No such channel') . EOL ); - goaway(z_root() . '/stream'); - } - - } - - if (! $this->updating) { - - // search terms header - - // hide the terms we use to search for videos from - // the activity_filter widget because it doesn't look very good - if ($search && $search !== 'video]') { - $o .= replace_macros(get_markup_template("section_title.tpl"), - [ '$title' => t('Search Results For:') . ' ' . htmlspecialchars($search, ENT_COMPAT,'UTF-8') ] - ); - } + $search = (isset($_GET['search']) ? $_GET['search'] : ''); + if ($search) { + $_GET['netsearch'] = escape_tags($search); + if (strpos($search, '@') === 0) { + $r = q( + "select abook_id from abook left join xchan on abook_xchan = xchan_hash where xchan_name = '%s' and abook_channel = %d limit 1", + dbesc(substr($search, 1)), + intval(local_channel()) + ); + if ($r) { + $_GET['cid'] = $r[0]['abook_id']; + $search = $_GET['search'] = ''; + } + } elseif (strpos($search, '#') === 0) { + $hashtags = substr($search, 1); + $search = $_GET['search'] = ''; + } + } - $body = EMPTY_STR; + if ($datequery) { + $order = 'post'; + } - nav_set_selected('Stream'); + // filter by collection (e.g. group) - $channel_acl = [ - 'allow_cid' => $channel['channel_allow_cid'], - 'allow_gid' => $channel['channel_allow_gid'], - 'deny_cid' => $channel['channel_deny_cid'], - 'deny_gid' => $channel['channel_deny_gid'] - ]; + $vg = false; - $x = [ - 'is_owner' => true, - 'allow_location' => ((intval(get_pconfig($channel['channel_id'],'system','use_browser_location'))) ? '1' : ''), - 'default_location' => $channel['channel_location'], - 'nickname' => $channel['channel_address'], - 'lockstate' => (($channel['channel_allow_cid'] || $channel['channel_allow_gid'] || $channel['channel_deny_cid'] || $channel['channel_deny_gid']) ? 'lock' : 'unlock'), - 'acl' => populate_acl($channel_acl, true, PermissionDescription::fromGlobalPermission('view_stream'), get_post_aclDialogDescription(), 'acl_dialog_post'), - 'permissions' => $channel_acl, - 'bang' => EMPTY_STR, - 'body' => $body, - 'visitor' => true, - 'profile_uid' => local_channel(), - 'editor_autocomplete' => true, - 'bbco_autocomplete' => 'bbcode', - 'bbcode' => true, - 'jotnets' => true, - 'reset' => t('Reset form') - ]; - - if ($deftag) { - $x['pretext'] = $deftag; - } - - $status_editor = status_editor($x); - $o .= $status_editor; - - $static = channel_manual_conv_update(local_channel()); - - } - - - // We don't have to deal with ACL's on this page. You're looking at everything - // that belongs to you, hence you can see all of it. We will filter by group if - // desired. - - - $sql_options = (($star) - ? " and item_starred = 1 " - : ''); - - $sql_nets = ''; - - $item_thread_top = ' AND item_thread_top = 1 '; - - $sql_extra = ''; - - if ($draft) { - $item_normal = item_normal_draft(); - $sql_extra = " AND item.parent IN ( SELECT DISTINCT parent FROM item WHERE item_unpublished = 1 and item_deleted = 0 ) "; - } - - if ($group) { - $contact_str = ''; - $contacts = AccessList::members(local_channel(),$group); - if ($contacts) { - $contact_str = ids_to_querystr($contacts,'xchan',true); - } - else { - $contact_str = " '0' "; - if (! $this->updating) { - info( t('Access list is empty')); - } - } - $item_thread_top = ''; - - $sql_extra = " AND item.parent IN ( SELECT DISTINCT parent FROM item WHERE true $sql_options AND (( author_xchan IN ( $contact_str ) OR owner_xchan in ( $contact_str )) or allow_gid like '" . protect_sprintf('%<' . dbesc($group_hash) . '>%') . "' ) and id = parent $item_normal ) "; - - - if (! $vg) { - $x = AccessList::rec_byhash(local_channel(), $group_hash); - } - - if ($x || $vg) { - $title = replace_macros(get_markup_template("section_title.tpl"),array( - '$title' => sprintf( t('Access list: %s'), (($vg) ? $vg : $x['gname'])) - )); - } - - $o = $title . $status_editor; - - } - elseif (isset($cid_r) && $cid_r) { - $item_thread_top = EMPTY_STR; - - if ($this->loading || $this->updating) { - if (!$pf && $nouveau) { - $sql_extra = " AND author_xchan = '" . dbesc($cid_r[0]['abook_xchan']) . "' "; - } - else { - $ttype = (($pf) ? TERM_FORUM : TERM_MENTION); - - $p1 = q("SELECT DISTINCT parent FROM item WHERE uid = " . intval(local_channel()) . " AND ( author_xchan = '" . dbesc($cid_r[0]['abook_xchan']) . "' OR owner_xchan = '" . dbesc($cid_r[0]['abook_xchan']) . "' ) $item_normal "); - $p2 = q("SELECT oid AS parent FROM term WHERE uid = " . intval(local_channel()) . " AND ttype = $ttype AND term = '" . dbesc($cid_r[0]['xchan_name']) . "'"); - - $p_str = ids_to_querystr(array_merge($p1,$p2),'parent'); - $sql_extra = " AND item.parent IN ( $p_str ) "; - } - } - - $title = replace_macros(get_markup_template('section_title.tpl'), [ - '$title' => '' . urlencode($cid_r[0]['xchan_name']) . ' ' . $cid_r[0]['xchan_name'] . '' - ]); - - $o = $title; - $o .= $status_editor; - } - elseif ($xchan) { - $r = q("select * from xchan where xchan_hash = '%s'", - dbesc($xchan) - ); - if ($r) { - $item_thread_top = ''; - $sql_extra = " AND item.parent IN ( SELECT DISTINCT parent FROM item WHERE true $sql_options AND uid = " . intval(local_channel()) . " AND ( author_xchan = '" . dbesc($xchan) . "' or owner_xchan = '" . dbesc($xchan) . "' ) $item_normal ) "; - $title = replace_macros(get_markup_template('section_title.tpl'), [ - '$title' => '' . urlencode($r[0]['xchan_name']) . ' ' . $r[0]['xchan_name'] . '' - ]); - - $o = $title; - $o .= $status_editor; - - } - else { - notice( t('Invalid channel.') . EOL); - goaway(z_root() . '/stream'); - } - - } - - if (x($category)) { - $sql_extra .= protect_sprintf(term_query('item', $category, TERM_CATEGORY)); - } - if (x($hashtags)) { - $sql_extra .= protect_sprintf(term_query('item', $hashtags, TERM_HASHTAG, TERM_COMMUNITYTAG)); - } - - if (! $this->updating) { - // The special div is needed for liveUpdate to kick in for this page. - // We only launch liveUpdate if you aren't filtering in some incompatible - // way and also you aren't writing a comment (discovered in javascript). - - $maxheight = get_pconfig(local_channel(),'system','stream_divmore_height'); - if(! $maxheight) - $maxheight = 400; - - - $o .= '
      ' . "\r\n"; - $o .= "\r\n"; - - App::$page['htmlhead'] .= replace_macros(get_markup_template('build_query.tpl'), [ - '$baseurl' => z_root(), - '$pgtype' => 'stream', - '$uid' => ((local_channel()) ? local_channel() : '0'), - '$gid' => (($gid) ? $gid : '0'), - '$cid' => (($cid) ? $cid : '0'), - '$cmin' => (($cmin) ? $cmin : '(-1)'), - '$cmax' => (($cmax) ? $cmax : '(-1)'), - '$star' => (($star) ? $star : '0'), - '$liked' => (($liked) ? $liked : '0'), - '$conv' => (($conv) ? $conv : '0'), - '$spam' => (($spam) ? $spam : '0'), - '$fh' => '0', - '$dm' => (($dm) ? $dm : '0'), - '$nouveau' => (($nouveau) ? $nouveau : '0'), - '$wall' => '0', - '$draft' => (($draft) ? $draft : '0'), - '$static' => $static, - '$list' => ((x($_REQUEST,'list')) ? intval($_REQUEST['list']) : 0), - '$page' => ((App::$pager['page'] != 1) ? App::$pager['page'] : 1), - '$search' => (($search) ? urlencode($search) : ''), - '$xchan' => (($xchan) ? urlencode($xchan) : ''), - '$order' => (($order) ? urlencode($order) : ''), - '$file' => (($file) ? urlencode($file) : ''), - '$cats' => (($category) ? urlencode($category) : ''), - '$tags' => (($hashtags) ? urlencode($hashtags) : ''), - '$dend' => $datequery, - '$mid' => '', - '$verb' => (($verb) ? urlencode($verb) : ''), - '$net' => (($net) ? urlencode($net) : ''), - '$dbegin' => $datequery2, - '$pf' => (($pf) ? intval($pf) : '0'), - ]); - } - - $sql_extra3 = ''; - - if ($datequery) { - $sql_extra3 .= protect_sprintf(sprintf(" AND item.created <= '%s' ", dbesc(datetime_convert(date_default_timezone_get(),'',$datequery)))); - } - if ($datequery2) { - $sql_extra3 .= protect_sprintf(sprintf(" AND item.created >= '%s' ", dbesc(datetime_convert(date_default_timezone_get(),'',$datequery2)))); - } - - $sql_extra2 = (($nouveau) ? '' : " AND item.parent = item.id "); - $sql_extra3 = (($nouveau) ? '' : $sql_extra3); - - if (x($_GET,'search')) { - $search = escape_tags($_GET['search']); - if (strpos($search,'#') === 0) { - $sql_extra .= term_query('item',substr($search,1),TERM_HASHTAG,TERM_COMMUNITYTAG); - } - else { - $sql_extra .= sprintf(" AND (item.body like '%s' OR item.title like '%s') ", - dbesc(protect_sprintf('%' . $search . '%')), - dbesc(protect_sprintf('%' . $search . '%')) - ); - } - } - - if ($verb) { - - // the presence of a leading dot in the verb determines - // whether to match the type of activity or the child object. - // The name 'verb' is a holdover from the earlier XML - // ActivityStreams specification. - - if (substr($verb,0,1) === '.') { - $verb = substr($verb,1); - $sql_extra .= sprintf(" AND item.obj_type like '%s' ", - dbesc(protect_sprintf('%' . $verb . '%')) - ); - } - else { - $sql_extra .= sprintf(" AND item.verb like '%s' ", - dbesc(protect_sprintf('%' . $verb . '%')) - ); - } - } - - if (strlen($file)) { - $sql_extra .= term_query('item',$file,TERM_FILE); - } - - if ($dm) { - $sql_extra .= " and item_private = 2 "; - } - - if ($conv) { - - $item_thread_top = ''; - - if ($nouveau) { - $sql_extra .= " AND author_xchan = '" . dbesc($channel['channel_hash']) . "' "; - } - else { - $sql_extra .= sprintf(" AND parent IN (SELECT distinct(parent) from item where ( author_xchan = '%s' or item_mentionsme = 1 ) and item_deleted = 0 ) ", - dbesc(protect_sprintf($channel['channel_hash'])) - ); - } - } - - if ($this->updating && ! $this->loading) { - - // only setup pagination on initial page view - $pager_sql = ''; - - } - else { - $itemspage = get_pconfig(local_channel(),'system','itemspage'); - App::set_pager_itemspage(((intval($itemspage)) ? $itemspage : 20)); - $pager_sql = sprintf(" LIMIT %d OFFSET %d ", intval(App::$pager['itemspage']), intval(App::$pager['start'])); - } - - // cmin and cmax are both -1 when the affinity tool is disabled - - if (($cmin != (-1)) || ($cmax != (-1))) { - - // Not everybody who shows up in the network stream will be in your address book. - // By default those that aren't are assumed to have closeness = 99; but this isn't - // recorded anywhere. So if cmax is 99, we'll open the search up to anybody in - // the stream with a NULL address book entry. - - $sql_nets .= " AND "; - - if ($cmax == 99) - $sql_nets .= " ( "; - - $sql_nets .= "( abook.abook_closeness >= " . intval($cmin) . " "; - $sql_nets .= " AND abook.abook_closeness <= " . intval($cmax) . " ) "; - - if ($cmax == 99) - $sql_nets .= " OR abook.abook_closeness IS NULL ) "; - - } - - $net_query = (($net) ? " left join xchan on xchan_hash = author_xchan " : ''); - $net_query2 = (($net) ? " and xchan_network = '" . protect_sprintf(dbesc($net)) . "' " : ''); - - $abook_uids = " and abook.abook_channel = " . local_channel() . " "; - $uids = " and item.uid = " . local_channel() . " "; - - if (get_pconfig(local_channel(),'system','stream_list_mode')) - $page_mode = 'list'; - else - $page_mode = 'client'; - - $simple_update = (($this->updating) ? " and item_changed > = '" . $_SESSION['loadtime_stream'] . "' " : ''); - - $parents_str = ''; - $update_unseen = ''; - $items = []; - - // This fixes a very subtle bug so I'd better explain it. You wake up in the morning or return after a day - // or three and look at your stream page - after opening up your browser. The first page loads just as it - // should. All of a sudden a few seconds later, page 2 will get inserted at the beginning of the page - // (before the page 1 content). The update code is actually doing just what it's supposed - // to, it's fetching posts that have the ITEM_UNSEEN bit set. But the reason that page 2 content is being - // returned in an UPDATE is because you hadn't gotten that far yet - you're still on page 1 and everything - // that we loaded for page 1 is now marked as seen. But the stuff on page 2 hasn't been. So... it's being - // treated as "new fresh" content because it is unseen. We need to distinguish it somehow from content - // which "arrived as you were reading page 1". We're going to do this - // by storing in your session the current UTC time whenever you LOAD a network page, and only UPDATE items - // which are both ITEM_UNSEEN and have "changed" since that time. Cross fingers... - - if ($this->updating && $_SESSION['loadtime_stream']) - $simple_update = " AND item.changed > '" . datetime_convert('UTC','UTC',$_SESSION['loadtime_stream']) . "' "; - if ($this->loading) - $simple_update = ''; - - if ($static && $simple_update) - $simple_update .= " and item_thread_top = 0 and author_xchan = '" . protect_sprintf(get_observer_hash()) . "' "; + if ($gid) { + if (strpos($gid, ':') === 0) { + $g = substr($gid, 1); + switch ($g) { + case '1': + $r = [['hash' => 'connections:' . $channel['channel_hash']]]; + $vg = t('Connections'); + break; + case '2': + $r = [['hash' => 'zot:' . $channel['channel_hash']]]; + $vg = t('Nomad'); + break; + case '3': + $r = [['hash' => 'activitypub:' . $channel['channel_hash']]]; + $vg = t('ActivityPub'); + break; + default: + break; + } + } else { + $r = q( + "SELECT * FROM pgrp WHERE id = %d AND uid = %d LIMIT 1", + intval($gid), + intval(local_channel()) + ); + if (!$r) { + if ($this->updating) { + killme(); + } + notice(t('Access list not found') . EOL); + goaway(z_root() . '/stream'); + } + } - // we are not yet using this in updates because the content may have just been marked seen - // and this might prevent us from loading the update. Will need to test further. - - $seenstr = EMPTY_STR; - if (local_channel()) { - $seen = PConfig::Get(local_channel(),'system','seen_items',[]); - if ($seen) { - $seenstr = " and not item.id in (" . implode(',',$seen) . ") "; - } - } + $group = $gid; + $group_hash = $r[0]['hash']; + } - if ($nouveau && $this->loading) { - // "New Item View" - show all items unthreaded in reverse created date order - - $items = q("SELECT item.*, item.id AS item_id, created FROM item + $default_cmin = ((Apps::system_app_installed(local_channel(), 'Friend Zoom')) ? get_pconfig(local_channel(), 'affinity', 'cmin', 0) : (-1)); + $default_cmax = ((Apps::system_app_installed(local_channel(), 'Friend Zoom')) ? get_pconfig(local_channel(), 'affinity', 'cmax', 99) : (-1)); + + $cid = ((x($_GET, 'cid')) ? intval($_GET['cid']) : 0); + $draft = ((x($_GET, 'draft')) ? intval($_GET['draft']) : 0); + $star = ((x($_GET, 'star')) ? intval($_GET['star']) : 0); + $liked = ((x($_GET, 'liked')) ? intval($_GET['liked']) : 0); + $conv = ((x($_GET, 'conv')) ? intval($_GET['conv']) : 0); + $spam = ((x($_GET, 'spam')) ? intval($_GET['spam']) : 0); + $cmin = ((array_key_exists('cmin', $_GET)) ? intval($_GET['cmin']) : $default_cmin); + $cmax = ((array_key_exists('cmax', $_GET)) ? intval($_GET['cmax']) : $default_cmax); + $file = ((x($_GET, 'file')) ? $_GET['file'] : ''); + $xchan = ((x($_GET, 'xchan')) ? $_GET['xchan'] : ''); + $net = ((x($_GET, 'net')) ? $_GET['net'] : ''); + $pf = ((x($_GET, 'pf')) ? $_GET['pf'] : ''); + + $deftag = ''; + + + if (x($_GET, 'search') || $file || (!$pf && $cid)) { + $nouveau = true; + } + + if ($cid) { + $cid_r = q( + "SELECT abook.abook_xchan, xchan.xchan_addr, xchan.xchan_name, xchan.xchan_url, xchan.xchan_photo_s, xchan.xchan_type from abook left join xchan on abook_xchan = xchan_hash where abook_id = %d and abook_channel = %d and abook_blocked = 0 limit 1", + intval($cid), + intval(local_channel()) + ); + + if (!$cid_r) { + if ($this->updating) { + killme(); + } + notice(t('No such channel') . EOL); + goaway(z_root() . '/stream'); + } + } + + if (!$this->updating) { + // search terms header + + // hide the terms we use to search for videos from + // the activity_filter widget because it doesn't look very good + if ($search && $search !== 'video]') { + $o .= replace_macros( + get_markup_template("section_title.tpl"), + ['$title' => t('Search Results For:') . ' ' . htmlspecialchars($search, ENT_COMPAT, 'UTF-8')] + ); + } + + $body = EMPTY_STR; + + nav_set_selected('Stream'); + + $channel_acl = [ + 'allow_cid' => $channel['channel_allow_cid'], + 'allow_gid' => $channel['channel_allow_gid'], + 'deny_cid' => $channel['channel_deny_cid'], + 'deny_gid' => $channel['channel_deny_gid'] + ]; + + $x = [ + 'is_owner' => true, + 'allow_location' => ((intval(get_pconfig($channel['channel_id'], 'system', 'use_browser_location'))) ? '1' : ''), + 'default_location' => $channel['channel_location'], + 'nickname' => $channel['channel_address'], + 'lockstate' => (($channel['channel_allow_cid'] || $channel['channel_allow_gid'] || $channel['channel_deny_cid'] || $channel['channel_deny_gid']) ? 'lock' : 'unlock'), + 'acl' => populate_acl($channel_acl, true, PermissionDescription::fromGlobalPermission('view_stream'), get_post_aclDialogDescription(), 'acl_dialog_post'), + 'permissions' => $channel_acl, + 'bang' => EMPTY_STR, + 'body' => $body, + 'visitor' => true, + 'profile_uid' => local_channel(), + 'editor_autocomplete' => true, + 'bbco_autocomplete' => 'bbcode', + 'bbcode' => true, + 'jotnets' => true, + 'reset' => t('Reset form') + ]; + + if ($deftag) { + $x['pretext'] = $deftag; + } + + $status_editor = status_editor($x); + $o .= $status_editor; + + $static = channel_manual_conv_update(local_channel()); + } + + + // We don't have to deal with ACL's on this page. You're looking at everything + // that belongs to you, hence you can see all of it. We will filter by group if + // desired. + + + $sql_options = (($star) + ? " and item_starred = 1 " + : ''); + + $sql_nets = ''; + + $item_thread_top = ' AND item_thread_top = 1 '; + + $sql_extra = ''; + + if ($draft) { + $item_normal = item_normal_draft(); + $sql_extra = " AND item.parent IN ( SELECT DISTINCT parent FROM item WHERE item_unpublished = 1 and item_deleted = 0 ) "; + } + + if ($group) { + $contact_str = ''; + $contacts = AccessList::members(local_channel(), $group); + if ($contacts) { + $contact_str = ids_to_querystr($contacts, 'xchan', true); + } else { + $contact_str = " '0' "; + if (!$this->updating) { + info(t('Access list is empty')); + } + } + $item_thread_top = ''; + + $sql_extra = " AND item.parent IN ( SELECT DISTINCT parent FROM item WHERE true $sql_options AND (( author_xchan IN ( $contact_str ) OR owner_xchan in ( $contact_str )) or allow_gid like '" . protect_sprintf('%<' . dbesc($group_hash) . '>%') . "' ) and id = parent $item_normal ) "; + + + if (!$vg) { + $x = AccessList::rec_byhash(local_channel(), $group_hash); + } + + if ($x || $vg) { + $title = replace_macros(get_markup_template("section_title.tpl"), array( + '$title' => sprintf(t('Access list: %s'), (($vg) ? $vg : $x['gname'])) + )); + } + + $o = $title . $status_editor; + } elseif (isset($cid_r) && $cid_r) { + $item_thread_top = EMPTY_STR; + + if ($this->loading || $this->updating) { + if (!$pf && $nouveau) { + $sql_extra = " AND author_xchan = '" . dbesc($cid_r[0]['abook_xchan']) . "' "; + } else { + $ttype = (($pf) ? TERM_FORUM : TERM_MENTION); + + $p1 = q("SELECT DISTINCT parent FROM item WHERE uid = " . intval(local_channel()) . " AND ( author_xchan = '" . dbesc($cid_r[0]['abook_xchan']) . "' OR owner_xchan = '" . dbesc($cid_r[0]['abook_xchan']) . "' ) $item_normal "); + $p2 = q("SELECT oid AS parent FROM term WHERE uid = " . intval(local_channel()) . " AND ttype = $ttype AND term = '" . dbesc($cid_r[0]['xchan_name']) . "'"); + + $p_str = ids_to_querystr(array_merge($p1, $p2), 'parent'); + $sql_extra = " AND item.parent IN ( $p_str ) "; + } + } + + $title = replace_macros(get_markup_template('section_title.tpl'), [ + '$title' => '' . urlencode($cid_r[0]['xchan_name']) . ' ' . $cid_r[0]['xchan_name'] . '' + ]); + + $o = $title; + $o .= $status_editor; + } elseif ($xchan) { + $r = q( + "select * from xchan where xchan_hash = '%s'", + dbesc($xchan) + ); + if ($r) { + $item_thread_top = ''; + $sql_extra = " AND item.parent IN ( SELECT DISTINCT parent FROM item WHERE true $sql_options AND uid = " . intval(local_channel()) . " AND ( author_xchan = '" . dbesc($xchan) . "' or owner_xchan = '" . dbesc($xchan) . "' ) $item_normal ) "; + $title = replace_macros(get_markup_template('section_title.tpl'), [ + '$title' => '' . urlencode($r[0]['xchan_name']) . ' ' . $r[0]['xchan_name'] . '' + ]); + + $o = $title; + $o .= $status_editor; + } else { + notice(t('Invalid channel.') . EOL); + goaway(z_root() . '/stream'); + } + } + + if (x($category)) { + $sql_extra .= protect_sprintf(term_query('item', $category, TERM_CATEGORY)); + } + if (x($hashtags)) { + $sql_extra .= protect_sprintf(term_query('item', $hashtags, TERM_HASHTAG, TERM_COMMUNITYTAG)); + } + + if (!$this->updating) { + // The special div is needed for liveUpdate to kick in for this page. + // We only launch liveUpdate if you aren't filtering in some incompatible + // way and also you aren't writing a comment (discovered in javascript). + + $maxheight = get_pconfig(local_channel(), 'system', 'stream_divmore_height'); + if (!$maxheight) { + $maxheight = 400; + } + + + $o .= '
      ' . "\r\n"; + $o .= "\r\n"; + + App::$page['htmlhead'] .= replace_macros(get_markup_template('build_query.tpl'), [ + '$baseurl' => z_root(), + '$pgtype' => 'stream', + '$uid' => ((local_channel()) ? local_channel() : '0'), + '$gid' => (($gid) ? $gid : '0'), + '$cid' => (($cid) ? $cid : '0'), + '$cmin' => (($cmin) ? $cmin : '(-1)'), + '$cmax' => (($cmax) ? $cmax : '(-1)'), + '$star' => (($star) ? $star : '0'), + '$liked' => (($liked) ? $liked : '0'), + '$conv' => (($conv) ? $conv : '0'), + '$spam' => (($spam) ? $spam : '0'), + '$fh' => '0', + '$dm' => (($dm) ? $dm : '0'), + '$nouveau' => (($nouveau) ? $nouveau : '0'), + '$wall' => '0', + '$draft' => (($draft) ? $draft : '0'), + '$static' => $static, + '$list' => ((x($_REQUEST, 'list')) ? intval($_REQUEST['list']) : 0), + '$page' => ((App::$pager['page'] != 1) ? App::$pager['page'] : 1), + '$search' => (($search) ? urlencode($search) : ''), + '$xchan' => (($xchan) ? urlencode($xchan) : ''), + '$order' => (($order) ? urlencode($order) : ''), + '$file' => (($file) ? urlencode($file) : ''), + '$cats' => (($category) ? urlencode($category) : ''), + '$tags' => (($hashtags) ? urlencode($hashtags) : ''), + '$dend' => $datequery, + '$mid' => '', + '$verb' => (($verb) ? urlencode($verb) : ''), + '$net' => (($net) ? urlencode($net) : ''), + '$dbegin' => $datequery2, + '$pf' => (($pf) ? intval($pf) : '0'), + ]); + } + + $sql_extra3 = ''; + + if ($datequery) { + $sql_extra3 .= protect_sprintf(sprintf(" AND item.created <= '%s' ", dbesc(datetime_convert(date_default_timezone_get(), '', $datequery)))); + } + if ($datequery2) { + $sql_extra3 .= protect_sprintf(sprintf(" AND item.created >= '%s' ", dbesc(datetime_convert(date_default_timezone_get(), '', $datequery2)))); + } + + $sql_extra2 = (($nouveau) ? '' : " AND item.parent = item.id "); + $sql_extra3 = (($nouveau) ? '' : $sql_extra3); + + if (x($_GET, 'search')) { + $search = escape_tags($_GET['search']); + if (strpos($search, '#') === 0) { + $sql_extra .= term_query('item', substr($search, 1), TERM_HASHTAG, TERM_COMMUNITYTAG); + } else { + $sql_extra .= sprintf( + " AND (item.body like '%s' OR item.title like '%s') ", + dbesc(protect_sprintf('%' . $search . '%')), + dbesc(protect_sprintf('%' . $search . '%')) + ); + } + } + + if ($verb) { + // the presence of a leading dot in the verb determines + // whether to match the type of activity or the child object. + // The name 'verb' is a holdover from the earlier XML + // ActivityStreams specification. + + if (substr($verb, 0, 1) === '.') { + $verb = substr($verb, 1); + $sql_extra .= sprintf( + " AND item.obj_type like '%s' ", + dbesc(protect_sprintf('%' . $verb . '%')) + ); + } else { + $sql_extra .= sprintf( + " AND item.verb like '%s' ", + dbesc(protect_sprintf('%' . $verb . '%')) + ); + } + } + + if (strlen($file)) { + $sql_extra .= term_query('item', $file, TERM_FILE); + } + + if ($dm) { + $sql_extra .= " and item_private = 2 "; + } + + if ($conv) { + $item_thread_top = ''; + + if ($nouveau) { + $sql_extra .= " AND author_xchan = '" . dbesc($channel['channel_hash']) . "' "; + } else { + $sql_extra .= sprintf( + " AND parent IN (SELECT distinct(parent) from item where ( author_xchan = '%s' or item_mentionsme = 1 ) and item_deleted = 0 ) ", + dbesc(protect_sprintf($channel['channel_hash'])) + ); + } + } + + if ($this->updating && !$this->loading) { + // only setup pagination on initial page view + $pager_sql = ''; + } else { + $itemspage = get_pconfig(local_channel(), 'system', 'itemspage'); + App::set_pager_itemspage(((intval($itemspage)) ? $itemspage : 20)); + $pager_sql = sprintf(" LIMIT %d OFFSET %d ", intval(App::$pager['itemspage']), intval(App::$pager['start'])); + } + + // cmin and cmax are both -1 when the affinity tool is disabled + + if (($cmin != (-1)) || ($cmax != (-1))) { + // Not everybody who shows up in the network stream will be in your address book. + // By default those that aren't are assumed to have closeness = 99; but this isn't + // recorded anywhere. So if cmax is 99, we'll open the search up to anybody in + // the stream with a NULL address book entry. + + $sql_nets .= " AND "; + + if ($cmax == 99) { + $sql_nets .= " ( "; + } + + $sql_nets .= "( abook.abook_closeness >= " . intval($cmin) . " "; + $sql_nets .= " AND abook.abook_closeness <= " . intval($cmax) . " ) "; + + if ($cmax == 99) { + $sql_nets .= " OR abook.abook_closeness IS NULL ) "; + } + } + + $net_query = (($net) ? " left join xchan on xchan_hash = author_xchan " : ''); + $net_query2 = (($net) ? " and xchan_network = '" . protect_sprintf(dbesc($net)) . "' " : ''); + + $abook_uids = " and abook.abook_channel = " . local_channel() . " "; + $uids = " and item.uid = " . local_channel() . " "; + + if (get_pconfig(local_channel(), 'system', 'stream_list_mode')) { + $page_mode = 'list'; + } else { + $page_mode = 'client'; + } + + $simple_update = (($this->updating) ? " and item_changed > = '" . $_SESSION['loadtime_stream'] . "' " : ''); + + $parents_str = ''; + $update_unseen = ''; + $items = []; + + // This fixes a very subtle bug so I'd better explain it. You wake up in the morning or return after a day + // or three and look at your stream page - after opening up your browser. The first page loads just as it + // should. All of a sudden a few seconds later, page 2 will get inserted at the beginning of the page + // (before the page 1 content). The update code is actually doing just what it's supposed + // to, it's fetching posts that have the ITEM_UNSEEN bit set. But the reason that page 2 content is being + // returned in an UPDATE is because you hadn't gotten that far yet - you're still on page 1 and everything + // that we loaded for page 1 is now marked as seen. But the stuff on page 2 hasn't been. So... it's being + // treated as "new fresh" content because it is unseen. We need to distinguish it somehow from content + // which "arrived as you were reading page 1". We're going to do this + // by storing in your session the current UTC time whenever you LOAD a network page, and only UPDATE items + // which are both ITEM_UNSEEN and have "changed" since that time. Cross fingers... + + if ($this->updating && $_SESSION['loadtime_stream']) { + $simple_update = " AND item.changed > '" . datetime_convert('UTC', 'UTC', $_SESSION['loadtime_stream']) . "' "; + } + if ($this->loading) { + $simple_update = ''; + } + + if ($static && $simple_update) { + $simple_update .= " and item_thread_top = 0 and author_xchan = '" . protect_sprintf(get_observer_hash()) . "' "; + } + + + // we are not yet using this in updates because the content may have just been marked seen + // and this might prevent us from loading the update. Will need to test further. + + $seenstr = EMPTY_STR; + if (local_channel()) { + $seen = PConfig::Get(local_channel(), 'system', 'seen_items', []); + if ($seen) { + $seenstr = " and not item.id in (" . implode(',', $seen) . ") "; + } + } + + if ($nouveau && $this->loading) { + // "New Item View" - show all items unthreaded in reverse created date order + + $items = q("SELECT item.*, item.id AS item_id, created FROM item left join abook on ( item.owner_xchan = abook.abook_xchan $abook_uids ) $net_query WHERE true $uids $item_normal @@ -573,25 +566,23 @@ class Stream extends Controller { $simple_update $sql_extra $sql_options $sql_nets $net_query2 - ORDER BY item.created DESC $pager_sql " - ); - - xchan_query($items); - - $items = fetch_post_tags($items,true); - } - elseif ($this->updating) { - - // Normal conversation view + ORDER BY item.created DESC $pager_sql "); - if($order === 'post') - $ordering = "created"; - else - $ordering = "commented"; + xchan_query($items); - if ($this->loading) { - // Fetch a page full of parent items for this page - $r = q("SELECT item.parent AS item_id FROM item + $items = fetch_post_tags($items, true); + } elseif ($this->updating) { + // Normal conversation view + + if ($order === 'post') { + $ordering = "created"; + } else { + $ordering = "commented"; + } + + if ($this->loading) { + // Fetch a page full of parent items for this page + $r = q("SELECT item.parent AS item_id FROM item left join abook on ( item.owner_xchan = abook.abook_xchan $abook_uids ) $net_query WHERE true $uids $item_thread_top $item_normal @@ -599,83 +590,77 @@ class Stream extends Controller { and (abook.abook_blocked = 0 or abook.abook_flags is null) $sql_extra3 $sql_extra $sql_options $sql_nets $net_query2 - ORDER BY $ordering DESC $pager_sql " - ); - } - else { + ORDER BY $ordering DESC $pager_sql "); + } else { + // this is an update - // this is an update - - $r = q("SELECT item.parent AS item_id FROM item + $r = q("SELECT item.parent AS item_id FROM item left join abook on ( item.owner_xchan = abook.abook_xchan $abook_uids ) $net_query WHERE true $uids $item_normal_update $simple_update and (abook.abook_blocked = 0 or abook.abook_flags is null) - $sql_extra3 $sql_extra $sql_options $sql_nets $net_query2" - ); - } + $sql_extra3 $sql_extra $sql_options $sql_nets $net_query2"); + } - if ($r) { - - $parents_str = ids_to_querystr($r,'item_id'); - - $items = q("SELECT item.*, item.id AS item_id FROM item + if ($r) { + $parents_str = ids_to_querystr($r, 'item_id'); + + $items = q( + "SELECT item.*, item.id AS item_id FROM item WHERE true $uids $item_normal AND item.parent IN ( %s ) $sql_extra ", - dbesc($parents_str) - ); - - xchan_query($items,true); - $items = fetch_post_tags($items,true); - $items = conv_sort($items,$ordering); + dbesc($parents_str) + ); - } + xchan_query($items, true); + $items = fetch_post_tags($items, true); + $items = conv_sort($items, $ordering); + } - if ($page_mode === 'list') { - - /** - * in "list mode", only mark the parent item and any like activities as "seen". - * We won't distinguish between comment likes and post likes. The important thing - * is that the number of unseen comments will be accurate. The SQL to separate the - * comment likes could also get somewhat hairy. - */ - - if ($parents_str) { - $update_unseen = " AND ( id IN ( " . dbesc($parents_str) . " )"; - $update_unseen .= " OR ( parent IN ( " . dbesc($parents_str) . " ) AND verb in ( '" . dbesc(ACTIVITY_LIKE) . "','" . dbesc(ACTIVITY_DISLIKE) . "' ))) "; - } - } - else { - if ($parents_str) { - $update_unseen = " AND parent IN ( " . dbesc($parents_str) . " )"; - } - } - } - - if ($update_unseen && (! (isset($_SESSION['sudo']) && $_SESSION['sudo']))) { - $x = [ 'channel_id' => local_channel(), 'update' => 'unset' ]; - call_hooks('update_unseen',$x); - if ($x['update'] === 'unset' || intval($x['update'])) { - $r = q("UPDATE item SET item_unseen = 0 WHERE item_unseen = 1 AND uid = %d $update_unseen ", - intval(local_channel()) - ); - } - } - - $mode = (($nouveau) ? 'stream-new' : 'stream'); + if ($page_mode === 'list') { - if ($search) { - $mode = 'search'; - } - - $o .= conversation($items,$mode,$this->updating,$page_mode); - - if (($items) && (! $this->updating)) { - $o .= alt_pager(count($items)); - } - - return $o; - } - + /** + * in "list mode", only mark the parent item and any like activities as "seen". + * We won't distinguish between comment likes and post likes. The important thing + * is that the number of unseen comments will be accurate. The SQL to separate the + * comment likes could also get somewhat hairy. + */ + + if ($parents_str) { + $update_unseen = " AND ( id IN ( " . dbesc($parents_str) . " )"; + $update_unseen .= " OR ( parent IN ( " . dbesc($parents_str) . " ) AND verb in ( '" . dbesc(ACTIVITY_LIKE) . "','" . dbesc(ACTIVITY_DISLIKE) . "' ))) "; + } + } else { + if ($parents_str) { + $update_unseen = " AND parent IN ( " . dbesc($parents_str) . " )"; + } + } + } + + if ($update_unseen && (!(isset($_SESSION['sudo']) && $_SESSION['sudo']))) { + $x = ['channel_id' => local_channel(), 'update' => 'unset']; + call_hooks('update_unseen', $x); + if ($x['update'] === 'unset' || intval($x['update'])) { + $r = q( + "UPDATE item SET item_unseen = 0 WHERE item_unseen = 1 AND uid = %d $update_unseen ", + intval(local_channel()) + ); + } + } + + $mode = (($nouveau) ? 'stream-new' : 'stream'); + + if ($search) { + $mode = 'search'; + } + + $o .= conversation($items, $mode, $this->updating, $page_mode); + + if (($items) && (!$this->updating)) { + $o .= alt_pager(count($items)); + } + + return $o; + } } diff --git a/Zotlabs/Module/Subthread.php b/Zotlabs/Module/Subthread.php index 2761d8103..2007ad6c9 100644 --- a/Zotlabs/Module/Subthread.php +++ b/Zotlabs/Module/Subthread.php @@ -1,4 +1,5 @@ 2) ? notags(trim(argv(2))) : 0); - - if (argv(1) === 'sub') { - $activity = ACTIVITY_FOLLOW; - } - elseif (argv(1) === 'unsub') { - $activity = ACTIVITY_IGNORE; - } - - $i = q("select * from item where id = %d and uid = %d", - intval($item_id), - intval(local_channel()) - ); + $sys = get_sys_channel(); + $channel = App::get_channel(); - if (! $i) { - // try the global public stream - $i = q("select * from item where id = %d and uid = %d", - intval($postid), - intval($sys['channel_id']) - ); - // try the local public stream - if (! $i) { - $i = q("select * from item where id = %d and item_wall = 1 and item_private = 0", - intval($postid) - ); - } + $item_id = ((argc() > 2) ? notags(trim(argv(2))) : 0); - if ($i && local_channel() && (! is_sys_channel(local_channel()))) { - $i = [ copy_of_pubitem($channel, $i[0]['mid']) ]; - $item_id = (($i) ? $i[0]['id'] : 0); - } - } - - if (! $i) { - return; - } + if (argv(1) === 'sub') { + $activity = ACTIVITY_FOLLOW; + } elseif (argv(1) === 'unsub') { + $activity = ACTIVITY_IGNORE; + } - $r = q("select * from item where id = parent and id = %d limit 1", - dbesc($i[0]['parent']) - ); - - if ((! $item_id) || (! $r)) { - logger('subthread: no item ' . $item_id); - return; - } - - $item = $r[0]; - - $owner_uid = $item['uid']; - $observer = App::get_observer(); - $ob_hash = (($observer) ? $observer['xchan_hash'] : ''); - - if (! perm_is_allowed($owner_uid,$ob_hash,'post_comments')) { - return; - } - - $sys = get_sys_channel(); - - $owner_uid = $item['uid']; - $owner_aid = $item['aid']; - - // if this is a "discover" item, (item['uid'] is the sys channel), - // fallback to the item comment policy, which should've been - // respected when generating the conversation thread. - // Even if the activity is rejected by the item owner, it should still get attached - // to the local discover conversation on this site. - - if (($owner_uid != $sys['channel_id']) && (! perm_is_allowed($owner_uid,$observer['xchan_hash'],'post_comments'))) { - notice( t('Permission denied') . EOL); - killme(); - } - - $r = q("select * from xchan where xchan_hash = '%s' limit 1", - dbesc($item['owner_xchan']) - ); - if ($r) { - $thread_owner = $r[0]; - } - else { - killme(); - } - - $r = q("select * from xchan where xchan_hash = '%s' limit 1", - dbesc($item['author_xchan']) - ); - if ($r) { - $item_author = $r[0]; - } - else { - killme(); - } - - $uuid = new_uuid(); - $mid = z_root() . '/item/' . $uuid; - - $post_type = (($item['resource_type'] === 'photo') ? t('photo') : t('status')); - - $links = array(array('rel' => 'alternate','type' => 'text/html', 'href' => $item['plink'])); - $objtype = (($item['resource_type'] === 'photo') ? ACTIVITY_OBJ_PHOTO : ACTIVITY_OBJ_NOTE ); - - $body = $item['body']; + $i = q( + "select * from item where id = %d and uid = %d", + intval($item_id), + intval(local_channel()) + ); - $obj = Activity::fetch_item( [ 'id' => $item['mid'] ] ); - $objtype = $obj['type']; + if (!$i) { + // try the global public stream + $i = q( + "select * from item where id = %d and uid = %d", + intval($postid), + intval($sys['channel_id']) + ); + // try the local public stream + if (!$i) { + $i = q( + "select * from item where id = %d and item_wall = 1 and item_private = 0", + intval($postid) + ); + } - if (! intval($item['item_thread_top'])) - $post_type = 'comment'; - - if ($activity === ACTIVITY_FOLLOW) { - $bodyverb = t('%1$s is following %2$s\'s %3$s'); - } - if ($activity === ACTIVITY_IGNORE) { - $bodyverb = t('%1$s stopped following %2$s\'s %3$s'); - } - - $arr = []; + if ($i && local_channel() && (!is_sys_channel(local_channel()))) { + $i = [copy_of_pubitem($channel, $i[0]['mid'])]; + $item_id = (($i) ? $i[0]['id'] : 0); + } + } - $arr['uuid'] = $uuid; - $arr['mid'] = $mid; - $arr['aid'] = $owner_aid; - $arr['uid'] = $owner_uid; - $arr['parent'] = $item['id']; - $arr['parent_mid'] = $item['mid']; - $arr['thr_parent'] = $item['mid']; - $arr['owner_xchan'] = $thread_owner['xchan_hash']; - $arr['author_xchan'] = $observer['xchan_hash']; - $arr['item_origin'] = 1; - $arr['item_notshown'] = 1; + if (!$i) { + return; + } - if (intval($item['item_wall'])) { - $arr['item_wall'] = 1; - } - else { - $arr['item_wall'] = 0; - } - - $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/' . gen_link_id($item['mid']) . ']' . $post_type . '[/zrl]'; - - $arr['body'] = sprintf( $bodyverb, $alink, $ulink, $plink ); - - $arr['verb'] = $activity; - $arr['obj_type'] = $objtype; - $arr['obj'] = json_encode($obj); - - $arr['allow_cid'] = $item['allow_cid']; - $arr['allow_gid'] = $item['allow_gid']; - $arr['deny_cid'] = $item['deny_cid']; - $arr['deny_gid'] = $item['deny_gid']; - - $post = item_store($arr); - $post_id = $post['item_id']; - - $arr['id'] = $post_id; - - call_hooks('post_local_end', $arr); - - killme(); - } + $r = q( + "select * from item where id = parent and id = %d limit 1", + dbesc($i[0]['parent']) + ); + + if ((!$item_id) || (!$r)) { + logger('subthread: no item ' . $item_id); + return; + } + + $item = $r[0]; + + $owner_uid = $item['uid']; + $observer = App::get_observer(); + $ob_hash = (($observer) ? $observer['xchan_hash'] : ''); + + if (!perm_is_allowed($owner_uid, $ob_hash, 'post_comments')) { + return; + } + + $sys = get_sys_channel(); + + $owner_uid = $item['uid']; + $owner_aid = $item['aid']; + + // if this is a "discover" item, (item['uid'] is the sys channel), + // fallback to the item comment policy, which should've been + // respected when generating the conversation thread. + // Even if the activity is rejected by the item owner, it should still get attached + // to the local discover conversation on this site. + + if (($owner_uid != $sys['channel_id']) && (!perm_is_allowed($owner_uid, $observer['xchan_hash'], 'post_comments'))) { + notice(t('Permission denied') . EOL); + killme(); + } + + $r = q( + "select * from xchan where xchan_hash = '%s' limit 1", + dbesc($item['owner_xchan']) + ); + if ($r) { + $thread_owner = $r[0]; + } else { + killme(); + } + + $r = q( + "select * from xchan where xchan_hash = '%s' limit 1", + dbesc($item['author_xchan']) + ); + if ($r) { + $item_author = $r[0]; + } else { + killme(); + } + + $uuid = new_uuid(); + $mid = z_root() . '/item/' . $uuid; + + $post_type = (($item['resource_type'] === 'photo') ? t('photo') : t('status')); + + $links = array(array('rel' => 'alternate', 'type' => 'text/html', 'href' => $item['plink'])); + $objtype = (($item['resource_type'] === 'photo') ? ACTIVITY_OBJ_PHOTO : ACTIVITY_OBJ_NOTE); + + $body = $item['body']; + + $obj = Activity::fetch_item(['id' => $item['mid']]); + $objtype = $obj['type']; + + if (!intval($item['item_thread_top'])) { + $post_type = 'comment'; + } + + if ($activity === ACTIVITY_FOLLOW) { + $bodyverb = t('%1$s is following %2$s\'s %3$s'); + } + if ($activity === ACTIVITY_IGNORE) { + $bodyverb = t('%1$s stopped following %2$s\'s %3$s'); + } + + $arr = []; + + $arr['uuid'] = $uuid; + $arr['mid'] = $mid; + $arr['aid'] = $owner_aid; + $arr['uid'] = $owner_uid; + $arr['parent'] = $item['id']; + $arr['parent_mid'] = $item['mid']; + $arr['thr_parent'] = $item['mid']; + $arr['owner_xchan'] = $thread_owner['xchan_hash']; + $arr['author_xchan'] = $observer['xchan_hash']; + $arr['item_origin'] = 1; + $arr['item_notshown'] = 1; + + if (intval($item['item_wall'])) { + $arr['item_wall'] = 1; + } else { + $arr['item_wall'] = 0; + } + + $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/' . gen_link_id($item['mid']) . ']' . $post_type . '[/zrl]'; + + $arr['body'] = sprintf($bodyverb, $alink, $ulink, $plink); + + $arr['verb'] = $activity; + $arr['obj_type'] = $objtype; + $arr['obj'] = json_encode($obj); + + $arr['allow_cid'] = $item['allow_cid']; + $arr['allow_gid'] = $item['allow_gid']; + $arr['deny_cid'] = $item['deny_cid']; + $arr['deny_gid'] = $item['deny_gid']; + + $post = item_store($arr); + $post_id = $post['item_id']; + + $arr['id'] = $post_id; + + call_hooks('post_local_end', $arr); + + killme(); + } } diff --git a/Zotlabs/Module/Suggestions.php b/Zotlabs/Module/Suggestions.php index 39cb545c2..81d51e97e 100644 --- a/Zotlabs/Module/Suggestions.php +++ b/Zotlabs/Module/Suggestions.php @@ -1,6 +1,6 @@ [ [ 'uid' => local_channel(), 'xchan' => $_GET['ignore'] ]]] ); - } - } - - - function get() { - - $o = ''; - if (! local_channel()) { - notice( t('Permission denied.') . EOL); - return; - } + public function init() + { + if (!local_channel()) { + return; + } - if (Apps::system_app_installed(local_channel(),'Suggest Channels')) { + if (x($_GET, 'ignore')) { + q( + "insert into xign ( uid, xchan ) values ( %d, '%s' ) ", + intval(local_channel()), + dbesc($_GET['ignore']) + ); + Libsync::build_sync_packet(local_channel(), ['xign' => [['uid' => local_channel(), 'xchan' => $_GET['ignore']]]]); + } + } + + + public function get() + { + + $o = ''; + if (!local_channel()) { + notice(t('Permission denied.') . EOL); + return; + } + + if (Apps::system_app_installed(local_channel(), 'Suggest Channels')) { goaway(z_root() . '/directory?f=&suggest=1'); } - $desc = t('This app (when installed) displays a small number of friend suggestions on selected pages or you can run the app to display a full list of channel suggestions.'); + $desc = t('This app (when installed) displays a small number of friend suggestions on selected pages or you can run the app to display a full list of channel suggestions.'); return ''; - - } - + } } diff --git a/Zotlabs/Module/Superblock.php b/Zotlabs/Module/Superblock.php index 8bb540f72..0d2dfc493 100644 --- a/Zotlabs/Module/Superblock.php +++ b/Zotlabs/Module/Superblock.php @@ -9,247 +9,253 @@ use Zotlabs\Lib\Libsync; use Zotlabs\Lib\LibBlock; use Zotlabs\Lib\Libzot; -class Superblock extends Controller { +class Superblock extends Controller +{ - function init() { - - if (! local_channel()) { - return; - } + public function init() + { - $inline = (isset($_REQUEST['manual_block']) ? true : false); + if (!local_channel()) { + return; + } - $type = BLOCKTYPE_CHANNEL; - $blocked = trim($_REQUEST['block']); - if (! $blocked) { - $blocked = trim($_REQUEST['blocksite']); - if ($blocked) { - $type = BLOCKTYPE_SERVER; - } - } + $inline = (isset($_REQUEST['manual_block']) ? true : false); - $m = parse_url($blocked); - if ($m['scheme'] && $m['host'] && (($type === BLOCKTYPE_SERVER) || (! $m['path']))) { - if (strcasecmp($m['host'],App::get_hostname()) === 0) { - notice(t('Blocking this site is not permitted.')); - if ($inline) { - return; - } - killme(); - } - $type = BLOCKTYPE_SERVER; - $blocked = $m['host']; - } + $type = BLOCKTYPE_CHANNEL; + $blocked = trim($_REQUEST['block']); + if (!$blocked) { + $blocked = trim($_REQUEST['blocksite']); + if ($blocked) { + $type = BLOCKTYPE_SERVER; + } + } - $handled = false; - $ignored = []; + $m = parse_url($blocked); + if ($m['scheme'] && $m['host'] && (($type === BLOCKTYPE_SERVER) || (!$m['path']))) { + if (strcasecmp($m['host'], App::get_hostname()) === 0) { + notice(t('Blocking this site is not permitted.')); + if ($inline) { + return; + } + killme(); + } + $type = BLOCKTYPE_SERVER; + $blocked = $m['host']; + } - if ($blocked) { - $handled = true; - if ($type === BLOCKTYPE_CHANNEL) { + $handled = false; + $ignored = []; - $r = q("select * from xchan where ( xchan_hash = '%s' or xchan_addr = '%s' or xchan_url = '%s' )", - dbesc($blocked), - dbesc($blocked), - dbesc($blocked) - ); + if ($blocked) { + $handled = true; + if ($type === BLOCKTYPE_CHANNEL) { + $r = q( + "select * from xchan where ( xchan_hash = '%s' or xchan_addr = '%s' or xchan_url = '%s' )", + dbesc($blocked), + dbesc($blocked), + dbesc($blocked) + ); - if (! $r) { - // not in cache - try discovery - $wf = discover_by_webbie($blocked,'',false); - - if (! $wf) { - notice( t('Channel not found.') . EOL); - if ($inline) { - return; - } - killme(); - } + if (!$r) { + // not in cache - try discovery + $wf = discover_by_webbie($blocked, '', false); - if ($wf) { + if (!$wf) { + notice(t('Channel not found.') . EOL); + if ($inline) { + return; + } + killme(); + } - // something was discovered - find the record which was just created. - - $r = q("select * from xchan where ( xchan_hash = '%s' or xchan_url = '%s' or xchan_addr = '%s' )", - dbesc(($wf) ? $wf : $blocked), - dbesc($blocked), - dbesc($blocked) - ); - } - } + if ($wf) { + // something was discovered - find the record which was just created. - if ($r) { - $r = Libzot::zot_record_preferred($r,'xchan_network'); - $blocked = $r['xchan_hash']; - } - } + $r = q( + "select * from xchan where ( xchan_hash = '%s' or xchan_url = '%s' or xchan_addr = '%s' )", + dbesc(($wf) ? $wf : $blocked), + dbesc($blocked), + dbesc($blocked) + ); + } + } - $bl = [ - 'block_channel_id' => local_channel(), - 'block_entity' => $blocked, - 'block_type' => $type, - 'block_comment' => t('Added by Superblock') - ]; + if ($r) { + $r = Libzot::zot_record_preferred($r, 'xchan_network'); + $blocked = $r['xchan_hash']; + } + } - LibBlock::store($bl); + $bl = [ + 'block_channel_id' => local_channel(), + 'block_entity' => $blocked, + 'block_type' => $type, + 'block_comment' => t('Added by Superblock') + ]; - $sync = []; - - $sync['block'] = [ LibBlock::fetch_by_entity(local_channel(),$blocked) ]; + LibBlock::store($bl); - if ($type === BLOCKTYPE_CHANNEL) { - $z = q("insert into xign ( uid, xchan ) values ( %d , '%s' ) ", - intval(local_channel()), - dbesc($blocked) - ); - $ab = q("select * from abook where abook_channel = %d and abook_xchan = '%s'", - intval(local_channel()), - dbesc($blocked) - ); - if (($ab) && (! intval($ab['abook_blocked']))) { - q("update abook set abook_blocked = 1 where abook_channel = %d and abook_xchan = '%s'", - intval(local_channel()), - dbesc($blocked) - ); - - $r = q("SELECT abook.*, xchan.* FROM abook left join xchan on abook_xchan = xchan_hash WHERE abook_channel = %d and abook_xchan = '%s' LIMIT 1", - intval(local_channel()), - dbesc($blocked) - ); - if ($r) { - $r = array_shift($r); - $abconfig = load_abconfig(local_channel(),$blocked); - if ($abconfig) { - $r['abconfig'] = $abconfig; - } - unset($r['abook_id']); - unset($r['abook_account']); - unset($r['abook_channel']); - $sync['abook'] = [ $r ]; - } - } - $sync['xign'] = [[ 'uid' => local_channel(), 'xchan' => $_GET['block'] ]]; - } - Libsync::build_sync_packet(0, $sync); - } + $sync = []; - $type = BLOCKTYPE_CHANNEL; - $unblocked = trim($_REQUEST['unblock']); - if (! $unblocked) { - $unblocked = trim($_REQUEST['unblocksite']); - if ($unblocked) { - $type = BLOCKTYPE_SERVER; - } - } - if ($unblocked) { - $handled = true; - if (check_form_security_token('superblock','sectok')) { - $r = LibBlock::fetch_by_entity(local_channel(), $unblocked); - if ($r) { - LibBlock::remove(local_channel(), $unblocked); + $sync['block'] = [LibBlock::fetch_by_entity(local_channel(), $blocked)]; - $sync = []; - $sync['block'] = [[ - 'block_channel_id' => local_channel(), - 'block_entity' => $unblocked, - 'block_type' => $type, - 'deleted' => true, - ]]; - if ($type === BLOCKTYPE_CHANNEL) { - $ab = q("select * from abook left join xchan on abook_xchan = xchan_hash where abook_channel = %d and abook_xchan = '%s'", - intval(local_channel()), - dbesc($unblocked) - ); - if (($ab) && (intval($ab['abook_blocked']))) { - q("update abook set abook_blocked = 1 where abook_channel = %d and abook_xchan = '%s'", - intval(local_channel()), - dbesc($unblocked) - ); - $ab['abook_blocked'] = 0; - $abconfig = load_abconfig(local_channel(),$unblocked); - if ($abconfig) { - $ab['abconfig'] = $abconfig; - } - unset($ab['abook_id']); - unset($ab['abook_account']); - unset($ab['abook_channel']); - $sync['abook'] = [ $ab ]; - } + if ($type === BLOCKTYPE_CHANNEL) { + $z = q( + "insert into xign ( uid, xchan ) values ( %d , '%s' ) ", + intval(local_channel()), + dbesc($blocked) + ); + $ab = q( + "select * from abook where abook_channel = %d and abook_xchan = '%s'", + intval(local_channel()), + dbesc($blocked) + ); + if (($ab) && (!intval($ab['abook_blocked']))) { + q( + "update abook set abook_blocked = 1 where abook_channel = %d and abook_xchan = '%s'", + intval(local_channel()), + dbesc($blocked) + ); - $z = q("delete from xign where uid = %d and xchan = '%s' ", - intval(local_channel()), - dbesc($unblocked) - ); - } - Libsync::build_sync_packet(0, $sync ); - } - } - } + $r = q( + "SELECT abook.*, xchan.* FROM abook left join xchan on abook_xchan = xchan_hash WHERE abook_channel = %d and abook_xchan = '%s' LIMIT 1", + intval(local_channel()), + dbesc($blocked) + ); + if ($r) { + $r = array_shift($r); + $abconfig = load_abconfig(local_channel(), $blocked); + if ($abconfig) { + $r['abconfig'] = $abconfig; + } + unset($r['abook_id']); + unset($r['abook_account']); + unset($r['abook_channel']); + $sync['abook'] = [$r]; + } + } + $sync['xign'] = [['uid' => local_channel(), 'xchan' => $_GET['block']]]; + } + Libsync::build_sync_packet(0, $sync); + } - if ($handled) { + $type = BLOCKTYPE_CHANNEL; + $unblocked = trim($_REQUEST['unblock']); + if (!$unblocked) { + $unblocked = trim($_REQUEST['unblocksite']); + if ($unblocked) { + $type = BLOCKTYPE_SERVER; + } + } + if ($unblocked) { + $handled = true; + if (check_form_security_token('superblock', 'sectok')) { + $r = LibBlock::fetch_by_entity(local_channel(), $unblocked); + if ($r) { + LibBlock::remove(local_channel(), $unblocked); - info( t('superblock settings updated') . EOL ); + $sync = []; + $sync['block'] = [[ + 'block_channel_id' => local_channel(), + 'block_entity' => $unblocked, + 'block_type' => $type, + 'deleted' => true, + ]]; + if ($type === BLOCKTYPE_CHANNEL) { + $ab = q( + "select * from abook left join xchan on abook_xchan = xchan_hash where abook_channel = %d and abook_xchan = '%s'", + intval(local_channel()), + dbesc($unblocked) + ); + if (($ab) && (intval($ab['abook_blocked']))) { + q( + "update abook set abook_blocked = 1 where abook_channel = %d and abook_xchan = '%s'", + intval(local_channel()), + dbesc($unblocked) + ); + $ab['abook_blocked'] = 0; + $abconfig = load_abconfig(local_channel(), $unblocked); + if ($abconfig) { + $ab['abconfig'] = $abconfig; + } + unset($ab['abook_id']); + unset($ab['abook_account']); + unset($ab['abook_channel']); + $sync['abook'] = [$ab]; + } - if ($unblocked || $inline) { - return; - } - - killme(); - } + $z = q( + "delete from xign where uid = %d and xchan = '%s' ", + intval(local_channel()), + dbesc($unblocked) + ); + } + Libsync::build_sync_packet(0, $sync); + } + } + } - } + if ($handled) { + info(t('superblock settings updated') . EOL); - function get() { + if ($unblocked || $inline) { + return; + } - $l = LibBlock::fetch(local_channel(),BLOCKTYPE_CHANNEL); + killme(); + } + } - $list = ids_to_array($l,'block_entity'); + public function get() + { - stringify_array_elms($list,true); - $query_str = implode(',',$list); - if ($query_str) { - $r = q("select * from xchan where xchan_hash in ( " . $query_str . " ) "); - } - else { - $r = []; - } - if ($r) { - for ($x = 0; $x < count($r); $x ++) { - $r[$x]['encoded_hash'] = urlencode($r[$x]['xchan_hash']); - } - } + $l = LibBlock::fetch(local_channel(), BLOCKTYPE_CHANNEL); - $sc .= replace_macros(get_markup_template('superblock_list.tpl'), [ - '$blocked' => t('Blocked channels'), - '$entries' => $r, - '$nothing' => (($r) ? '' : t('No channels currently blocked')), - '$token' => get_form_security_token('superblock'), - '$remove' => t('Remove') - ]); + $list = ids_to_array($l, 'block_entity'); + + stringify_array_elms($list, true); + $query_str = implode(',', $list); + if ($query_str) { + $r = q("select * from xchan where xchan_hash in ( " . $query_str . " ) "); + } else { + $r = []; + } + if ($r) { + for ($x = 0; $x < count($r); $x++) { + $r[$x]['encoded_hash'] = urlencode($r[$x]['xchan_hash']); + } + } + + $sc .= replace_macros(get_markup_template('superblock_list.tpl'), [ + '$blocked' => t('Blocked channels'), + '$entries' => $r, + '$nothing' => (($r) ? '' : t('No channels currently blocked')), + '$token' => get_form_security_token('superblock'), + '$remove' => t('Remove') + ]); - $l = LibBlock::fetch(local_channel(),BLOCKTYPE_SERVER); - $list = ids_to_array($l,'block_entity'); - if ($list) { - for ($x = 0; $x < count($list); $x ++ ) { - $list[$x] = [ $list[$x], urlencode($list[$x]) ]; - } - } + $l = LibBlock::fetch(local_channel(), BLOCKTYPE_SERVER); + $list = ids_to_array($l, 'block_entity'); + if ($list) { + for ($x = 0; $x < count($list); $x++) { + $list[$x] = [$list[$x], urlencode($list[$x])]; + } + } - $sc .= replace_macros(get_markup_template('superblock_serverlist.tpl'), [ - '$blocked' => t('Blocked servers'), - '$entries' => $list, - '$nothing' => (($list) ? '' : t('No servers currently blocked')), - '$token' => get_form_security_token('superblock'), - '$remove' => t('Remove') - ]); + $sc .= replace_macros(get_markup_template('superblock_serverlist.tpl'), [ + '$blocked' => t('Blocked servers'), + '$entries' => $list, + '$nothing' => (($list) ? '' : t('No servers currently blocked')), + '$token' => get_form_security_token('superblock'), + '$remove' => t('Remove') + ]); - $s .= replace_macros(get_markup_template('generic_app_settings.tpl'), [ - '$addon' => array('superblock', t('Manage Blocks'), '', t('Submit')), - '$content' => $sc - ]); + $s .= replace_macros(get_markup_template('generic_app_settings.tpl'), [ + '$addon' => array('superblock', t('Manage Blocks'), '', t('Submit')), + '$content' => $sc + ]); - return $s; - - } -} \ No newline at end of file + return $s; + } +} diff --git a/Zotlabs/Module/Tagadelic.php b/Zotlabs/Module/Tagadelic.php index d17fdb36a..a4d1b8352 100644 --- a/Zotlabs/Module/Tagadelic.php +++ b/Zotlabs/Module/Tagadelic.php @@ -9,28 +9,30 @@ use Zotlabs\Lib\Libprofile; use Zotlabs\Web\Controller; use Zotlabs\Render\Comanche; -class Tagadelic extends Controller { +class Tagadelic extends Controller +{ - function init() { + public function init() + { - if(local_channel()) { - $channel = App::get_channel(); - if($channel && $channel['channel_address']) { - $which = $channel['channel_address']; - } - Libprofile::load($which,0); - } - - } + if (local_channel()) { + $channel = App::get_channel(); + if ($channel && $channel['channel_address']) { + $which = $channel['channel_address']; + } + Libprofile::load($which, 0); + } + } - function get() { + public function get() + { $desc = t('This app displays a hashtag cloud on your channel homepage.'); $text = ''; - if(! ( local_channel() && Apps::system_app_installed(local_channel(),'Tagadelic'))) { + if (!(local_channel() && Apps::system_app_installed(local_channel(), 'Tagadelic'))) { return $text; } @@ -39,10 +41,7 @@ class Tagadelic extends Controller { $text = ''; - $c = new Comanche; - return $text . EOL . EOL . $c->widget('tagcloud_wall',EMPTY_STR); - - } - - + $c = new Comanche(); + return $text . EOL . EOL . $c->widget('tagcloud_wall', EMPTY_STR); + } } diff --git a/Zotlabs/Module/Tagger.php b/Zotlabs/Module/Tagger.php index 88745d164..9bef7e43e 100644 --- a/Zotlabs/Module/Tagger.php +++ b/Zotlabs/Module/Tagger.php @@ -1,170 +1,178 @@ 1) ? notags(trim(argv(1))) : 0); - - logger('tagger: tag ' . $term . ' item ' . $item_id); - - $r = q("select * from item where id = %d and uid = %d limit 1", - intval($item_id), - intval(local_channel()) - ); + $observer_hash = get_observer_hash(); + //strip html-tags + $term = notags(trim($_GET['term'])); + //check if empty + if (!$term) { + return; + } - if(! $r) { - $r = q("select * from item where id = %d and uid = %d limit 1", - intval($item_id), - intval($sys['channel_id']) - ); - if (! $r) { - $r = q("select * from item where id = %d and item_private = 0 and item_wall = 1", - intval($item_id) - ); - } - if ($r && local_channel() && (! is_sys_channel(local_channel()))) { - $r = [ copy_of_pubitem($channel, $i[0]['mid']) ]; + $item_id = ((argc() > 1) ? notags(trim(argv(1))) : 0); + + logger('tagger: tag ' . $term . ' item ' . $item_id); + + $r = q( + "select * from item where id = %d and uid = %d limit 1", + intval($item_id), + intval(local_channel()) + ); + + if (!$r) { + $r = q( + "select * from item where id = %d and uid = %d limit 1", + intval($item_id), + intval($sys['channel_id']) + ); + if (!$r) { + $r = q( + "select * from item where id = %d and item_private = 0 and item_wall = 1", + intval($item_id) + ); + } + if ($r && local_channel() && (!is_sys_channel(local_channel()))) { + $r = [copy_of_pubitem($channel, $i[0]['mid'])]; $item_id = (($r) ? $r[0]['id'] : 0); - } - } + } + } - if(! $r) { - notice( t('Post not found.') . EOL); - return; - } + if (!$r) { + notice(t('Post not found.') . EOL); + return; + } - $r = q("SELECT * FROM item left join xchan on xchan_hash = author_xchan WHERE id = %d and uid = %d LIMIT 1", - intval($item_id), - intval(local_channel()) - ); - - if((! $item_id) || (! $r)) { - logger('tagger: no item ' . $item_id); - return; - } - - $item = $r[0]; - - $owner_uid = $item['uid']; - - switch($item['resource_type']) { - case 'photo': - $targettype = ACTIVITY_OBJ_PHOTO; - $post_type = t('photo'); - break; - case 'event': - $targgettype = ACTIVITY_OBJ_EVENT; - $post_type = t('event'); - break; - default: - $targettype = ACTIVITY_OBJ_NOTE; - $post_type = t('post'); - if($item['mid'] != $item['parent_mid']) - $post_type = t('comment'); - break; - } - + $r = q( + "SELECT * FROM item left join xchan on xchan_hash = author_xchan WHERE id = %d and uid = %d LIMIT 1", + intval($item_id), + intval(local_channel()) + ); - $clean_term = trim($term,'"\' '); - - $links = array(array('rel' => 'alternate','type' => 'text/html', - 'href' => z_root() . '/display/' . gen_link_id($item['mid']))); - - $target = json_encode(array( - 'type' => $targettype, - 'id' => $item['mid'], - 'link' => $links, - 'title' => $item['title'], - 'content' => $item['body'], - 'created' => $item['created'], - 'edited' => $item['edited'], - 'author' => array( - 'name' => $item['xchan_name'], - 'address' => $item['xchan_addr'], - 'guid' => $item['xchan_guid'], - 'guid_sig' => $item['xchan_guid_sig'], - 'link' => array( - array('rel' => 'alternate', 'type' => 'text/html', 'href' => $item['xchan_url']), - array('rel' => 'photo', 'type' => $item['xchan_photo_mimetype'], 'href' => $item['xchan_photo_m'])), - ), - )); - - $tagid = z_root() . '/search?tag=' . $clean_term; - $objtype = ACTIVITY_OBJ_TAGTERM; - - $obj = json_encode(array( - 'type' => $objtype, - 'id' => $tagid, - 'link' => array(array('rel' => 'alternate','type' => 'text/html', 'href' => $tagid)), - 'title' => $clean_term, - 'content' => $clean_term - )); - - $bodyverb = t('%1$s tagged %2$s\'s %3$s with %4$s'); - - // saving here for reference - // also check out x22d5 and x2317 and x0d6b and x0db8 and x24d0 and xff20 !!! - - $termlink = html_entity_decode('⋕') . '[zrl=' . z_root() . '/search?tag=' . urlencode($clean_term) . ']'. $clean_term . '[/zrl]'; - - $channel = \App::get_channel(); - - $arr = []; - - $arr['owner_xchan'] = $item['owner_xchan']; - $arr['author_xchan'] = $channel['channel_hash']; - - $arr['item_origin'] = 1; - $arr['item_wall'] = ((intval($item['item_wall'])) ? 1 : 0); - - $ulink = '[zrl=' . $channel['xchan_url'] . ']' . $channel['channel_name'] . '[/zrl]'; - $alink = '[zrl=' . $item['xchan_url'] . ']' . $item['xchan_name'] . '[/zrl]'; - $plink = '[zrl=' . $item['plink'] . ']' . $post_type . '[/zrl]'; - - $arr['body'] = sprintf( $bodyverb, $ulink, $alink, $plink, $termlink ); - - $arr['verb'] = ACTIVITY_TAG; - $arr['tgt_type'] = $targettype; - $arr['target'] = $target; - $arr['obj_type'] = $objtype; - $arr['obj'] = $obj; - $arr['parent_mid'] = $item['mid']; - store_item_tag($item['uid'],$item['id'],TERM_OBJ_POST,TERM_COMMUNITYTAG,$clean_term,$tagid); - $ret = post_activity_item($arr); + if ((!$item_id) || (!$r)) { + logger('tagger: no item ' . $item_id); + return; + } - if($ret['success']) { - Libsync::build_sync_packet(local_channel(), - [ - 'item' => [ encode_item($ret['activity'],true) ] - ] - ); - } - - killme(); - - } - + $item = $r[0]; + + $owner_uid = $item['uid']; + + switch ($item['resource_type']) { + case 'photo': + $targettype = ACTIVITY_OBJ_PHOTO; + $post_type = t('photo'); + break; + case 'event': + $targgettype = ACTIVITY_OBJ_EVENT; + $post_type = t('event'); + break; + default: + $targettype = ACTIVITY_OBJ_NOTE; + $post_type = t('post'); + if ($item['mid'] != $item['parent_mid']) { + $post_type = t('comment'); + } + break; + } + + + $clean_term = trim($term, '"\' '); + + $links = array(array('rel' => 'alternate', 'type' => 'text/html', + 'href' => z_root() . '/display/' . gen_link_id($item['mid']))); + + $target = json_encode(array( + 'type' => $targettype, + 'id' => $item['mid'], + 'link' => $links, + 'title' => $item['title'], + 'content' => $item['body'], + 'created' => $item['created'], + 'edited' => $item['edited'], + 'author' => array( + 'name' => $item['xchan_name'], + 'address' => $item['xchan_addr'], + 'guid' => $item['xchan_guid'], + 'guid_sig' => $item['xchan_guid_sig'], + 'link' => array( + array('rel' => 'alternate', 'type' => 'text/html', 'href' => $item['xchan_url']), + array('rel' => 'photo', 'type' => $item['xchan_photo_mimetype'], 'href' => $item['xchan_photo_m'])), + ), + )); + + $tagid = z_root() . '/search?tag=' . $clean_term; + $objtype = ACTIVITY_OBJ_TAGTERM; + + $obj = json_encode(array( + 'type' => $objtype, + 'id' => $tagid, + 'link' => array(array('rel' => 'alternate', 'type' => 'text/html', 'href' => $tagid)), + 'title' => $clean_term, + 'content' => $clean_term + )); + + $bodyverb = t('%1$s tagged %2$s\'s %3$s with %4$s'); + + // saving here for reference + // also check out x22d5 and x2317 and x0d6b and x0db8 and x24d0 and xff20 !!! + + $termlink = html_entity_decode('⋕') . '[zrl=' . z_root() . '/search?tag=' . urlencode($clean_term) . ']' . $clean_term . '[/zrl]'; + + $channel = App::get_channel(); + + $arr = []; + + $arr['owner_xchan'] = $item['owner_xchan']; + $arr['author_xchan'] = $channel['channel_hash']; + + $arr['item_origin'] = 1; + $arr['item_wall'] = ((intval($item['item_wall'])) ? 1 : 0); + + $ulink = '[zrl=' . $channel['xchan_url'] . ']' . $channel['channel_name'] . '[/zrl]'; + $alink = '[zrl=' . $item['xchan_url'] . ']' . $item['xchan_name'] . '[/zrl]'; + $plink = '[zrl=' . $item['plink'] . ']' . $post_type . '[/zrl]'; + + $arr['body'] = sprintf($bodyverb, $ulink, $alink, $plink, $termlink); + + $arr['verb'] = ACTIVITY_TAG; + $arr['tgt_type'] = $targettype; + $arr['target'] = $target; + $arr['obj_type'] = $objtype; + $arr['obj'] = $obj; + $arr['parent_mid'] = $item['mid']; + store_item_tag($item['uid'], $item['id'], TERM_OBJ_POST, TERM_COMMUNITYTAG, $clean_term, $tagid); + $ret = post_activity_item($arr); + + if ($ret['success']) { + Libsync::build_sync_packet( + local_channel(), + [ + 'item' => [encode_item($ret['activity'], true)] + ] + ); + } + + killme(); + } } diff --git a/Zotlabs/Module/Tagrm.php b/Zotlabs/Module/Tagrm.php index c8b613258..4879170b6 100644 --- a/Zotlabs/Module/Tagrm.php +++ b/Zotlabs/Module/Tagrm.php @@ -1,147 +1,158 @@ ' . t('Remove Item Tag') . ''; - - $o .= '

      ' . t('Select a tag to remove: ') . '

      '; - - $o .= '
      '; - $o .= ''; - $o .= '
        '; - - - foreach($r[0]['term'] as $x) { - $o .= '
      • ' . bbcode($x['term']) . '
      • '; - } - - $o .= '
      '; - $o .= ''; - $o .= ''; - $o .= '
      '; - - return $o; - - } - - } - + public function post() + { + + if (!local_channel()) { + goaway(z_root() . '/' . $_SESSION['photo_return']); + } + + + if ((x($_POST, 'submit')) && ($_POST['submit'] === t('Cancel'))) { + goaway(z_root() . '/' . $_SESSION['photo_return']); + } + + $tag = ((x($_POST, 'tag')) ? trim($_POST['tag']) : ''); + $item = ((x($_POST, 'item')) ? intval($_POST['item']) : 0); + + $r = q( + "SELECT * FROM item WHERE id = %d AND uid = %d LIMIT 1", + intval($item), + intval(local_channel()) + ); + + if (!$r) { + goaway(z_root() . '/' . $_SESSION['photo_return']); + } + + $r = fetch_post_tags($r, true); + + $item = $r[0]; + $new_tags = []; + + if ($item['term']) { + for ($x = 0; $x < count($item['term']); $x++) { + if ($item['term'][$x]['term'] !== hex2bin($tag)) { + $new_tags[] = $item['term'][$x]; + } + } + } + + if ($new_tags) { + $item['term'] = $new_tags; + } else { + unset($item['term']); + } + + item_store_update($item); + + info(t('Tag removed') . EOL); + goaway(z_root() . '/' . $_SESSION['photo_return']); + + // NOTREACHED + } + + + public function get() + { + + if (!local_channel()) { + goaway(z_root() . '/' . $_SESSION['photo_return']); + // NOTREACHED + } + + // remove tag on the fly if item and tag are provided + if ((argc() == 4) && (argv(1) === 'drop') && intval(argv(2))) { + $item = intval(argv(2)); + $tag = argv(3); + + $r = q( + "SELECT * FROM item WHERE id = %d AND uid = %d LIMIT 1", + intval($item), + intval(local_channel()) + ); + + if (!$r) { + goaway(z_root() . '/' . $_SESSION['photo_return']); + } + + $r = fetch_post_tags($r, true); + + $item = $r[0]; + + $new_tags = []; + + if ($item['term']) { + for ($x = 0; $x < count($item['term']); $x++) { + if ($item['term'][$x]['term'] !== hex2bin($tag)) { + $new_tags[] = $item['term'][$x]; + } + } + } + + if ($new_tags) { + $item['term'] = $new_tags; + } else { + unset($item['term']); + } + + item_store_update($item); + + info(t('Tag removed') . EOL); + goaway(z_root() . '/' . $_SESSION['photo_return']); + } + + //if we got only the item print a list of tags to select + if ((argc() == 3) && (argv(1) === 'drop') && intval(argv(2))) { + $o = ''; + + $item = intval(argv(2)); + + $r = q( + "SELECT * FROM item WHERE id = %d AND uid = %d LIMIT 1", + intval($item), + intval(local_channel()) + ); + + if (!$r) { + goaway(z_root() . '/' . $_SESSION['photo_return']); + } + + $r = fetch_post_tags($r, true); + + if (!count($r[0]['term'])) { + goaway(z_root() . '/' . $_SESSION['photo_return']); + } + + $o .= '

      ' . t('Remove Item Tag') . '

      '; + + $o .= '

      ' . t('Select a tag to remove: ') . '

      '; + + $o .= '
      '; + $o .= ''; + $o .= '
        '; + + + foreach ($r[0]['term'] as $x) { + $o .= '
      • ' . bbcode($x['term']) . '
      • '; + } + + $o .= '
      '; + $o .= ''; + $o .= ''; + $o .= '
      '; + + return $o; + } + } } diff --git a/Zotlabs/Module/Tasks.php b/Zotlabs/Module/Tasks.php index 17c578c52..38deac91d 100644 --- a/Zotlabs/Module/Tasks.php +++ b/Zotlabs/Module/Tasks.php @@ -1,115 +1,117 @@ 1 && argv(1) === 'fetch') { - if (argc() > 2 && argv(2) === 'all') - $arr['all'] = 1; - - $x = tasks_fetch($arr); - $x['html'] = ''; - if(isset($x['tasks']) && is_array($x['tasks'])) { - foreach($x['tasks'] as $y) { - $x['html'] .= '
      ' . $y['summary'] . '
      '; - } - } - json_return_and_die($x); - } - - } - - - - function post() { - // logger('post: ' . print_r($_POST,true)); - - - if (! local_channel()) { - return; - } - - $channel = App::get_channel(); - - if ((argc() > 2) && (argv(1) === 'complete') && intval(argv(2))) { - $ret = array('success' => false); - $r = q("select * from event where etype = 'task' and uid = %d and id = %d limit 1", - intval(local_channel()), - intval(argv(2)) - ); - if ($r) { - $event = $r[0]; - if ($event['event_status'] === 'COMPLETED') { - $event['event_status'] = 'IN-PROCESS'; - $event['event_status_date'] = NULL_DATE; - $event['event_percent'] = 0; - $event['event_sequence'] = $event['event_sequence'] + 1; - $event['edited'] = datetime_convert(); - } - else { - $event['event_status'] = 'COMPLETED'; - $event['event_status_date'] = datetime_convert(); - $event['event_percent'] = 100; - $event['event_sequence'] = $event['event_sequence'] + 1; - $event['edited'] = datetime_convert(); - } - $x = event_store_event($event); - if ($x) { - $ret['success'] = true; - } - } - json_return_and_die($ret); - } - - if (argc() == 2 && argv(1) === 'new') { - $text = escape_tags(trim($_REQUEST['summary'])); - if (! $text) { - return [ 'success' => false ]; - } - $event = []; - $event['account'] = $channel['channel_account_id']; - $event['uid'] = $channel['channel_id']; - $event['event_xchan'] = $channel['channel_hash']; - $event['etype'] = 'task'; - $event['nofinish'] = true; - $event['created'] = $event['edited'] = $event['dtstart'] = datetime_convert(); - $event['adjust'] = 1; - $event['allow_cid'] = '<' . $channel['channel_hash'] . '>'; - $event['summary'] = escape_tags($_REQUEST['summary']); - $x = event_store_event($event); - if ($x) { - $x['success'] = true; - } - else { - $x = [ 'success' => false ]; - } - json_return_and_die($x); - } - } + public function init() + { + // logger('request: ' . print_r($_REQUEST,true)); - function get() { + $arr = []; + + if (argc() > 1 && argv(1) === 'fetch') { + if (argc() > 2 && argv(2) === 'all') { + $arr['all'] = 1; + } + + $x = tasks_fetch($arr); + $x['html'] = ''; + if (isset($x['tasks']) && is_array($x['tasks'])) { + foreach ($x['tasks'] as $y) { + $x['html'] .= '
      ' . $y['summary'] . '
      '; + } + } + json_return_and_die($x); + } + } + + + public function post() + { + // logger('post: ' . print_r($_POST,true)); + + + if (!local_channel()) { + return; + } + + $channel = App::get_channel(); + + if ((argc() > 2) && (argv(1) === 'complete') && intval(argv(2))) { + $ret = array('success' => false); + $r = q( + "select * from event where etype = 'task' and uid = %d and id = %d limit 1", + intval(local_channel()), + intval(argv(2)) + ); + if ($r) { + $event = $r[0]; + if ($event['event_status'] === 'COMPLETED') { + $event['event_status'] = 'IN-PROCESS'; + $event['event_status_date'] = NULL_DATE; + $event['event_percent'] = 0; + $event['event_sequence'] = $event['event_sequence'] + 1; + $event['edited'] = datetime_convert(); + } else { + $event['event_status'] = 'COMPLETED'; + $event['event_status_date'] = datetime_convert(); + $event['event_percent'] = 100; + $event['event_sequence'] = $event['event_sequence'] + 1; + $event['edited'] = datetime_convert(); + } + $x = event_store_event($event); + if ($x) { + $ret['success'] = true; + } + } + json_return_and_die($ret); + } + + if (argc() == 2 && argv(1) === 'new') { + $text = escape_tags(trim($_REQUEST['summary'])); + if (!$text) { + return ['success' => false]; + } + $event = []; + $event['account'] = $channel['channel_account_id']; + $event['uid'] = $channel['channel_id']; + $event['event_xchan'] = $channel['channel_hash']; + $event['etype'] = 'task'; + $event['nofinish'] = true; + $event['created'] = $event['edited'] = $event['dtstart'] = datetime_convert(); + $event['adjust'] = 1; + $event['allow_cid'] = '<' . $channel['channel_hash'] . '>'; + $event['summary'] = escape_tags($_REQUEST['summary']); + $x = event_store_event($event); + if ($x) { + $x['success'] = true; + } else { + $x = ['success' => false]; + } + json_return_and_die($x); + } + } + + public function get() + { $desc = t('This app provides a simple personal and task list.'); $text = ''; - if(! ( local_channel() && Apps::system_app_installed(local_channel(),'Tasks'))) { + if (!(local_channel() && Apps::system_app_installed(local_channel(), 'Tasks'))) { return $text; } - $obj = new \Zotlabs\Widget\Tasklist; - return $obj->widget([]); - } - - + $obj = new Tasklist(); + return $obj->widget([]); + } } diff --git a/Zotlabs/Module/Theme_info.php b/Zotlabs/Module/Theme_info.php index a03b9b160..8334b8497 100644 --- a/Zotlabs/Module/Theme_info.php +++ b/Zotlabs/Module/Theme_info.php @@ -2,70 +2,72 @@ namespace Zotlabs\Module; +use App; +use Zotlabs\Web\Controller; -class Theme_info extends \Zotlabs\Web\Controller { +class Theme_info extends Controller +{ - function get() { - $theme = argv(1); - if(! $theme) - killme(); - - $schemalist = []; + public function get() + { + $theme = argv(1); + if (!$theme) { + killme(); + } - $theme_config = ""; - if(($themeconfigfile = $this->get_theme_config_file($theme)) != null){ - require_once($themeconfigfile); - if(class_exists('\\Zotlabs\\Theme\\' . ucfirst($theme) . 'Config')) { - $clsname = '\\Zotlabs\\Theme\\' . ucfirst($theme) . 'Config'; - $th_config = new $clsname(); - $schemas = $th_config->get_schemas(); - if($schemas) { - foreach($schemas as $k => $v) { - $schemalist[] = [ 'key' => $k, 'val' => $v ]; - } - } - $theme_config = $th_config->get(); - } - } - $info = get_theme_info($theme); - if($info) { - // unfortunately there will be no translation for this string - $desc = $info['description']; - $version = $info['version']; - $credits = $info['credits']; - } - else { - $desc = ''; - $version = ''; - $credits = ''; - } + $schemalist = []; - $ret = [ - 'theme' => $theme, - 'img' => get_theme_screenshot($theme), - 'desc' => $desc, - 'version' => $version, - 'credits' => $credits, - 'schemas' => $schemalist, - 'config' => $theme_config - ]; - json_return_and_die($ret); - - } + $theme_config = ""; + if (($themeconfigfile = $this->get_theme_config_file($theme)) != null) { + require_once($themeconfigfile); + if (class_exists('\\Zotlabs\\Theme\\' . ucfirst($theme) . 'Config')) { + $clsname = '\\Zotlabs\\Theme\\' . ucfirst($theme) . 'Config'; + $th_config = new $clsname(); + $schemas = $th_config->get_schemas(); + if ($schemas) { + foreach ($schemas as $k => $v) { + $schemalist[] = ['key' => $k, 'val' => $v]; + } + } + $theme_config = $th_config->get(); + } + } + $info = get_theme_info($theme); + if ($info) { + // unfortunately there will be no translation for this string + $desc = $info['description']; + $version = $info['version']; + $credits = $info['credits']; + } else { + $desc = ''; + $version = ''; + $credits = ''; + } + + $ret = [ + 'theme' => $theme, + 'img' => get_theme_screenshot($theme), + 'desc' => $desc, + 'version' => $version, + 'credits' => $credits, + 'schemas' => $schemalist, + 'config' => $theme_config + ]; + json_return_and_die($ret); + } - function get_theme_config_file($theme){ + public function get_theme_config_file($theme) + { - $base_theme = \App::$theme_info['extends']; - - if (file_exists("view/theme/$theme/php/config.php")){ - return "view/theme/$theme/php/config.php"; - } - if (file_exists("view/theme/$base_theme/php/config.php")){ - return "view/theme/$base_theme/php/config.php"; - } - return null; - } + $base_theme = App::$theme_info['extends']; - -} \ No newline at end of file + if (file_exists("view/theme/$theme/php/config.php")) { + return "view/theme/$theme/php/config.php"; + } + if (file_exists("view/theme/$base_theme/php/config.php")) { + return "view/theme/$base_theme/php/config.php"; + } + return null; + } +} diff --git a/Zotlabs/Module/Thing.php b/Zotlabs/Module/Thing.php index 460556fe5..9228177eb 100644 --- a/Zotlabs/Module/Thing.php +++ b/Zotlabs/Module/Thing.php @@ -2,13 +2,12 @@ namespace Zotlabs\Module; - use App; +use Zotlabs\Access\AccessControl; use Zotlabs\Web\Controller; use Zotlabs\Lib\Libprofile; use Zotlabs\Lib\Libsync; - /** * @file Zotlabs/Module/Thing.php */ @@ -18,387 +17,406 @@ require_once('include/security.php'); require_once('include/acl_selectors.php'); -class Thing extends Controller { - - function init() { - - if(! local_channel()) - return; - - $channel = \App::get_channel(); - - if($_SERVER['REQUEST_METHOD'] === 'GET' && argc() < 2) { - Libprofile::load($channel['channel_address']); - } - - - $term_hash = (($_REQUEST['term_hash']) ? $_REQUEST['term_hash'] : ''); - - $name = escape_tags($_REQUEST['term']); - $verb = escape_tags($_REQUEST['verb']); - $activity = intval($_REQUEST['activity']); - $profile_guid = escape_tags($_REQUEST['profile_assign']); - $url = $_REQUEST['url']; - $photo = $_REQUEST['img']; - - $hash = random_string(); - - $verbs = obj_verbs(); - - /** - * verbs: [0] = first person singular, e.g. "I want", [1] = 3rd person singular, e.g. "Bill wants" - * We use the first person form when creating an activity, but the third person for use in activities - * @FIXME There is no accounting for verb gender for languages where this is significant. We may eventually - * require obj_verbs() to provide full conjugations and specify which form to use in the $_REQUEST params to this module. - */ - - $translated_verb = $verbs[$verb][1]; - - /* - * The site administrator can do things that normals cannot. - * This is restricted because it will likely cause - * an activitystreams protocol violation and the activity might - * choke in some other network and result in unnecessary - * support requests. It isn't because we're trying to be heavy-handed - * about what you can and can't do. - */ - - if(! $translated_verb) { - if(is_site_admin()) - $translated_verb = $verb; - } - - /* - * Things, objects: We do not provide definite (a, an) or indefinite (the) articles or singular/plural designators - * That needs to be specified in your thing. e.g. Mike has "a carrot", Greg wants "balls", Bob likes "the Boston Red Sox". - */ - - /* - * Future work on this module might produce more complex activities with targets, e.g. Phillip likes Karen's moustache - * and to describe other non-thing objects like channels, such as Karl wants Susan - where Susan represents a channel profile. - */ - - if((! $name) || (! $translated_verb)) - return; - - $acl = new \Zotlabs\Access\AccessControl($channel); - - if(array_key_exists('contact_allow',$_REQUEST) - || array_key_exists('group_allow',$_REQUEST) - || array_key_exists('contact_deny',$_REQUEST) - || array_key_exists('group_deny',$_REQUEST)) { - $acl->set_from_array($_REQUEST); - } - - $x = $acl->get(); - - if($term_hash) { - $t = q("select * from obj where obj_obj = '%s' and obj_channel = %d limit 1", - dbesc($term_hash), - intval(local_channel()) - ); - if(! $t) { - notice( t('Item not found.') . EOL); - return; - } - $orig_record = $t[0]; - if($photo != $orig_record['obj_imgurl']) { - delete_thing_photo($orig_record['obj_imgurl'],get_observer_hash()); - $arr = import_remote_xchan_photo($photo,get_observer_hash(),true); - if ($arr) { - $local_photo = $arr[0]; - $local_photo_type = $arr[3]; - } - else { - $local_photo = $orig_record['obj_imgurl']; - } - } - else { - $local_photo = $orig_record['obj_imgurl']; - } - if ($local_photo) { - $r = q("update obj set obj_term = '%s', obj_url = '%s', obj_imgurl = '%s', obj_edited = '%s', allow_cid = '%s', allow_gid = '%s', deny_cid = '%s', deny_gid = '%s' where obj_obj = '%s' and obj_channel = %d ", - dbesc($name), - dbesc(($url) ? $url : z_root() . '/thing/' . $term_hash), - dbesc($local_photo), - dbesc(datetime_convert()), - dbesc($x['allow_cid']), - dbesc($x['allow_gid']), - dbesc($x['deny_cid']), - dbesc($x['deny_gid']), - dbesc($term_hash), - intval(local_channel()) - ); - } - info( t('Thing updated') . EOL); - - $r = q("select * from obj where obj_channel = %d and obj_obj = '%s' limit 1", - intval(local_channel()), - dbesc($term_hash) - ); - if($r) { - Libsync::build_sync_packet(0, array('obj' => $r)); - } - - return; - } - - $sql = (($profile_guid) ? " and profile_guid = '" . dbesc($profile_guid) . "' " : " and is_default = 1 "); - $p = q("select profile_guid, is_default from profile where uid = %d $sql limit 1", - intval(local_channel()) - ); - - if($p) - $profile = $p[0]; - else - return; - - $local_photo = null; - - if($photo) { - $arr = import_remote_xchan_photo($photo,get_observer_hash(),true); - if ($arr) { - $local_photo = $arr[0]; - $local_photo_type = $arr[3]; - } - else { - $local_photo = $photo; - } - } - - - $created = datetime_convert(); - $url = (($url) ? $url : z_root() . '/thing/' . $hash); - - $r = q("insert into obj ( obj_page, obj_verb, obj_type, obj_channel, obj_obj, obj_term, obj_url, obj_imgurl, obj_created, obj_edited, allow_cid, allow_gid, deny_cid, deny_gid ) values ('%s','%s', %d, %d, '%s','%s','%s','%s','%s','%s','%s','%s','%s','%s') ", - dbesc($profile['profile_guid']), - dbesc($verb), - intval(TERM_OBJ_THING), - intval(local_channel()), - dbesc($hash), - dbesc($name), - dbesc($url), - dbesc(($photo) ? $local_photo : ''), - dbesc($created), - dbesc($created), - dbesc($x['allow_cid']), - dbesc($x['allow_gid']), - dbesc($x['deny_cid']), - dbesc($x['deny_gid']) - ); - - if(! $r) { - notice( t('Object store: failed')); - return; - } - - info( t('Thing added')); - - $r = q("select * from obj where obj_channel = %d and obj_obj = '%s' limit 1", - intval(local_channel()), - dbesc($hash) - ); - if($r) { - Libsync::build_sync_packet(0, array('obj' => $r)); - } - - if($activity) { - $arr = []; - $links = array(array('rel' => 'alternate','type' => 'text/html', 'href' => $url)); - if($local_photo) - $links[] = array('rel' => 'photo', 'type' => $local_photo_type, 'href' => $local_photo); - - $objtype = ACTIVITY_OBJ_THING; - - $obj = json_encode(array( - 'type' => $objtype, - 'id' => $url, - 'link' => $links, - 'title' => $name, - 'content' => $name - )); - - $bodyverb = str_replace('OBJ: ', '',t('OBJ: %1$s %2$s %3$s')); - - $arr['owner_xchan'] = $channel['channel_hash']; - $arr['author_xchan'] = $channel['channel_hash']; - - $arr['item_origin'] = 1; - $arr['item_wall'] = 1; - $arr['item_thread_top'] = 1; - - $ulink = '[zrl=' . $channel['xchan_url'] . ']' . $channel['channel_name'] . '[/zrl]'; - $plink = '[zrl=' . $url . ']' . $name . '[/zrl]'; - - $arr['body'] = sprintf( $bodyverb, $ulink, $translated_verb, $plink ); - - if($local_photo) - $arr['body'] .= "\n\n[zmg]" . $local_photo . "[/zmg]"; - - $arr['verb'] = $verb; - $arr['obj_type'] = $objtype; - $arr['obj'] = $obj; - - if(! $profile['is_default']) { - $arr['item_private'] = true; - $str = ''; - $r = q("select abook_xchan from abook where abook_channel = %d and abook_profile = '%s'", - intval(local_channel()), - dbesc($profile_guid) - ); - if($r) { - $arr['allow_cid'] = ''; - foreach($r as $rr) - $arr['allow_cid'] .= '<' . $rr['abook_xchan'] . '>'; - } - else - $arr['allow_cid'] = '<' . get_observer_hash() . '>'; - } - - $ret = post_activity_item($arr); - } - } - - - function get() { - - // @FIXME one problem with things is we can't share them unless we provide the channel in the url - // so we can definitively lookup the owner. - - if(argc() == 2) { - - $r = q("select obj_channel from obj where obj_type = %d and obj_obj = '%s' limit 1", - intval(TERM_OBJ_THING), - dbesc(argv(1)) - ); - if($r) - $sql_extra = permissions_sql($r[0]['obj_channel']); - - $r = q("select * from obj where obj_type = %d and obj_obj = '%s' $sql_extra limit 1", - intval(TERM_OBJ_THING), - dbesc(argv(1)) - ); - - if($r) { - return replace_macros(get_markup_template('show_thing.tpl'), array( - '$header' => t('Show Thing'), - '$edit' => t('Edit'), - '$delete' => t('Delete'), - '$canedit' => ((local_channel() && local_channel() == $r[0]['obj_channel']) ? true : false), - '$thing' => $r[0] )); - } - else { - notice( t('item not found.') . EOL); - return; - } - } - - $channel = \App::get_channel(); - - if(! (local_channel() && $channel)) { - notice( t('Permission denied.') . EOL); - return; - } - - $acl = new \Zotlabs\Access\AccessControl($channel); - $channel_acl = $acl->get(); - - $lockstate = (($acl->is_private()) ? 'lock' : 'unlock'); - - $thing_hash = ''; - - if(argc() == 3 && argv(1) === 'edit') { - $thing_hash = argv(2); - - $r = q("select * from obj where obj_type = %d and obj_obj = '%s' limit 1", - intval(TERM_OBJ_THING), - dbesc($thing_hash) - ); - - if((! $r) || ($r[0]['obj_channel'] != local_channel())) { - notice( t('Permission denied.') . EOL); - return ''; - } - - $o .= replace_macros(get_markup_template('thing_edit.tpl'),array( - '$thing_hdr' => t('Edit Thing'), - '$multiprof' => feature_enabled(local_channel(),'multi_profiles'), - '$profile_lbl' => t('Select a profile'), - '$profile_select' => contact_profile_assign($r[0]['obj_page']), - '$verb_lbl' => $channel['channel_name'], - '$verb_select' => obj_verb_selector($r[0]['obj_verb']), - '$activity' => array('activity',t('Post an activity'),true,t('Only sends to viewers of the applicable profile')), - '$thing_hash' => $thing_hash, - '$thing_lbl' => t('Name of thing e.g. something'), - '$thething' => $r[0]['obj_term'], - '$url_lbl' => t('URL of thing (optional)'), - '$theurl' => $r[0]['obj_url'], - '$img_lbl' => t('URL for photo of thing (optional)'), - '$imgurl' => $r[0]['obj_imgurl'], - '$permissions' => t('Permissions'), - '$aclselect' => populate_acl($channel_acl,false), - '$allow_cid' => acl2json($channel_acl['allow_cid']), - '$allow_gid' => acl2json($channel_acl['allow_gid']), - '$deny_cid' => acl2json($channel_acl['deny_cid']), - '$deny_gid' => acl2json($channel_acl['deny_gid']), - '$lockstate' => $lockstate, - '$submit' => t('Submit') - )); - - return $o; - } - - if(argc() == 3 && argv(1) === 'drop') { - $thing_hash = argv(2); - - $r = q("select * from obj where obj_type = %d and obj_obj = '%s' limit 1", - intval(TERM_OBJ_THING), - dbesc($thing_hash) - ); - - if((! $r) || ($r[0]['obj_channel'] != local_channel())) { - notice( t('Permission denied.') . EOL); - return ''; - } - - - delete_thing_photo($r[0]['obj_imgurl'],get_observer_hash()); - - $x = q("delete from obj where obj_obj = '%s' and obj_type = %d and obj_channel = %d", - dbesc($thing_hash), - intval(TERM_OBJ_THING), - intval(local_channel()) - ); - - $r[0]['obj_deleted'] = 1; - - Libsync::build_sync_packet(0,array('obj' => $r)); - - return $o; - } - - $o .= replace_macros(get_markup_template('thing_input.tpl'),array( - '$thing_hdr' => t('Add Thing to your Profile'), - '$multiprof' => feature_enabled(local_channel(),'multi_profiles'), - '$profile_lbl' => t('Select a profile'), - '$profile_select' => contact_profile_assign(''), - '$verb_lbl' => $channel['channel_name'], - '$activity' => array('activity',t('Post an activity'),((array_key_exists('activity',$_REQUEST)) ? $_REQUEST['activity'] : true),t('Only sends to viewers of the applicable profile')), - '$verb_select' => obj_verb_selector(), - '$thing_lbl' => t('Name of thing e.g. something'), - '$url_lbl' => t('URL of thing (optional)'), - '$img_lbl' => t('URL for photo of thing (optional)'), - '$permissions' => t('Permissions'), - '$aclselect' => populate_acl($channel_acl,false), - '$allow_cid' => acl2json($channel_acl['allow_cid']), - '$allow_gid' => acl2json($channel_acl['allow_gid']), - '$deny_cid' => acl2json($channel_acl['deny_cid']), - '$deny_gid' => acl2json($channel_acl['deny_gid']), - '$lockstate' => $lockstate, - '$submit' => t('Submit') - )); - - return $o; - } - +class Thing extends Controller +{ + + public function init() + { + + if (!local_channel()) { + return; + } + + $channel = App::get_channel(); + + if ($_SERVER['REQUEST_METHOD'] === 'GET' && argc() < 2) { + Libprofile::load($channel['channel_address']); + } + + + $term_hash = (($_REQUEST['term_hash']) ? $_REQUEST['term_hash'] : ''); + + $name = escape_tags($_REQUEST['term']); + $verb = escape_tags($_REQUEST['verb']); + $activity = intval($_REQUEST['activity']); + $profile_guid = escape_tags($_REQUEST['profile_assign']); + $url = $_REQUEST['url']; + $photo = $_REQUEST['img']; + + $hash = random_string(); + + $verbs = obj_verbs(); + + /** + * verbs: [0] = first person singular, e.g. "I want", [1] = 3rd person singular, e.g. "Bill wants" + * We use the first person form when creating an activity, but the third person for use in activities + * @FIXME There is no accounting for verb gender for languages where this is significant. We may eventually + * require obj_verbs() to provide full conjugations and specify which form to use in the $_REQUEST params to this module. + */ + + $translated_verb = $verbs[$verb][1]; + + /* + * The site administrator can do things that normals cannot. + * This is restricted because it will likely cause + * an activitystreams protocol violation and the activity might + * choke in some other network and result in unnecessary + * support requests. It isn't because we're trying to be heavy-handed + * about what you can and can't do. + */ + + if (!$translated_verb) { + if (is_site_admin()) { + $translated_verb = $verb; + } + } + + /* + * Things, objects: We do not provide definite (a, an) or indefinite (the) articles or singular/plural designators + * That needs to be specified in your thing. e.g. Mike has "a carrot", Greg wants "balls", Bob likes "the Boston Red Sox". + */ + + /* + * Future work on this module might produce more complex activities with targets, e.g. Phillip likes Karen's moustache + * and to describe other non-thing objects like channels, such as Karl wants Susan - where Susan represents a channel profile. + */ + + if ((!$name) || (!$translated_verb)) { + return; + } + + $acl = new AccessControl($channel); + + if ( + array_key_exists('contact_allow', $_REQUEST) + || array_key_exists('group_allow', $_REQUEST) + || array_key_exists('contact_deny', $_REQUEST) + || array_key_exists('group_deny', $_REQUEST) + ) { + $acl->set_from_array($_REQUEST); + } + + $x = $acl->get(); + + if ($term_hash) { + $t = q( + "select * from obj where obj_obj = '%s' and obj_channel = %d limit 1", + dbesc($term_hash), + intval(local_channel()) + ); + if (!$t) { + notice(t('Item not found.') . EOL); + return; + } + $orig_record = $t[0]; + if ($photo != $orig_record['obj_imgurl']) { + delete_thing_photo($orig_record['obj_imgurl'], get_observer_hash()); + $arr = import_remote_xchan_photo($photo, get_observer_hash(), true); + if ($arr) { + $local_photo = $arr[0]; + $local_photo_type = $arr[3]; + } else { + $local_photo = $orig_record['obj_imgurl']; + } + } else { + $local_photo = $orig_record['obj_imgurl']; + } + if ($local_photo) { + $r = q( + "update obj set obj_term = '%s', obj_url = '%s', obj_imgurl = '%s', obj_edited = '%s', allow_cid = '%s', allow_gid = '%s', deny_cid = '%s', deny_gid = '%s' where obj_obj = '%s' and obj_channel = %d ", + dbesc($name), + dbesc(($url) ? $url : z_root() . '/thing/' . $term_hash), + dbesc($local_photo), + dbesc(datetime_convert()), + dbesc($x['allow_cid']), + dbesc($x['allow_gid']), + dbesc($x['deny_cid']), + dbesc($x['deny_gid']), + dbesc($term_hash), + intval(local_channel()) + ); + } + info(t('Thing updated') . EOL); + + $r = q( + "select * from obj where obj_channel = %d and obj_obj = '%s' limit 1", + intval(local_channel()), + dbesc($term_hash) + ); + if ($r) { + Libsync::build_sync_packet(0, array('obj' => $r)); + } + + return; + } + + $sql = (($profile_guid) ? " and profile_guid = '" . dbesc($profile_guid) . "' " : " and is_default = 1 "); + $p = q( + "select profile_guid, is_default from profile where uid = %d $sql limit 1", + intval(local_channel()) + ); + + if ($p) { + $profile = $p[0]; + } else { + return; + } + + $local_photo = null; + + if ($photo) { + $arr = import_remote_xchan_photo($photo, get_observer_hash(), true); + if ($arr) { + $local_photo = $arr[0]; + $local_photo_type = $arr[3]; + } else { + $local_photo = $photo; + } + } + + + $created = datetime_convert(); + $url = (($url) ? $url : z_root() . '/thing/' . $hash); + + $r = q( + "insert into obj ( obj_page, obj_verb, obj_type, obj_channel, obj_obj, obj_term, obj_url, obj_imgurl, obj_created, obj_edited, allow_cid, allow_gid, deny_cid, deny_gid ) values ('%s','%s', %d, %d, '%s','%s','%s','%s','%s','%s','%s','%s','%s','%s') ", + dbesc($profile['profile_guid']), + dbesc($verb), + intval(TERM_OBJ_THING), + intval(local_channel()), + dbesc($hash), + dbesc($name), + dbesc($url), + dbesc(($photo) ? $local_photo : ''), + dbesc($created), + dbesc($created), + dbesc($x['allow_cid']), + dbesc($x['allow_gid']), + dbesc($x['deny_cid']), + dbesc($x['deny_gid']) + ); + + if (!$r) { + notice(t('Object store: failed')); + return; + } + + info(t('Thing added')); + + $r = q( + "select * from obj where obj_channel = %d and obj_obj = '%s' limit 1", + intval(local_channel()), + dbesc($hash) + ); + if ($r) { + Libsync::build_sync_packet(0, array('obj' => $r)); + } + + if ($activity) { + $arr = []; + $links = array(array('rel' => 'alternate', 'type' => 'text/html', 'href' => $url)); + if ($local_photo) { + $links[] = array('rel' => 'photo', 'type' => $local_photo_type, 'href' => $local_photo); + } + + $objtype = ACTIVITY_OBJ_THING; + + $obj = json_encode(array( + 'type' => $objtype, + 'id' => $url, + 'link' => $links, + 'title' => $name, + 'content' => $name + )); + + $bodyverb = str_replace('OBJ: ', '', t('OBJ: %1$s %2$s %3$s')); + + $arr['owner_xchan'] = $channel['channel_hash']; + $arr['author_xchan'] = $channel['channel_hash']; + + $arr['item_origin'] = 1; + $arr['item_wall'] = 1; + $arr['item_thread_top'] = 1; + + $ulink = '[zrl=' . $channel['xchan_url'] . ']' . $channel['channel_name'] . '[/zrl]'; + $plink = '[zrl=' . $url . ']' . $name . '[/zrl]'; + + $arr['body'] = sprintf($bodyverb, $ulink, $translated_verb, $plink); + + if ($local_photo) { + $arr['body'] .= "\n\n[zmg]" . $local_photo . "[/zmg]"; + } + + $arr['verb'] = $verb; + $arr['obj_type'] = $objtype; + $arr['obj'] = $obj; + + if (!$profile['is_default']) { + $arr['item_private'] = true; + $str = ''; + $r = q( + "select abook_xchan from abook where abook_channel = %d and abook_profile = '%s'", + intval(local_channel()), + dbesc($profile_guid) + ); + if ($r) { + $arr['allow_cid'] = ''; + foreach ($r as $rr) { + $arr['allow_cid'] .= '<' . $rr['abook_xchan'] . '>'; + } + } else { + $arr['allow_cid'] = '<' . get_observer_hash() . '>'; + } + } + + $ret = post_activity_item($arr); + } + } + + + public function get() + { + + // @FIXME one problem with things is we can't share them unless we provide the channel in the url + // so we can definitively lookup the owner. + + if (argc() == 2) { + $r = q( + "select obj_channel from obj where obj_type = %d and obj_obj = '%s' limit 1", + intval(TERM_OBJ_THING), + dbesc(argv(1)) + ); + if ($r) { + $sql_extra = permissions_sql($r[0]['obj_channel']); + } + + $r = q( + "select * from obj where obj_type = %d and obj_obj = '%s' $sql_extra limit 1", + intval(TERM_OBJ_THING), + dbesc(argv(1)) + ); + + if ($r) { + return replace_macros(get_markup_template('show_thing.tpl'), array( + '$header' => t('Show Thing'), + '$edit' => t('Edit'), + '$delete' => t('Delete'), + '$canedit' => ((local_channel() && local_channel() == $r[0]['obj_channel']) ? true : false), + '$thing' => $r[0])); + } else { + notice(t('item not found.') . EOL); + return; + } + } + + $channel = App::get_channel(); + + if (!(local_channel() && $channel)) { + notice(t('Permission denied.') . EOL); + return; + } + + $acl = new AccessControl($channel); + $channel_acl = $acl->get(); + + $lockstate = (($acl->is_private()) ? 'lock' : 'unlock'); + + $thing_hash = ''; + + if (argc() == 3 && argv(1) === 'edit') { + $thing_hash = argv(2); + + $r = q( + "select * from obj where obj_type = %d and obj_obj = '%s' limit 1", + intval(TERM_OBJ_THING), + dbesc($thing_hash) + ); + + if ((!$r) || ($r[0]['obj_channel'] != local_channel())) { + notice(t('Permission denied.') . EOL); + return ''; + } + + $o .= replace_macros(get_markup_template('thing_edit.tpl'), array( + '$thing_hdr' => t('Edit Thing'), + '$multiprof' => feature_enabled(local_channel(), 'multi_profiles'), + '$profile_lbl' => t('Select a profile'), + '$profile_select' => contact_profile_assign($r[0]['obj_page']), + '$verb_lbl' => $channel['channel_name'], + '$verb_select' => obj_verb_selector($r[0]['obj_verb']), + '$activity' => array('activity', t('Post an activity'), true, t('Only sends to viewers of the applicable profile')), + '$thing_hash' => $thing_hash, + '$thing_lbl' => t('Name of thing e.g. something'), + '$thething' => $r[0]['obj_term'], + '$url_lbl' => t('URL of thing (optional)'), + '$theurl' => $r[0]['obj_url'], + '$img_lbl' => t('URL for photo of thing (optional)'), + '$imgurl' => $r[0]['obj_imgurl'], + '$permissions' => t('Permissions'), + '$aclselect' => populate_acl($channel_acl, false), + '$allow_cid' => acl2json($channel_acl['allow_cid']), + '$allow_gid' => acl2json($channel_acl['allow_gid']), + '$deny_cid' => acl2json($channel_acl['deny_cid']), + '$deny_gid' => acl2json($channel_acl['deny_gid']), + '$lockstate' => $lockstate, + '$submit' => t('Submit') + )); + + return $o; + } + + if (argc() == 3 && argv(1) === 'drop') { + $thing_hash = argv(2); + + $r = q( + "select * from obj where obj_type = %d and obj_obj = '%s' limit 1", + intval(TERM_OBJ_THING), + dbesc($thing_hash) + ); + + if ((!$r) || ($r[0]['obj_channel'] != local_channel())) { + notice(t('Permission denied.') . EOL); + return ''; + } + + + delete_thing_photo($r[0]['obj_imgurl'], get_observer_hash()); + + $x = q( + "delete from obj where obj_obj = '%s' and obj_type = %d and obj_channel = %d", + dbesc($thing_hash), + intval(TERM_OBJ_THING), + intval(local_channel()) + ); + + $r[0]['obj_deleted'] = 1; + + Libsync::build_sync_packet(0, array('obj' => $r)); + + return $o; + } + + $o .= replace_macros(get_markup_template('thing_input.tpl'), array( + '$thing_hdr' => t('Add Thing to your Profile'), + '$multiprof' => feature_enabled(local_channel(), 'multi_profiles'), + '$profile_lbl' => t('Select a profile'), + '$profile_select' => contact_profile_assign(''), + '$verb_lbl' => $channel['channel_name'], + '$activity' => array('activity', t('Post an activity'), ((array_key_exists('activity', $_REQUEST)) ? $_REQUEST['activity'] : true), t('Only sends to viewers of the applicable profile')), + '$verb_select' => obj_verb_selector(), + '$thing_lbl' => t('Name of thing e.g. something'), + '$url_lbl' => t('URL of thing (optional)'), + '$img_lbl' => t('URL for photo of thing (optional)'), + '$permissions' => t('Permissions'), + '$aclselect' => populate_acl($channel_acl, false), + '$allow_cid' => acl2json($channel_acl['allow_cid']), + '$allow_gid' => acl2json($channel_acl['allow_gid']), + '$deny_cid' => acl2json($channel_acl['deny_cid']), + '$deny_gid' => acl2json($channel_acl['deny_gid']), + '$lockstate' => $lockstate, + '$submit' => t('Submit') + )); + + return $o; + } } diff --git a/Zotlabs/Module/Toggle_safesearch.php b/Zotlabs/Module/Toggle_safesearch.php index 2e9bc2575..cab77ba50 100644 --- a/Zotlabs/Module/Toggle_safesearch.php +++ b/Zotlabs/Module/Toggle_safesearch.php @@ -1,31 +1,37 @@ db); - $s = new OAuth2Server($storage); - $request = Request::createFromGlobals(); - $response = $s->handleTokenRequest($request); - $response->send(); - killme(); - } + if (x($_SERVER, 'HTTP_AUTHORIZATION')) { + $userpass = base64_decode(substr($_SERVER["HTTP_AUTHORIZATION"], 6)); + if (strlen($userpass)) { + list($name, $password) = explode(':', $userpass); + $_SERVER['PHP_AUTH_USER'] = $name; + $_SERVER['PHP_AUTH_PW'] = $password; + } + } + $storage = new OAuth2Storage(DBA::$dba->db); + $s = new OAuth2Server($storage); + $request = Request::createFromGlobals(); + $response = $s->handleTokenRequest($request); + $response->send(); + killme(); + } } diff --git a/Zotlabs/Module/Uexport.php b/Zotlabs/Module/Uexport.php index 7bea295a1..10a0646fa 100644 --- a/Zotlabs/Module/Uexport.php +++ b/Zotlabs/Module/Uexport.php @@ -1,78 +1,80 @@ 1) { + if (argc() > 1) { + $channel = App::get_channel(); - $channel = App::get_channel(); + if (argc() > 1 && intval(argv(1)) > 1900) { + $year = intval(argv(1)); + } - if (argc() > 1 && intval(argv(1)) > 1900) { - $year = intval(argv(1)); - } - - if (argc() > 2 && intval(argv(2)) > 0 && intval(argv(2)) <= 12) { - $month = intval(argv(2)); - } - - header('content-type: application/json'); - header('Content-Disposition: attachment; filename="' . $channel['channel_address'] . (($year) ? '-' . $year : '') . (($month) ? '-' . $month : '') . (($_REQUEST['sections']) ? '-' . $_REQUEST['sections'] : '') . '.json"' ); - - $flags = ((version_compare(PHP_VERSION,'7.2.0') >= 0) ? JSON_INVALID_UTF8_SUBSTITUTE : 0); + if (argc() > 2 && intval(argv(2)) > 0 && intval(argv(2)) <= 12) { + $month = intval(argv(2)); + } - if ($year) { - echo json_encode(identity_export_year(local_channel(),$year,$month), $flags); - killme(); - } - - if (argc() > 1 && argv(1) === 'basic') { - echo json_encode(identity_basic_export(local_channel(),$sections), $flags); - killme(); - } - - // Warning: this option may consume a lot of memory - - if(argc() > 1 && argv(1) === 'complete') { - $sections[] = 'items'; - echo json_encode(identity_basic_export(local_channel(),$sections)); - killme(); - } - } - } - - function get() { - - $y = datetime_convert('UTC',date_default_timezone_get(),'now','Y'); - - $yearurl = z_root() . '/uexport/' . $y; - $janurl = z_root() . '/uexport/' . $y . '/1'; - $impurl = '/import_items'; - $o = replace_macros(get_markup_template('uexport.tpl'), array( - '$title' => t('Export Channel'), - '$basictitle' => t('Export Channel'), - '$basic' => t('Export your basic channel information to a file. This acts as a backup of your connections, permissions, profile and basic data, which can be used to import your data to a new server hub, but does not contain your content.'), - '$fulltitle' => t('Export Content'), - '$full' => t('Export your channel information and recent content to a JSON backup that can be restored or imported to another server hub. This backs up all of your connections, permissions, profile data and several months of posts. This file may be VERY large. Please be patient - it may take several minutes for this download to begin.'), + header('content-type: application/json'); + header('Content-Disposition: attachment; filename="' . $channel['channel_address'] . (($year) ? '-' . $year : '') . (($month) ? '-' . $month : '') . (($_REQUEST['sections']) ? '-' . $_REQUEST['sections'] : '') . '.json"'); - '$by_year' => t('Export your posts from a given year.'), - - '$extra' => t('You may also export your posts and conversations for a particular year or month. Adjust the date in your browser location bar to select other dates. If the export fails (possibly due to memory exhaustion on your server hub), please try again selecting a more limited date range.'), - '$extra2' => sprintf( t('To select all posts for a given year, such as this year, visit %2$s'),$yearurl,$yearurl), - '$extra3' => sprintf( t('To select all posts for a given month, such as January of this year, visit %2$s'),$janurl,$janurl), - '$extra4' => sprintf( t('These content files may be imported or restored by visiting %2$s on any site containing your channel. For best results please import or restore these in date order (oldest first).'),$impurl,$impurl) - - )); - return $o; - } - + $flags = ((version_compare(PHP_VERSION, '7.2.0') >= 0) ? JSON_INVALID_UTF8_SUBSTITUTE : 0); + + if ($year) { + echo json_encode(identity_export_year(local_channel(), $year, $month), $flags); + killme(); + } + + if (argc() > 1 && argv(1) === 'basic') { + echo json_encode(identity_basic_export(local_channel(), $sections), $flags); + killme(); + } + + // Warning: this option may consume a lot of memory + + if (argc() > 1 && argv(1) === 'complete') { + $sections[] = 'items'; + echo json_encode(identity_basic_export(local_channel(), $sections)); + killme(); + } + } + } + + public function get() + { + + $y = datetime_convert('UTC', date_default_timezone_get(), 'now', 'Y'); + + $yearurl = z_root() . '/uexport/' . $y; + $janurl = z_root() . '/uexport/' . $y . '/1'; + $impurl = '/import_items'; + $o = replace_macros(get_markup_template('uexport.tpl'), array( + '$title' => t('Export Channel'), + '$basictitle' => t('Export Channel'), + '$basic' => t('Export your basic channel information to a file. This acts as a backup of your connections, permissions, profile and basic data, which can be used to import your data to a new server hub, but does not contain your content.'), + '$fulltitle' => t('Export Content'), + '$full' => t('Export your channel information and recent content to a JSON backup that can be restored or imported to another server hub. This backs up all of your connections, permissions, profile data and several months of posts. This file may be VERY large. Please be patient - it may take several minutes for this download to begin.'), + + '$by_year' => t('Export your posts from a given year.'), + + '$extra' => t('You may also export your posts and conversations for a particular year or month. Adjust the date in your browser location bar to select other dates. If the export fails (possibly due to memory exhaustion on your server hub), please try again selecting a more limited date range.'), + '$extra2' => sprintf(t('To select all posts for a given year, such as this year, visit %2$s'), $yearurl, $yearurl), + '$extra3' => sprintf(t('To select all posts for a given month, such as January of this year, visit %2$s'), $janurl, $janurl), + '$extra4' => sprintf(t('These content files may be imported or restored by visiting %2$s on any site containing your channel. For best results please import or restore these in date order (oldest first).'), $impurl, $impurl) + + )); + return $o; + } } diff --git a/Zotlabs/Module/Update.php b/Zotlabs/Module/Update.php index c83fc6b18..b71113504 100644 --- a/Zotlabs/Module/Update.php +++ b/Zotlabs/Module/Update.php @@ -23,59 +23,61 @@ namespace Zotlabs\Module; use App; use Zotlabs\Web\Controller; -class Update extends Controller { +class Update extends Controller +{ - function get() { + public function get() + { - $profile_uid = intval($_GET['p']); + $profile_uid = intval($_GET['p']); - // Change a profile_uid of 0 (not logged in) to (-1) for selected controllers - // as they only respond to liveUpdate with non-zero values - - if ((! $profile_uid) && in_array(argv(1),[ 'display', 'search', 'pubstream', 'home' ])) { - $profile_uid = (-1); - } + // Change a profile_uid of 0 (not logged in) to (-1) for selected controllers + // as they only respond to liveUpdate with non-zero values - if (argc() < 2) { - killme(); - } + if ((!$profile_uid) && in_array(argv(1), ['display', 'search', 'pubstream', 'home'])) { + $profile_uid = (-1); + } - // These modules don't have a completely working liveUpdate implementation currently + if (argc() < 2) { + killme(); + } - if (in_array(strtolower(argv(1)),[ 'articles', 'cards' ])) { - killme(); - } + // These modules don't have a completely working liveUpdate implementation currently - $module = "\\Zotlabs\\Module\\" . ucfirst(argv(1)); - $load = (((argc() > 2) && (argv(2) == 'load')) ? 1 : 0); + if (in_array(strtolower(argv(1)), ['articles', 'cards'])) { + killme(); + } - $mod = new $module; + $module = "\\Zotlabs\\Module\\" . ucfirst(argv(1)); + $load = (((argc() > 2) && (argv(2) == 'load')) ? 1 : 0); - // Set the state flags of the relevant module (only conversational - // modules support state flags - - if (isset($mod->profile_uid)) { - $mod->profile_uid = $profile_uid; - } - if (isset($mod->updating)) { - $mod->updating = 1; - } - if (isset($mod->loading) && $load) { - $mod->loading = 1; - } + $mod = new $module(); - header("Content-type: text/html"); + // Set the state flags of the relevant module (only conversational + // modules support state flags - // Modify the argument parameters to match what the new controller - // expects. They are currently set to what this controller expects. + if (isset($mod->profile_uid)) { + $mod->profile_uid = $profile_uid; + } + if (isset($mod->updating)) { + $mod->updating = 1; + } + if (isset($mod->loading) && $load) { + $mod->loading = 1; + } - App::$argv = [ argv(1) ]; - App::$argc = 1; + header("Content-type: text/html"); - echo "
      \r\n"; - echo $mod->get(); - echo "
      \r\n"; + // Modify the argument parameters to match what the new controller + // expects. They are currently set to what this controller expects. - killme(); - } + App::$argv = [argv(1)]; + App::$argc = 1; + + echo "
      \r\n"; + echo $mod->get(); + echo "
      \r\n"; + + killme(); + } } diff --git a/Zotlabs/Module/Userinfo.php b/Zotlabs/Module/Userinfo.php index 06c9ee81b..798b47926 100644 --- a/Zotlabs/Module/Userinfo.php +++ b/Zotlabs/Module/Userinfo.php @@ -8,13 +8,14 @@ use Zotlabs\Identity\OAuth2Storage; use Zotlabs\Identity\OAuth2Server; use OAuth2\Request; -class Userinfo extends Controller { - - function init() { - $s = new OAuth2Server(new OAuth2Storage(DBA::$dba->db)); - $request = Request::createFromGlobals(); - $s->handleUserInfoRequest($request)->send(); - killme(); - } +class Userinfo extends Controller +{ + public function init() + { + $s = new OAuth2Server(new OAuth2Storage(DBA::$dba->db)); + $request = Request::createFromGlobals(); + $s->handleUserInfoRequest($request)->send(); + killme(); + } } diff --git a/Zotlabs/Module/View.php b/Zotlabs/Module/View.php index 85497a2a4..0e9bdf234 100644 --- a/Zotlabs/Module/View.php +++ b/Zotlabs/Module/View.php @@ -1,20 +1,24 @@ 1) { - Libprofile::load(argv(1)); - } - } - - function get() { + if (argc() > 1) { + Libprofile::load(argv(1)); + } + } - // logger('request: ' . print_r($_REQUEST,true)); + public function get() + { - if (observer_prohibited()) { - notice( t('Public access denied.') . EOL); - return; - } + // logger('request: ' . print_r($_REQUEST,true)); - if (((! (is_array(App::$profile) && count(App::$profile))) || (App::$profile['hide_friends']))) { - notice( t('Permission denied.') . EOL); - return; - } - - if (! perm_is_allowed(App::$profile['uid'], get_observer_hash(),'view_contacts')) { - notice( t('Permission denied.') . EOL); - return; - } - - if (! $_REQUEST['aj']) { - $_SESSION['return_url'] = App::$query_string; - } - - $is_owner = ((local_channel() && local_channel() == App::$profile['uid']) ? true : false); - - $abook_flags = " and abook_pending = 0 and abook_self = 0 "; - $sql_extra = ''; - - if (! $is_owner) { - $abook_flags .= " and abook_hidden = 0 "; - $sql_extra = " and xchan_hidden = 0 "; - } - - $r = q("SELECT * FROM abook left join xchan on abook_xchan = xchan_hash where abook_channel = %d $abook_flags and xchan_orphan = 0 and xchan_deleted = 0 $sql_extra order by xchan_name LIMIT %d OFFSET %d ", - intval(App::$profile['uid']), - intval(App::$pager['itemspage']), - intval(App::$pager['start']) - ); - - if ((! $r) && (! $_REQUEST['aj'])) { - info( t('No connections.') . EOL ); - return $o; - } - - $contacts = []; - - foreach ($r as $rr) { + if (observer_prohibited()) { + notice(t('Public access denied.') . EOL); + return; + } - $oneway = false; - if (! their_perms_contains(App::$profile['uid'],$rr['xchan_hash'],'post_comments')) { - $oneway = true; - } - - $url = chanlink_hash($rr['xchan_hash']); - if ($url) { - $contacts[] = [ - 'id' => $rr['abook_id'], - 'archived' => (intval($rr['abook_archived']) ? true : false), - 'img_hover' => sprintf( t('Visit %1$s\'s profile [%2$s]'), $rr['xchan_name'], $rr['xchan_url']), - 'thumb' => $rr['xchan_photo_m'], - 'name' => substr($rr['xchan_name'],0,20), - 'username' => $rr['xchan_addr'], - 'link' => $url, - 'sparkle' => '', - 'itemurl' => $rr['url'], - 'network' => '', - 'oneway' => $oneway - ]; - } - } - - - if ($_REQUEST['aj']) { - if ($contacts) { - $o = replace_macros(get_markup_template('viewcontactsajax.tpl'), [ - '$contacts' => $contacts - ]); - } - else { - $o = '
      '; - } - echo $o; - killme(); - } - else { - $o .= ""; - $o .= replace_macros(get_markup_template('viewcontact_template.tpl'), [ - '$title' => t('View Connections'), - '$contacts' => $contacts, - ]); - } - - if (! $contacts) { - $o .= '
      '; - } - return $o; - } - + if (((!(is_array(App::$profile) && count(App::$profile))) || (App::$profile['hide_friends']))) { + notice(t('Permission denied.') . EOL); + return; + } + + if (!perm_is_allowed(App::$profile['uid'], get_observer_hash(), 'view_contacts')) { + notice(t('Permission denied.') . EOL); + return; + } + + if (!$_REQUEST['aj']) { + $_SESSION['return_url'] = App::$query_string; + } + + $is_owner = ((local_channel() && local_channel() == App::$profile['uid']) ? true : false); + + $abook_flags = " and abook_pending = 0 and abook_self = 0 "; + $sql_extra = ''; + + if (!$is_owner) { + $abook_flags .= " and abook_hidden = 0 "; + $sql_extra = " and xchan_hidden = 0 "; + } + + $r = q( + "SELECT * FROM abook left join xchan on abook_xchan = xchan_hash where abook_channel = %d $abook_flags and xchan_orphan = 0 and xchan_deleted = 0 $sql_extra order by xchan_name LIMIT %d OFFSET %d ", + intval(App::$profile['uid']), + intval(App::$pager['itemspage']), + intval(App::$pager['start']) + ); + + if ((!$r) && (!$_REQUEST['aj'])) { + info(t('No connections.') . EOL); + return $o; + } + + $contacts = []; + + foreach ($r as $rr) { + $oneway = false; + if (!their_perms_contains(App::$profile['uid'], $rr['xchan_hash'], 'post_comments')) { + $oneway = true; + } + + $url = chanlink_hash($rr['xchan_hash']); + if ($url) { + $contacts[] = [ + 'id' => $rr['abook_id'], + 'archived' => (intval($rr['abook_archived']) ? true : false), + 'img_hover' => sprintf(t('Visit %1$s\'s profile [%2$s]'), $rr['xchan_name'], $rr['xchan_url']), + 'thumb' => $rr['xchan_photo_m'], + 'name' => substr($rr['xchan_name'], 0, 20), + 'username' => $rr['xchan_addr'], + 'link' => $url, + 'sparkle' => '', + 'itemurl' => $rr['url'], + 'network' => '', + 'oneway' => $oneway + ]; + } + } + + + if ($_REQUEST['aj']) { + if ($contacts) { + $o = replace_macros(get_markup_template('viewcontactsajax.tpl'), [ + '$contacts' => $contacts + ]); + } else { + $o = '
      '; + } + echo $o; + killme(); + } else { + $o .= ""; + $o .= replace_macros(get_markup_template('viewcontact_template.tpl'), [ + '$title' => t('View Connections'), + '$contacts' => $contacts, + ]); + } + + if (!$contacts) { + $o .= '
      '; + } + return $o; + } } diff --git a/Zotlabs/Module/Viewsrc.php b/Zotlabs/Module/Viewsrc.php index 91d11dcdb..0636be433 100644 --- a/Zotlabs/Module/Viewsrc.php +++ b/Zotlabs/Module/Viewsrc.php @@ -1,71 +1,72 @@ 1) ? intval(argv(1)) : 0); + $json = ((argc() > 2 && argv(2) === 'json') ? true : false); + $dload = ((argc() > 2 && argv(2) === 'download') ? true : false); + + if (!local_channel()) { + notice(t('Permission denied.') . EOL); + } + + if (!$item_id) { + App::$error = 404; + notice(t('Item not found.') . EOL); + } + + $item_normal = item_normal_search(); + + if (local_channel() && $item_id) { + $r = q( + "select id, item_flags, mimetype, item_obscured, body, llink, plink from item where uid in (%d , %d) and id = %d $item_normal limit 1", + intval(local_channel()), + intval($sys['channel_id']), + intval($item_id) + ); + + if ($r) { + if (intval($r[0]['item_obscured'])) { + $dload = true; + } + + if ($dload) { + header('Content-type: ' . $r[0]['mimetype']); + header('Content-Disposition: attachment; filename="' . t('item') . '-' . $item_id . '"'); + echo $r[0]['body']; + killme(); + } + + $content = escape_tags($r[0]['body']); + $o = (($json) ? json_encode($content) : str_replace("\n", '
      ', $content)); + } + } + + $inspect = ((is_site_admin()) ? '| ' . t('inspect') . '' : EMPTY_STR); -class Viewsrc extends Controller { + if (is_ajax()) { + echo '
      '; + echo '
      id: ' . $r[0]['id'] . ' | plink | llink' . $inspect . '
      '; + echo '
      '; + echo '
      ' . $o . '
      '; + echo '
      '; + killme(); + } - function get() { - - $o = ''; - - $sys = get_sys_channel(); - - $item_id = ((argc() > 1) ? intval(argv(1)) : 0); - $json = ((argc() > 2 && argv(2) === 'json') ? true : false); - $dload = ((argc() > 2 && argv(2) === 'download') ? true : false); - - if (! local_channel()) { - notice( t('Permission denied.') . EOL); - } - - if (! $item_id) { - App::$error = 404; - notice( t('Item not found.') . EOL); - } - - $item_normal = item_normal_search(); - - if (local_channel() && $item_id) { - $r = q("select id, item_flags, mimetype, item_obscured, body, llink, plink from item where uid in (%d , %d) and id = %d $item_normal limit 1", - intval(local_channel()), - intval($sys['channel_id']), - intval($item_id) - ); - - if ($r) { - if (intval($r[0]['item_obscured'])) - $dload = true; - - if ($dload) { - header('Content-type: ' . $r[0]['mimetype']); - header('Content-Disposition: attachment; filename="' . t('item') . '-' . $item_id . '"' ); - echo $r[0]['body']; - killme(); - } - - $content = escape_tags($r[0]['body']); - $o = (($json) ? json_encode($content) : str_replace("\n",'
      ',$content)); - } - } - - $inspect = ((is_site_admin()) ? '| ' . t('inspect') . '' : EMPTY_STR); - - - if (is_ajax()) { - echo '
      '; - echo '
      id: ' . $r[0]['id'] . ' | plink | llink' . $inspect . '
      '; - echo '
      '; - echo '
      ' . $o . '
      '; - echo '
      '; - killme(); - } - - return $o; - } - - + return $o; + } } diff --git a/Zotlabs/Module/Vlists.php b/Zotlabs/Module/Vlists.php index a63ccd405..4f36d7df7 100644 --- a/Zotlabs/Module/Vlists.php +++ b/Zotlabs/Module/Vlists.php @@ -6,10 +6,12 @@ use Zotlabs\Lib\Apps; use Zotlabs\Lib\Libsync; use Zotlabs\Web\Controller; -class Vlists extends Controller { +class Vlists extends Controller +{ - function get() { + public function get() + { $desc = t('This app creates dynamic access lists corresponding to [1] all connections, [2] all ActivityPub protocol connections, and [3] all Nomad or Zot/6 protocol connections. These additional selections will be found within the Permissions setting tool.'); @@ -20,10 +22,10 @@ class Vlists extends Controller { $text2 = ''; - if (local_channel() && Apps::system_app_installed(local_channel(),'Virtual Lists')) { - $o .= $text2; + if (local_channel() && Apps::system_app_installed(local_channel(), 'Virtual Lists')) { + $o .= $text2; } - return $o; - } + return $o; + } } diff --git a/Zotlabs/Module/Vote.php b/Zotlabs/Module/Vote.php index e614b3f73..accc4abd5 100644 --- a/Zotlabs/Module/Vote.php +++ b/Zotlabs/Module/Vote.php @@ -1,4 +1,5 @@ false, 'message' => EMPTY_STR ]; + $ret = ['success' => false, 'message' => EMPTY_STR]; - $channel = App::get_channel(); + $channel = App::get_channel(); - if (! $channel) { - $ret['message'] = t('Permission denied.'); - json_return_and_die($ret); - } + if (!$channel) { + $ret['message'] = t('Permission denied.'); + json_return_and_die($ret); + } - $fetch = null; - $id = argv(1); - $response = $_REQUEST['answer']; - - if ($id) { - $fetch = q("select * from item where id = %d limit 1", - intval($id) - ); - } + $fetch = null; + $id = argv(1); + $response = $_REQUEST['answer']; - if ($fetch && $fetch[0]['obj_type'] === 'Question') { - $obj = json_decode($fetch[0]['obj'],true); + if ($id) { + $fetch = q( + "select * from item where id = %d limit 1", + intval($id) + ); + } - } - else { - $ret['message'] = t('Poll not found.'); - json_return_and_die($ret); - } + if ($fetch && $fetch[0]['obj_type'] === 'Question') { + $obj = json_decode($fetch[0]['obj'], true); + } else { + $ret['message'] = t('Poll not found.'); + json_return_and_die($ret); + } - $valid = false; - - if ($obj['oneOf']) { - foreach($obj['oneOf'] as $selection) { - // logger('selection: ' . $selection); - // logger('response: ' . $response); - if($selection['name'] && $selection['name'] === $response) { - $valid = true; - } - } - } + $valid = false; - $choices = []; - if ($obj['anyOf']) { - foreach ($obj['anyOf'] as $selection) { - $choices[] = $selection['name']; - } - foreach ($response as $res) { - if (! in_array($res,$choices)) { - $valid = false; - break; - } - $valid = true; - } - } + if ($obj['oneOf']) { + foreach ($obj['oneOf'] as $selection) { + // logger('selection: ' . $selection); + // logger('response: ' . $response); + if ($selection['name'] && $selection['name'] === $response) { + $valid = true; + } + } + } - if (! $valid) { - $ret['message'] = t('Invalid response.'); - json_return_and_die($ret); - } + $choices = []; + if ($obj['anyOf']) { + foreach ($obj['anyOf'] as $selection) { + $choices[] = $selection['name']; + } + foreach ($response as $res) { + if (!in_array($res, $choices)) { + $valid = false; + break; + } + $valid = true; + } + } - if (! is_array($response)) { - $response = [ $response ]; - } + if (!$valid) { + $ret['message'] = t('Invalid response.'); + json_return_and_die($ret); + } - foreach ($response as $res) { + if (!is_array($response)) { + $response = [$response]; + } - $item = []; + foreach ($response as $res) { + $item = []; - $item['aid'] = $channel['channel_account_id']; - $item['uid'] = $channel['channel_id']; - $item['item_origin'] = true; - $item['parent'] = $fetch[0]['id']; - $item['parent_mid'] = $fetch[0]['mid']; - $item['thr_parent'] = $fetch[0]['mid']; - $item['uuid'] = new_uuid(); - $item['mid'] = z_root() . '/item/' . $item['uuid']; - $item['verb'] = 'Create'; - $item['title'] = $res; - $item['author_xchan'] = $channel['channel_hash']; - $item['owner_xchan'] = $fetch[0]['author_xchan']; -// $item['allow_cid'] = '<' . $fetch[0]['author_xchan'] . '>'; -// $item['item_private'] = 1; - - // These two are placeholder values that will be reset after - // we encode the item - - $item['obj_type'] = 'Note'; - $item['author'] = channelx_by_n($channel['channel_id']); + $item['aid'] = $channel['channel_account_id']; + $item['uid'] = $channel['channel_id']; + $item['item_origin'] = true; + $item['parent'] = $fetch[0]['id']; + $item['parent_mid'] = $fetch[0]['mid']; + $item['thr_parent'] = $fetch[0]['mid']; + $item['uuid'] = new_uuid(); + $item['mid'] = z_root() . '/item/' . $item['uuid']; + $item['verb'] = 'Create'; + $item['title'] = $res; + $item['author_xchan'] = $channel['channel_hash']; + $item['owner_xchan'] = $fetch[0]['author_xchan']; +// $item['allow_cid'] = '<' . $fetch[0]['author_xchan'] . '>'; +// $item['item_private'] = 1; - $item['obj'] = Activity::encode_item($item,((get_config('system','activitypub', ACTIVITYPUB_ENABLED)) ? true : false)); + // These two are placeholder values that will be reset after + // we encode the item - // now reset the placeholders + $item['obj_type'] = 'Note'; + $item['author'] = channelx_by_n($channel['channel_id']); - $item['obj_type'] = 'Answer'; - unset($item['author']); - - $x = item_store($item); + $item['obj'] = Activity::encode_item($item, ((get_config('system', 'activitypub', ACTIVITYPUB_ENABLED)) ? true : false)); - retain_item($fetch[0]['id']); + // now reset the placeholders - if($x['success']) { - $itemid = $x['item_id']; - Run::Summon( [ 'Notifier', 'like', $itemid ] ); - } - - $r = q("select * from item where id = %d", - intval($itemid) - ); - if ($r) { - xchan_query($r); - $sync_item = fetch_post_tags($r); - Libsync::build_sync_packet($channel['channel_id'], [ 'item' => [ encode_item($sync_item[0],true) ] ]); - } - } - $ret['success'] = true; - $ret['message'] = t('Response submitted. Updates may not appear instantly.'); - json_return_and_die($ret); - } + $item['obj_type'] = 'Answer'; + unset($item['author']); + + $x = item_store($item); + + retain_item($fetch[0]['id']); + + if ($x['success']) { + $itemid = $x['item_id']; + Run::Summon(['Notifier', 'like', $itemid]); + } + + $r = q( + "select * from item where id = %d", + intval($itemid) + ); + if ($r) { + xchan_query($r); + $sync_item = fetch_post_tags($r); + Libsync::build_sync_packet($channel['channel_id'], ['item' => [encode_item($sync_item[0], true)]]); + } + } + $ret['success'] = true; + $ret['message'] = t('Response submitted. Updates may not appear instantly.'); + json_return_and_die($ret); + } } - - - - - - - - diff --git a/Zotlabs/Module/Wall_attach.php b/Zotlabs/Module/Wall_attach.php index 1039e29d7..98ad2eb10 100644 --- a/Zotlabs/Module/Wall_attach.php +++ b/Zotlabs/Module/Wall_attach.php @@ -1,4 +1,5 @@ 1) - $channel = channelx_by_nick(argv(1)); - } + if ($_REQUEST['api_source'] && array_key_exists('media', $_FILES)) { + $using_api = true; + } - if (! $channel) - killme(); + if ($using_api) { + require_once('include/api.php'); + if (api_user()) { + $channel = channelx_by_n(api_user()); + } + } else { + if (argc() > 1) { + $channel = channelx_by_nick(argv(1)); + } + } - $matches = []; - $partial = false; + if (!$channel) { + killme(); + } - if (array_key_exists('HTTP_CONTENT_RANGE',$_SERVER)) { - $pm = preg_match('/bytes (\d*)\-(\d*)\/(\d*)/',$_SERVER['HTTP_CONTENT_RANGE'],$matches); - if ($pm) { - // logger('Content-Range: ' . print_r($matches,true)); - $partial = true; - } - } + $matches = []; + $partial = false; - if ($partial) { - $x = save_chunk($channel,$matches[1],$matches[2],$matches[3]); - if ($x['partial']) { - header('Range: bytes=0-' . (($x['length']) ? $x['length'] - 1 : 0)); - json_return_and_die($x); - } - else { - header('Range: bytes=0-' . (($x['size']) ? $x['size'] - 1 : 0)); + if (array_key_exists('HTTP_CONTENT_RANGE', $_SERVER)) { + $pm = preg_match('/bytes (\d*)\-(\d*)\/(\d*)/', $_SERVER['HTTP_CONTENT_RANGE'], $matches); + if ($pm) { + // logger('Content-Range: ' . print_r($matches,true)); + $partial = true; + } + } - $_FILES['userfile'] = [ - 'name' => $x['name'], - 'type' => $x['type'], - 'tmp_name' => $x['tmp_name'], - 'error' => $x['error'], - 'size' => $x['size'] - ]; - } - } - else { - if (! array_key_exists('userfile',$_FILES)) { - $_FILES['userfile'] = [ - 'name' => $_FILES['files']['name'], - 'type' => $_FILES['files']['type'], - 'tmp_name' => $_FILES['files']['tmp_name'], - 'error' => $_FILES['files']['error'], - 'size' => $_FILES['files']['size'] - ]; - } - } + if ($partial) { + $x = save_chunk($channel, $matches[1], $matches[2], $matches[3]); + if ($x['partial']) { + header('Range: bytes=0-' . (($x['length']) ? $x['length'] - 1 : 0)); + json_return_and_die($x); + } else { + header('Range: bytes=0-' . (($x['size']) ? $x['size'] - 1 : 0)); - $observer = App::get_observer(); - - - $def_album = get_pconfig($channel['channel_id'],'system','photo_path'); - $def_attach = get_pconfig($channel['channel_id'],'system','attach_path'); - - $r = attach_store($channel, (($observer) ? $observer['xchan_hash'] : ''), '', [ - 'source' => 'editor', - 'visible' => 0, - 'album' => $def_album, - 'directory' => $def_attach, - 'flags' => 1, // indicates temporary permissions are created - 'allow_cid' => '<' . $channel['channel_hash'] . '>' - ]); - - if (! $r['success']) { - notice( $r['message'] . EOL); - killme(); - } + $_FILES['userfile'] = [ + 'name' => $x['name'], + 'type' => $x['type'], + 'tmp_name' => $x['tmp_name'], + 'error' => $x['error'], + 'size' => $x['size'] + ]; + } + } else { + if (!array_key_exists('userfile', $_FILES)) { + $_FILES['userfile'] = [ + 'name' => $_FILES['files']['name'], + 'type' => $_FILES['files']['type'], + 'tmp_name' => $_FILES['files']['tmp_name'], + 'error' => $_FILES['files']['error'], + 'size' => $_FILES['files']['size'] + ]; + } + } - $s = EMPTY_STR; - - if (intval($r['data']['is_photo'])) { - $s .= "\n\n" . $r['body'] . "\n\n"; - } + $observer = App::get_observer(); - $url = z_root() . '/cloud/' . $channel['channel_address'] . '/' . $r['data']['display_path']; - if (strpos($r['data']['filetype'],'video') === 0) { - for ($n = 0; $n < 15; $n ++) { - $thumb = Linkinfo::get_video_poster($url); - if ($thumb) { - break; - } - sleep(1); - continue; - } + $def_album = get_pconfig($channel['channel_id'], 'system', 'photo_path'); + $def_attach = get_pconfig($channel['channel_id'], 'system', 'attach_path'); - if ($thumb) { - $s .= "\n\n" . '[zvideo poster=\'' . $thumb . '\']' . $url . '[/zvideo]' . "\n\n"; - } - else { - $s .= "\n\n" . '[zvideo]' . $url . '[/zvideo]' . "\n\n"; - } - } - if (strpos($r['data']['filetype'],'audio') === 0) { - $s .= "\n\n" . '[zaudio]' . $url . '[/zaudio]' . "\n\n"; - } - if ($r['data']['filetype'] === 'image/svg+xml') { - $x = @file_get_contents('store/' . $channel['channel_address'] . '/' . $r['data']['os_path']); - if ($x) { - $bb = svg2bb($x); - if ($bb) { - $s .= "\n\n" . $bb; - } - else { - logger('empty return from svgbb'); - } - } - else { - logger('unable to read svg data file: ' . 'store/' . $channel['channel_address'] . '/' . $r['data']['os_path']); - } - } - if ($r['data']['filetype'] === 'text/vnd.abc' && addon_is_installed('abc')) { - $x = @file_get_contents('store/' . $channel['channel_address'] . '/' . $r['data']['os_path']); - if ($x) { - $s .= "\n\n" . '[abc]' . $x . '[/abc]'; - } - else { - logger('unable to read ABC data file: ' . 'store/' . $channel['channel_address'] . '/' . $r['data']['os_path']); - } - } - if ($r['data']['filetype'] === 'text/calendar') { - $content = @file_get_contents('store/' . $channel['channel_address'] . '/' . $r['data']['os_path']); - if ($content) { - $ev = ical_to_ev($content); - if ($ev) { - $s .= "\n\n" . format_event_bbcode($ev[0]) . "\n\n"; - } - } - } + $r = attach_store($channel, (($observer) ? $observer['xchan_hash'] : ''), '', [ + 'source' => 'editor', + 'visible' => 0, + 'album' => $def_album, + 'directory' => $def_attach, + 'flags' => 1, // indicates temporary permissions are created + 'allow_cid' => '<' . $channel['channel_hash'] . '>' + ]); - $s .= "\n\n" . '[attachment]' . $r['data']['hash'] . ',' . $r['data']['revision'] . '[/attachment]' . "\n"; + if (!$r['success']) { + notice($r['message'] . EOL); + killme(); + } - - if ($using_api) { - return $s; - } + $s = EMPTY_STR; - $result['message'] = $s; - json_return_and_die($result); - } + if (intval($r['data']['is_photo'])) { + $s .= "\n\n" . $r['body'] . "\n\n"; + } + + $url = z_root() . '/cloud/' . $channel['channel_address'] . '/' . $r['data']['display_path']; + + if (strpos($r['data']['filetype'], 'video') === 0) { + for ($n = 0; $n < 15; $n++) { + $thumb = Linkinfo::get_video_poster($url); + if ($thumb) { + break; + } + sleep(1); + continue; + } + + if ($thumb) { + $s .= "\n\n" . '[zvideo poster=\'' . $thumb . '\']' . $url . '[/zvideo]' . "\n\n"; + } else { + $s .= "\n\n" . '[zvideo]' . $url . '[/zvideo]' . "\n\n"; + } + } + if (strpos($r['data']['filetype'], 'audio') === 0) { + $s .= "\n\n" . '[zaudio]' . $url . '[/zaudio]' . "\n\n"; + } + if ($r['data']['filetype'] === 'image/svg+xml') { + $x = @file_get_contents('store/' . $channel['channel_address'] . '/' . $r['data']['os_path']); + if ($x) { + $bb = svg2bb($x); + if ($bb) { + $s .= "\n\n" . $bb; + } else { + logger('empty return from svgbb'); + } + } else { + logger('unable to read svg data file: ' . 'store/' . $channel['channel_address'] . '/' . $r['data']['os_path']); + } + } + if ($r['data']['filetype'] === 'text/vnd.abc' && addon_is_installed('abc')) { + $x = @file_get_contents('store/' . $channel['channel_address'] . '/' . $r['data']['os_path']); + if ($x) { + $s .= "\n\n" . '[abc]' . $x . '[/abc]'; + } else { + logger('unable to read ABC data file: ' . 'store/' . $channel['channel_address'] . '/' . $r['data']['os_path']); + } + } + if ($r['data']['filetype'] === 'text/calendar') { + $content = @file_get_contents('store/' . $channel['channel_address'] . '/' . $r['data']['os_path']); + if ($content) { + $ev = ical_to_ev($content); + if ($ev) { + $s .= "\n\n" . format_event_bbcode($ev[0]) . "\n\n"; + } + } + } + + $s .= "\n\n" . '[attachment]' . $r['data']['hash'] . ',' . $r['data']['revision'] . '[/attachment]' . "\n"; + + + if ($using_api) { + return $s; + } + + $result['message'] = $s; + json_return_and_die($result); + } } diff --git a/Zotlabs/Module/Wall_upload.php b/Zotlabs/Module/Wall_upload.php index 413494bfd..9e71e651e 100644 --- a/Zotlabs/Module/Wall_upload.php +++ b/Zotlabs/Module/Wall_upload.php @@ -1,55 +1,62 @@ 1) - $channel = channelx_by_nick(argv(1)); - } - - if(! $channel) { - if($using_api) - return; - notice( t('Channel not found.') . EOL); - killme(); - } - - $observer = \App::get_observer(); - - $args = array( 'source' => 'editor', 'visible' => 0, 'contact_allow' => array($channel['channel_hash'])); - - $ret = photo_upload($channel,$observer,$args); - - if(! $ret['success']) { - if($using_api) - return; - notice($ret['message']); - killme(); - } - - if($using_api) - return("\n\n" . $ret['body'] . "\n\n"); - else - echo "\n\n" . $ret['body'] . "\n\n"; - killme(); - } - + if ($using_api) { + require_once('include/api.php'); + if (api_user()) { + $channel = channelx_by_n(api_user()); + } + } else { + if (argc() > 1) { + $channel = channelx_by_nick(argv(1)); + } + } + + if (!$channel) { + if ($using_api) { + return; + } + notice(t('Channel not found.') . EOL); + killme(); + } + + $observer = App::get_observer(); + + $args = array('source' => 'editor', 'visible' => 0, 'contact_allow' => array($channel['channel_hash'])); + + $ret = photo_upload($channel, $observer, $args); + + if (!$ret['success']) { + if ($using_api) { + return; + } + notice($ret['message']); + killme(); + } + + if ($using_api) { + return ("\n\n" . $ret['body'] . "\n\n"); + } else { + echo "\n\n" . $ret['body'] . "\n\n"; + } + killme(); + } } diff --git a/Zotlabs/Module/Webfinger.php b/Zotlabs/Module/Webfinger.php index 9c5a2a220..4e195e7e5 100644 --- a/Zotlabs/Module/Webfinger.php +++ b/Zotlabs/Module/Webfinger.php @@ -1,135 +1,145 @@ 3 && $arr[2] === 'zotid') { - $hash = $arr[3]; - $channel_target = channelx_by_hash($hash); - } - } - - if (strpos($resource,'acct:') === 0) { - $channel_nickname = punify(str_replace('acct:','',$resource)); - if (strrpos($channel_nickname,'@') !== false) { - $host = punify(substr($channel_nickname,strrpos($channel_nickname,'@')+1)); + if (!$resource) { + http_status_exit(404, 'Not found'); + } - // If the webfinger address points off site, redirect to the correct site + logger('webfinger: ' . $resource, LOGGER_DEBUG); - if (strcasecmp($host, App::get_hostname())) { - goaway('https://' . $host . '/.well-known/webfinger?f=&resource=' . $resource); - } - $channel_nickname = substr($channel_nickname,0,strrpos($channel_nickname,'@')); - } - } - if (strpos($resource,'http') === 0) { - $channel_nickname = str_replace( ['~','@'],['',''],basename($resource)); - } + // Determine whether to respond as a site actor + // or a normal channel - if ($channel_nickname) { - $channel_target = channelx_by_nick($channel_nickname); - } - - if ($channel_target || $site_query) { - - $h = get_hubloc_addrs_by_hash($channel_target['channel_hash']); + $site_query = false; + if ( + strcasecmp(rtrim($resource, '/'), z_root()) === 0 + || strcasecmp($resource, 'acct:sys@' . App::get_hostname()) === 0 + || $resource === z_root() . '/channel/sys' + ) { + $site_query = true; + $result['subject'] = $resource; + $resource = z_root() . '/channel/sys'; + } - if (! isset($result['subject'])) { - $result['subject'] = $resource; - } + if (strpos($resource, 'tag:' === 0)) { + $arr = explode(':', $resource); + if (count($arr) > 3 && $arr[2] === 'zotid') { + $hash = $arr[3]; + $channel_target = channelx_by_hash($hash); + } + } - $aliases = [ - z_root() . '/channel/' . $channel_target['channel_address'], - z_root() . '/~' . $channel_target['channel_address'], - z_root() . '/@' . $channel_target['channel_address'] + if (strpos($resource, 'acct:') === 0) { + $channel_nickname = punify(str_replace('acct:', '', $resource)); + if (strrpos($channel_nickname, '@') !== false) { + $host = punify(substr($channel_nickname, strrpos($channel_nickname, '@') + 1)); - ]; - - if ($h) { - foreach ($h as $hh) { - $aliases[] = 'acct:' . $hh['hubloc_addr']; - } - } - - $result['aliases'] = []; - - $result['properties'] = [ - 'http://webfinger.net/ns/name' => $site_query ? System::get_site_name() : $channel_target['channel_name'], - 'http://xmlns.com/foaf/0.1/name' => $site_query ? System::get_site_name() : $channel_target['channel_name'], - 'https://w3id.org/security/v1#publicKeyPem' => (($site_query) ? get_config('system','pubkey') : $channel_target['xchan_pubkey']), - 'http://purl.org/zot/federation' => ((get_config('system','activitypub', ACTIVITYPUB_ENABLED)) ? 'nomad,zot6,activitypub' : 'nomad,zot6') - ]; + // If the webfinger address points off site, redirect to the correct site - if ($site_query) { - $aliases[] = z_root(); - $aliases[] = 'acct:sys@' . App::get_hostname(); - } - - foreach ($aliases as $alias) { - if ($alias !== $result['subject']) { - $result['aliases'][] = $alias; - } - } - - $result['links'] = [ + if (strcasecmp($host, App::get_hostname())) { + goaway('https://' . $host . '/.well-known/webfinger?f=&resource=' . $resource); + } + $channel_nickname = substr($channel_nickname, 0, strrpos($channel_nickname, '@')); + } + } + if (strpos($resource, 'http') === 0) { + $channel_nickname = str_replace(['~', '@'], ['', ''], basename($resource)); + } - [ - 'rel' => 'http://webfinger.net/rel/avatar', - 'type' => $channel_target['xchan_photo_mimetype'], - 'href' => $channel_target['xchan_photo_l'] - ], + if ($channel_nickname) { + $channel_target = channelx_by_nick($channel_nickname); + } - [ - 'rel' => 'http://webfinger.net/rel/blog', - 'href' => z_root() . '/channel/' . $channel_target['channel_address'], - ], - - [ - 'rel' => 'http://openid.net/specs/connect/1.0/issuer', - 'href' => z_root() - ], + if ($channel_target || $site_query) { + $h = get_hubloc_addrs_by_hash($channel_target['channel_hash']); + + if (!isset($result['subject'])) { + $result['subject'] = $resource; + } + + $aliases = [ + z_root() . '/channel/' . $channel_target['channel_address'], + z_root() . '/~' . $channel_target['channel_address'], + z_root() . '/@' . $channel_target['channel_address'] + + ]; + + if ($h) { + foreach ($h as $hh) { + $aliases[] = 'acct:' . $hh['hubloc_addr']; + } + } + + $result['aliases'] = []; + + $result['properties'] = [ + 'http://webfinger.net/ns/name' => $site_query ? System::get_site_name() : $channel_target['channel_name'], + 'http://xmlns.com/foaf/0.1/name' => $site_query ? System::get_site_name() : $channel_target['channel_name'], + 'https://w3id.org/security/v1#publicKeyPem' => (($site_query) ? get_config('system', 'pubkey') : $channel_target['xchan_pubkey']), + 'http://purl.org/zot/federation' => ((get_config('system', 'activitypub', ACTIVITYPUB_ENABLED)) ? 'nomad,zot6,activitypub' : 'nomad,zot6') + ]; + + if ($site_query) { + $aliases[] = z_root(); + $aliases[] = 'acct:sys@' . App::get_hostname(); + } + + foreach ($aliases as $alias) { + if ($alias !== $result['subject']) { + $result['aliases'][] = $alias; + } + } + + $result['links'] = [ + + [ + 'rel' => 'http://webfinger.net/rel/avatar', + 'type' => $channel_target['xchan_photo_mimetype'], + 'href' => $channel_target['xchan_photo_l'] + ], + + [ + 'rel' => 'http://webfinger.net/rel/blog', + 'href' => z_root() . '/channel/' . $channel_target['channel_address'], + ], + + [ + 'rel' => 'http://openid.net/specs/connect/1.0/issuer', + 'href' => z_root() + ], + + [ + 'rel' => 'http://purl.org/zot/protocol/6.0', + 'type' => 'application/x-zot+json', + 'href' => (($site_query) ? z_root() : z_root() . '/channel/' . $channel_target['channel_address']), + ], [ 'rel' => 'http://purl.org/nomad', @@ -143,40 +153,40 @@ class Webfinger extends Controller { 'href' => z_root() . '/owa' ], - [ - 'rel' => 'http://purl.org/zot/protocol/6.0', - 'type' => 'application/x-zot+json', - 'href' => (($site_query) ? z_root() : z_root() . '/channel/' . $channel_target['channel_address']), - ], + [ + 'rel' => 'http://purl.org/openwebauth/v1', + 'type' => 'application/x-zot+json', + 'href' => z_root() . '/owa' + ], - [ - 'rel' => 'self', - 'type' => 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"', - 'href' => (($site_query) ? z_root() : z_root() . '/channel/' . $channel_target['channel_address']) - ], - - [ - 'rel' => 'self', - 'type' => 'application/activity+json', - 'href' => (($site_query) ? z_root() : z_root() . '/channel/' . $channel_target['channel_address']) - ], + [ + 'rel' => 'self', + 'type' => 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"', + 'href' => (($site_query) ? z_root() : z_root() . '/channel/' . $channel_target['channel_address']) + ], - [ - 'rel' => 'http://ostatus.org/schema/1.0/subscribe', - 'template' => z_root() . '/follow?url={uri}' - ], - ]; - } + [ + 'rel' => 'self', + 'type' => 'application/activity+json', + 'href' => (($site_query) ? z_root() : z_root() . '/channel/' . $channel_target['channel_address']) + ], - if (! $result) { - header($_SERVER['SERVER_PROTOCOL'] . ' ' . 400 . ' ' . 'Bad Request'); - killme(); - } - - $arr = [ 'channel' => $channel_target, 'request' => $_REQUEST, 'result' => $result ]; - call_hooks('webfinger',$arr); + [ + 'rel' => 'http://ostatus.org/schema/1.0/subscribe', + 'template' => z_root() . '/follow?url={uri}' + ], + ]; + } + + if (!$result) { + header($_SERVER['SERVER_PROTOCOL'] . ' ' . 400 . ' ' . 'Bad Request'); + killme(); + } + + $arr = ['channel' => $channel_target, 'request' => $_REQUEST, 'result' => $result]; + call_hooks('webfinger', $arr); - json_return_and_die($arr['result'],'application/jrd+json',true); - } + json_return_and_die($arr['result'], 'application/jrd+json', true); + } } diff --git a/Zotlabs/Module/Webpages.php b/Zotlabs/Module/Webpages.php index 53ab1d006..2394e9017 100644 --- a/Zotlabs/Module/Webpages.php +++ b/Zotlabs/Module/Webpages.php @@ -1,4 +1,5 @@ 1 && argv(1) === 'sys' && is_site_admin()) { - $sys = get_sys_channel(); - if($sys && intval($sys['channel_id'])) { - App::$is_sys = true; - } - } - - if(argc() > 1) - $which = argv(1); - else - return; - - Libprofile::load($which); - - } - - - function get() { - - if(! App::$profile) { - notice( t('Requested profile is not available.') . EOL ); - App::$error = 404; - return; - } + public function init() + { - if(! Apps::system_app_installed(App::$profile_uid, 'Webpages')) { - //Do not display any associated widgets at this point - App::$pdl = ''; + if (argc() > 1 && argv(1) === 'sys' && is_site_admin()) { + $sys = get_sys_channel(); + if ($sys && intval($sys['channel_id'])) { + App::$is_sys = true; + } + } - $o = 'Webpages App (Not Installed):
      '; - $o .= t('Provide managed web pages on your channel'); - return $o; - } + if (argc() > 1) { + $which = argv(1); + } else { + return; + } - nav_set_selected('Webpages'); + Libprofile::load($which); + } - $which = argv(1); - - $_SESSION['return_url'] = App::$query_string; - - $uid = local_channel(); - $owner = 0; - $observer = App::get_observer(); - - $channel = App::get_channel(); - switch ($_SESSION['action']) { - case 'import': - $_SESSION['action'] = null; - $o .= replace_macros(get_markup_template('webpage_import.tpl'), array( - '$title' => t('Import Webpage Elements'), - '$importbtn' => t('Import selected'), - '$action' => 'import', - '$pages' => $_SESSION['pages'], - '$layouts' => $_SESSION['layouts'], - '$blocks' => $_SESSION['blocks'], - )); - return $o; - - case 'importselected': - $_SESSION['action'] = null; - break; - case 'export_select_list': - $_SESSION['action'] = null; - if(!$uid) { - $_SESSION['export'] = null; - break; - } - require_once('include/import.php'); - - $pages = get_webpage_elements($channel, 'pages'); - $layouts = get_webpage_elements($channel, 'layouts'); - $blocks = get_webpage_elements($channel, 'blocks'); - $o .= replace_macros(get_markup_template('webpage_export_list.tpl'), array( - '$title' => t('Export Webpage Elements'), - '$exportbtn' => t('Export selected'), - '$action' => $_SESSION['export'], // value should be 'zipfile' or 'cloud' - '$pages' => $pages['pages'], - '$layouts' => $layouts['layouts'], - '$blocks' => $blocks['blocks'], - )); - $_SESSION['export'] = null; - return $o; - - default : - $_SESSION['action'] = null; - break; - } - - - if(App::$is_sys && is_site_admin()) { - $sys = get_sys_channel(); - if($sys && intval($sys['channel_id'])) { - $uid = $owner = intval($sys['channel_id']); - $channel = $sys; - $observer = $sys; - } - } - - if(! $owner) { - // Figure out who the page owner is. - $r = q("select channel_id from channel where channel_address = '%s'", - dbesc($which) - ); - if($r) { - $owner = intval($r[0]['channel_id']); - } - } - - $ob_hash = (($observer) ? $observer['xchan_hash'] : ''); - - $perms = get_all_perms($owner,$ob_hash); - - if(! $perms['write_pages']) { - notice( t('Permission denied.') . EOL); - return; - } - - $mimetype = (($_REQUEST['mimetype']) ? $_REQUEST['mimetype'] : get_pconfig($owner,'system','page_mimetype')); - - $layout = (($_REQUEST['layout']) ? $_REQUEST['layout'] : get_pconfig($owner,'system','page_layout')); - - // Create a status editor (for now - we'll need a WYSIWYG eventually) to create pages - // Nickname is set to the observers xchan, and profile_uid to the owner's. - // This lets you post pages at other people's channels. - - if((! $channel) && ($uid) && ($uid == App::$profile_uid)) { - $channel = App::get_channel(); - } - if($channel) { - $channel_acl = array( - 'allow_cid' => $channel['channel_allow_cid'], - 'allow_gid' => $channel['channel_allow_gid'], - 'deny_cid' => $channel['channel_deny_cid'], - 'deny_gid' => $channel['channel_deny_gid'] - ); - } - else { - $channel_acl = [ 'allow_cid' => '', 'allow_gid' => '', 'deny_cid' => '', 'deny_gid' => '' ]; - } - + public function get() + { - $is_owner = ($uid && $uid == $owner); + if (!App::$profile) { + notice(t('Requested profile is not available.') . EOL); + App::$error = 404; + return; + } - $o = ''; - - $x = array( - 'webpage' => ITEM_TYPE_WEBPAGE, - 'is_owner' => true, - 'nickname' => App::$profile['channel_address'], - 'lockstate' => (($channel['channel_allow_cid'] || $channel['channel_allow_gid'] || $channel['channel_deny_cid'] || $channel['channel_deny_gid']) ? 'lock' : 'unlock'), - 'acl' => (($is_owner) ? populate_acl($channel_acl,false, PermissionDescription::fromGlobalPermission('view_pages')) : ''), - 'permissions' => $channel_acl, - 'showacl' => (($is_owner) ? true : false), - 'visitor' => true, - 'hide_location' => true, - 'hide_voting' => true, - 'profile_uid' => intval($owner), - 'mimetype' => $mimetype, - 'mimeselect' => true, - 'layout' => $layout, - 'layoutselect' => true, - 'expanded' => true, - 'novoting'=> true, - 'bbco_autocomplete' => 'bbcode', - 'bbcode' => true - ); - - if($_REQUEST['title']) - $x['title'] = $_REQUEST['title']; - if($_REQUEST['body']) - $x['body'] = $_REQUEST['body']; - if($_REQUEST['pagetitle']) - $x['pagetitle'] = $_REQUEST['pagetitle']; - - - // 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. - - - $sql_extra = item_permissions_sql($owner); - - $r = q("select * from iconfig left join item on iconfig.iid = item.id + if (!Apps::system_app_installed(App::$profile_uid, 'Webpages')) { + //Do not display any associated widgets at this point + App::$pdl = ''; + + $o = 'Webpages App (Not Installed):
      '; + $o .= t('Provide managed web pages on your channel'); + return $o; + } + + nav_set_selected('Webpages'); + + $which = argv(1); + + $_SESSION['return_url'] = App::$query_string; + + $uid = local_channel(); + $owner = 0; + $observer = App::get_observer(); + + $channel = App::get_channel(); + + switch ($_SESSION['action']) { + case 'import': + $_SESSION['action'] = null; + $o .= replace_macros(get_markup_template('webpage_import.tpl'), array( + '$title' => t('Import Webpage Elements'), + '$importbtn' => t('Import selected'), + '$action' => 'import', + '$pages' => $_SESSION['pages'], + '$layouts' => $_SESSION['layouts'], + '$blocks' => $_SESSION['blocks'], + )); + return $o; + + case 'importselected': + $_SESSION['action'] = null; + break; + case 'export_select_list': + $_SESSION['action'] = null; + if (!$uid) { + $_SESSION['export'] = null; + break; + } + require_once('include/import.php'); + + $pages = get_webpage_elements($channel, 'pages'); + $layouts = get_webpage_elements($channel, 'layouts'); + $blocks = get_webpage_elements($channel, 'blocks'); + $o .= replace_macros(get_markup_template('webpage_export_list.tpl'), array( + '$title' => t('Export Webpage Elements'), + '$exportbtn' => t('Export selected'), + '$action' => $_SESSION['export'], // value should be 'zipfile' or 'cloud' + '$pages' => $pages['pages'], + '$layouts' => $layouts['layouts'], + '$blocks' => $blocks['blocks'], + )); + $_SESSION['export'] = null; + return $o; + + default: + $_SESSION['action'] = null; + break; + } + + + if (App::$is_sys && is_site_admin()) { + $sys = get_sys_channel(); + if ($sys && intval($sys['channel_id'])) { + $uid = $owner = intval($sys['channel_id']); + $channel = $sys; + $observer = $sys; + } + } + + if (!$owner) { + // Figure out who the page owner is. + $r = q( + "select channel_id from channel where channel_address = '%s'", + dbesc($which) + ); + if ($r) { + $owner = intval($r[0]['channel_id']); + } + } + + $ob_hash = (($observer) ? $observer['xchan_hash'] : ''); + + $perms = get_all_perms($owner, $ob_hash); + + if (!$perms['write_pages']) { + notice(t('Permission denied.') . EOL); + return; + } + + $mimetype = (($_REQUEST['mimetype']) ? $_REQUEST['mimetype'] : get_pconfig($owner, 'system', 'page_mimetype')); + + $layout = (($_REQUEST['layout']) ? $_REQUEST['layout'] : get_pconfig($owner, 'system', 'page_layout')); + + // Create a status editor (for now - we'll need a WYSIWYG eventually) to create pages + // Nickname is set to the observers xchan, and profile_uid to the owner's. + // This lets you post pages at other people's channels. + + if ((!$channel) && ($uid) && ($uid == App::$profile_uid)) { + $channel = App::get_channel(); + } + if ($channel) { + $channel_acl = array( + 'allow_cid' => $channel['channel_allow_cid'], + 'allow_gid' => $channel['channel_allow_gid'], + 'deny_cid' => $channel['channel_deny_cid'], + 'deny_gid' => $channel['channel_deny_gid'] + ); + } else { + $channel_acl = ['allow_cid' => '', 'allow_gid' => '', 'deny_cid' => '', 'deny_gid' => '']; + } + + + $is_owner = ($uid && $uid == $owner); + + $o = ''; + + $x = array( + 'webpage' => ITEM_TYPE_WEBPAGE, + 'is_owner' => true, + 'nickname' => App::$profile['channel_address'], + 'lockstate' => (($channel['channel_allow_cid'] || $channel['channel_allow_gid'] || $channel['channel_deny_cid'] || $channel['channel_deny_gid']) ? 'lock' : 'unlock'), + 'acl' => (($is_owner) ? populate_acl($channel_acl, false, PermissionDescription::fromGlobalPermission('view_pages')) : ''), + 'permissions' => $channel_acl, + 'showacl' => (($is_owner) ? true : false), + 'visitor' => true, + 'hide_location' => true, + 'hide_voting' => true, + 'profile_uid' => intval($owner), + 'mimetype' => $mimetype, + 'mimeselect' => true, + 'layout' => $layout, + 'layoutselect' => true, + 'expanded' => true, + 'novoting' => true, + 'bbco_autocomplete' => 'bbcode', + 'bbcode' => true + ); + + if ($_REQUEST['title']) { + $x['title'] = $_REQUEST['title']; + } + if ($_REQUEST['body']) { + $x['body'] = $_REQUEST['body']; + } + if ($_REQUEST['pagetitle']) { + $x['pagetitle'] = $_REQUEST['pagetitle']; + } + + + // 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. + + + $sql_extra = item_permissions_sql($owner); + + $r = q( + "select * from iconfig left join item on iconfig.iid = item.id where item.uid = %d and iconfig.cat = 'system' and iconfig.k = 'WEBPAGE' and item_type = %d $sql_extra order by item.created desc", - intval($owner), - intval(ITEM_TYPE_WEBPAGE) - ); + intval($owner), + intval(ITEM_TYPE_WEBPAGE) + ); - if(! $r) - $x['pagetitle'] = 'home'; + if (!$r) { + $x['pagetitle'] = 'home'; + } - $editor = status_editor($x); + $editor = status_editor($x); - $pages = null; - - if($r) { - $pages = []; - foreach($r as $rr) { - unobscure($rr); - - $lockstate = (($rr['allow_cid'] || $rr['allow_gid'] || $rr['deny_cid'] || $rr['deny_gid']) ? 'lock' : 'unlock'); - - $element_arr = array( - 'type' => 'webpage', - 'title' => $rr['title'], - 'body' => $rr['body'], - 'created' => $rr['created'], - 'edited' => $rr['edited'], - 'mimetype' => $rr['mimetype'], - 'pageurl' => str_replace('%2f','/',$rr['v']), - 'pagetitle' => urldecode($rr['v']), - 'mid' => $rr['mid'], - 'layout_mid' => $rr['layout_mid'] - ); - $pages[$rr['iid']][] = array( - 'url' => $rr['iid'], - 'pageurl' => str_replace('%2f','/',$rr['v']), - 'pagetitle' => urldecode($rr['v']), - 'title' => $rr['title'], - 'created' => datetime_convert('UTC',date_default_timezone_get(),$rr['created']), - 'edited' => datetime_convert('UTC',date_default_timezone_get(),$rr['edited']), - 'bb_element' => '[element]' . base64url_encode(json_encode($element_arr)) . '[/element]', - 'lockstate' => $lockstate - ); - } - } - - - //Build the base URL for edit links - $url = z_root() . '/editwebpage/' . $which; - - $o .= replace_macros(get_markup_template('webpagelist.tpl'), array( - '$listtitle' => t('Webpages'), - '$baseurl' => $url, - '$create' => t('Create'), - '$edit' => t('Edit'), - '$share' => t('Share'), - '$delete' => t('Delete'), - '$pages' => $pages, - '$channel' => $which, - '$editor' => $editor, - '$view' => t('View'), - '$preview' => t('Preview'), - '$actions_txt' => t('Actions'), - '$pagelink_txt' => t('Page Link'), - '$title_txt' => t('Page Title'), - '$created_txt' => t('Created'), - '$edited_txt' => t('Edited') - )); - - return $o; - } - - function post() { - $action = $_REQUEST['action']; - if( $action ){ - switch ($action) { - case 'scan': - - // the state of this variable tracks whether website files have been scanned (null, true, false) - $cloud = null; - - // Website files are to be imported from an uploaded zip file - if(($_FILES) && array_key_exists('zip_file',$_FILES) && isset($_POST['w_upload'])) { - $source = $_FILES["zip_file"]["tmp_name"]; - $type = $_FILES["zip_file"]["type"]; - $okay = false; - $accepted_types = array('application/zip', 'application/x-zip-compressed', 'multipart/x-zip', 'application/x-compressed'); - foreach ($accepted_types as $mime_type) { - if ($mime_type == $type) { - $okay = true; - break; - } - } - if(!$okay) { - notice( t('Invalid file type.') . EOL); - return; - } - $zip = new ZipArchive(); - if ($zip->open($source) === true) { - $tmp_folder_name = random_string(5); - $website = dirname($source) . '/' . $tmp_folder_name; - $zip->extractTo($website); // change this to the correct site path - $zip->close(); - @unlink($source); // delete the compressed file now that the content has been extracted - $cloud = false; - } else { - notice( t('Error opening zip file') . EOL); - return null; - } - } + $pages = null; - // Website files are to be imported from the channel cloud files - if (($_POST) && array_key_exists('path',$_POST) && isset($_POST['cloudsubmit'])) { - - $channel = App::get_channel(); - $dirpath = get_dirpath_by_cloudpath($channel, $_POST['path']); - if(!$dirpath) { - notice( t('Invalid folder path.') . EOL); - return null; - } - $cloud = true; - - } - - // If the website files were uploaded or specified in the cloud files, then $cloud - // should be either true or false - if ($cloud !== null) { - require_once('include/import.php'); - $elements = []; - if($cloud) { - $path = $_POST['path']; - } else { - $path = $website; - } - $elements['pages'] = scan_webpage_elements($path, 'page', $cloud); - $elements['layouts'] = scan_webpage_elements($path, 'layout', $cloud); - $elements['blocks'] = scan_webpage_elements($path, 'block', $cloud); - $_SESSION['blocks'] = $elements['blocks']; - $_SESSION['layouts'] = $elements['layouts']; - $_SESSION['pages'] = $elements['pages']; - if(!(empty($elements['pages']) && empty($elements['blocks']) && empty($elements['layouts']))) { - //info( t('Webpages elements detected.') . EOL); - $_SESSION['action'] = 'import'; - } else { - notice( t('No webpage elements detected.') . EOL); - $_SESSION['action'] = null; - } - - } - - // If the website elements were imported from a zip file, delete the temporary decompressed files - if ($cloud === false && $website && $elements) { - $_SESSION['tempimportpath'] = $website; - //rrmdir($website); // Delete the temporary decompressed files - } - - break; - - case 'importselected': - require_once('include/import.php'); - $channel = App::get_channel(); - - // Import layout first so that pages that reference new layouts will find - // the mid of layout items in the database - - // Obtain the user-selected layouts to import and import them - $checkedlayouts = $_POST['layout']; - $layouts = []; - if (!empty($checkedlayouts)) { - foreach ($checkedlayouts as $name) { - foreach ($_SESSION['layouts'] as &$layout) { - if ($layout['name'] === $name) { - $layout['import'] = 1; - $layoutstoimport[] = $layout; - } - } - } - foreach ($layoutstoimport as $elementtoimport) { - $layouts[] = import_webpage_element($elementtoimport, $channel, 'layout'); - } - } - $_SESSION['import_layouts'] = $layouts; - - // Obtain the user-selected blocks to import and import them - $checkedblocks = $_POST['block']; - $blocks = []; - if (!empty($checkedblocks)) { - foreach ($checkedblocks as $name) { - foreach ($_SESSION['blocks'] as &$block) { - if ($block['name'] === $name) { - $block['import'] = 1; - $blockstoimport[] = $block; - } - } - } - foreach ($blockstoimport as $elementtoimport) { - $blocks[] = import_webpage_element($elementtoimport, $channel, 'block'); - } - } - $_SESSION['import_blocks'] = $blocks; - - // Obtain the user-selected pages to import and import them - $checkedpages = $_POST['page']; + if ($r) { $pages = []; - if (!empty($checkedpages)) { - foreach ($checkedpages as $pagelink) { - foreach ($_SESSION['pages'] as &$page) { - if ($page['pagelink'] === $pagelink) { - $page['import'] = 1; - $pagestoimport[] = $page; + foreach ($r as $rr) { + unobscure($rr); + + $lockstate = (($rr['allow_cid'] || $rr['allow_gid'] || $rr['deny_cid'] || $rr['deny_gid']) ? 'lock' : 'unlock'); + + $element_arr = array( + 'type' => 'webpage', + 'title' => $rr['title'], + 'body' => $rr['body'], + 'created' => $rr['created'], + 'edited' => $rr['edited'], + 'mimetype' => $rr['mimetype'], + 'pageurl' => str_replace('%2f', '/', $rr['v']), + 'pagetitle' => urldecode($rr['v']), + 'mid' => $rr['mid'], + 'layout_mid' => $rr['layout_mid'] + ); + $pages[$rr['iid']][] = array( + 'url' => $rr['iid'], + 'pageurl' => str_replace('%2f', '/', $rr['v']), + 'pagetitle' => urldecode($rr['v']), + 'title' => $rr['title'], + 'created' => datetime_convert('UTC', date_default_timezone_get(), $rr['created']), + 'edited' => datetime_convert('UTC', date_default_timezone_get(), $rr['edited']), + 'bb_element' => '[element]' . base64url_encode(json_encode($element_arr)) . '[/element]', + 'lockstate' => $lockstate + ); + } + } + + + //Build the base URL for edit links + $url = z_root() . '/editwebpage/' . $which; + + $o .= replace_macros(get_markup_template('webpagelist.tpl'), array( + '$listtitle' => t('Webpages'), + '$baseurl' => $url, + '$create' => t('Create'), + '$edit' => t('Edit'), + '$share' => t('Share'), + '$delete' => t('Delete'), + '$pages' => $pages, + '$channel' => $which, + '$editor' => $editor, + '$view' => t('View'), + '$preview' => t('Preview'), + '$actions_txt' => t('Actions'), + '$pagelink_txt' => t('Page Link'), + '$title_txt' => t('Page Title'), + '$created_txt' => t('Created'), + '$edited_txt' => t('Edited') + )); + + return $o; + } + + public function post() + { + $action = $_REQUEST['action']; + if ($action) { + switch ($action) { + case 'scan': + // the state of this variable tracks whether website files have been scanned (null, true, false) + $cloud = null; + + // Website files are to be imported from an uploaded zip file + if (($_FILES) && array_key_exists('zip_file', $_FILES) && isset($_POST['w_upload'])) { + $source = $_FILES["zip_file"]["tmp_name"]; + $type = $_FILES["zip_file"]["type"]; + $okay = false; + $accepted_types = array('application/zip', 'application/x-zip-compressed', 'multipart/x-zip', 'application/x-compressed'); + foreach ($accepted_types as $mime_type) { + if ($mime_type == $type) { + $okay = true; + break; + } + } + if (!$okay) { + notice(t('Invalid file type.') . EOL); + return; + } + $zip = new ZipArchive(); + if ($zip->open($source) === true) { + $tmp_folder_name = random_string(5); + $website = dirname($source) . '/' . $tmp_folder_name; + $zip->extractTo($website); // change this to the correct site path + $zip->close(); + @unlink($source); // delete the compressed file now that the content has been extracted + $cloud = false; + } else { + notice(t('Error opening zip file') . EOL); + return null; } } - } - foreach ($pagestoimport as $elementtoimport) { - $pages[] = import_webpage_element($elementtoimport, $channel, 'page'); - } - } - $_SESSION['import_pages'] = $pages; - if(!(empty($_SESSION['import_pages']) && empty($_SESSION['import_blocks']) && empty($_SESSION['import_layouts']))) { - info( t('Import complete.') . EOL); - } - if(isset($_SESSION['tempimportpath'])) { - rrmdir($_SESSION['tempimportpath']); // Delete the temporary decompressed files - unset($_SESSION['tempimportpath']); - } - break; - - case 'exportzipfile': - - if(isset($_POST['w_download'])) { - $_SESSION['action'] = 'export_select_list'; - $_SESSION['export'] = 'zipfile'; - if(isset($_POST['zipfilename']) && $_POST['zipfilename'] !== '') { - $filename = filter_var($_POST['zipfilename'], FILTER_SANITIZE_ENCODED); - } else { - $filename = 'website.zip'; - } - $_SESSION['zipfilename'] = $filename; - - } - - break; - - case 'exportcloud': - if(isset($_POST['exportcloudpath']) && $_POST['exportcloudpath'] !== '') { - $_SESSION['action'] = 'export_select_list'; - $_SESSION['export'] = 'cloud'; - $_SESSION['exportcloudpath'] = filter_var($_POST['exportcloudpath'], FILTER_SANITIZE_ENCODED); - } - - break; - - case 'cloud': - case 'zipfile': - - $channel = App::get_channel(); - - $tmp_folder_name = random_string(10); - $zip_folder_name = random_string(10); - $zip_filename = $_SESSION['zipfilename']; - $tmp_folderpath = '/tmp/' . $tmp_folder_name; - $zip_folderpath = '/tmp/' . $zip_folder_name; - if (!mkdir($zip_folderpath, 0770, false)) { - logger('Error creating zip file export folder: ' . $zip_folderpath, LOGGER_NORMAL); - json_return_and_die(array('message' => 'Error creating zip file export folder')); - } - $zip_filepath = '/tmp/' . $zip_folder_name . '/' . $zip_filename; - - $checkedblocks = $_POST['block']; - $blocks = []; - if (!empty($checkedblocks)) { - foreach ($checkedblocks as $mid) { - $b = q("select iconfig.v, iconfig.k, mimetype, title, body from iconfig + + // Website files are to be imported from the channel cloud files + if (($_POST) && array_key_exists('path', $_POST) && isset($_POST['cloudsubmit'])) { + $channel = App::get_channel(); + $dirpath = get_dirpath_by_cloudpath($channel, $_POST['path']); + if (!$dirpath) { + notice(t('Invalid folder path.') . EOL); + return null; + } + $cloud = true; + } + + // If the website files were uploaded or specified in the cloud files, then $cloud + // should be either true or false + if ($cloud !== null) { + require_once('include/import.php'); + $elements = []; + if ($cloud) { + $path = $_POST['path']; + } else { + $path = $website; + } + $elements['pages'] = scan_webpage_elements($path, 'page', $cloud); + $elements['layouts'] = scan_webpage_elements($path, 'layout', $cloud); + $elements['blocks'] = scan_webpage_elements($path, 'block', $cloud); + $_SESSION['blocks'] = $elements['blocks']; + $_SESSION['layouts'] = $elements['layouts']; + $_SESSION['pages'] = $elements['pages']; + if (!(empty($elements['pages']) && empty($elements['blocks']) && empty($elements['layouts']))) { + //info( t('Webpages elements detected.') . EOL); + $_SESSION['action'] = 'import'; + } else { + notice(t('No webpage elements detected.') . EOL); + $_SESSION['action'] = null; + } + } + + // If the website elements were imported from a zip file, delete the temporary decompressed files + if ($cloud === false && $website && $elements) { + $_SESSION['tempimportpath'] = $website; + //rrmdir($website); // Delete the temporary decompressed files + } + + break; + + case 'importselected': + require_once('include/import.php'); + $channel = App::get_channel(); + + // Import layout first so that pages that reference new layouts will find + // the mid of layout items in the database + + // Obtain the user-selected layouts to import and import them + $checkedlayouts = $_POST['layout']; + $layouts = []; + if (!empty($checkedlayouts)) { + foreach ($checkedlayouts as $name) { + foreach ($_SESSION['layouts'] as &$layout) { + if ($layout['name'] === $name) { + $layout['import'] = 1; + $layoutstoimport[] = $layout; + } + } + } + foreach ($layoutstoimport as $elementtoimport) { + $layouts[] = import_webpage_element($elementtoimport, $channel, 'layout'); + } + } + $_SESSION['import_layouts'] = $layouts; + + // Obtain the user-selected blocks to import and import them + $checkedblocks = $_POST['block']; + $blocks = []; + if (!empty($checkedblocks)) { + foreach ($checkedblocks as $name) { + foreach ($_SESSION['blocks'] as &$block) { + if ($block['name'] === $name) { + $block['import'] = 1; + $blockstoimport[] = $block; + } + } + } + foreach ($blockstoimport as $elementtoimport) { + $blocks[] = import_webpage_element($elementtoimport, $channel, 'block'); + } + } + $_SESSION['import_blocks'] = $blocks; + + // Obtain the user-selected pages to import and import them + $checkedpages = $_POST['page']; + $pages = []; + if (!empty($checkedpages)) { + foreach ($checkedpages as $pagelink) { + foreach ($_SESSION['pages'] as &$page) { + if ($page['pagelink'] === $pagelink) { + $page['import'] = 1; + $pagestoimport[] = $page; + } + } + } + foreach ($pagestoimport as $elementtoimport) { + $pages[] = import_webpage_element($elementtoimport, $channel, 'page'); + } + } + $_SESSION['import_pages'] = $pages; + if (!(empty($_SESSION['import_pages']) && empty($_SESSION['import_blocks']) && empty($_SESSION['import_layouts']))) { + info(t('Import complete.') . EOL); + } + if (isset($_SESSION['tempimportpath'])) { + rrmdir($_SESSION['tempimportpath']); // Delete the temporary decompressed files + unset($_SESSION['tempimportpath']); + } + break; + + case 'exportzipfile': + if (isset($_POST['w_download'])) { + $_SESSION['action'] = 'export_select_list'; + $_SESSION['export'] = 'zipfile'; + if (isset($_POST['zipfilename']) && $_POST['zipfilename'] !== '') { + $filename = filter_var($_POST['zipfilename'], FILTER_SANITIZE_ENCODED); + } else { + $filename = 'website.zip'; + } + $_SESSION['zipfilename'] = $filename; + } + + break; + + case 'exportcloud': + if (isset($_POST['exportcloudpath']) && $_POST['exportcloudpath'] !== '') { + $_SESSION['action'] = 'export_select_list'; + $_SESSION['export'] = 'cloud'; + $_SESSION['exportcloudpath'] = filter_var($_POST['exportcloudpath'], FILTER_SANITIZE_ENCODED); + } + + break; + + case 'cloud': + case 'zipfile': + $channel = App::get_channel(); + + $tmp_folder_name = random_string(10); + $zip_folder_name = random_string(10); + $zip_filename = $_SESSION['zipfilename']; + $tmp_folderpath = '/tmp/' . $tmp_folder_name; + $zip_folderpath = '/tmp/' . $zip_folder_name; + if (!mkdir($zip_folderpath, 0770, false)) { + logger('Error creating zip file export folder: ' . $zip_folderpath, LOGGER_NORMAL); + json_return_and_die(array('message' => 'Error creating zip file export folder')); + } + $zip_filepath = '/tmp/' . $zip_folder_name . '/' . $zip_filename; + + $checkedblocks = $_POST['block']; + $blocks = []; + if (!empty($checkedblocks)) { + foreach ($checkedblocks as $mid) { + $b = q( + "select iconfig.v, iconfig.k, mimetype, title, body from iconfig left join item on item.id = iconfig.iid where mid = '%s' and item.uid = %d and iconfig.cat = 'system' and iconfig.k = 'BUILDBLOCK' order by iconfig.v asc limit 1", - dbesc($mid), - intval($channel['channel_id']) - ); - if($b) { - $b = $b[0]; - $blockinfo = array( - 'body' => $b['body'], - 'mimetype' => $b['mimetype'], - 'title' => $b['title'], - 'name' => $b['v'], - 'json' => array( - 'title' => $b['title'], - 'name' => $b['v'], - 'mimetype' => $b['mimetype'], - ) - ); - switch ($blockinfo['mimetype']) { - case 'text/html': - $block_ext = 'html'; - break; - case 'text/bbcode': - case 'text/x-multicode': - $block_ext = 'bbcode'; - break; - case 'text/markdown': - $block_ext = 'md'; - break; - case 'application/x-pdl': - $block_ext = 'pdl'; - break; - case 'application/x-php': - $block_ext = 'php'; - break; - default: - $block_ext = 'bbcode'; - break; - } - $block_filename = $blockinfo['name'] . '.' . $block_ext; - $tmp_blockfolder = $tmp_folderpath . '/blocks/' . $blockinfo['name']; - $block_filepath = $tmp_blockfolder . '/' . $block_filename; - $blockinfo['json']['contentfile'] = $block_filename; - $block_jsonpath = $tmp_blockfolder . '/block.json'; - if (!is_dir($tmp_blockfolder) && !mkdir($tmp_blockfolder, 0770, true)) { - logger('Error creating temp export folder: ' . $tmp_blockfolder, LOGGER_NORMAL); - json_return_and_die(array('message' => 'Error creating temp export folder')); - } - file_put_contents($block_filepath, $blockinfo['body']); - file_put_contents($block_jsonpath, json_encode($blockinfo['json'], JSON_UNESCAPED_SLASHES)); - } - } - } - - $checkedlayouts = $_POST['layout']; - $layouts = []; - if (!empty($checkedlayouts)) { - foreach ($checkedlayouts as $mid) { - $l = q("select iconfig.v, iconfig.k, mimetype, title, body from iconfig + dbesc($mid), + intval($channel['channel_id']) + ); + if ($b) { + $b = $b[0]; + $blockinfo = array( + 'body' => $b['body'], + 'mimetype' => $b['mimetype'], + 'title' => $b['title'], + 'name' => $b['v'], + 'json' => array( + 'title' => $b['title'], + 'name' => $b['v'], + 'mimetype' => $b['mimetype'], + ) + ); + switch ($blockinfo['mimetype']) { + case 'text/html': + $block_ext = 'html'; + break; + case 'text/bbcode': + case 'text/x-multicode': + $block_ext = 'bbcode'; + break; + case 'text/markdown': + $block_ext = 'md'; + break; + case 'application/x-pdl': + $block_ext = 'pdl'; + break; + case 'application/x-php': + $block_ext = 'php'; + break; + default: + $block_ext = 'bbcode'; + break; + } + $block_filename = $blockinfo['name'] . '.' . $block_ext; + $tmp_blockfolder = $tmp_folderpath . '/blocks/' . $blockinfo['name']; + $block_filepath = $tmp_blockfolder . '/' . $block_filename; + $blockinfo['json']['contentfile'] = $block_filename; + $block_jsonpath = $tmp_blockfolder . '/block.json'; + if (!is_dir($tmp_blockfolder) && !mkdir($tmp_blockfolder, 0770, true)) { + logger('Error creating temp export folder: ' . $tmp_blockfolder, LOGGER_NORMAL); + json_return_and_die(array('message' => 'Error creating temp export folder')); + } + file_put_contents($block_filepath, $blockinfo['body']); + file_put_contents($block_jsonpath, json_encode($blockinfo['json'], JSON_UNESCAPED_SLASHES)); + } + } + } + + $checkedlayouts = $_POST['layout']; + $layouts = []; + if (!empty($checkedlayouts)) { + foreach ($checkedlayouts as $mid) { + $l = q( + "select iconfig.v, iconfig.k, mimetype, title, body from iconfig left join item on item.id = iconfig.iid where mid = '%s' and item.uid = %d and iconfig.cat = 'system' and iconfig.k = 'PDL' order by iconfig.v asc limit 1", - dbesc($mid), - intval($channel['channel_id']) - ); - if($l) { - $l = $l[0]; - $layoutinfo = array( - 'body' => $l['body'], - 'mimetype' => $l['mimetype'], - 'description' => $l['title'], - 'name' => $l['v'], - 'json' => array( - 'description' => $l['title'], - 'name' => $l['v'], - 'mimetype' => $l['mimetype'], - ) - ); - switch ($layoutinfo['mimetype']) { - case 'text/bbcode': - case 'text/x-multicode': - default: - $layout_ext = 'bbcode'; - break; - } - $layout_filename = $layoutinfo['name'] . '.' . $layout_ext; - $tmp_layoutfolder = $tmp_folderpath . '/layouts/' . $layoutinfo['name']; - $layout_filepath = $tmp_layoutfolder . '/' . $layout_filename; - $layoutinfo['json']['contentfile'] = $layout_filename; - $layout_jsonpath = $tmp_layoutfolder . '/layout.json'; - if (!is_dir($tmp_layoutfolder) && !mkdir($tmp_layoutfolder, 0770, true)) { - logger('Error creating temp export folder: ' . $tmp_layoutfolder, LOGGER_NORMAL); - json_return_and_die(array('message' => 'Error creating temp export folder')); - } - file_put_contents($layout_filepath, $layoutinfo['body']); - file_put_contents($layout_jsonpath, json_encode($layoutinfo['json'], JSON_UNESCAPED_SLASHES)); - } - } - } - - $checkedpages = $_POST['page']; - $pages = []; - if (!empty($checkedpages)) { - foreach ($checkedpages as $mid) { - - $p = q("select * from iconfig left join item on iconfig.iid = item.id + dbesc($mid), + intval($channel['channel_id']) + ); + if ($l) { + $l = $l[0]; + $layoutinfo = array( + 'body' => $l['body'], + 'mimetype' => $l['mimetype'], + 'description' => $l['title'], + 'name' => $l['v'], + 'json' => array( + 'description' => $l['title'], + 'name' => $l['v'], + 'mimetype' => $l['mimetype'], + ) + ); + switch ($layoutinfo['mimetype']) { + case 'text/bbcode': + case 'text/x-multicode': + default: + $layout_ext = 'bbcode'; + break; + } + $layout_filename = $layoutinfo['name'] . '.' . $layout_ext; + $tmp_layoutfolder = $tmp_folderpath . '/layouts/' . $layoutinfo['name']; + $layout_filepath = $tmp_layoutfolder . '/' . $layout_filename; + $layoutinfo['json']['contentfile'] = $layout_filename; + $layout_jsonpath = $tmp_layoutfolder . '/layout.json'; + if (!is_dir($tmp_layoutfolder) && !mkdir($tmp_layoutfolder, 0770, true)) { + logger('Error creating temp export folder: ' . $tmp_layoutfolder, LOGGER_NORMAL); + json_return_and_die(array('message' => 'Error creating temp export folder')); + } + file_put_contents($layout_filepath, $layoutinfo['body']); + file_put_contents($layout_jsonpath, json_encode($layoutinfo['json'], JSON_UNESCAPED_SLASHES)); + } + } + } + + $checkedpages = $_POST['page']; + $pages = []; + if (!empty($checkedpages)) { + foreach ($checkedpages as $mid) { + $p = q( + "select * from iconfig left join item on iconfig.iid = item.id where item.uid = %d and item.mid = '%s' and iconfig.cat = 'system' and iconfig.k = 'WEBPAGE' and item_type = %d", - intval($channel['channel_id']), - dbesc($mid), - intval(ITEM_TYPE_WEBPAGE) - ); - - if($p) { - foreach ($p as $pp) { - // Get the associated layout - $layoutinfo = []; - if($pp['layout_mid']) { - $l = q("select iconfig.v, iconfig.k, mimetype, title, body from iconfig + intval($channel['channel_id']), + dbesc($mid), + intval(ITEM_TYPE_WEBPAGE) + ); + + if ($p) { + foreach ($p as $pp) { + // Get the associated layout + $layoutinfo = []; + if ($pp['layout_mid']) { + $l = q( + "select iconfig.v, iconfig.k, mimetype, title, body from iconfig left join item on item.id = iconfig.iid where mid = '%s' and item.uid = %d and iconfig.cat = 'system' and iconfig.k = 'PDL' order by iconfig.v asc limit 1", - dbesc($pp['layout_mid']), - intval($channel['channel_id']) - ); - if($l) { - $l = $l[0]; - $layoutinfo = array( - 'body' => $l['body'], - 'mimetype' => $l['mimetype'], - 'description' => $l['title'], - 'name' => $l['v'], - 'json' => array( - 'description' => $l['title'], - 'name' => $l['v'], - ) - ); - switch ($layoutinfo['mimetype']) { - case 'text/bbcode': - case 'text/x-multicode': - default: - $layout_ext = 'bbcode'; - break; - } - $layout_filename = $layoutinfo['name'] . '.' . $layout_ext; - $tmp_layoutfolder = $tmp_folderpath . '/layouts/' . $layoutinfo['name']; - $layout_filepath = $tmp_layoutfolder . '/' . $layout_filename; - $layoutinfo['json']['contentfile'] = $layout_filename; - $layout_jsonpath = $tmp_layoutfolder . '/layout.json'; - if (!is_dir($tmp_layoutfolder) && !mkdir($tmp_layoutfolder, 0770, true)) { - logger('Error creating temp export folder: ' . $tmp_layoutfolder, LOGGER_NORMAL); - json_return_and_die(array('message' => 'Error creating temp export folder')); - } - file_put_contents($layout_filepath, $layoutinfo['body']); - file_put_contents($layout_jsonpath, json_encode($layoutinfo['json'], JSON_UNESCAPED_SLASHES)); - } - } - switch ($pp['mimetype']) { - case 'text/html': - $page_ext = 'html'; - break; - case 'text/bbcode': - case 'text/x-multicode': - $page_ext = 'bbcode'; - break; - case 'text/markdown': - $page_ext = 'md'; - break; - case 'application/x-pdl': - $page_ext = 'pdl'; - break; - case 'application/x-php': - $page_ext = 'php'; - break; - default: - break; - } - $pageinfo = array( - 'title' => $pp['title'], - 'body' => $pp['body'], - 'pagelink' => $pp['v'], - 'mimetype' => $pp['mimetype'], - 'contentfile' => $pp['v'] . '.' . $page_ext, - 'layout' => ((x($layoutinfo,'name')) ? $layoutinfo['name'] : ''), - 'json' => array( - 'title' => $pp['title'], - 'pagelink' => $pp['v'], - 'mimetype' => $pp['mimetype'], - 'layout' => ((x($layoutinfo,'name')) ? $layoutinfo['name'] : ''), - ) - ); - $page_filename = $pageinfo['pagelink'] . '.' . $page_ext; - $tmp_pagefolder = $tmp_folderpath . '/pages/' . $pageinfo['pagelink']; - $page_filepath = $tmp_pagefolder . '/' . $page_filename; - $page_jsonpath = $tmp_pagefolder . '/page.json'; - $pageinfo['json']['contentfile'] = $page_filename; - if (!is_dir($tmp_pagefolder) && !mkdir($tmp_pagefolder, 0770, true)) { - logger('Error creating temp export folder: ' . $tmp_pagefolder, LOGGER_NORMAL); - json_return_and_die(array('message' => 'Error creating temp export folder')); - } - file_put_contents($page_filepath, $pageinfo['body']); - file_put_contents($page_jsonpath, json_encode($pageinfo['json'], JSON_UNESCAPED_SLASHES)); - } - } - } - } - if($action === 'zipfile') { - // Generate the zip file - ExtendedZip::zipTree($tmp_folderpath, $zip_filepath, ZipArchive::CREATE); - // Output the file for download - header('Content-Disposition: attachment; filename="' . $zip_filename . '"'); - header("Content-Type: application/zip"); - $success = readfile($zip_filepath); - } elseif ($action === 'cloud') { // Only zipfile or cloud should be possible values for $action here - if(isset($_SESSION['exportcloudpath'])) { - require_once('include/attach.php'); - $cloudpath = urldecode($_SESSION['exportcloudpath']); - $channel = App::get_channel(); - $dirpath = get_dirpath_by_cloudpath($channel, $cloudpath); - if(!$dirpath) { - $x = attach_mkdirp($channel, $channel['channel_hash'], array('pathname' => $cloudpath)); - $folder_hash = (($x['success']) ? $x['data']['hash'] : ''); - - if (!$x['success']) { - logger('Failed to create cloud file folder', LOGGER_NORMAL); - } - $dirpath = get_dirpath_by_cloudpath($channel, $cloudpath); - if (!is_dir($dirpath)) { - logger('Failed to create cloud file folder', LOGGER_NORMAL); - } - } - - $success = copy_folder_to_cloudfiles($channel, $channel['channel_hash'], $tmp_folderpath, $cloudpath); - } - } - if(!$success) { - logger('Error exporting webpage elements', LOGGER_NORMAL); - } - - rrmdir($zip_folderpath); rrmdir($tmp_folderpath); // delete temporary files - killme(); + dbesc($pp['layout_mid']), + intval($channel['channel_id']) + ); + if ($l) { + $l = $l[0]; + $layoutinfo = array( + 'body' => $l['body'], + 'mimetype' => $l['mimetype'], + 'description' => $l['title'], + 'name' => $l['v'], + 'json' => array( + 'description' => $l['title'], + 'name' => $l['v'], + ) + ); + switch ($layoutinfo['mimetype']) { + case 'text/bbcode': + case 'text/x-multicode': + default: + $layout_ext = 'bbcode'; + break; + } + $layout_filename = $layoutinfo['name'] . '.' . $layout_ext; + $tmp_layoutfolder = $tmp_folderpath . '/layouts/' . $layoutinfo['name']; + $layout_filepath = $tmp_layoutfolder . '/' . $layout_filename; + $layoutinfo['json']['contentfile'] = $layout_filename; + $layout_jsonpath = $tmp_layoutfolder . '/layout.json'; + if (!is_dir($tmp_layoutfolder) && !mkdir($tmp_layoutfolder, 0770, true)) { + logger('Error creating temp export folder: ' . $tmp_layoutfolder, LOGGER_NORMAL); + json_return_and_die(array('message' => 'Error creating temp export folder')); + } + file_put_contents($layout_filepath, $layoutinfo['body']); + file_put_contents($layout_jsonpath, json_encode($layoutinfo['json'], JSON_UNESCAPED_SLASHES)); + } + } + switch ($pp['mimetype']) { + case 'text/html': + $page_ext = 'html'; + break; + case 'text/bbcode': + case 'text/x-multicode': + $page_ext = 'bbcode'; + break; + case 'text/markdown': + $page_ext = 'md'; + break; + case 'application/x-pdl': + $page_ext = 'pdl'; + break; + case 'application/x-php': + $page_ext = 'php'; + break; + default: + break; + } + $pageinfo = array( + 'title' => $pp['title'], + 'body' => $pp['body'], + 'pagelink' => $pp['v'], + 'mimetype' => $pp['mimetype'], + 'contentfile' => $pp['v'] . '.' . $page_ext, + 'layout' => ((x($layoutinfo, 'name')) ? $layoutinfo['name'] : ''), + 'json' => array( + 'title' => $pp['title'], + 'pagelink' => $pp['v'], + 'mimetype' => $pp['mimetype'], + 'layout' => ((x($layoutinfo, 'name')) ? $layoutinfo['name'] : ''), + ) + ); + $page_filename = $pageinfo['pagelink'] . '.' . $page_ext; + $tmp_pagefolder = $tmp_folderpath . '/pages/' . $pageinfo['pagelink']; + $page_filepath = $tmp_pagefolder . '/' . $page_filename; + $page_jsonpath = $tmp_pagefolder . '/page.json'; + $pageinfo['json']['contentfile'] = $page_filename; + if (!is_dir($tmp_pagefolder) && !mkdir($tmp_pagefolder, 0770, true)) { + logger('Error creating temp export folder: ' . $tmp_pagefolder, LOGGER_NORMAL); + json_return_and_die(array('message' => 'Error creating temp export folder')); + } + file_put_contents($page_filepath, $pageinfo['body']); + file_put_contents($page_jsonpath, json_encode($pageinfo['json'], JSON_UNESCAPED_SLASHES)); + } + } + } + } + if ($action === 'zipfile') { + // Generate the zip file + ExtendedZip::zipTree($tmp_folderpath, $zip_filepath, ZipArchive::CREATE); + // Output the file for download + header('Content-Disposition: attachment; filename="' . $zip_filename . '"'); + header("Content-Type: application/zip"); + $success = readfile($zip_filepath); + } elseif ($action === 'cloud') { // Only zipfile or cloud should be possible values for $action here + if (isset($_SESSION['exportcloudpath'])) { + require_once('include/attach.php'); + $cloudpath = urldecode($_SESSION['exportcloudpath']); + $channel = App::get_channel(); + $dirpath = get_dirpath_by_cloudpath($channel, $cloudpath); + if (!$dirpath) { + $x = attach_mkdirp($channel, $channel['channel_hash'], array('pathname' => $cloudpath)); + $folder_hash = (($x['success']) ? $x['data']['hash'] : ''); - break; - default : - break; - } - - } - - } - + if (!$x['success']) { + logger('Failed to create cloud file folder', LOGGER_NORMAL); + } + $dirpath = get_dirpath_by_cloudpath($channel, $cloudpath); + if (!is_dir($dirpath)) { + logger('Failed to create cloud file folder', LOGGER_NORMAL); + } + } + + $success = copy_folder_to_cloudfiles($channel, $channel['channel_hash'], $tmp_folderpath, $cloudpath); + } + } + if (!$success) { + logger('Error exporting webpage elements', LOGGER_NORMAL); + } + + rrmdir($zip_folderpath); + rrmdir($tmp_folderpath); // delete temporary files + killme(); + + break; + default: + break; + } + } + } } diff --git a/Zotlabs/Module/Well_known.php b/Zotlabs/Module/Well_known.php index bd4cc3173..492aed0ea 100644 --- a/Zotlabs/Module/Well_known.php +++ b/Zotlabs/Module/Well_known.php @@ -1,4 +1,5 @@ 1) { - - $arr = [ 'server' => $_SERVER, 'request' => $_REQUEST ]; - call_hooks('well_known', $arr); - - if (! check_siteallowed($_SERVER['REMOTE_ADDR'])) { - logger('well_known: site not allowed. ' . $_SERVER['REMOTE_ADDR']); - killme(); - } - - // from php.net re: REMOTE_HOST: - // Note: Your web server must be configured to create this variable. - // For example in Apache you'll need HostnameLookups On inside httpd.conf - // for it to exist. See also gethostbyaddr(). - - if (get_config('system','siteallowed_remote_host') && (! check_siteallowed($_SERVER['REMOTE_HOST']))) { - logger('well_known: site not allowed. ' . $_SERVER['REMOTE_HOST']); - killme(); - } - - switch(argv(1)) { - - case 'webfinger': - App::$argc -= 1; - array_shift(App::$argv); - App::$argv[0] = 'webfinger'; - $module = new Webfinger(); - $module->init(); - break; - - case 'oauth-authorization-server': - case 'openid-configuration': - App::$argc -= 1; - array_shift(\App::$argv); - App::$argv[0] = 'oauthinfo'; - $module = new Oauthinfo(); - $module->init(); - break; + public function init() + { - case 'dnt-policy.txt': - echo file_get_contents('doc/global/dnt-policy.txt'); - killme(); + if (argc() > 1) { + $arr = ['server' => $_SERVER, 'request' => $_REQUEST]; + call_hooks('well_known', $arr); - default: - if (file_exists(App::$cmd)) { - echo file_get_contents(App::$cmd); - killme(); - } - elseif (file_exists(App::$cmd . '.php')) - require_once(App::$cmd . '.php'); - break; - } - } - - http_status_exit(404); - } + if (!check_siteallowed($_SERVER['REMOTE_ADDR'])) { + logger('well_known: site not allowed. ' . $_SERVER['REMOTE_ADDR']); + killme(); + } + + // from php.net re: REMOTE_HOST: + // Note: Your web server must be configured to create this variable. + // For example in Apache you'll need HostnameLookups On inside httpd.conf + // for it to exist. See also gethostbyaddr(). + + if (get_config('system', 'siteallowed_remote_host') && (!check_siteallowed($_SERVER['REMOTE_HOST']))) { + logger('well_known: site not allowed. ' . $_SERVER['REMOTE_HOST']); + killme(); + } + + switch (argv(1)) { + case 'webfinger': + App::$argc -= 1; + array_shift(App::$argv); + App::$argv[0] = 'webfinger'; + $module = new Webfinger(); + $module->init(); + break; + + case 'oauth-authorization-server': + case 'openid-configuration': + App::$argc -= 1; + array_shift(App::$argv); + App::$argv[0] = 'oauthinfo'; + $module = new Oauthinfo(); + $module->init(); + break; + + case 'dnt-policy.txt': + echo file_get_contents('doc/global/dnt-policy.txt'); + killme(); + + default: + if (file_exists(App::$cmd)) { + echo file_get_contents(App::$cmd); + killme(); + } elseif (file_exists(App::$cmd . '.php')) { + require_once(App::$cmd . '.php'); + } + break; + } + } + + http_status_exit(404); + } } diff --git a/Zotlabs/Module/Xchan.php b/Zotlabs/Module/Xchan.php index 526580fad..18290519b 100644 --- a/Zotlabs/Module/Xchan.php +++ b/Zotlabs/Module/Xchan.php @@ -1,47 +1,51 @@ ' . t('Xchan Lookup') . ''; - - $o .= '
      '; - $o .= t('Lookup xchan beginning with (or webbie): '); - $o .= ''; - $o .= '
      '; - $o .= '

      '; - - if(x($_GET, 'addr')) { - $addr = trim($_GET['addr']); - - $r = q("select * from xchan where xchan_hash like '%s%%' or xchan_addr = '%s' group by xchan_hash", - dbesc($addr), - dbesc($addr) - ); - - if($r) { - foreach($r as $rr) { - $o .= str_replace(array("\n", " "), array("
      ", " "), print_r($rr, true)) . EOL; - - $s = q("select * from hubloc where hubloc_hash like '%s'", - dbesc($r[0]['xchan_hash']) - ); - - if($s) { - foreach($s as $rrr) - $o .= str_replace(array("\n", " "), array("
      ", " "), print_r($rrr, true)) . EOL; - } - } - } - else - notice( t('Not found.') . EOL); - - } - return $o; - } - + $o = '

      ' . t('Xchan Lookup') . '

      '; + + $o .= '
      '; + $o .= t('Lookup xchan beginning with (or webbie): '); + $o .= ''; + $o .= '
      '; + $o .= '

      '; + + if (x($_GET, 'addr')) { + $addr = trim($_GET['addr']); + + $r = q( + "select * from xchan where xchan_hash like '%s%%' or xchan_addr = '%s' group by xchan_hash", + dbesc($addr), + dbesc($addr) + ); + + if ($r) { + foreach ($r as $rr) { + $o .= str_replace(array("\n", " "), array("
      ", " "), print_r($rr, true)) . EOL; + + $s = q( + "select * from hubloc where hubloc_hash like '%s'", + dbesc($r[0]['xchan_hash']) + ); + + if ($s) { + foreach ($s as $rrr) { + $o .= str_replace(array("\n", " "), array("
      ", " "), print_r($rrr, true)) . EOL; + } + } + } + } else { + notice(t('Not found.') . EOL); + } + } + return $o; + } } diff --git a/Zotlabs/Module/Xp.php b/Zotlabs/Module/Xp.php index 23a4e2cea..e7fba797f 100644 --- a/Zotlabs/Module/Xp.php +++ b/Zotlabs/Module/Xp.php @@ -3,61 +3,63 @@ namespace Zotlabs\Module; use Zotlabs\Web\Controller; -class Xp extends Controller { +class Xp extends Controller +{ - function get() { - if (argc() > 1) { - $path = 'cache/xp/' . substr(argv(1),0,2) . '/' . substr(argv(1),2,2) . '/' . argv(1); + public function get() + { + if (argc() > 1) { + $path = 'cache/xp/' . substr(argv(1), 0, 2) . '/' . substr(argv(1), 2, 2) . '/' . argv(1); - if (! file_exists($path)) { - // no longer cached for some reason, perhaps expired - $resolution = substr(argv(1),(-2),2); - if ($resolution && substr($resolution,0,1) === '-') { - switch (substr($resolution,1,1)) { - case '4': - $path = get_default_profile_photo(); - break; - case '5': - $path = get_default_profile_photo(80); - break; - case '6': - $path = get_default_profile_photo(48); - break; - default: - break; - } - } - } - - if (! file_exists($path)) { - http_status_exit(404,'Not found'); - } - - $x = @getimagesize($path); - if ($x) { - header('Content-Type: ' . $x['mime']); - } + if (!file_exists($path)) { + // no longer cached for some reason, perhaps expired + $resolution = substr(argv(1), (-2), 2); + if ($resolution && substr($resolution, 0, 1) === '-') { + switch (substr($resolution, 1, 1)) { + case '4': + $path = get_default_profile_photo(); + break; + case '5': + $path = get_default_profile_photo(80); + break; + case '6': + $path = get_default_profile_photo(48); + break; + default: + break; + } + } + } - $cache = intval(get_config('system','photo_cache_time')); - if (! $cache) { - $cache = (3600 * 24); // 1 day - } - header('Expires: ' . gmdate('D, d M Y H:i:s', time() + $cache) . ' GMT'); - // Set browser cache age as $cache. But set timeout of 'shared caches' - // much lower in the event that infrastructure caching is present. - $smaxage = intval($cache/12); - header('Cache-Control: s-maxage=' . $smaxage . '; max-age=' . $cache . ';'); - - $infile = fopen($path,'rb'); - $outfile = fopen('php://output','wb'); - if ($infile && $outfile) { - pipe_streams($infile,$outfile); - } - fclose($infile); - fclose($outfile); - killme(); - } + if (!file_exists($path)) { + http_status_exit(404, 'Not found'); + } - http_status_exit(404,'Not found'); - } + $x = @getimagesize($path); + if ($x) { + header('Content-Type: ' . $x['mime']); + } + + $cache = intval(get_config('system', 'photo_cache_time')); + if (!$cache) { + $cache = (3600 * 24); // 1 day + } + header('Expires: ' . gmdate('D, d M Y H:i:s', time() + $cache) . ' GMT'); + // Set browser cache age as $cache. But set timeout of 'shared caches' + // much lower in the event that infrastructure caching is present. + $smaxage = intval($cache / 12); + header('Cache-Control: s-maxage=' . $smaxage . '; max-age=' . $cache . ';'); + + $infile = fopen($path, 'rb'); + $outfile = fopen('php://output', 'wb'); + if ($infile && $outfile) { + pipe_streams($infile, $outfile); + } + fclose($infile); + fclose($outfile); + killme(); + } + + http_status_exit(404, 'Not found'); + } } diff --git a/Zotlabs/Module/Xref.php b/Zotlabs/Module/Xref.php index e9d494da4..1bd3e2b59 100644 --- a/Zotlabs/Module/Xref.php +++ b/Zotlabs/Module/Xref.php @@ -1,26 +1,29 @@ 2) - $url = argv(2); - - goaway (z_root() . '/' . $url); - - } - + public function init() + { + // Sets a referral URL using an xchan directly + // Link format: example.com/xref/[xchan]/[TargetURL] + // Target URL is optional. + // Cookie lasts 24 hours to survive a browser restart. Contains no personal + // information at all - just somebody else's xchan. + $referrer = argv(1); + $expire = time() + 60 * 60 * 2; + $path = 'xref'; + setcookie($path, $referrer, $expire, "/"); + $url = ''; + + if (argc() > 2) { + $url = argv(2); + } + + goaway(z_root() . '/' . $url); + } } diff --git a/Zotlabs/Module/Zot.php b/Zotlabs/Module/Zot.php index e470d5f02..830006d87 100644 --- a/Zotlabs/Module/Zot.php +++ b/Zotlabs/Module/Zot.php @@ -1,4 +1,5 @@ run(),'application/x-nomad+json'); } diff --git a/Zotlabs/Module/Zot_probe.php b/Zotlabs/Module/Zot_probe.php index fad6d4d7d..91c2b7293 100644 --- a/Zotlabs/Module/Zot_probe.php +++ b/Zotlabs/Module/Zot_probe.php @@ -2,53 +2,50 @@ namespace Zotlabs\Module; +use App; use Zotlabs\Lib\ZotURL; use Zotlabs\Lib\Zotfinger; - +use Zotlabs\Web\Controller; use Zotlabs\Web\HTTPSig; -class Zot_probe extends \Zotlabs\Web\Controller { +class Zot_probe extends Controller +{ - function get() { + public function get() + { - $o = replace_macros(get_markup_template('zot_probe.tpl'), [ - '$page_title' => t('Zot6 Probe Diagnostic'), - '$resource' => [ 'resource', t('Object URL') , $_REQUEST['resource'], EMPTY_STR ], - '$authf' => [ 'authf', t('Authenticated fetch'), $_REQUEST['authf'], EMPTY_STR, [ t('No'), t('Yes') ] ], - '$submit' => t('Submit') - ]); + $o = replace_macros(get_markup_template('zot_probe.tpl'), [ + '$page_title' => t('Zot6 Probe Diagnostic'), + '$resource' => ['resource', t('Object URL'), $_REQUEST['resource'], EMPTY_STR], + '$authf' => ['authf', t('Authenticated fetch'), $_REQUEST['authf'], EMPTY_STR, [t('No'), t('Yes')]], + '$submit' => t('Submit') + ]); + if (x($_GET, 'resource')) { + $resource = $_GET['resource']; + $channel = (($_GET['authf']) ? App::get_channel() : null); - if(x($_GET,'resource')) { - $resource = $_GET['resource']; - $channel = (($_GET['authf']) ? \App::get_channel() : null); + if (strpos($resource, 'x-zot:') === 0) { + $x = ZotURL::fetch($resource, $channel); + } else { + $x = Zotfinger::exec($resource, $channel); - if(strpos($resource,'x-zot:') === 0) { - $x = ZotURL::fetch($resource,$channel); - } - else { - $x = Zotfinger::exec($resource,$channel); - - $o .= '
      ' . htmlspecialchars(print_array($x)) . '
      '; + $o .= '
      ' . htmlspecialchars(print_array($x)) . '
      '; $headers = 'Accept: application/x-nomad+json, application/x-zot+json, application/jrd+json, application/json'; - $redirects = 0; - $x = z_fetch_url($resource,true,$redirects, [ 'headers' => [ $headers ]]); - } + $redirects = 0; + $x = z_fetch_url($resource, true, $redirects, ['headers' => [$headers]]); + } - if($x['success']) { + if ($x['success']) { + $o .= '
      ' . htmlspecialchars($x['header']) . '
      ' . EOL; - $o .= '
      ' . htmlspecialchars($x['header']) . '
      ' . EOL; + $o .= 'verify returns: ' . str_replace("\n", EOL, print_r(HTTPSig::verify($x, EMPTY_STR, 'zot6'), true)) . EOL; - $o .= 'verify returns: ' . str_replace("\n",EOL,print_r(HTTPSig::verify($x, EMPTY_STR, 'zot6'),true)) . EOL; - - $o .= '
      ' . htmlspecialchars(json_encode(json_decode($x['body']),JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES)) . '
      ' . EOL; - - } - - } - return $o; - } - + $o .= '
      ' . htmlspecialchars(json_encode(json_decode($x['body']), JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)) . '
      ' . EOL; + } + } + return $o; + } } diff --git a/Zotlabs/Module/Zotfinger.php b/Zotlabs/Module/Zotfinger.php index 9411bab82..420c5cc44 100644 --- a/Zotlabs/Module/Zotfinger.php +++ b/Zotlabs/Module/Zotfinger.php @@ -1,4 +1,5 @@ t('Zotfinger Diagnostic'), - '$resource' => [ 'resource', t('Lookup URL') , $_GET['resource'], EMPTY_STR ], - '$submit' => t('Submit') - ]); + $o = replace_macros(get_markup_template('zotfinger.tpl'), [ + '$page_title' => t('Zotfinger Diagnostic'), + '$resource' => ['resource', t('Lookup URL'), $_GET['resource'], EMPTY_STR], + '$submit' => t('Submit') + ]); - if ($_GET['resource']) { - $channel = App::get_channel(); - $resource = trim(escape_tags($_GET['resource'])); - $do_import = ((intval($_GET['import']) && is_site_admin()) ? true : false); - - $j = Zfinger::exec($resource,$channel); + if ($_GET['resource']) { + $channel = App::get_channel(); + $resource = trim(escape_tags($_GET['resource'])); + $do_import = ((intval($_GET['import']) && is_site_admin()) ? true : false); - if ($do_import && $j) { - $x = Libzot::import_xchan($j['data']); - } - $o .= '
      ' .  str_replace("\n",'
      ',print_array($j)) . '
      '; - } - return $o; - } - + $j = Zfinger::exec($resource, $channel); + + if ($do_import && $j) { + $x = Libzot::import_xchan($j['data']); + } + $o .= '
      ' . str_replace("\n", '
      ', print_array($j)) . '
      '; + } + return $o; + } } diff --git a/Zotlabs/Photo/PhotoDriver.php b/Zotlabs/Photo/PhotoDriver.php index 030ee2cc6..8a5c1d595 100644 --- a/Zotlabs/Photo/PhotoDriver.php +++ b/Zotlabs/Photo/PhotoDriver.php @@ -3,6 +3,7 @@ namespace Zotlabs\Photo; use App; +use Imagick; /** * @brief Abstract photo driver class. @@ -10,470 +11,475 @@ use App; * Inheritance seems not to be the best design pattern for such photo drivers. */ -abstract class PhotoDriver { +abstract class PhotoDriver +{ - /** - * @brief This variable keeps the image. - * - * For GD it is a PHP image resource. - * For ImageMagick it is an \Imagick object. - * - * @var resource|\Imagick - */ - protected $image; + /** + * @brief This variable keeps the image. + * + * For GD it is a PHP image resource. + * For ImageMagick it is an \Imagick object. + * + * @var resource|Imagick + */ + protected $image; - /** - * @var integer - */ - protected $width; + /** + * @var int + */ + protected $width; - /** - * @var integer - */ - protected $height; + /** + * @var int + */ + protected $height; - /** - * @var boolean - */ - protected $valid; + /** + * @var bool + */ + protected $valid; - /** - * @brief The mimetype of the image. - * - * @var string - */ - protected $type; + /** + * @brief The mimetype of the image. + * + * @var string + */ + protected $type; - /** - * @brief Supported mimetypes by the used photo driver. - * - * @var array - */ - protected $types; + /** + * @brief Supported mimetypes by the used photo driver. + * + * @var array + */ + protected $types; - /** - * @brief Return an array with supported mimetypes. - * - * @return array - * Associative array with mimetype as key and file extension as value. - */ - abstract public function supportedTypes(); + /** + * @brief Return an array with supported mimetypes. + * + * @return array + * Associative array with mimetype as key and file extension as value. + */ + abstract public function supportedTypes(); - abstract protected function load($data, $type); + abstract protected function load($data, $type); - abstract protected function destroy(); + abstract protected function destroy(); - abstract protected function setDimensions(); + abstract protected function setDimensions(); - /** - * @brief Return the current image. - * - * @fixme Shouldn't his method be protected, because outside of the current - * driver it makes no sense at all because of the different return values. - * - * @return boolean|resource|\Imagick - * false on failure, a PHP image resource for GD driver, an \Imagick object - * for ImageMagick driver. - */ + /** + * @brief Return the current image. + * + * @fixme Shouldn't his method be protected, because outside of the current + * driver it makes no sense at all because of the different return values. + * + * @return bool|resource|Imagick + * false on failure, a PHP image resource for GD driver, an \Imagick object + * for ImageMagick driver. + */ - abstract public function getImage(); + abstract public function getImage(); - abstract public function doScaleImage($new_width, $new_height); + abstract public function doScaleImage($new_width, $new_height); - abstract public function rotate($degrees); + abstract public function rotate($degrees); - abstract public function flip($horiz = true, $vert = false); + abstract public function flip($horiz = true, $vert = false); - /** - * @brief Crops the image. - * - * @param int $maxx width of the new image - * @param int $maxy height of the new image - * @param int $x x-offset for region - * @param int $y y-offset for region - * @param int $w width of region - * @param int $h height of region - * - * @return boolean|void false on failure - */ - abstract public function cropImageRect($maxx, $maxy, $x, $y, $w, $h); + /** + * @brief Crops the image. + * + * @param int $maxx width of the new image + * @param int $maxy height of the new image + * @param int $x x-offset for region + * @param int $y y-offset for region + * @param int $w width of region + * @param int $h height of region + * + * @return bool|void false on failure + */ + abstract public function cropImageRect($maxx, $maxy, $x, $y, $w, $h); - /** - * @brief Return a binary string from the image resource. - * - * @return string A Binary String. - */ - abstract public function imageString($animations = true); + /** + * @brief Return a binary string from the image resource. + * + * @return string A Binary String. + */ + abstract public function imageString($animations = true); - abstract public function clearexif(); + abstract public function clearexif(); - /** - * @brief PhotoDriver constructor. - * - * @param string $data Image - * @param string $type mimetype - */ - public function __construct($data, $type = '') { - $this->types = $this->supportedTypes(); - if (! array_key_exists($type, $this->types)) { - $type = 'image/jpeg'; - } - $this->type = $type; - $this->valid = false; - $this->load($data, $type); - } + /** + * @brief PhotoDriver constructor. + * + * @param string $data Image + * @param string $type mimetype + */ + public function __construct($data, $type = '') + { + $this->types = $this->supportedTypes(); + if (! array_key_exists($type, $this->types)) { + $type = 'image/jpeg'; + } + $this->type = $type; + $this->valid = false; + $this->load($data, $type); + } - public function __destruct() { - if ($this->is_valid()) { - $this->destroy(); - } - } + public function __destruct() + { + if ($this->is_valid()) { + $this->destroy(); + } + } - /** - * @brief Is it a valid image object. - * - * @return boolean - */ - public function is_valid() { - return $this->valid; - } + /** + * @brief Is it a valid image object. + * + * @return bool + */ + public function is_valid() + { + return $this->valid; + } - /** - * @brief Get the width of the image. - * - * @return boolean|number Width of image in pixels, or false on failure - */ - public function getWidth() { - if (! $this->is_valid()) { - return false; - } - return $this->width; - } + /** + * @brief Get the width of the image. + * + * @return bool|number Width of image in pixels, or false on failure + */ + public function getWidth() + { + if (! $this->is_valid()) { + return false; + } + return $this->width; + } - /** - * @brief Get the height of the image. - * - * @return boolean|number Height of image in pixels, or false on failure - */ - public function getHeight() { - if (! $this->is_valid()) { - return false; - } - return $this->height; - } + /** + * @brief Get the height of the image. + * + * @return bool|number Height of image in pixels, or false on failure + */ + public function getHeight() + { + if (! $this->is_valid()) { + return false; + } + return $this->height; + } - /** - * @brief Saves the image resource to a file in filesystem. - * - * @param string $path Path and filename where to save the image - * @return boolean False on failure, otherwise true - */ - public function saveImage($path, $animated = true) { - if (! $this->is_valid()) { - return false; - } - return (file_put_contents($path, $this->imageString($animated)) ? true : false); - } + /** + * @brief Saves the image resource to a file in filesystem. + * + * @param string $path Path and filename where to save the image + * @return bool False on failure, otherwise true + */ + public function saveImage($path, $animated = true) + { + if (! $this->is_valid()) { + return false; + } + return (file_put_contents($path, $this->imageString($animated)) ? true : false); + } - /** - * @brief Return mimetype of the image resource. - * - * @return boolean|string False on failure, otherwise mimetype. - */ - public function getType() { - if (! $this->is_valid()) { - return false; - } - return $this->type; - } + /** + * @brief Return mimetype of the image resource. + * + * @return bool|string False on failure, otherwise mimetype. + */ + public function getType() + { + if (! $this->is_valid()) { + return false; + } + return $this->type; + } - /** - * @brief Return file extension of the image resource. - * - * @return boolean|string False on failure, otherwise file extension. - */ - public function getExt() { - if (! $this->is_valid()) { - return false; - } - return $this->types[$this->getType()]; - } + /** + * @brief Return file extension of the image resource. + * + * @return bool|string False on failure, otherwise file extension. + */ + public function getExt() + { + if (! $this->is_valid()) { + return false; + } + return $this->types[$this->getType()]; + } - /** - * @brief Scale image to max pixel size in either dimension. - * - * @param int $max maximum pixel size in either dimension - * @param boolean $float_height (optional) - * If true allow height to float to any length on tall images, constraining - * only the width - * @return boolean|void false on failure, otherwise void - */ - public function scaleImage($max, $float_height = true) { - if (! $this->is_valid()) { - return false; - } + /** + * @brief Scale image to max pixel size in either dimension. + * + * @param int $max maximum pixel size in either dimension + * @param bool $float_height (optional) + * If true allow height to float to any length on tall images, constraining + * only the width + * @return bool|void false on failure, otherwise void + */ + public function scaleImage($max, $float_height = true) + { + if (! $this->is_valid()) { + return false; + } - $width = $this->width; - $height = $this->height; + $width = $this->width; + $height = $this->height; - $dest_width = $dest_height = 0; + $dest_width = $dest_height = 0; - if (! ($width && $height)) { - return false; - } + if (! ($width && $height)) { + return false; + } - if ($width > $max && $height > $max) { + if ($width > $max && $height > $max) { + // very tall image (greater than 16:9) + // constrain the width - let the height float. - // very tall image (greater than 16:9) - // constrain the width - let the height float. + if (((($height * 9) / 16) > $width) && ($float_height)) { + $dest_width = $max; + $dest_height = intval(($height * $max) / $width); + } // else constrain both dimensions + elseif ($width > $height) { + $dest_width = $max; + $dest_height = intval(($height * $max) / $width); + } else { + $dest_width = intval(($width * $max) / $height); + $dest_height = $max; + } + } else { + if ($width > $max) { + $dest_width = $max; + $dest_height = intval(($height * $max) / $width); + } else { + if ($height > $max) { + // very tall image (greater than 16:9) + // but width is OK - don't do anything - if (((($height * 9) / 16) > $width) && ($float_height)) { - $dest_width = $max; - $dest_height = intval(($height * $max) / $width); - } // else constrain both dimensions - elseif ($width > $height) { - $dest_width = $max; - $dest_height = intval(($height * $max) / $width); - } - else { - $dest_width = intval(($width * $max) / $height); - $dest_height = $max; - } - } - else { - if ($width > $max) { - $dest_width = $max; - $dest_height = intval(($height * $max) / $width); - } - else { - if ($height > $max) { + if (((($height * 9) / 16) > $width) && ($float_height)) { + $dest_width = $width; + $dest_height = $height; + } else { + $dest_width = intval(($width * $max) / $height); + $dest_height = $max; + } + } else { + $dest_width = $width; + $dest_height = $height; + } + } + } + $this->doScaleImage($dest_width, $dest_height); + } - // very tall image (greater than 16:9) - // but width is OK - don't do anything + public function scaleImageUp($min) + { + if (! $this->is_valid()) { + return false; + } - if (((($height * 9) / 16) > $width) && ($float_height)) { - $dest_width = $width; - $dest_height = $height; - } - else { - $dest_width = intval(($width * $max) / $height); - $dest_height = $max; - } - } - else { - $dest_width = $width; - $dest_height = $height; - } - } - } - $this->doScaleImage($dest_width, $dest_height); - } + $width = $this->width; + $height = $this->height; - public function scaleImageUp($min) { - if (! $this->is_valid()) { - return false; - } + $dest_width = $dest_height = 0; - $width = $this->width; - $height = $this->height; + if (! ($width && $height)) { + return false; + } - $dest_width = $dest_height = 0; + if ($width < $min && $height < $min) { + if ($width > $height) { + $dest_width = $min; + $dest_height = intval(($height * $min) / $width); + } else { + $dest_width = intval(($width * $min) / $height); + $dest_height = $min; + } + } else { + if ($width < $min) { + $dest_width = $min; + $dest_height = intval(($height * $min) / $width); + } else { + if ($height < $min) { + $dest_width = intval(($width * $min) / $height); + $dest_height = $min; + } else { + $dest_width = $width; + $dest_height = $height; + } + } + } + $this->doScaleImage($dest_width, $dest_height); + } - if (! ($width && $height)) { - return false; - } + /** + * @brief Scales image to a square. + * + * @param int $dim Pixel of square image + * @return bool|void false on failure, otherwise void + */ + public function scaleImageSquare($dim) + { + if (! $this->is_valid()) { + return false; + } + $this->doScaleImage($dim, $dim); + } - if ($width < $min && $height < $min) { - if ($width > $height) { - $dest_width = $min; - $dest_height = intval(($height * $min) / $width); - } - else { - $dest_width = intval(($width * $min) / $height); - $dest_height = $min; - } - } - else { - if ($width < $min) { - $dest_width = $min; - $dest_height = intval(($height * $min) / $width); - } - else { - if ($height < $min) { - $dest_width = intval(($width * $min) / $height); - $dest_height = $min; - } - else { - $dest_width = $width; - $dest_height = $height; - } - } - } - $this->doScaleImage($dest_width, $dest_height); - } + /** + * @brief Crops a square image. + * + * @see cropImageRect() + * + * @param int $max size of the new image + * @param int $x x-offset for region + * @param int $y y-offset for region + * @param int $w width of region + * @param int $h height of region + * + * @return bool|void false on failure + */ + public function cropImage($max, $x, $y, $w, $h) + { + if (! $this->is_valid()) { + return false; + } + $this->cropImageRect($max, $max, $x, $y, $w, $h); + } - /** - * @brief Scales image to a square. - * - * @param int $dim Pixel of square image - * @return boolean|void false on failure, otherwise void - */ - public function scaleImageSquare($dim) { - if (! $this->is_valid()) { - return false; - } - $this->doScaleImage($dim, $dim); - } + /** + * @brief Reads exif data from a given filename. + * + * @param string $filename + * @return bool|array + */ + public function exif($filename) + { + if ((! function_exists('exif_read_data')) || (! in_array($this->getType(), ['image/jpeg', 'image/tiff']))) { + return false; + } - /** - * @brief Crops a square image. - * - * @see cropImageRect() - * - * @param int $max size of the new image - * @param int $x x-offset for region - * @param int $y y-offset for region - * @param int $w width of region - * @param int $h height of region - * - * @return boolean|void false on failure - */ - public function cropImage($max, $x, $y, $w, $h) { - if (! $this->is_valid()) { - return false; - } - $this->cropImageRect($max, $max, $x, $y, $w, $h); - } + /* + * PHP 7.2 allows you to use a stream resource, which should reduce/avoid + * memory exhaustion on large images. + */ - /** - * @brief Reads exif data from a given filename. - * - * @param string $filename - * @return boolean|array - */ - public function exif($filename) { - if ((! function_exists('exif_read_data')) || (! in_array($this->getType(), ['image/jpeg', 'image/tiff']))) { - return false; - } + if (version_compare(PHP_VERSION, '7.2.0') >= 0) { + $f = @fopen($filename, 'rb'); + } else { + $f = $filename; + } - /* - * PHP 7.2 allows you to use a stream resource, which should reduce/avoid - * memory exhaustion on large images. - */ + if ($f) { + return @exif_read_data($f, null, true); + } - if (version_compare(PHP_VERSION, '7.2.0') >= 0) { - $f = @fopen($filename, 'rb'); - } - else { - $f = $filename; - } + return false; + } - if ($f) { - return @exif_read_data($f, null, true); - } + /** + * @brief Orients current image based on exif orientation information. + * + * @param array $exif + * @return bool true if oriented, otherwise false + */ + public function orient($exif) + { + if (! ($this->is_valid() && $exif)) { + return false; + } - return false; - } + $ort = ((array_key_exists('IFD0', $exif)) ? $exif['IFD0']['Orientation'] : $exif['Orientation']); - /** - * @brief Orients current image based on exif orientation information. - * - * @param array $exif - * @return boolean true if oriented, otherwise false - */ - public function orient($exif) { - if (! ($this->is_valid() && $exif)) { - return false; - } + if (! $ort) { + return false; + } - $ort = ((array_key_exists('IFD0', $exif)) ? $exif['IFD0']['Orientation'] : $exif['Orientation']); + switch ($ort) { + case 1: // nothing + break; + case 2: // horizontal flip + $this->flip(); + break; + case 3: // 180 rotate left + $this->rotate(180); + break; + case 4: // vertical flip + $this->flip(false, true); + break; + case 5: // vertical flip + 90 rotate right + $this->flip(false, true); + $this->rotate(-90); + break; + case 6: // 90 rotate right + $this->rotate(-90); + break; + case 7: // horizontal flip + 90 rotate right + $this->flip(); + $this->rotate(-90); + break; + case 8: // 90 rotate left + $this->rotate(90); + break; + default: + break; + } - if (! $ort) { - return false; - } + return true; + } - switch ($ort) { - case 1 : // nothing - break; - case 2 : // horizontal flip - $this->flip(); - break; - case 3 : // 180 rotate left - $this->rotate(180); - break; - case 4 : // vertical flip - $this->flip(false, true); - break; - case 5 : // vertical flip + 90 rotate right - $this->flip(false, true); - $this->rotate(-90); - break; - case 6 : // 90 rotate right - $this->rotate(-90); - break; - case 7 : // horizontal flip + 90 rotate right - $this->flip(); - $this->rotate(-90); - break; - case 8 : // 90 rotate left - $this->rotate(90); - break; - default : - break; - } + /** + * @brief Save photo to database. + * + * @param array $arr + * @param bool $skipcheck (optional) default false + * @return bool|array + */ + public function save($arr, $skipcheck = false) + { + if (! ($skipcheck || $this->is_valid())) { + logger('Attempt to store invalid photo.'); + return false; + } - return true; - } + $p = []; - /** - * @brief Save photo to database. - * - * @param array $arr - * @param boolean $skipcheck (optional) default false - * @return boolean|array - */ - public function save($arr, $skipcheck = false) { - if (! ($skipcheck || $this->is_valid())) { - logger('Attempt to store invalid photo.'); - return false; - } + $p['aid'] = ((intval($arr['aid'])) ? intval($arr['aid']) : 0); + $p['uid'] = ((intval($arr['uid'])) ? intval($arr['uid']) : 0); + $p['xchan'] = (($arr['xchan']) ? $arr['xchan'] : ''); + $p['resource_id'] = (($arr['resource_id']) ? $arr['resource_id'] : ''); + $p['filename'] = (($arr['filename']) ? $arr['filename'] : ''); + $p['mimetype'] = (($arr['mimetype']) ? $arr['mimetype'] : $this->getType()); + $p['album'] = (($arr['album']) ? $arr['album'] : ''); + $p['imgscale'] = ((intval($arr['imgscale'])) ? intval($arr['imgscale']) : 0); + $p['allow_cid'] = (($arr['allow_cid']) ? $arr['allow_cid'] : ''); + $p['allow_gid'] = (($arr['allow_gid']) ? $arr['allow_gid'] : ''); + $p['deny_cid'] = (($arr['deny_cid']) ? $arr['deny_cid'] : ''); + $p['deny_gid'] = (($arr['deny_gid']) ? $arr['deny_gid'] : ''); + $p['edited'] = (($arr['edited']) ? $arr['edited'] : datetime_convert()); + $p['title'] = (($arr['title']) ? $arr['title'] : ''); + $p['description'] = (($arr['description']) ? $arr['description'] : ''); + $p['photo_usage'] = intval($arr['photo_usage']); + $p['os_storage'] = intval($arr['os_storage']); + $p['os_path'] = $arr['os_path']; + $p['os_syspath'] = ((array_key_exists('os_syspath', $arr)) ? $arr['os_syspath'] : ''); + $p['display_path'] = (($arr['display_path']) ? $arr['display_path'] : ''); + $p['width'] = (($arr['width']) ? $arr['width'] : $this->getWidth()); + $p['height'] = (($arr['height']) ? $arr['height'] : $this->getHeight()); + $p['expires'] = (($arr['expires']) ? $arr['expires'] : gmdate('Y-m-d H:i:s', time() + get_config('system', 'photo_cache_time', 86400))); + $p['profile'] = ((array_key_exists('profile', $arr)) ? intval($arr['profile']) : 0); - $p = []; + if (! intval($p['imgscale'])) { + logger('save: ' . print_r($arr, true), LOGGER_DATA); + } + $x = q("select id, created from photo where resource_id = '%s' and uid = %d and xchan = '%s' and imgscale = %d limit 1", dbesc($p['resource_id']), intval($p['uid']), dbesc($p['xchan']), intval($p['imgscale'])); - $p['aid'] = ((intval($arr['aid'])) ? intval($arr['aid']) : 0); - $p['uid'] = ((intval($arr['uid'])) ? intval($arr['uid']) : 0); - $p['xchan'] = (($arr['xchan']) ? $arr['xchan'] : ''); - $p['resource_id'] = (($arr['resource_id']) ? $arr['resource_id'] : ''); - $p['filename'] = (($arr['filename']) ? $arr['filename'] : ''); - $p['mimetype'] = (($arr['mimetype']) ? $arr['mimetype'] : $this->getType()); - $p['album'] = (($arr['album']) ? $arr['album'] : ''); - $p['imgscale'] = ((intval($arr['imgscale'])) ? intval($arr['imgscale']) : 0); - $p['allow_cid'] = (($arr['allow_cid']) ? $arr['allow_cid'] : ''); - $p['allow_gid'] = (($arr['allow_gid']) ? $arr['allow_gid'] : ''); - $p['deny_cid'] = (($arr['deny_cid']) ? $arr['deny_cid'] : ''); - $p['deny_gid'] = (($arr['deny_gid']) ? $arr['deny_gid'] : ''); - $p['edited'] = (($arr['edited']) ? $arr['edited'] : datetime_convert()); - $p['title'] = (($arr['title']) ? $arr['title'] : ''); - $p['description'] = (($arr['description']) ? $arr['description'] : ''); - $p['photo_usage'] = intval($arr['photo_usage']); - $p['os_storage'] = intval($arr['os_storage']); - $p['os_path'] = $arr['os_path']; - $p['os_syspath'] = ((array_key_exists('os_syspath', $arr)) ? $arr['os_syspath'] : ''); - $p['display_path'] = (($arr['display_path']) ? $arr['display_path'] : ''); - $p['width'] = (($arr['width']) ? $arr['width'] : $this->getWidth()); - $p['height'] = (($arr['height']) ? $arr['height'] : $this->getHeight()); - $p['expires'] = (($arr['expires']) ? $arr['expires'] : gmdate('Y-m-d H:i:s', time() + get_config('system', 'photo_cache_time', 86400))); - $p['profile'] = ((array_key_exists('profile', $arr)) ? intval($arr['profile']) : 0); - - if (! intval($p['imgscale'])) { - logger('save: ' . print_r($arr, true), LOGGER_DATA); - } - $x = q("select id, created from photo where resource_id = '%s' and uid = %d and xchan = '%s' and imgscale = %d limit 1", dbesc($p['resource_id']), intval($p['uid']), dbesc($p['xchan']), intval($p['imgscale'])); - - if ($x) { - $p['created'] = (($x['created']) ? $x['created'] : $p['edited']); - $r = q("UPDATE photo set + if ($x) { + $p['created'] = (($x['created']) ? $x['created'] : $p['edited']); + $r = q( + "UPDATE photo set aid = %d, uid = %d, xchan = '%s', @@ -501,48 +507,74 @@ abstract class PhotoDriver { expires = '%s', profile = %d where id = %d", - intval($p['aid']), intval($p['uid']), dbesc($p['xchan']), dbesc($p['resource_id']), dbescdate($p['created']), dbescdate($p['edited']), dbesc(basename($p['filename'])), dbesc($p['mimetype']), dbesc($p['album']), intval($p['height']), intval($p['width']), (intval($p['os_storage']) ? dbescbin($p['os_syspath']) : dbescbin($this->imageString())), intval($p['os_storage']), (intval($p['os_storage']) ? @filesize($p['os_syspath']) : strlen($this->imageString())), intval($p['imgscale']), intval($p['photo_usage']), dbesc($p['title']), dbesc($p['description']), dbesc($p['os_path']), dbesc($p['display_path']), dbesc($p['allow_cid']), dbesc($p['allow_gid']), dbesc($p['deny_cid']), dbesc($p['deny_gid']), dbescdate($p['expires']), intval($p['profile']), intval($x[0]['id'])); - } - else { - $p['created'] = (($arr['created']) ? $arr['created'] : $p['edited']); - $r = q("INSERT INTO photo + intval($p['aid']), + intval($p['uid']), + dbesc($p['xchan']), + dbesc($p['resource_id']), + dbescdate($p['created']), + dbescdate($p['edited']), + dbesc(basename($p['filename'])), + dbesc($p['mimetype']), + dbesc($p['album']), + intval($p['height']), + intval($p['width']), + (intval($p['os_storage']) ? dbescbin($p['os_syspath']) : dbescbin($this->imageString())), + intval($p['os_storage']), + (intval($p['os_storage']) ? @filesize($p['os_syspath']) : strlen($this->imageString())), + intval($p['imgscale']), + intval($p['photo_usage']), + dbesc($p['title']), + dbesc($p['description']), + dbesc($p['os_path']), + dbesc($p['display_path']), + dbesc($p['allow_cid']), + dbesc($p['allow_gid']), + dbesc($p['deny_cid']), + dbesc($p['deny_gid']), + dbescdate($p['expires']), + intval($p['profile']), + intval($x[0]['id']) + ); + } else { + $p['created'] = (($arr['created']) ? $arr['created'] : $p['edited']); + $r = q("INSERT INTO photo ( aid, uid, xchan, resource_id, created, edited, filename, mimetype, album, height, width, content, os_storage, filesize, imgscale, photo_usage, title, description, os_path, display_path, allow_cid, allow_gid, deny_cid, deny_gid, expires, profile ) VALUES ( %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, %d, '%s', %d, %d, %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d)", intval($p['aid']), intval($p['uid']), dbesc($p['xchan']), dbesc($p['resource_id']), dbescdate($p['created']), dbescdate($p['edited']), dbesc(basename($p['filename'])), dbesc($p['mimetype']), dbesc($p['album']), intval($p['height']), intval($p['width']), (intval($p['os_storage']) ? dbescbin($p['os_syspath']) : dbescbin($this->imageString())), intval($p['os_storage']), (intval($p['os_storage']) ? @filesize($p['os_syspath']) : strlen($this->imageString())), intval($p['imgscale']), intval($p['photo_usage']), dbesc($p['title']), dbesc($p['description']), dbesc($p['os_path']), dbesc($p['display_path']), dbesc($p['allow_cid']), dbesc($p['allow_gid']), dbesc($p['deny_cid']), dbesc($p['deny_gid']), dbescdate($p['expires']), intval($p['profile'])); - } - logger('Photo save imgscale ' . $p['imgscale'] . ' returned ' . intval($r)); + } + logger('Photo save imgscale ' . $p['imgscale'] . ' returned ' . intval($r)); - return $r; - } - - /** - * @brief Stores thumbnail to database or filesystem. - * - * @param array $arr - * @param scale int - * @return boolean|array - */ + return $r; + } - public function storeThumbnail($arr, $scale = 0, $animated = true) { - - $arr['imgscale'] = $scale; - - if (boolval(get_config('system','filesystem_storage_thumbnails', 1)) && $scale > 0) { - $channel = channelx_by_n($arr['uid']); - $arr['os_storage'] = 1; - $arr['os_syspath'] = 'store/' . $channel['channel_address'] . '/' . $arr['os_path'] . '-' . $scale; - if (! $this->saveImage($arr['os_syspath'], $animated)) { - return false; - } - } - - if (! $this->save($arr)) { - if (array_key_exists('os_syspath', $arr)) { - @unlink($arr['os_syspath']); - } - return false; - } - - return true; - } + /** + * @brief Stores thumbnail to database or filesystem. + * + * @param array $arr + * @param scale int + * @return bool|array + */ + public function storeThumbnail($arr, $scale = 0, $animated = true) + { + + $arr['imgscale'] = $scale; + + if (boolval(get_config('system', 'filesystem_storage_thumbnails', 1)) && $scale > 0) { + $channel = channelx_by_n($arr['uid']); + $arr['os_storage'] = 1; + $arr['os_syspath'] = 'store/' . $channel['channel_address'] . '/' . $arr['os_path'] . '-' . $scale; + if (! $this->saveImage($arr['os_syspath'], $animated)) { + return false; + } + } + + if (! $this->save($arr)) { + if (array_key_exists('os_syspath', $arr)) { + @unlink($arr['os_syspath']); + } + return false; + } + + return true; + } } diff --git a/Zotlabs/Photo/PhotoGd.php b/Zotlabs/Photo/PhotoGd.php index 83c450968..92c5244d7 100644 --- a/Zotlabs/Photo/PhotoGd.php +++ b/Zotlabs/Photo/PhotoGd.php @@ -2,199 +2,212 @@ namespace Zotlabs\Photo; +use function imagejpeg; +use function imagepng; +use function imagewebp; + /** * @brief GD photo driver. * */ -class PhotoGd extends PhotoDriver { +class PhotoGd extends PhotoDriver +{ - /** - * {@inheritDoc} - * @see \Zotlabs\Photo\PhotoDriver::supportedTypes() - */ - public function supportedTypes() { - $t = []; - $t['image/jpeg'] = 'jpg'; - if (imagetypes() & IMG_PNG) { - $t['image/png'] = 'png'; - } - if (imagetypes() & IMG_GIF) { - $t['image/gif'] = 'gif'; - } - if (imagetypes() & IMG_WEBP) { - $t['image/webp'] = 'webp'; - } + /** + * {@inheritDoc} + * @see \Zotlabs\Photo\PhotoDriver::supportedTypes() + */ + public function supportedTypes() + { + $t = []; + $t['image/jpeg'] = 'jpg'; + if (imagetypes() & IMG_PNG) { + $t['image/png'] = 'png'; + } + if (imagetypes() & IMG_GIF) { + $t['image/gif'] = 'gif'; + } + if (imagetypes() & IMG_WEBP) { + $t['image/webp'] = 'webp'; + } - return $t; - } + return $t; + } - protected function load($data, $type) { - $this->valid = false; - if (! $data) { - return; - } + protected function load($data, $type) + { + $this->valid = false; + if (! $data) { + return; + } - $this->image = @imagecreatefromstring($data); + $this->image = @imagecreatefromstring($data); - if ($this->image !== false) { - $this->valid = true; - $this->setDimensions(); - imagealphablending($this->image, false); - imagesavealpha($this->image, true); - } - else { - logger('image load failed'); - } - } + if ($this->image !== false) { + $this->valid = true; + $this->setDimensions(); + imagealphablending($this->image, false); + imagesavealpha($this->image, true); + } else { + logger('image load failed'); + } + } - protected function setDimensions() { - $this->width = imagesx($this->image); - $this->height = imagesy($this->image); - } + protected function setDimensions() + { + $this->width = imagesx($this->image); + $this->height = imagesy($this->image); + } - /** - * @brief GD driver does not preserve EXIF, so not need to clear it. - * - * @return void - */ - public function clearexif() { - return; - } + /** + * @brief GD driver does not preserve EXIF, so not need to clear it. + * + * @return void + */ + public function clearexif() + { + return; + } - protected function destroy() { - if ($this->is_valid()) { - imagedestroy($this->image); - } - } + protected function destroy() + { + if ($this->is_valid()) { + imagedestroy($this->image); + } + } - /** - * @brief Return a PHP image resource of the current image. - * - * @see \Zotlabs\Photo\PhotoDriver::getImage() - * - * @return boolean|resource - */ - public function getImage() { - if (! $this->is_valid()) { - return false; - } + /** + * @brief Return a PHP image resource of the current image. + * + * @see \Zotlabs\Photo\PhotoDriver::getImage() + * + * @return bool|resource + */ + public function getImage() + { + if (! $this->is_valid()) { + return false; + } - return $this->image; - } + return $this->image; + } - public function doScaleImage($dest_width, $dest_height) { + public function doScaleImage($dest_width, $dest_height) + { - $dest = imagecreatetruecolor($dest_width, $dest_height); - $width = imagesx($this->image); - $height = imagesy($this->image); + $dest = imagecreatetruecolor($dest_width, $dest_height); + $width = imagesx($this->image); + $height = imagesy($this->image); - imagealphablending($dest, false); - imagesavealpha($dest, true); - if ($this->type == 'image/png') { - imagefill($dest, 0, 0, imagecolorallocatealpha($dest, 0, 0, 0, 127)); // fill with alpha - } - imagecopyresampled($dest, $this->image, 0, 0, 0, 0, $dest_width, $dest_height, $width, $height); - if ($this->image) { - imagedestroy($this->image); - } + imagealphablending($dest, false); + imagesavealpha($dest, true); + if ($this->type == 'image/png') { + imagefill($dest, 0, 0, imagecolorallocatealpha($dest, 0, 0, 0, 127)); // fill with alpha + } + imagecopyresampled($dest, $this->image, 0, 0, 0, 0, $dest_width, $dest_height, $width, $height); + if ($this->image) { + imagedestroy($this->image); + } - $this->image = $dest; - $this->setDimensions(); - } + $this->image = $dest; + $this->setDimensions(); + } - public function rotate($degrees) { - if (! $this->is_valid()) { - return false; - } + public function rotate($degrees) + { + if (! $this->is_valid()) { + return false; + } - $this->image = imagerotate($this->image, $degrees, 0); - $this->setDimensions(); - } + $this->image = imagerotate($this->image, $degrees, 0); + $this->setDimensions(); + } - public function flip($horiz = true, $vert = false) { - if (! $this->is_valid()) { - return false; - } + public function flip($horiz = true, $vert = false) + { + if (! $this->is_valid()) { + return false; + } - $w = imagesx($this->image); - $h = imagesy($this->image); - $flipped = imagecreate($w, $h); - if ($horiz) { - for ($x = 0; $x < $w; $x++) { - imagecopy($flipped, $this->image, $x, 0, $w - $x - 1, 0, 1, $h); - } - } - if ($vert) { - for ($y = 0; $y < $h; $y++) { - imagecopy($flipped, $this->image, 0, $y, 0, $h - $y - 1, $w, 1); - } - } - $this->image = $flipped; - $this->setDimensions(); // Shouldn't really be necessary - } + $w = imagesx($this->image); + $h = imagesy($this->image); + $flipped = imagecreate($w, $h); + if ($horiz) { + for ($x = 0; $x < $w; $x++) { + imagecopy($flipped, $this->image, $x, 0, $w - $x - 1, 0, 1, $h); + } + } + if ($vert) { + for ($y = 0; $y < $h; $y++) { + imagecopy($flipped, $this->image, 0, $y, 0, $h - $y - 1, $w, 1); + } + } + $this->image = $flipped; + $this->setDimensions(); // Shouldn't really be necessary + } - public function cropImageRect($maxx, $maxy, $x, $y, $w, $h) { - if (! $this->is_valid()) { - return false; - } + public function cropImageRect($maxx, $maxy, $x, $y, $w, $h) + { + if (! $this->is_valid()) { + return false; + } - $dest = imagecreatetruecolor($maxx, $maxy); - imagealphablending($dest, false); - imagesavealpha($dest, true); - if ($this->type == 'image/png') { - imagefill($dest, 0, 0, imagecolorallocatealpha($dest, 0, 0, 0, 127)); // fill with alpha - } - imagecopyresampled($dest, $this->image, 0, 0, $x, $y, $maxx, $maxy, $w, $h); - if ($this->image) { - imagedestroy($this->image); - } - $this->image = $dest; - $this->setDimensions(); - } + $dest = imagecreatetruecolor($maxx, $maxy); + imagealphablending($dest, false); + imagesavealpha($dest, true); + if ($this->type == 'image/png') { + imagefill($dest, 0, 0, imagecolorallocatealpha($dest, 0, 0, 0, 127)); // fill with alpha + } + imagecopyresampled($dest, $this->image, 0, 0, $x, $y, $maxx, $maxy, $w, $h); + if ($this->image) { + imagedestroy($this->image); + } + $this->image = $dest; + $this->setDimensions(); + } - /** - * {@inheritDoc} - * @see \Zotlabs\Photo\PhotoDriver::imageString() - */ - public function imageString($animated = true) { - if (! $this->is_valid()) { - return false; - } + /** + * {@inheritDoc} + * @see \Zotlabs\Photo\PhotoDriver::imageString() + */ + public function imageString($animated = true) + { + if (! $this->is_valid()) { + return false; + } - $quality = false; + $quality = false; - ob_start(); + ob_start(); - switch ($this->getType()){ - case 'image/webp': + switch ($this->getType()) { + case 'image/webp': + imagewebp($this->image); + break; - \imagewebp($this->image); - break; - - case 'image/png': - $quality = get_config('system', 'png_quality'); - if ((! $quality) || ($quality > 9)) { - $quality = PNG_QUALITY; - } + case 'image/png': + $quality = get_config('system', 'png_quality'); + if ((! $quality) || ($quality > 9)) { + $quality = PNG_QUALITY; + } - \imagepng($this->image, NULL, $quality); - break; - case 'image/jpeg': - // gd can lack imagejpeg(), but we verify during installation it is available - default: - $quality = get_config('system', 'jpeg_quality'); - if ((! $quality) || ($quality > 100)) { - $quality = JPEG_QUALITY; - } + imagepng($this->image, null, $quality); + break; + case 'image/jpeg': + // gd can lack imagejpeg(), but we verify during installation it is available + default: + $quality = get_config('system', 'jpeg_quality'); + if ((! $quality) || ($quality > 100)) { + $quality = JPEG_QUALITY; + } - \imagejpeg($this->image, NULL, $quality); - break; - } - $string = ob_get_contents(); - ob_end_clean(); - - return $string; - } + imagejpeg($this->image, null, $quality); + break; + } + $string = ob_get_contents(); + ob_end_clean(); + return $string; + } } diff --git a/Zotlabs/Photo/PhotoImagick.php b/Zotlabs/Photo/PhotoImagick.php index 56867866d..14e757dbe 100644 --- a/Zotlabs/Photo/PhotoImagick.php +++ b/Zotlabs/Photo/PhotoImagick.php @@ -4,214 +4,230 @@ namespace Zotlabs\Photo; use Imagick; use Exception; - +use ImagickPixel; /** * @brief ImageMagick photo driver. */ -class PhotoImagick extends PhotoDriver { +class PhotoImagick extends PhotoDriver +{ - public function supportedTypes() { - return [ - 'image/jpeg' => 'jpg', - 'image/png' => 'png', - 'image/gif' => 'gif', - 'image/webp' => 'webp', - ]; - } + public function supportedTypes() + { + return [ + 'image/jpeg' => 'jpg', + 'image/png' => 'png', + 'image/gif' => 'gif', + 'image/webp' => 'webp', + ]; + } - private function get_FormatsMap() { - return [ - 'image/jpeg' => 'JPG', - 'image/png' => 'PNG', - 'image/gif' => 'GIF', - 'image/webp' => 'WEBP' - ]; - } + private function get_FormatsMap() + { + return [ + 'image/jpeg' => 'JPG', + 'image/png' => 'PNG', + 'image/gif' => 'GIF', + 'image/webp' => 'WEBP' + ]; + } - protected function load($data, $type) { - $this->valid = false; - $this->image = new Imagick(); + protected function load($data, $type) + { + $this->valid = false; + $this->image = new Imagick(); - if (! $data) { - return; - } + if (! $data) { + return; + } - try { - $this->image->readImageBlob($data); - } catch(Exception $e) { - logger('Imagick read failed'); - // logger('Imagick readImageBlob() exception:' . print_r($e, true)); - return; - } + try { + $this->image->readImageBlob($data); + } catch (Exception $e) { + logger('Imagick read failed'); + // logger('Imagick readImageBlob() exception:' . print_r($e, true)); + return; + } - /* - * Setup the image to the format it will be saved to - */ + /* + * Setup the image to the format it will be saved to + */ - $map = $this->get_FormatsMap(); - $format = $map[$type]; + $map = $this->get_FormatsMap(); + $format = $map[$type]; - if ($this->image) { - $this->image->setFormat($format); + if ($this->image) { + $this->image->setFormat($format); - // Always coalesce, if it is not a multi-frame image it won't hurt anyway - $this->image = $this->image->coalesceImages(); + // Always coalesce, if it is not a multi-frame image it won't hurt anyway + $this->image = $this->image->coalesceImages(); - $this->valid = true; - $this->setDimensions(); + $this->valid = true; + $this->setDimensions(); - /* - * setup the compression here, so we'll do it only once - */ - switch ($this->getType()) { - case 'image/png': - $quality = get_config('system', 'png_quality'); - if ((! $quality) || ($quality > 9)) { - $quality = PNG_QUALITY; - } - /* - * From http://www.imagemagick.org/script/command-line-options.php#quality: - * - * 'For the MNG and PNG image formats, the quality value sets - * the zlib compression level (quality / 10) and filter-type (quality % 10). - * The default PNG "quality" is 75, which means compression level 7 with adaptive PNG filtering, - * unless the image has a color map, in which case it means compression level 7 with no PNG filtering' - */ - $quality = $quality * 10; - $this->image->setCompressionQuality($quality); - break; - case 'image/jpeg': - $quality = get_config('system', 'jpeg_quality'); - if ((! $quality) || ($quality > 100)) { - $quality = JPEG_QUALITY; - } - $this->image->setCompressionQuality($quality); - default: - break; - } - } - } + /* + * setup the compression here, so we'll do it only once + */ + switch ($this->getType()) { + case 'image/png': + $quality = get_config('system', 'png_quality'); + if ((! $quality) || ($quality > 9)) { + $quality = PNG_QUALITY; + } + /* + * From http://www.imagemagick.org/script/command-line-options.php#quality: + * + * 'For the MNG and PNG image formats, the quality value sets + * the zlib compression level (quality / 10) and filter-type (quality % 10). + * The default PNG "quality" is 75, which means compression level 7 with adaptive PNG filtering, + * unless the image has a color map, in which case it means compression level 7 with no PNG filtering' + */ + $quality = $quality * 10; + $this->image->setCompressionQuality($quality); + break; + case 'image/jpeg': + $quality = get_config('system', 'jpeg_quality'); + if ((! $quality) || ($quality > 100)) { + $quality = JPEG_QUALITY; + } + $this->image->setCompressionQuality($quality); + default: + break; + } + } + } - protected function destroy() { - if ($this->is_valid()) { - $this->image->clear(); - $this->image->destroy(); - } - } + protected function destroy() + { + if ($this->is_valid()) { + $this->image->clear(); + $this->image->destroy(); + } + } - protected function setDimensions() { - $this->width = $this->image->getImageWidth(); - $this->height = $this->image->getImageHeight(); - } + protected function setDimensions() + { + $this->width = $this->image->getImageWidth(); + $this->height = $this->image->getImageHeight(); + } - /** - * @brief Strips the image of all profiles and comments. - * - * Keep ICC profile for better colors. - * - * @see \Zotlabs\Photo\PhotoDriver::clearexif() - */ - public function clearexif() { - $profiles = $this->image->getImageProfiles('icc', true); + /** + * @brief Strips the image of all profiles and comments. + * + * Keep ICC profile for better colors. + * + * @see \Zotlabs\Photo\PhotoDriver::clearexif() + */ + public function clearexif() + { + $profiles = $this->image->getImageProfiles('icc', true); - $this->image->stripImage(); + $this->image->stripImage(); - if (! empty($profiles)) { - $this->image->profileImage('icc', $profiles['icc']); - } - } + if (! empty($profiles)) { + $this->image->profileImage('icc', $profiles['icc']); + } + } - /** - * @brief Return a \Imagick object of the current image. - * - * @see \Zotlabs\Photo\PhotoDriver::getImage() - * - * @return boolean|\Imagick - */ - public function getImage() { - if (! $this->is_valid()) { - return false; - } + /** + * @brief Return a \Imagick object of the current image. + * + * @see \Zotlabs\Photo\PhotoDriver::getImage() + * + * @return bool|Imagick + */ + public function getImage() + { + if (! $this->is_valid()) { + return false; + } - $this->image = $this->image->deconstructImages(); - return $this->image; - } + $this->image = $this->image->deconstructImages(); + return $this->image; + } - public function doScaleImage($dest_width, $dest_height) { - /* - * If it is not animated, there will be only one iteration here, - * so don't bother checking - */ - // Don't forget to go back to the first frame - $this->image->setFirstIterator(); - do { - $this->image->scaleImage($dest_width, $dest_height); - } while($this->image->nextImage()); + public function doScaleImage($dest_width, $dest_height) + { + /* + * If it is not animated, there will be only one iteration here, + * so don't bother checking + */ + // Don't forget to go back to the first frame + $this->image->setFirstIterator(); + do { + $this->image->scaleImage($dest_width, $dest_height); + } while ($this->image->nextImage()); - $this->setDimensions(); - } + $this->setDimensions(); + } - public function rotate($degrees) { - if (! $this->is_valid()) { - return false; - } + public function rotate($degrees) + { + if (! $this->is_valid()) { + return false; + } - $this->image->setFirstIterator(); - do { - // ImageMagick rotates in the opposite direction of imagerotate() - $this->image->rotateImage(new \ImagickPixel(), -$degrees); - } while($this->image->nextImage()); + $this->image->setFirstIterator(); + do { + // ImageMagick rotates in the opposite direction of imagerotate() + $this->image->rotateImage(new ImagickPixel(), -$degrees); + } while ($this->image->nextImage()); - $this->setDimensions(); - } + $this->setDimensions(); + } - public function flip($horiz = true, $vert = false) { - if (! $this->is_valid()) { - return false; - } + public function flip($horiz = true, $vert = false) + { + if (! $this->is_valid()) { + return false; + } - $this->image->setFirstIterator(); - do { - if($horiz) $this->image->flipImage(); - if($vert) $this->image->flopImage(); - } while($this->image->nextImage()); + $this->image->setFirstIterator(); + do { + if ($horiz) { + $this->image->flipImage(); + } + if ($vert) { + $this->image->flopImage(); + } + } while ($this->image->nextImage()); - $this->setDimensions(); // Shouldn't really be necessary - } + $this->setDimensions(); // Shouldn't really be necessary + } - public function cropImageRect($maxx, $maxy, $x, $y, $w, $h) { - if (! $this->is_valid()) { - return false; - } + public function cropImageRect($maxx, $maxy, $x, $y, $w, $h) + { + if (! $this->is_valid()) { + return false; + } - $this->image->setFirstIterator(); - do { - $this->image->cropImage($w, $h, $x, $y); - /* - * We need to remove the canvas, - * or the image is not resized to the crop: - * http://php.net/manual/en/imagick.cropimage.php#97232 - */ - $this->image->setImagePage(0, 0, 0, 0); - } while($this->image->nextImage()); + $this->image->setFirstIterator(); + do { + $this->image->cropImage($w, $h, $x, $y); + /* + * We need to remove the canvas, + * or the image is not resized to the crop: + * http://php.net/manual/en/imagick.cropimage.php#97232 + */ + $this->image->setImagePage(0, 0, 0, 0); + } while ($this->image->nextImage()); - $this->doScaleImage($maxx, $maxy); - } + $this->doScaleImage($maxx, $maxy); + } - public function imageString($animated = true) { - if (! $this->is_valid()) { - return false; - } - - /* Clean it */ - $this->image = $this->image->deconstructImages(); - if ($animated) { - return $this->image->getImagesBlob(); - } - return $this->image->getImageBlob(); - } + public function imageString($animated = true) + { + if (! $this->is_valid()) { + return false; + } + /* Clean it */ + $this->image = $this->image->deconstructImages(); + if ($animated) { + return $this->image->getImagesBlob(); + } + return $this->image->getImageBlob(); + } } diff --git a/Zotlabs/Render/Comanche.php b/Zotlabs/Render/Comanche.php index 3adef7c1e..883774870 100644 --- a/Zotlabs/Render/Comanche.php +++ b/Zotlabs/Render/Comanche.php @@ -1,8 +1,11 @@ -get_condition_var($mtch[1]); + $default = $mtch[3]; + $cases = []; + $cntt = preg_match_all("/\[case (.*?)\](.*?)\[\/case\]/ism", $mtch[2], $cases, PREG_SET_ORDER); + if ($cntt) { + foreach ($cases as $case) { + if ($case[1] === $switch_var) { + $switch_done = 1; + $s = str_replace($mtch[0], $case[2], $s); + break; + } + } + if ($switch_done === 0) { + $s = str_replace($mtch[0], $default, $s); + } + } + } + } - $cnt = preg_match_all("/\[switch (.*?)\](.*?)\[default\](.*?)\[\/default\]\s*\[\/switch\]/ism", $s, $matches, PREG_SET_ORDER); - if ($cnt) { - foreach ($matches as $mtch) { - $switch_done = 0; - $switch_var = $this->get_condition_var($mtch[1]); - $default = $mtch[3]; - $cases = []; - $cntt = preg_match_all("/\[case (.*?)\](.*?)\[\/case\]/ism", $mtch[2], $cases, PREG_SET_ORDER); - if ($cntt) { - foreach ($cases as $case) { - if ($case[1] === $switch_var) { - $switch_done = 1; - $s = str_replace($mtch[0], $case[2], $s); - break; - } - } - if ($switch_done === 0) { - $s = str_replace($mtch[0], $default, $s); - } - } - } - } + $cnt = preg_match_all("/\[if (.*?)\](.*?)\[else\](.*?)\[\/if\]/ism", $s, $matches, PREG_SET_ORDER); + if ($cnt) { + foreach ($matches as $mtch) { + if ($this->test_condition($mtch[1])) { + $s = str_replace($mtch[0], $mtch[2], $s); + } else { + $s = str_replace($mtch[0], $mtch[3], $s); + } + } + } else { + $cnt = preg_match_all("/\[if (.*?)\](.*?)\[\/if\]/ism", $s, $matches, PREG_SET_ORDER); + if ($cnt) { + foreach ($matches as $mtch) { + if ($this->test_condition($mtch[1])) { + $s = str_replace($mtch[0], $mtch[2], $s); + } else { + $s = str_replace($mtch[0], '', $s); + } + } + } + } + if ($pass == 0) { + $this->parse_pass0($s); + } else { + $this->parse_pass1($s); + } + } - $cnt = preg_match_all("/\[if (.*?)\](.*?)\[else\](.*?)\[\/if\]/ism", $s, $matches, PREG_SET_ORDER); - if ($cnt) { - foreach ($matches as $mtch) { - if ($this->test_condition($mtch[1])) { - $s = str_replace($mtch[0], $mtch[2], $s); - } - else { - $s = str_replace($mtch[0], $mtch[3], $s); - } - } - } - else { - $cnt = preg_match_all("/\[if (.*?)\](.*?)\[\/if\]/ism", $s, $matches, PREG_SET_ORDER); - if ($cnt) { - foreach ($matches as $mtch) { - if ($this->test_condition($mtch[1])) { - $s = str_replace($mtch[0], $mtch[2], $s); - } - else { - $s = str_replace($mtch[0], '', $s); - } - } - } - } - if ($pass == 0) { - $this->parse_pass0($s); - } - else { - $this->parse_pass1($s); - } - } + public function parse_pass0($s) + { - function parse_pass0($s) { + $matches = null; - $matches = null; + $cnt = preg_match("/\[layout\](.*?)\[\/layout\]/ism", $s, $matches); + if ($cnt) { + App::$page['template'] = trim($matches[1]); + } - $cnt = preg_match("/\[layout\](.*?)\[\/layout\]/ism", $s, $matches); - if ($cnt) { - App::$page['template'] = trim($matches[1]); - } - - $cnt = preg_match("/\[template=(.*?)\](.*?)\[\/template\]/ism", $s, $matches); - if ($cnt) { - App::$page['template'] = trim($matches[2]); - App::$page['template_style'] = trim($matches[2]) . '_' . $matches[1]; - } + $cnt = preg_match("/\[template=(.*?)\](.*?)\[\/template\]/ism", $s, $matches); + if ($cnt) { + App::$page['template'] = trim($matches[2]); + App::$page['template_style'] = trim($matches[2]) . '_' . $matches[1]; + } - $cnt = preg_match("/\[template\](.*?)\[\/template\]/ism", $s, $matches); - if ($cnt) { - App::$page['template'] = trim($matches[1]); - } + $cnt = preg_match("/\[template\](.*?)\[\/template\]/ism", $s, $matches); + if ($cnt) { + App::$page['template'] = trim($matches[1]); + } - $cnt = preg_match("/\[theme=(.*?)\](.*?)\[\/theme\]/ism", $s, $matches); - if ($cnt) { - App::$layout['schema'] = trim($matches[1]); - App::$layout['theme'] = trim($matches[2]); - } + $cnt = preg_match("/\[theme=(.*?)\](.*?)\[\/theme\]/ism", $s, $matches); + if ($cnt) { + App::$layout['schema'] = trim($matches[1]); + App::$layout['theme'] = trim($matches[2]); + } - $cnt = preg_match("/\[theme\](.*?)\[\/theme\]/ism", $s, $matches); - if ($cnt) { - App::$layout['theme'] = trim($matches[1]); - } - - $cnt = preg_match("/\[navbar\](.*?)\[\/navbar\]/ism", $s, $matches); - if ($cnt) { - App::$layout['navbar'] = trim($matches[1]); - } + $cnt = preg_match("/\[theme\](.*?)\[\/theme\]/ism", $s, $matches); + if ($cnt) { + App::$layout['theme'] = trim($matches[1]); + } - $cnt = preg_match_all("/\[webpage\](.*?)\[\/webpage\]/ism", $s, $matches, PREG_SET_ORDER); - if ($cnt) { - // only the last webpage definition is used if there is more than one - foreach ($matches as $mtch) { - App::$layout['webpage'] = $this->webpage($a,$mtch[1]); - } - } - } + $cnt = preg_match("/\[navbar\](.*?)\[\/navbar\]/ism", $s, $matches); + if ($cnt) { + App::$layout['navbar'] = trim($matches[1]); + } - function parse_pass1($s) { - $cnt = preg_match_all("/\[region=(.*?)\](.*?)\[\/region\]/ism", $s, $matches, PREG_SET_ORDER); - if ($cnt) { - foreach ($matches as $mtch) { - App::$layout['region_' . $mtch[1]] = $this->region($mtch[2],$mtch[1]); - } - } - } + $cnt = preg_match_all("/\[webpage\](.*?)\[\/webpage\]/ism", $s, $matches, PREG_SET_ORDER); + if ($cnt) { + // only the last webpage definition is used if there is more than one + foreach ($matches as $mtch) { + App::$layout['webpage'] = $this->webpage($a, $mtch[1]); + } + } + } - /** - * @brief Replace conditional variables with real values. - * - * Currently supported condition variables: - * * $config.xxx.yyy - get_config with cat = xxx and k = yyy - * * $request - request uri for this page - * * $observer.language - viewer's preferred language (closest match) - * * $observer.address - xchan_addr or false - * * $observer.name - xchan_name or false - * * $observer - xchan_hash of observer or empty string - * * $local_channel - logged in channel_id or false - * - * @param string $v The conditional variable name - * @return string|boolean - */ - function get_condition_var($v) { - if ($v) { - $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] === 'local_channel') { - return local_channel(); - } - elseif ($x[0] === 'observer') { - if (count($x) > 1) { - if ($x[1] == 'language') { - return App::$language; - } - $y = App::get_observer(); - if (! $y) { - return false; - } - if ($x[1] == 'address') { - return $y['xchan_addr']; - } - elseif ($x[1] == 'name') { - return $y['xchan_name']; - } - elseif ($x[1] == 'webname') { - return substr($y['xchan_addr'],0,strpos($y['xchan_addr'],'@')); - } - return false; - } - return get_observer_hash(); - } - else { - return false; - } - } - return false; - } + public function parse_pass1($s) + { + $cnt = preg_match_all("/\[region=(.*?)\](.*?)\[\/region\]/ism", $s, $matches, PREG_SET_ORDER); + if ($cnt) { + foreach ($matches as $mtch) { + App::$layout['region_' . $mtch[1]] = $this->region($mtch[2], $mtch[1]); + } + } + } - /** - * @brief Test for Conditional Execution conditions. - * - * 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'; - * - [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. - * - * @param int|string $s - * @return boolean - */ - function test_condition($s) { + /** + * @brief Replace conditional variables with real values. + * + * Currently supported condition variables: + * * $config.xxx.yyy - get_config with cat = xxx and k = yyy + * * $request - request uri for this page + * * $observer.language - viewer's preferred language (closest match) + * * $observer.address - xchan_addr or false + * * $observer.name - xchan_name or false + * * $observer - xchan_hash of observer or empty string + * * $local_channel - logged in channel_id or false + * + * @param string $v The conditional variable name + * @return string|bool + */ + public function get_condition_var($v) + { + if ($v) { + $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] === 'local_channel') { + return local_channel(); + } elseif ($x[0] === 'observer') { + if (count($x) > 1) { + if ($x[1] == 'language') { + return App::$language; + } + $y = App::get_observer(); + if (!$y) { + return false; + } + if ($x[1] == 'address') { + return $y['xchan_addr']; + } elseif ($x[1] == 'name') { + return $y['xchan_name']; + } elseif ($x[1] == 'webname') { + return substr($y['xchan_addr'], 0, strpos($y['xchan_addr'], '@')); + } + return false; + } + return get_observer_hash(); + } else { + return false; + } + } + return 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; - } + /** + * @brief Test for Conditional Execution conditions. + * + * 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'; + * - [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. + * + * @param int|string $s + * @return bool + */ + public function test_condition($s) + { - 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 (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])) { + 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 ($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 ($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)) { - 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) && array_key_exists(trim($matches[2]),$x)) { - 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)) { + return true; + } + return false; + } - if (preg_match('/[\$](.*?)$/',$s,$matches)) { - $x = $this->get_condition_var($matches[1]); - if ($x) { - return true; - } - return false; - } - return false; - } + if (preg_match('/[\$](.*?)\s\{\*\}\s(.*?)$/', $s, $matches)) { + $x = $this->get_condition_var($matches[1]); + if (is_array($x) && array_key_exists(trim($matches[2]), $x)) { + return true; + } + return false; + } - /** - * @brief Return rendered menu for current channel_id. - * - * @see menu_render() - * @param string $s - * @param string $class (optional) default empty - * @return string - */ - function menu($s, $class = '') { + if (preg_match('/[\$](.*?)$/', $s, $matches)) { + $x = $this->get_condition_var($matches[1]); + if ($x) { + return true; + } + return false; + } + return false; + } - $channel_id = $this->get_channel_id(); - $name = $s; + /** + * @brief Return rendered menu for current channel_id. + * + * @param string $s + * @param string $class (optional) default empty + * @return string + * @see menu_render() + */ + public function menu($s, $class = '') + { - $cnt = preg_match_all("/\[var=(.*?)\](.*?)\[\/var\]/ism", $s, $matches, PREG_SET_ORDER); - if ($cnt) { - foreach ($matches as $mtch) { - $var[$mtch[1]] = $mtch[2]; - $name = str_replace($mtch[0], '', $name); - } - } + $channel_id = $this->get_channel_id(); + $name = $s; - if ($channel_id) { - $m = menu_fetch($name, $channel_id, get_observer_hash()); - return menu_render($m, $class, $edit = false, $var); - } - } + $cnt = preg_match_all("/\[var=(.*?)\](.*?)\[\/var\]/ism", $s, $matches, PREG_SET_ORDER); + if ($cnt) { + foreach ($matches as $mtch) { + $var[$mtch[1]] = $mtch[2]; + $name = str_replace($mtch[0], '', $name); + } + } + + if ($channel_id) { + $m = menu_fetch($name, $channel_id, get_observer_hash()); + return menu_render($m, $class, $edit = false, $var); + } + } - function replace_region($match) { - if (array_key_exists($match[1], App::$page)) { - return App::$page[$match[1]]; - } - } + public function replace_region($match) + { + if (array_key_exists($match[1], App::$page)) { + return App::$page[$match[1]]; + } + } - /** - * @brief Returns the channel_id of the profile owner of the page. - * - * Returns the channel_id of the profile owner of the page, or the local_channel - * if there is no profile owner. Otherwise returns 0. - * - * @return int channel_id - */ - function get_channel_id() { - $channel_id = ((is_array(App::$profile)) ? App::$profile['profile_uid'] : 0); + /** + * @brief Returns the channel_id of the profile owner of the page. + * + * Returns the channel_id of the profile owner of the page, or the local_channel + * if there is no profile owner. Otherwise returns 0. + * + * @return int channel_id + */ + public function get_channel_id() + { + $channel_id = ((is_array(App::$profile)) ? App::$profile['profile_uid'] : 0); - if ((! $channel_id) && (local_channel())) { - $channel_id = local_channel(); - } - return $channel_id; - } + if ((!$channel_id) && (local_channel())) { + $channel_id = local_channel(); + } + return $channel_id; + } - /** - * @brief Returns a parsed block. - * - * @param string $s - * @param string $class (optional) default empty - * @return string parsed HTML of block - */ - function block($s, $class = '') { - $var = []; - $matches = []; - $name = $s; - $class = (($class) ? $class : 'bblock widget'); + /** + * @brief Returns a parsed block. + * + * @param string $s + * @param string $class (optional) default empty + * @return string parsed HTML of block + */ + public function block($s, $class = '') + { + $var = []; + $matches = []; + $name = $s; + $class = (($class) ? $class : 'bblock widget'); - $cnt = preg_match_all("/\[var=(.*?)\](.*?)\[\/var\]/ism", $s, $matches, PREG_SET_ORDER); - if ($cnt) { - foreach ($matches as $mtch) { - $var[$mtch[1]] = $mtch[2]; - $name = str_replace($mtch[0], '', $name); - } - } + $cnt = preg_match_all("/\[var=(.*?)\](.*?)\[\/var\]/ism", $s, $matches, PREG_SET_ORDER); + if ($cnt) { + foreach ($matches as $mtch) { + $var[$mtch[1]] = $mtch[2]; + $name = str_replace($mtch[0], '', $name); + } + } - $o = ''; - $channel_id = $this->get_channel_id(); + $o = ''; + $channel_id = $this->get_channel_id(); - if ($channel_id) { - $r = q("select * from item inner join iconfig on iconfig.iid = item.id and item.uid = %d + if ($channel_id) { + $r = q( + "select * from item inner join iconfig on iconfig.iid = item.id and item.uid = %d and iconfig.cat = 'system' and iconfig.k = 'BUILDBLOCK' and iconfig.v = '%s' limit 1", - intval($channel_id), - dbesc($name) - ); + intval($channel_id), + dbesc($name) + ); - if ($r) { - //check for eventual menus in the block and parse them - $cnt = preg_match_all("/\[menu\](.*?)\[\/menu\]/ism", $r[0]['body'], $matches, PREG_SET_ORDER); - if ($cnt) { - foreach ($matches as $mtch) { - $r[0]['body'] = str_replace($mtch[0], $this->menu(trim($mtch[1])), $r[0]['body']); - } - } - $cnt = preg_match_all("/\[menu=(.*?)\](.*?)\[\/menu\]/ism", $r[0]['body'], $matches, PREG_SET_ORDER); - if ($cnt) { - foreach ($matches as $mtch) { - $r[0]['body'] = str_replace($mtch[0],$this->menu(trim($mtch[2]),$mtch[1]),$r[0]['body']); - } - } + if ($r) { + //check for eventual menus in the block and parse them + $cnt = preg_match_all("/\[menu\](.*?)\[\/menu\]/ism", $r[0]['body'], $matches, PREG_SET_ORDER); + if ($cnt) { + foreach ($matches as $mtch) { + $r[0]['body'] = str_replace($mtch[0], $this->menu(trim($mtch[1])), $r[0]['body']); + } + } + $cnt = preg_match_all("/\[menu=(.*?)\](.*?)\[\/menu\]/ism", $r[0]['body'], $matches, PREG_SET_ORDER); + if ($cnt) { + foreach ($matches as $mtch) { + $r[0]['body'] = str_replace($mtch[0], $this->menu(trim($mtch[2]), $mtch[1]), $r[0]['body']); + } + } - // emit the block - $o .= (($var['wrap'] == 'none') ? '' : '
      '); + // emit the block + $o .= (($var['wrap'] == 'none') ? '' : '
      '); - if ($r[0]['title'] && trim($r[0]['body']) != '$content') { - $o .= '

      ' . $r[0]['title'] . '

      '; - } + if ($r[0]['title'] && trim($r[0]['body']) != '$content') { + $o .= '

      ' . $r[0]['title'] . '

      '; + } - if (trim($r[0]['body']) === '$content') { - $o .= App::$page['content']; - } - else { - $o .= prepare_text($r[0]['body'], $r[0]['mimetype']); - } + if (trim($r[0]['body']) === '$content') { + $o .= App::$page['content']; + } else { + $o .= prepare_text($r[0]['body'], $r[0]['mimetype']); + } - $o .= (($var['wrap'] == 'none') ? '' : '
      '); - } - } + $o .= (($var['wrap'] == 'none') ? '' : '
      '); + } + } - return $o; - } + return $o; + } - /** - * @brief Include JS depending on framework. - * - * @param string $s - * @return string - */ - function js($s) { + /** + * @brief Include JS depending on framework. + * + * @param string $s + * @return string + */ + public function js($s) + { - switch ($s) { - case 'jquery': - $path = 'view/js/jquery.js'; - break; - case 'bootstrap': - $path = 'vendor/twbs/bootstrap/dist/js/bootstrap.bundle.min.js'; - break; - case 'foundation': - $path = 'library/foundation/js/foundation.js'; - $init = "\r\n" . ''; - break; - } + switch ($s) { + case 'jquery': + $path = 'view/js/jquery.js'; + break; + case 'bootstrap': + $path = 'vendor/twbs/bootstrap/dist/js/bootstrap.bundle.min.js'; + break; + case 'foundation': + $path = 'library/foundation/js/foundation.js'; + $init = "\r\n" . ''; + break; + } - $ret = ''; - if ($init) { - $ret .= $init; - } - return $ret; - } + $ret = ''; + if ($init) { + $ret .= $init; + } + return $ret; + } - /** - * @brief Include CSS depending on framework. - * - * @param string $s - * @return string - */ - function css($s) { + /** + * @brief Include CSS depending on framework. + * + * @param string $s + * @return string + */ + public function css($s) + { - switch ($s) { - case 'bootstrap': - $path = 'vendor/twbs/bootstrap/dist/css/bootstrap.min.css'; - break; - case 'foundation': - $path = 'library/foundation/css/foundation.min.css'; - break; - } + switch ($s) { + case 'bootstrap': + $path = 'vendor/twbs/bootstrap/dist/css/bootstrap.min.css'; + break; + case 'foundation': + $path = 'library/foundation/css/foundation.min.css'; + break; + } - $ret = ''; + $ret = ''; - return $ret; - } + return $ret; + } - /** - * This doesn't really belong in Comanche, but it could also be argued that it is the perfect place. - * We need to be able to select what kind of template and decoration to use for the webpage at the heart of our content. - * For now we'll allow an '[authored]' element which defaults to name and date, or 'none' to remove these, and perhaps - * 'full' to provide a social network style profile photo. - * - * But leave it open to have richer templating options and perhaps ultimately discard this one, once we have a better idea - * of what template and webpage options we might desire. - * - * @param[in,out] array $a - * @param string $s - * @return array - */ - function webpage(&$a, $s) { - $ret = []; - $matches = []; + /** + * This doesn't really belong in Comanche, but it could also be argued that it is the perfect place. + * We need to be able to select what kind of template and decoration to use for the webpage at the heart of our content. + * For now we'll allow an '[authored]' element which defaults to name and date, or 'none' to remove these, and perhaps + * 'full' to provide a social network style profile photo. + * + * But leave it open to have richer templating options and perhaps ultimately discard this one, once we have a better idea + * of what template and webpage options we might desire. + * + * @param[in,out] array $a + * @param string $s + * @return array + */ + public function webpage(&$a, $s) + { + $ret = []; + $matches = []; - $cnt = preg_match_all("/\[authored\](.*?)\[\/authored\]/ism", $s, $matches, PREG_SET_ORDER); - if ($cnt) { - foreach ($matches as $mtch) { - $ret['authored'] = $mtch[1]; - } - } + $cnt = preg_match_all("/\[authored\](.*?)\[\/authored\]/ism", $s, $matches, PREG_SET_ORDER); + if ($cnt) { + foreach ($matches as $mtch) { + $ret['authored'] = $mtch[1]; + } + } - return $ret; - } + return $ret; + } - /** - * @brief Render a widget. - * - * @param string $name - * @param string $text - */ - function widget($name, $text) { - $vars = []; - $matches = []; + /** + * @brief Render a widget. + * + * @param string $name + * @param string $text + */ + public function widget($name, $text) + { + $vars = []; + $matches = []; - $cnt = preg_match_all("/\[var=(.*?)\](.*?)\[\/var\]/ism", $text, $matches, PREG_SET_ORDER); - if ($cnt) { - foreach ($matches as $mtch) { - $vars[$mtch[1]] = $mtch[2]; - } - } + $cnt = preg_match_all("/\[var=(.*?)\](.*?)\[\/var\]/ism", $text, $matches, PREG_SET_ORDER); + if ($cnt) { + foreach ($matches as $mtch) { + $vars[$mtch[1]] = $mtch[2]; + } + } - if (! purify_filename($name)) { - return ''; - } + if (!purify_filename($name)) { + return ''; + } - $clsname = ucfirst($name); - $nsname = "\\Zotlabs\\Widget\\" . $clsname; + $clsname = ucfirst($name); + $nsname = "\\Zotlabs\\Widget\\" . $clsname; - $found = false; - $widgets = \Zotlabs\Extend\Widget::get(); - if ($widgets) { - foreach ($widgets as $widget) { - if (is_array($widget) && strtolower($widget[1]) === strtolower($name) && file_exists($widget[0])) { - require_once($widget[0]); - $found = true; - } - } - } + $found = false; + $widgets = Widget::get(); + if ($widgets) { + foreach ($widgets as $widget) { + if (is_array($widget) && strtolower($widget[1]) === strtolower($name) && file_exists($widget[0])) { + require_once($widget[0]); + $found = true; + } + } + } - if (! $found) { - if (file_exists('Zotlabs/SiteWidget/' . $clsname . '.php')) { - require_once('Zotlabs/SiteWidget/' . $clsname . '.php'); - } - elseif (file_exists('widget/' . $clsname . '/' . $clsname . '.php')) { - require_once('widget/' . $clsname . '/' . $clsname . '.php'); - } - elseif (file_exists('Zotlabs/Widget/' . $clsname . '.php')) { - require_once('Zotlabs/Widget/' . $clsname . '.php'); - } - else { - $pth = theme_include($clsname . '.php'); - if ($pth) { - require_once($pth); - } - } - } + if (!$found) { + if (file_exists('Zotlabs/SiteWidget/' . $clsname . '.php')) { + require_once('Zotlabs/SiteWidget/' . $clsname . '.php'); + } elseif (file_exists('widget/' . $clsname . '/' . $clsname . '.php')) { + require_once('widget/' . $clsname . '/' . $clsname . '.php'); + } elseif (file_exists('Zotlabs/Widget/' . $clsname . '.php')) { + require_once('Zotlabs/Widget/' . $clsname . '.php'); + } else { + $pth = theme_include($clsname . '.php'); + if ($pth) { + require_once($pth); + } + } + } - if (class_exists($nsname)) { - $x = new $nsname; - $f = 'widget'; - if (method_exists($x,$f)) { - return $x->$f($vars); - } - } + if (class_exists($nsname)) { + $x = new $nsname(); + $f = 'widget'; + if (method_exists($x, $f)) { + return $x->$f($vars); + } + } - $func = 'widget_' . trim($name); + $func = 'widget_' . trim($name); - if (! function_exists($func)) { - if (file_exists('widget/' . trim($name) . '.php')) { - require_once('widget/' . trim($name) . '.php'); - } - elseif (file_exists('widget/' . trim($name) . '/' . trim($name) . '.php')) { - require_once('widget/' . trim($name) . '/' . trim($name) . '.php'); - } - if (! function_exists($func)) { - $theme_widget = $func . '.php'; - if (theme_include($theme_widget)) { - require_once(theme_include($theme_widget)); - } - } - } + if (!function_exists($func)) { + if (file_exists('widget/' . trim($name) . '.php')) { + require_once('widget/' . trim($name) . '.php'); + } elseif (file_exists('widget/' . trim($name) . '/' . trim($name) . '.php')) { + require_once('widget/' . trim($name) . '/' . trim($name) . '.php'); + } + if (!function_exists($func)) { + $theme_widget = $func . '.php'; + if (theme_include($theme_widget)) { + require_once(theme_include($theme_widget)); + } + } + } - if (function_exists($func)) { - return $func($vars); - } - } + if (function_exists($func)) { + return $func($vars); + } + } - function region($s,$region_name) { + public function region($s, $region_name) + { - $s = str_replace('$region',$region_name,$s); + $s = str_replace('$region', $region_name, $s); - $matches = []; + $matches = []; - $cnt = preg_match_all("/\[menu\](.*?)\[\/menu\]/ism", $s, $matches, PREG_SET_ORDER); - if ($cnt) { - foreach ($matches as $mtch) { - $s = str_replace($mtch[0], $this->menu(trim($mtch[1])), $s); - } - } + $cnt = preg_match_all("/\[menu\](.*?)\[\/menu\]/ism", $s, $matches, PREG_SET_ORDER); + if ($cnt) { + foreach ($matches as $mtch) { + $s = str_replace($mtch[0], $this->menu(trim($mtch[1])), $s); + } + } - // menu class e.g. [menu=horizontal]my_menu[/menu] or [menu=tabbed]my_menu[/menu] - // allows different menu renderings to be applied + // menu class e.g. [menu=horizontal]my_menu[/menu] or [menu=tabbed]my_menu[/menu] + // allows different menu renderings to be applied - $cnt = preg_match_all("/\[menu=(.*?)\](.*?)\[\/menu\]/ism", $s, $matches, PREG_SET_ORDER); - if ($cnt) { - foreach ($matches as $mtch) { - $s = str_replace($mtch[0],$this->menu(trim($mtch[2]),$mtch[1]),$s); - } - } - $cnt = preg_match_all("/\[block\](.*?)\[\/block\]/ism", $s, $matches, PREG_SET_ORDER); - if ($cnt) { - foreach ($matches as $mtch) { - $s = str_replace($mtch[0],$this->block(trim($mtch[1])),$s); - } - } + $cnt = preg_match_all("/\[menu=(.*?)\](.*?)\[\/menu\]/ism", $s, $matches, PREG_SET_ORDER); + if ($cnt) { + foreach ($matches as $mtch) { + $s = str_replace($mtch[0], $this->menu(trim($mtch[2]), $mtch[1]), $s); + } + } + $cnt = preg_match_all("/\[block\](.*?)\[\/block\]/ism", $s, $matches, PREG_SET_ORDER); + if ($cnt) { + foreach ($matches as $mtch) { + $s = str_replace($mtch[0], $this->block(trim($mtch[1])), $s); + } + } - $cnt = preg_match_all("/\[block=(.*?)\](.*?)\[\/block\]/ism", $s, $matches, PREG_SET_ORDER); - if ($cnt) { - foreach ($matches as $mtch) { - $s = str_replace($mtch[0],$this->block(trim($mtch[2]),trim($mtch[1])),$s); - } - } + $cnt = preg_match_all("/\[block=(.*?)\](.*?)\[\/block\]/ism", $s, $matches, PREG_SET_ORDER); + if ($cnt) { + foreach ($matches as $mtch) { + $s = str_replace($mtch[0], $this->block(trim($mtch[2]), trim($mtch[1])), $s); + } + } - $cnt = preg_match_all("/\[js\](.*?)\[\/js\]/ism", $s, $matches, PREG_SET_ORDER); - if ($cnt) { - foreach ($matches as $mtch) { - $s = str_replace($mtch[0],$this->js(trim($mtch[1])),$s); - } - } + $cnt = preg_match_all("/\[js\](.*?)\[\/js\]/ism", $s, $matches, PREG_SET_ORDER); + if ($cnt) { + foreach ($matches as $mtch) { + $s = str_replace($mtch[0], $this->js(trim($mtch[1])), $s); + } + } - $cnt = preg_match_all("/\[css\](.*?)\[\/css\]/ism", $s, $matches, PREG_SET_ORDER); - if ($cnt) { - foreach ($matches as $mtch) { - $s = str_replace($mtch[0],$this->css(trim($mtch[1])),$s); - } - } + $cnt = preg_match_all("/\[css\](.*?)\[\/css\]/ism", $s, $matches, PREG_SET_ORDER); + if ($cnt) { + foreach ($matches as $mtch) { + $s = str_replace($mtch[0], $this->css(trim($mtch[1])), $s); + } + } - $cnt = preg_match_all("/\[widget=(.*?)\](.*?)\[\/widget\]/ism", $s, $matches, PREG_SET_ORDER); - if ($cnt) { - foreach ($matches as $mtch) { - $s = str_replace($mtch[0],$this->widget(trim($mtch[1]),$mtch[2]),$s); - } - } + $cnt = preg_match_all("/\[widget=(.*?)\](.*?)\[\/widget\]/ism", $s, $matches, PREG_SET_ORDER); + if ($cnt) { + foreach ($matches as $mtch) { + $s = str_replace($mtch[0], $this->widget(trim($mtch[1]), $mtch[2]), $s); + } + } - return $s; - } + return $s; + } - /** - * @brief Registers a page template/variant for use by Comanche selectors. - * - * @param array $arr - * 'template' => template name - * 'variant' => array( - * 'name' => variant name - * 'desc' => text description - * 'regions' => array( - * 'name' => name - * 'desc' => text description - * ) - * ) - */ - function register_page_template($arr) { - App::$page_layouts[$arr['template']] = array($arr['variant']); - return; - } - + /** + * @brief Registers a page template/variant for use by Comanche selectors. + * + * @param array $arr + * 'template' => template name + * 'variant' => array( + * 'name' => variant name + * 'desc' => text description + * 'regions' => array( + * 'name' => name + * 'desc' => text description + * ) + * ) + */ + public function register_page_template($arr) + { + App::$page_layouts[$arr['template']] = array($arr['variant']); + return; + } } diff --git a/Zotlabs/Render/SimpleTemplate.php b/Zotlabs/Render/SimpleTemplate.php index 83b92eb28..27c062720 100644 --- a/Zotlabs/Render/SimpleTemplate.php +++ b/Zotlabs/Render/SimpleTemplate.php @@ -2,326 +2,351 @@ namespace Zotlabs\Render; -define ("KEY_NOT_EXISTS", '^R_key_not_Exists^'); +define("KEY_NOT_EXISTS", '^R_key_not_Exists^'); -class SimpleTemplate implements TemplateEngine { +class SimpleTemplate implements TemplateEngine +{ - static $name = 'internal'; + public static $name = 'internal'; - var $r; - var $search; - var $replace; - var $stack = []; - var $nodes = []; - var $done = false; - var $d = false; - var $lang = null; - var $debug = false; + public $r; + public $search; + public $replace; + public $stack = []; + public $nodes = []; + public $done = false; + public $d = false; + public $lang = null; + public $debug = false; - private function _preg_error() { - switch (preg_last_error()) { - case PREG_INTERNAL_ERROR: echo('PREG_INTERNAL_ERROR'); break; - case PREG_BACKTRACK_LIMIT_ERROR: echo('PREG_BACKTRACK_LIMIT_ERROR'); break; - case PREG_RECURSION_LIMIT_ERROR: echo('PREG_RECURSION_LIMIT_ERROR'); break; - case PREG_BAD_UTF8_ERROR: echo('PREG_BAD_UTF8_ERROR'); break; + private function _preg_error() + { + switch (preg_last_error()) { + case PREG_INTERNAL_ERROR: + echo('PREG_INTERNAL_ERROR'); + break; + case PREG_BACKTRACK_LIMIT_ERROR: + echo('PREG_BACKTRACK_LIMIT_ERROR'); + break; + case PREG_RECURSION_LIMIT_ERROR: + echo('PREG_RECURSION_LIMIT_ERROR'); + break; + case PREG_BAD_UTF8_ERROR: + echo('PREG_BAD_UTF8_ERROR'); + break; // This is only valid for php > 5.3, not certain how to code around it for unit tests -// case PREG_BAD_UTF8_OFFSET_ERROR: echo('PREG_BAD_UTF8_OFFSET_ERROR'); break; - default: - // die("Unknown preg error."); - return; - } - echo "
      ";
      -		debug_print_backtrace();
      -		die();
      -	}
      +//          case PREG_BAD_UTF8_OFFSET_ERROR: echo('PREG_BAD_UTF8_OFFSET_ERROR'); break;
      +            default:
      +                // die("Unknown preg error.");
      +                return;
      +        }
      +        echo "
      ";
      +        debug_print_backtrace();
      +        die();
      +    }
       
      -	private function _push_stack() {
      -		$this->stack[] = [ $this->r, $this->nodes ];
      -	}
      +    private function _push_stack()
      +    {
      +        $this->stack[] = [ $this->r, $this->nodes ];
      +    }
       
      -	private function _pop_stack(){
      -		list($this->r, $this->nodes) = array_pop($this->stack);
      -	}
      +    private function _pop_stack()
      +    {
      +        list($this->r, $this->nodes) = array_pop($this->stack);
      +    }
       
      -	private function _get_var($name, $retNoKey = false) {
      -		$keys = array_map('trim',explode('.',$name));
      -		if ($retNoKey && ! array_key_exists($keys[0], $this->r)) {
      -			return KEY_NOT_EXISTS;
      -		}
      +    private function _get_var($name, $retNoKey = false)
      +    {
      +        $keys = array_map('trim', explode('.', $name));
      +        if ($retNoKey && ! array_key_exists($keys[0], $this->r)) {
      +            return KEY_NOT_EXISTS;
      +        }
       
      -		$val = $this->r;
      -		foreach ($keys as $k) {
      -			$val = (isset($val[$k]) ? $val[$k] : null);
      -		}
      +        $val = $this->r;
      +        foreach ($keys as $k) {
      +            $val = (isset($val[$k]) ? $val[$k] : null);
      +        }
       
      -		return template_escape($val);
      -	}
      +        return template_escape($val);
      +    }
       
      -	/**
      -	 * IF node
      -	 * \code
      -	 * {{ if <$var> }}...[{{ else }} ...] {{ endif }}
      -	 * {{ if <$var>== }}...[{{ else }} ...]{{ endif }}
      -	 * {{ if <$var>!= }}...[{{ else }} ...]{{ endif }}
      -	 * \endcode
      -	 */
      -	private function _replcb_if($args) {
      -		if (strpos($args[2],'==') > 0){
      -			list($a,$b) = array_map('trim',explode('==',$args[2]));
      -			$a = $this->_get_var($a);
      -			if ($b[0] == '$') {
      -				$b =  $this->_get_var($b);
      -			}
      -			$val = ($a == $b);
      -		}
      -		elseif (strpos($args[2], '!=') > 0) {
      -			list($a,$b) = array_map('trim', explode('!=',$args[2]));
      -			$a = $this->_get_var($a);
      -			if ($b[0] == '$') {
      -				$b =  $this->_get_var($b);
      -			}
      -			$val = ($a != $b);
      -		}
      -		else {
      -			$val = $this->_get_var($args[2]);
      -		}
      -		$x = preg_split("|{{ *else *}}|", $args[3]);
      +    /**
      +     * IF node
      +     * \code
      +     * {{ if <$var> }}...[{{ else }} ...] {{ endif }}
      +     * {{ if <$var>== }}...[{{ else }} ...]{{ endif }}
      +     * {{ if <$var>!= }}...[{{ else }} ...]{{ endif }}
      +     * \endcode
      +     */
      +    private function _replcb_if($args)
      +    {
      +        if (strpos($args[2], '==') > 0) {
      +            list($a,$b) = array_map('trim', explode('==', $args[2]));
      +            $a = $this->_get_var($a);
      +            if ($b[0] == '$') {
      +                $b =  $this->_get_var($b);
      +            }
      +            $val = ($a == $b);
      +        } elseif (strpos($args[2], '!=') > 0) {
      +            list($a,$b) = array_map('trim', explode('!=', $args[2]));
      +            $a = $this->_get_var($a);
      +            if ($b[0] == '$') {
      +                $b =  $this->_get_var($b);
      +            }
      +            $val = ($a != $b);
      +        } else {
      +            $val = $this->_get_var($args[2]);
      +        }
      +        $x = preg_split("|{{ *else *}}|", $args[3]);
       
      -		return ( ($val) ? $x[0] : (isset($x[1]) ? $x[1] : EMPTY_STR));
      -	}
      +        return ( ($val) ? $x[0] : (isset($x[1]) ? $x[1] : EMPTY_STR));
      +    }
       
      -	/**
      -	 * FOR node
      -	 * \code
      -	 * {{ for <$var> as $name }}...{{ endfor }}
      -	 * {{ for <$var> as $key=>$name }}...{{ endfor }}
      -	 * \endcode
      -	 */
      -	private function _replcb_for($args) {
      -		$m = array_map('trim', explode(" as ", $args[2]));
      -		$x = explode('=>',$m[1]);
      -		if (count($x) == 1) {
      -			$varname = $x[0];
      -			$keyname = EMPTY_STR;
      -		}
      -		else {
      -			list($keyname, $varname) = $x;
      -		}
      -		if ($m[0] == EMPTY_STR || $varname == EMPTY_STR || is_null($varname)) {
      -			die("template error: 'for ".$m[0]." as ".$varname."'") ;
      -		}
      +    /**
      +     * FOR node
      +     * \code
      +     * {{ for <$var> as $name }}...{{ endfor }}
      +     * {{ for <$var> as $key=>$name }}...{{ endfor }}
      +     * \endcode
      +     */
      +    private function _replcb_for($args)
      +    {
      +        $m = array_map('trim', explode(" as ", $args[2]));
      +        $x = explode('=>', $m[1]);
      +        if (count($x) == 1) {
      +            $varname = $x[0];
      +            $keyname = EMPTY_STR;
      +        } else {
      +            list($keyname, $varname) = $x;
      +        }
      +        if ($m[0] == EMPTY_STR || $varname == EMPTY_STR || is_null($varname)) {
      +            die("template error: 'for " . $m[0] . " as " . $varname . "'") ;
      +        }
       
      -		$vals = $this->_get_var($m[0]);
      -		$ret = EMPTY_STR;
      -		if (!is_array($vals)) {
      -			return $ret;
      -		}
      +        $vals = $this->_get_var($m[0]);
      +        $ret = EMPTY_STR;
      +        if (!is_array($vals)) {
      +            return $ret;
      +        }
       
      -		foreach ($vals as $k => $v) {
      -			$this->_push_stack();
      -			$r = $this->r;
      -			$r[$varname] = $v;
      -			if ($keyname != EMPTY_STR) {
      -				$r[$keyname] = (($k === 0) ? '0' : $k);
      -			}
      -			$ret .=  $this->replace($args[3], $r);
      -			$this->_pop_stack();
      -		}
      +        foreach ($vals as $k => $v) {
      +            $this->_push_stack();
      +            $r = $this->r;
      +            $r[$varname] = $v;
      +            if ($keyname != EMPTY_STR) {
      +                $r[$keyname] = (($k === 0) ? '0' : $k);
      +            }
      +            $ret .=  $this->replace($args[3], $r);
      +            $this->_pop_stack();
      +        }
       
      -		return $ret;
      -	}
      +        return $ret;
      +    }
       
      -	/**
      -	 * INC node
      -	 * \code
      -	 * {{ inc  [with $var1=$var2] }}{{ endinc }}
      -	 * \endcode
      -	 */
      -	private function _replcb_inc($args) {
      -		if (strpos($args[2],'with')) {
      -			list($tplfile, $newctx) = array_map('trim', explode("with",$args[2]));
      -		}
      -		else {
      -			$tplfile = trim($args[2]);
      -			$newctx = null;
      -		}
      +    /**
      +     * INC node
      +     * \code
      +     * {{ inc  [with $var1=$var2] }}{{ endinc }}
      +     * \endcode
      +     */
      +    private function _replcb_inc($args)
      +    {
      +        if (strpos($args[2], 'with')) {
      +            list($tplfile, $newctx) = array_map('trim', explode("with", $args[2]));
      +        } else {
      +            $tplfile = trim($args[2]);
      +            $newctx = null;
      +        }
       
      -		if ($tplfile[0] == '$') {
      -			$tplfile = $this->_get_var($tplfile);
      -		}
      +        if ($tplfile[0] == '$') {
      +            $tplfile = $this->_get_var($tplfile);
      +        }
       
      -		$this->_push_stack();
      -		$r = $this->r;
      -		if (! is_null($newctx)) {
      -			list($a,$b) = array_map('trim', explode('=',$newctx));
      -			$r[$a] = $this->_get_var($b); 
      -		}
      -		$this->nodes = Array();
      -		$tpl = get_markup_template($tplfile);
      -		$ret = $this->replace($tpl, $r);
      -		$this->_pop_stack();
      +        $this->_push_stack();
      +        $r = $this->r;
      +        if (! is_null($newctx)) {
      +            list($a,$b) = array_map('trim', explode('=', $newctx));
      +            $r[$a] = $this->_get_var($b);
      +        }
      +        $this->nodes = array();
      +        $tpl = get_markup_template($tplfile);
      +        $ret = $this->replace($tpl, $r);
      +        $this->_pop_stack();
       
      -		return $ret;
      -	}
      +        return $ret;
      +    }
       
      -	/**
      -	 * DEBUG node
      -	 * \code
      -	 * {{ debug $var [$var [$var [...]]] }}{{ enddebug }}
      -	 * \endcode
      -	 * replace node with 
      var_dump($var, $var, ...);
      - */ - private function _replcb_debug($args) { - $vars = array_map('trim', explode(" ",$args[2])); - $vars[] = $args[1]; + /** + * DEBUG node + * \code + * {{ debug $var [$var [$var [...]]] }}{{ enddebug }} + * \endcode + * replace node with
      var_dump($var, $var, ...);
      + */ + private function _replcb_debug($args) + { + $vars = array_map('trim', explode(" ", $args[2])); + $vars[] = $args[1]; - $ret = "
      ";
      -		foreach ($vars as $var) {
      -			$ret .= htmlspecialchars(var_export( $this->_get_var($var), true ));
      -			$ret .= "\n";
      -		}
      -		$ret .= "
      "; + $ret = "
      ";
      +        foreach ($vars as $var) {
      +            $ret .= htmlspecialchars(var_export($this->_get_var($var), true));
      +            $ret .= "\n";
      +        }
      +        $ret .= "
      "; - return $ret; - } + return $ret; + } - private function _replcb_node($m) { - $node = $this->nodes[$m[1]]; - if (method_exists($this, "_replcb_" . $node[1])) { - $s = call_user_func(array($this, "_replcb_".$node[1]), $node); - } else { - $s = ""; - } - $s = preg_replace_callback('/\|\|([0-9]+)\|\|/', array($this, "_replcb_node"), $s); + private function _replcb_node($m) + { + $node = $this->nodes[$m[1]]; + if (method_exists($this, "_replcb_" . $node[1])) { + $s = call_user_func(array($this, "_replcb_" . $node[1]), $node); + } else { + $s = ""; + } + $s = preg_replace_callback('/\|\|([0-9]+)\|\|/', array($this, "_replcb_node"), $s); - return $s; - } + return $s; + } - private function _replcb($m) { - //var_dump(array_map('htmlspecialchars', $m)); - $this->done = false; - $this->nodes[] = (array) $m; + private function _replcb($m) + { + //var_dump(array_map('htmlspecialchars', $m)); + $this->done = false; + $this->nodes[] = (array) $m; - return "||". (count($this->nodes)-1) ."||"; - } + return "||" . (count($this->nodes) - 1) . "||"; + } - private function _build_nodes($s) { - $this->done = false; - while (!$this->done) { - $this->done=true; - $s = preg_replace_callback('|{{ *([a-z]*) *([^}]*)}}([^{]*({{ *else *}}[^{]*)?){{ *end\1 *}}|', array($this, "_replcb"), $s); - if ($s==Null) $this->_preg_error(); - } - //({{ *else *}}[^{]*)? - krsort($this->nodes); + private function _build_nodes($s) + { + $this->done = false; + while (!$this->done) { + $this->done = true; + $s = preg_replace_callback('|{{ *([a-z]*) *([^}]*)}}([^{]*({{ *else *}}[^{]*)?){{ *end\1 *}}|', array($this, "_replcb"), $s); + if ($s == null) { + $this->_preg_error(); + } + } + //({{ *else *}}[^{]*)? + krsort($this->nodes); - return $s; - } + return $s; + } - private function var_replace($s) { - $m = []; - /** regexp: - * \$ literal $ - * (\[)? optional open square bracket - * ([a-zA-Z0-9-_]+\.?)+ var name, followed by optional - * dot, repeated at least 1 time - * (?(1)\]) if there was opened square bracket - * (subgrup 1), match close bracket - */ - if (preg_match_all('/\$(\[)?([a-zA-Z0-9-_]+\.?)+(?(1)\])/', $s,$m)) { - foreach ($m[0] as $var) { - $exp = str_replace(array("[", "]"), array("", ""), $var); - $exptks = explode("|", $exp); + private function var_replace($s) + { + $m = []; + /** regexp: + * \$ literal $ + * (\[)? optional open square bracket + * ([a-zA-Z0-9-_]+\.?)+ var name, followed by optional + * dot, repeated at least 1 time + * (?(1)\]) if there was opened square bracket + * (subgrup 1), match close bracket + */ + if (preg_match_all('/\$(\[)?([a-zA-Z0-9-_]+\.?)+(?(1)\])/', $s, $m)) { + foreach ($m[0] as $var) { + $exp = str_replace(array("[", "]"), array("", ""), $var); + $exptks = explode("|", $exp); - $varn = $exptks[0]; - unset($exptks[0]); - $val = $this->_get_var($varn, true); - if ($val != KEY_NOT_EXISTS) { - /* run filters */ - /* - * Filter are in form of: - * filtername:arg:arg:arg - * - * "filtername" is function name - * "arg"s are optional, var value is appended to the end - * if one "arg"==='x' , is replaced with var value - * - * examples: - * $item.body|htmlspecialchars // escape html chars - * $item.body|htmlspecialchars|strtoupper // escape html and uppercase result - * $item.created|date:%Y %M %j // format date (created is a timestamp) - * $item.body|str_replace:cat:dog // replace all "cat" with "dog" - * $item.body|str_replace:cat:dog:x:1 // replace one "cat" with "dog" - */ - foreach ($exptks as $filterstr) { - $filter = explode(":", $filterstr); - $filtername = $filter[0]; - unset($filter[0]); - $valkey = array_search("x", $filter); - if ($valkey === false) { - $filter[] = $val; - } else { - $filter[$valkey] = $val; - } - if (function_exists($filtername)) { - $val = call_user_func_array($filtername, $filter); - } - } - $s = str_replace($var, $val, $s); - } - } - } + $varn = $exptks[0]; + unset($exptks[0]); + $val = $this->_get_var($varn, true); + if ($val != KEY_NOT_EXISTS) { + /* run filters */ + /* + * Filter are in form of: + * filtername:arg:arg:arg + * + * "filtername" is function name + * "arg"s are optional, var value is appended to the end + * if one "arg"==='x' , is replaced with var value + * + * examples: + * $item.body|htmlspecialchars // escape html chars + * $item.body|htmlspecialchars|strtoupper // escape html and uppercase result + * $item.created|date:%Y %M %j // format date (created is a timestamp) + * $item.body|str_replace:cat:dog // replace all "cat" with "dog" + * $item.body|str_replace:cat:dog:x:1 // replace one "cat" with "dog" + */ + foreach ($exptks as $filterstr) { + $filter = explode(":", $filterstr); + $filtername = $filter[0]; + unset($filter[0]); + $valkey = array_search("x", $filter); + if ($valkey === false) { + $filter[] = $val; + } else { + $filter[$valkey] = $val; + } + if (function_exists($filtername)) { + $val = call_user_func_array($filtername, $filter); + } + } + $s = str_replace($var, $val, $s); + } + } + } - return $s; - } + return $s; + } - private function replace($s, $r) { - $this->replace_macros($s, $r); - } + private function replace($s, $r) + { + $this->replace_macros($s, $r); + } - // TemplateEngine interface + // TemplateEngine interface - public function replace_macros($s, $r) { - $this->r = $r; + public function replace_macros($s, $r) + { + $this->r = $r; - $s = $this->_build_nodes($s); + $s = $this->_build_nodes($s); - $s = preg_replace_callback('/\|\|([0-9]+)\|\|/', array($this, "_replcb_node"), $s); - if ($s == Null) - $this->_preg_error(); + $s = preg_replace_callback('/\|\|([0-9]+)\|\|/', array($this, "_replcb_node"), $s); + if ($s == null) { + $this->_preg_error(); + } - // remove comments block - $s = preg_replace('/{#[^#]*#}/', "" , $s); + // remove comments block + $s = preg_replace('/{#[^#]*#}/', "", $s); - //$t2 = dba_timer(); + //$t2 = dba_timer(); - // replace strings recursively (limit to 10 loops) - $os = ""; - $count=0; - while (($os !== $s) && $count<10) { - $os = $s; - $count++; - $s = $this->var_replace($s); - } + // replace strings recursively (limit to 10 loops) + $os = ""; + $count = 0; + while (($os !== $s) && $count < 10) { + $os = $s; + $count++; + $s = $this->var_replace($s); + } - return $s; - } + return $s; + } - public function get_markup_template($file, $root='') { - $template_file = theme_include($file, $root); - if ($template_file) { - $content = file_get_contents($template_file); - } + public function get_markup_template($file, $root = '') + { + $template_file = theme_include($file, $root); + if ($template_file) { + $content = file_get_contents($template_file); + } - return $content; - } + return $content; + } } -function template_escape($s) { - return str_replace(array('$','{{'),array('!_Doll^Ars1Az_!','!_DoubLe^BraceS4Rw_!'),$s); +function template_escape($s) +{ + return str_replace(array('$','{{'), array('!_Doll^Ars1Az_!','!_DoubLe^BraceS4Rw_!'), $s); } -function template_unescape($s) { - return str_replace(array('!_Doll^Ars1Az_!','!_DoubLe^BraceS4Rw_!'),array('$','{{'),$s); +function template_unescape($s) +{ + return str_replace(array('!_Doll^Ars1Az_!','!_DoubLe^BraceS4Rw_!'), array('$','{{'), $s); } diff --git a/Zotlabs/Render/SmartyInterface.php b/Zotlabs/Render/SmartyInterface.php index 72db8116b..58747744e 100644 --- a/Zotlabs/Render/SmartyInterface.php +++ b/Zotlabs/Render/SmartyInterface.php @@ -1,69 +1,72 @@ - "view/theme/$thname/tpl/" ]; - if ( x(App::$theme_info,"extends") ) { - $template_dirs = $template_dirs + [ 'extends' => "view/theme/" . App::$theme_info["extends"] . '/tpl/' ]; - } - $template_dirs = $template_dirs + array('base' => 'view/tpl/'); - $this->setTemplateDir($template_dirs); + $template_dirs = ['theme' => "view/theme/$thname/tpl/"]; + if (x(App::$theme_info, "extends")) { + $template_dirs = $template_dirs + ['extends' => "view/theme/" . App::$theme_info["extends"] . '/tpl/']; + } + $template_dirs = $template_dirs + array('base' => 'view/tpl/'); + $this->setTemplateDir($template_dirs); - // Cannot use get_config() here because it is called during installation when there is no DB. - // FIXME: this may leak private information such as system pathnames. + // Cannot use get_config() here because it is called during installation when there is no DB. + // FIXME: this may leak private information such as system pathnames. - $basecompiledir = ((array_key_exists('smarty3_folder', App::$config['system'])) - ? App::$config['system']['smarty3_folder'] : ''); - if (! $basecompiledir) { - $basecompiledir = str_replace('Zotlabs','',dirname(__DIR__)) . TEMPLATE_BUILD_PATH; - } - if (! is_dir($basecompiledir)) { - @os_mkdir(TEMPLATE_BUILD_PATH, STORAGE_DEFAULT_PERMISSIONS, true); - } - if (! is_dir($basecompiledir)) { - echo "ERROR: folder $basecompiledir does not exist."; killme(); - - } + $basecompiledir = ((array_key_exists('smarty3_folder', App::$config['system'])) + ? App::$config['system']['smarty3_folder'] : ''); + if (!$basecompiledir) { + $basecompiledir = str_replace('Zotlabs', '', dirname(__DIR__)) . TEMPLATE_BUILD_PATH; + } + if (!is_dir($basecompiledir)) { + @os_mkdir(TEMPLATE_BUILD_PATH, STORAGE_DEFAULT_PERMISSIONS, true); + } + if (!is_dir($basecompiledir)) { + echo "ERROR: folder $basecompiledir does not exist."; + killme(); + } - if (! is_writable($basecompiledir)) { - echo "ERROR: folder $basecompiledir must be writable by webserver."; killme(); - } - App::$config['system']['smarty3_folder'] = $basecompiledir; + if (!is_writable($basecompiledir)) { + echo "ERROR: folder $basecompiledir must be writable by webserver."; + killme(); + } + App::$config['system']['smarty3_folder'] = $basecompiledir; - $this->setCompileDir($basecompiledir.'/compiled/'); - $this->setConfigDir($basecompiledir.'/config/'); - $this->setCacheDir($basecompiledir.'/cache/'); + $this->setCompileDir($basecompiledir . '/compiled/'); + $this->setConfigDir($basecompiledir . '/config/'); + $this->setCacheDir($basecompiledir . '/cache/'); - $this->left_delimiter = App::get_template_ldelim('smarty3'); - $this->right_delimiter = App::get_template_rdelim('smarty3'); + $this->left_delimiter = App::get_template_ldelim('smarty3'); + $this->right_delimiter = App::get_template_rdelim('smarty3'); - // Don't report errors so verbosely - $this->error_reporting = (E_ERROR | E_PARSE); - } + // Don't report errors so verbosely + $this->error_reporting = (E_ERROR | E_PARSE); + } - function parsed($template = '') { - if ($template) { - return $this->fetch('string:' . $template); - } - return $this->fetch('file:' . $this->filename); - } + public function parsed($template = '') + { + if ($template) { + return $this->fetch('string:' . $template); + } + return $this->fetch('file:' . $this->filename); + } } - - - diff --git a/Zotlabs/Render/SmartyTemplate.php b/Zotlabs/Render/SmartyTemplate.php index 7eaa8a55c..e0cc329c6 100644 --- a/Zotlabs/Render/SmartyTemplate.php +++ b/Zotlabs/Render/SmartyTemplate.php @@ -1,91 +1,96 @@ -ERROR: folder $basecompiledir does not exist."; killme(); - } + $basecompiledir = str_replace('Zotlabs', '', dirname(__dir__)) . "/" . TEMPLATE_BUILD_PATH; } - if (! is_writable($basecompiledir)) { - echo "ERROR: folder $basecompiledir must be writable by webserver."; killme(); - } - App::$config['system']['smarty3_folder'] = $basecompiledir; - } - - // TemplateEngine interface + if (! is_dir($basecompiledir)) { + @os_mkdir(TEMPLATE_BUILD_PATH, STORAGE_DEFAULT_PERMISSIONS, true); + if (! is_dir($basecompiledir)) { + echo "ERROR: folder $basecompiledir does not exist."; + killme(); + } + } + if (! is_writable($basecompiledir)) { + echo "ERROR: folder $basecompiledir must be writable by webserver."; + killme(); + } + App::$config['system']['smarty3_folder'] = $basecompiledir; + } - public function replace_macros($s, $r) { - $template = ''; + // TemplateEngine interface - // macro or macros available for use in all templates + public function replace_macros($s, $r) + { + $template = ''; - $r['$z_baseurl'] = z_root(); + // macro or macros available for use in all templates - if (gettype($s) === 'string') { - $template = $s; - $s = new SmartyInterface(); - } - foreach ($r as $key => $value) { - if ($key[0] === '$') { - $key = substr($key, 1); - } - $s->assign($key, $value); - } - return $s->parsed($template); - } - - public function get_markup_template($file, $root = '') { - $template_file = theme_include($file, $root); - if ($template_file) { - $template = new SmartyInterface(); - $template->filename = $template_file; + $r['$z_baseurl'] = z_root(); - return $template; - } - return EMPTY_STR; - } + if (gettype($s) === 'string') { + $template = $s; + $s = new SmartyInterface(); + } + foreach ($r as $key => $value) { + if ($key[0] === '$') { + $key = substr($key, 1); + } + $s->assign($key, $value); + } + return $s->parsed($template); + } - public function get_intltext_template($file, $root = '') { - - $lang = App::$language; - if ($root != '' && substr($root,-1) != '/' ) { - $root .= '/'; - } - foreach ( [ $root . "view/$lang/$file", $root . "view/en/$file", '' ] as $template_file) { - if (is_file($template_file)) { - break; - } - } - if ($template_file == '') { - $template_file = theme_include($file,$root); - } - if ($template_file) { - $template = new SmartyInterface(); - $template->filename = $template_file; - return $template; - } - return EMPTY_STR; - } + public function get_markup_template($file, $root = '') + { + $template_file = theme_include($file, $root); + if ($template_file) { + $template = new SmartyInterface(); + $template->filename = $template_file; + return $template; + } + return EMPTY_STR; + } + public function get_intltext_template($file, $root = '') + { + $lang = App::$language; + if ($root != '' && substr($root, -1) != '/') { + $root .= '/'; + } + foreach ([ $root . "view/$lang/$file", $root . "view/en/$file", '' ] as $template_file) { + if (is_file($template_file)) { + break; + } + } + if ($template_file == '') { + $template_file = theme_include($file, $root); + } + if ($template_file) { + $template = new SmartyInterface(); + $template->filename = $template_file; + return $template; + } + return EMPTY_STR; + } } diff --git a/Zotlabs/Render/TemplateEngine.php b/Zotlabs/Render/TemplateEngine.php index bc25ce1af..b9e85edb4 100644 --- a/Zotlabs/Render/TemplateEngine.php +++ b/Zotlabs/Render/TemplateEngine.php @@ -6,7 +6,8 @@ namespace Zotlabs\Render; * @brief Interface for template engines. */ -interface TemplateEngine { - public function replace_macros($s, $v); - public function get_markup_template($file, $root = ''); +interface TemplateEngine +{ + public function replace_macros($s, $v); + public function get_markup_template($file, $root = ''); } diff --git a/Zotlabs/Render/Theme.php b/Zotlabs/Render/Theme.php index 0eebba1ba..1c1e9279e 100644 --- a/Zotlabs/Render/Theme.php +++ b/Zotlabs/Render/Theme.php @@ -4,129 +4,139 @@ namespace Zotlabs\Render; use App; +class Theme +{ -class Theme { + public static $system_theme = null; - static $system_theme = null; + public static $session_theme = null; - static $session_theme = null; - - /** - * @brief Array with base or fallback themes. - */ - static $base_themes = array('redbasic'); + /** + * @brief Array with base or fallback themes. + */ + public static $base_themes = array('redbasic'); - /** - * @brief Figure out the best matching theme and return it. - * - * The theme will depend on channel settings, mobile, session, core compatibility, etc. - * - * @return array - */ - static public function current(){ + /** + * @brief Figure out the best matching theme and return it. + * + * The theme will depend on channel settings, mobile, session, core compatibility, etc. + * + * @return array + */ + public static function current() + { - self::$system_theme = ((isset(\App::$config['system']['theme'])) - ? \App::$config['system']['theme'] : ''); - self::$session_theme = ((isset($_SESSION) && x($_SESSION, 'theme')) - ? $_SESSION['theme'] : self::$system_theme); + self::$system_theme = ((isset(App::$config['system']['theme'])) + ? App::$config['system']['theme'] : ''); + self::$session_theme = ((isset($_SESSION) && x($_SESSION, 'theme')) + ? $_SESSION['theme'] : self::$system_theme); - $page_theme = null; + $page_theme = null; - // Find the theme that belongs to the channel whose stuff we are looking at + // Find the theme that belongs to the channel whose stuff we are looking at - if(\App::$profile_uid) { - $r = q("select channel_theme from channel where channel_id = %d limit 1", - intval(\App::$profile_uid) - ); - if($r) { - $page_theme = $r[0]['channel_theme']; - } - } + if (App::$profile_uid) { + $r = q( + "select channel_theme from channel where channel_id = %d limit 1", + intval(App::$profile_uid) + ); + if ($r) { + $page_theme = $r[0]['channel_theme']; + } + } - // Themes from Comanche layouts over-ride the channel theme + // Themes from Comanche layouts over-ride the channel theme - if(array_key_exists('theme', \App::$layout) && \App::$layout['theme']) - $page_theme = \App::$layout['theme']; + if (array_key_exists('theme', App::$layout) && App::$layout['theme']) { + $page_theme = App::$layout['theme']; + } - $chosen_theme = self::$session_theme; + $chosen_theme = self::$session_theme; - if($page_theme) { - $chosen_theme = $page_theme; - } + if ($page_theme) { + $chosen_theme = $page_theme; + } - if(array_key_exists('theme_preview', $_GET)) - $chosen_theme = $_GET['theme_preview']; + if (array_key_exists('theme_preview', $_GET)) { + $chosen_theme = $_GET['theme_preview']; + } - // Allow theme selection of the form 'theme_name:schema_name' - $themepair = explode(':', $chosen_theme); + // Allow theme selection of the form 'theme_name:schema_name' + $themepair = explode(':', $chosen_theme); - // Check if $chosen_theme is compatible with core. If not fall back to default - $info = get_theme_info($themepair[0]); - $compatible = check_plugin_versions($info); - if(!$compatible) { - $chosen_theme = ''; - } + // Check if $chosen_theme is compatible with core. If not fall back to default + $info = get_theme_info($themepair[0]); + $compatible = check_plugin_versions($info); + if (!$compatible) { + $chosen_theme = ''; + } - if($chosen_theme && (file_exists('view/theme/' . $themepair[0] . '/css/style.css') || file_exists('view/theme/' . $themepair[0] . '/php/style.php'))) { - return($themepair); - } + if ($chosen_theme && (file_exists('view/theme/' . $themepair[0] . '/css/style.css') || file_exists('view/theme/' . $themepair[0] . '/php/style.php'))) { + return ($themepair); + } - foreach(self::$base_themes as $t) { - if(file_exists('view/theme/' . $t . '/css/style.css') || - file_exists('view/theme/' . $t . '/php/style.php')) { - return(array($t)); - } - } + foreach (self::$base_themes as $t) { + if ( + file_exists('view/theme/' . $t . '/css/style.css') || + file_exists('view/theme/' . $t . '/php/style.php') + ) { + return (array($t)); + } + } - // Worst case scenario, the default base theme or themes don't exist; perhaps somebody renamed it/them. + // Worst case scenario, the default base theme or themes don't exist; perhaps somebody renamed it/them. - // Find any theme at all and use it. + // Find any theme at all and use it. - $fallback = array_merge(glob('view/theme/*/css/style.css'), glob('view/theme/*/php/style.php')); - if(count($fallback)) - return(array(str_replace('view/theme/', '', substr($fallback[0], 0, -14)))); - } + $fallback = array_merge(glob('view/theme/*/css/style.css'), glob('view/theme/*/php/style.php')); + if (count($fallback)) { + return (array(str_replace('view/theme/', '', substr($fallback[0], 0, -14)))); + } + } - /** - * @brief Return full URL to theme which is currently in effect. - * - * Provide a sane default if nothing is chosen or the specified theme does not exist. - * - * @param bool $installing (optional) default false, if true return the name of the first base theme - * - * @return string - */ - static public function url($installing = false) { + /** + * @brief Return full URL to theme which is currently in effect. + * + * Provide a sane default if nothing is chosen or the specified theme does not exist. + * + * @param bool $installing (optional) default false, if true return the name of the first base theme + * + * @return string + */ + public static function url($installing = false) + { - if($installing) - return self::$base_themes[0]; + if ($installing) { + return self::$base_themes[0]; + } - $theme = self::current(); + $theme = self::current(); - $t = $theme[0]; - $s = ((count($theme) > 1) ? $theme[1] : ''); + $t = $theme[0]; + $s = ((count($theme) > 1) ? $theme[1] : ''); - $opts = ''; - $opts = ((\App::$profile_uid) ? '?f=&puid=' . \App::$profile_uid : ''); + $opts = ''; + $opts = ((App::$profile_uid) ? '?f=&puid=' . App::$profile_uid : ''); - $schema_str = ((x(\App::$layout,'schema')) ? '&schema=' . App::$layout['schema'] : ''); - if(($s) && (! $schema_str)) - $schema_str = '&schema=' . $s; + $schema_str = ((x(App::$layout, 'schema')) ? '&schema=' . App::$layout['schema'] : ''); + if (($s) && (!$schema_str)) { + $schema_str = '&schema=' . $s; + } - $opts .= $schema_str; + $opts .= $schema_str; - if(file_exists('view/theme/' . $t . '/php/style.php')) - return('/view/theme/' . $t . '/php/style.pcss' . $opts); + if (file_exists('view/theme/' . $t . '/php/style.php')) { + return ('/view/theme/' . $t . '/php/style.pcss' . $opts); + } - return('/view/theme/' . $t . '/css/style.css'); - } - - function debug() { - logger('system_theme: ' . self::$system_theme); - logger('session_theme: ' . self::$session_theme); - } + return ('/view/theme/' . $t . '/css/style.css'); + } + public function debug() + { + logger('system_theme: ' . self::$system_theme); + logger('session_theme: ' . self::$session_theme); + } } - diff --git a/Zotlabs/Storage/BasicAuth.php b/Zotlabs/Storage/BasicAuth.php index 52a8986f8..cb3375bf5 100644 --- a/Zotlabs/Storage/BasicAuth.php +++ b/Zotlabs/Storage/BasicAuth.php @@ -4,6 +4,8 @@ namespace Zotlabs\Storage; use App; use Sabre\DAV; +use Sabre\DAV\Browser\Plugin; +use Sabre\HTTP\Auth\Basic; use Sabre\HTTP\RequestInterface; use Sabre\HTTP\ResponseInterface; @@ -13,137 +15,140 @@ use Sabre\HTTP\ResponseInterface; * This class also contains some data which is not necessary for authentication * like timezone settings. * - * @extends \\Sabre\\DAV\\Auth\\Backend\\AbstractBasic + * @extends \Sabre\\DAV\\Auth\\Backend\\AbstractBasic * * @link http://github.com/friendica/red * @license http://opensource.org/unlicense.org */ -class BasicAuth extends DAV\Auth\Backend\AbstractBasic { +class BasicAuth extends DAV\Auth\Backend\AbstractBasic +{ - /** - * @brief This variable holds the currently logged-in channel_address. - * - * It is used for building path in filestorage/. - * - * @var string|null $channel_name - */ - public $channel_name = null; - /** - * @brief channel_id of the current channel of the logged-in account. - * - * @var int $channel_id - */ - public $channel_id = 0; - /** - * @brief channel_hash of the current channel of the logged-in account. - * - * @var string $channel_hash - */ - public $channel_hash = ''; - /** - * @brief Set in mod/cloud.php to observer_hash. - * - * @var string $observer - */ - public $observer = ''; - /** - * - * @see Browser::set_writeable() - * @var \\Sabre\\DAV\\Browser\\Plugin $browser - */ - public $browser; - /** - * @brief channel_id of the current visited path. Set in Directory::getDir(). - * - * @var int $owner_id - */ - public $owner_id = 0; - /** - * channel_name of the current visited path. Set in Directory::getDir(). - * - * Used for creating the path in cloud/ - * - * @var string $owner_nick - */ - public $owner_nick = ''; - /** - * Timezone from the visiting channel's channel_timezone. - * - * Used in @ref Browser - * - * @var string $timezone - */ - protected $timezone = ''; + /** + * @brief This variable holds the currently logged-in channel_address. + * + * It is used for building path in filestorage/. + * + * @var string|null $channel_name + */ + public $channel_name = null; + /** + * @brief channel_id of the current channel of the logged-in account. + * + * @var int $channel_id + */ + public $channel_id = 0; + /** + * @brief channel_hash of the current channel of the logged-in account. + * + * @var string $channel_hash + */ + public $channel_hash = ''; + /** + * @brief Set in mod/cloud.php to observer_hash. + * + * @var string $observer + */ + public $observer = ''; + /** + * + * @see Browser::set_writeable() + * @var \Sabre\\DAV\\Browser\\Plugin $browser + */ + public $browser; + /** + * @brief channel_id of the current visited path. Set in Directory::getDir(). + * + * @var int $owner_id + */ + public $owner_id = 0; + /** + * channel_name of the current visited path. Set in Directory::getDir(). + * + * Used for creating the path in cloud/ + * + * @var string $owner_nick + */ + public $owner_nick = ''; + /** + * Timezone from the visiting channel's channel_timezone. + * + * Used in @ref Browser + * + * @var string $timezone + */ + protected $timezone = ''; - public $module_disabled = false; + public $module_disabled = false; - /** - * @brief Validates a username and password. - * - * - * @see \\Sabre\\DAV\\Auth\\Backend\\AbstractBasic::validateUserPass - * @param string $username - * @param string $password - * @return bool - */ - protected function validateUserPass($username, $password) { + /** + * @brief Validates a username and password. + * + * + * @param string $username + * @param string $password + * @return bool + * @see \\Sabre\\DAV\\Auth\\Backend\\AbstractBasic::validateUserPass + */ + protected function validateUserPass($username, $password) + { - require_once('include/auth.php'); - $record = account_verify_password($username, $password); - if($record && $record['account']) { - if($record['channel']) - $channel = $record['channel']; - else { - $r = q("SELECT * FROM channel WHERE channel_account_id = %d AND channel_id = %d LIMIT 1", - intval($record['account']['account_id']), - intval($record['account']['account_default_channel']) - ); - if($r) - $channel = $r[0]; - } - } - if($channel && $this->check_module_access($channel['channel_id'])) { - return $this->setAuthenticated($channel); - } + require_once('include/auth.php'); + $record = account_verify_password($username, $password); + if ($record && $record['account']) { + if ($record['channel']) + $channel = $record['channel']; + else { + $r = q("SELECT * FROM channel WHERE channel_account_id = %d AND channel_id = %d LIMIT 1", + intval($record['account']['account_id']), + intval($record['account']['account_default_channel']) + ); + if ($r) + $channel = $r[0]; + } + } + if ($channel && $this->check_module_access($channel['channel_id'])) { + return $this->setAuthenticated($channel); + } - if($this->module_disabled) - $error = 'module not enabled for ' . $username; - else - $error = 'password failed for ' . $username; - logger($error); - log_failed_login($error); + if ($this->module_disabled) + $error = 'module not enabled for ' . $username; + else + $error = 'password failed for ' . $username; + logger($error); + log_failed_login($error); - return false; - } + return false; + } - /** - * @brief Sets variables and session parameters after successfull authentication. - * - * @param array $r - * Array with the values for the authenticated channel. - * @return bool - */ - protected function setAuthenticated($channel) { - $this->channel_name = $channel['channel_address']; - $this->channel_id = $channel['channel_id']; - $this->channel_hash = $this->observer = $channel['channel_hash']; - - if ($this->observer) { - $r = q("select * from xchan where xchan_hash = '%s' limit 1", - dbesc($this->observer) - ); - if ($r) { - App::set_observer(array_shift($r)); - } - } + /** + * @brief Sets variables and session parameters after successfull authentication. + * + * @param array $r + * Array with the values for the authenticated channel. + * @return bool + */ + protected function setAuthenticated($channel) + { + $this->channel_name = $channel['channel_address']; + $this->channel_id = $channel['channel_id']; + $this->channel_hash = $this->observer = $channel['channel_hash']; - $_SESSION['uid'] = $channel['channel_id']; - $_SESSION['account_id'] = $channel['channel_account_id']; - $_SESSION['authenticated'] = true; - return true; - } + if ($this->observer) { + $r = q("select * from xchan where xchan_hash = '%s' limit 1", + dbesc($this->observer) + ); + if ($r) { + App::set_observer(array_shift($r)); + } + } + + $_SESSION['uid'] = $channel['channel_id']; + $_SESSION['account_id'] = $channel['channel_account_id']; + $_SESSION['authenticated'] = true; + return true; + } /** * When this method is called, the backend must check if authentication was @@ -173,17 +178,17 @@ class BasicAuth extends DAV\Auth\Backend\AbstractBasic { * @param ResponseInterface $response * @return array */ - function check(RequestInterface $request, ResponseInterface $response) { + public function check(RequestInterface $request, ResponseInterface $response) + { - if (local_channel()) { - $this->setAuthenticated(\App::get_channel()); - return [ true, $this->principalPrefix . $this->channel_name ]; - } - elseif (remote_channel()) { - return [ true, $this->principalPrefix . $this->observer ]; - } + if (local_channel()) { + $this->setAuthenticated(App::get_channel()); + return [true, $this->principalPrefix . $this->channel_name]; + } elseif (remote_channel()) { + return [true, $this->principalPrefix . $this->observer]; + } - $auth = new \Sabre\HTTP\Auth\Basic( + $auth = new Basic( $this->realm, $request, $response @@ -200,78 +205,87 @@ class BasicAuth extends DAV\Auth\Backend\AbstractBasic { } - protected function check_module_access($channel_id) { - if($channel_id && in_array(\App::$module,[ 'dav', 'cdav', 'snap'] )) { - return true; - } - $this->module_disabled = true; - return false; - } + protected function check_module_access($channel_id) + { + if ($channel_id && in_array(App::$module, ['dav', 'cdav', 'snap'])) { + return true; + } + $this->module_disabled = true; + return false; + } - /** - * Sets the channel_name from the currently logged-in channel. - * - * @param string $name - * The channel's name - */ - public function setCurrentUser($name) { - $this->channel_name = $name; - } - /** - * Returns information about the currently logged-in channel. - * - * If nobody is currently logged in, this method should return null. - * - * @see \\Sabre\\DAV\\Auth\\Backend\\AbstractBasic::getCurrentUser - * @return string|null - */ - public function getCurrentUser() { - return $this->channel_name; - } + /** + * Sets the channel_name from the currently logged-in channel. + * + * @param string $name + * The channel's name + */ + public function setCurrentUser($name) + { + $this->channel_name = $name; + } - /** - * @brief Sets the timezone from the channel in BasicAuth. - * - * Set in mod/cloud.php if the channel has a timezone set. - * - * @param string $timezone - * The channel's timezone. - * @return void - */ - public function setTimezone($timezone) { - $this->timezone = $timezone; - } - /** - * @brief Returns the timezone. - * - * @return string - * Return the channel's timezone. - */ - public function getTimezone() { - return $this->timezone; - } + /** + * Returns information about the currently logged-in channel. + * + * If nobody is currently logged in, this method should return null. + * + * @return string|null + * @see \\Sabre\\DAV\\Auth\\Backend\\AbstractBasic::getCurrentUser + */ + public function getCurrentUser() + { + return $this->channel_name; + } - /** - * @brief Set browser plugin for SabreDAV. - * - * @see RedBrowser::set_writeable() - * @param \Sabre\DAV\Browser\Plugin $browser - */ - public function setBrowserPlugin($browser) { - $this->browser = $browser; - } + /** + * @brief Sets the timezone from the channel in BasicAuth. + * + * Set in mod/cloud.php if the channel has a timezone set. + * + * @param string $timezone + * The channel's timezone. + * @return void + */ + public function setTimezone($timezone) + { + $this->timezone = $timezone; + } - /** - * @brief Prints out all BasicAuth variables to logger(). - * - * @return void - */ - public function log() { + /** + * @brief Returns the timezone. + * + * @return string + * Return the channel's timezone. + */ + public function getTimezone() + { + return $this->timezone; + } + + /** + * @brief Set browser plugin for SabreDAV. + * + * @param Plugin $browser + * @see RedBrowser::set_writeable() + */ + public function setBrowserPlugin($browser) + { + $this->browser = $browser; + } + + /** + * @brief Prints out all BasicAuth variables to logger(). + * + * @return void + */ + public function log() + { // logger('channel_name ' . $this->channel_name, LOGGER_DATA); // logger('channel_id ' . $this->channel_id, LOGGER_DATA); // logger('channel_hash ' . $this->channel_hash, LOGGER_DATA); // logger('observer ' . $this->observer, LOGGER_DATA); // logger('owner_id ' . $this->owner_id, LOGGER_DATA); // logger('owner_nick ' . $this->owner_nick, LOGGER_DATA); - } + } } diff --git a/Zotlabs/Storage/Browser.php b/Zotlabs/Storage/Browser.php index cdd85046a..8448ba3a0 100644 --- a/Zotlabs/Storage/Browser.php +++ b/Zotlabs/Storage/Browser.php @@ -1,11 +1,14 @@ server->getBaseUri() . $parentUri); + $fullPath = encodePath($this->server->getBaseUri() . $parentUri); $parentpath['icon'] = $this->enableAssets ? '' . t('parent') . '' : ''; $parentpath['path'] = $fullPath; @@ -182,7 +185,7 @@ class Browser extends DAV\Browser\Plugin { $size = isset($file[200]['{DAV:}getcontentlength']) ? (int)$file[200]['{DAV:}getcontentlength'] : ''; $lastmodified = ((isset($file[200]['{DAV:}getlastmodified'])) ? $file[200]['{DAV:}getlastmodified']->getTime()->format('Y-m-d H:i:s') : ''); - $fullPath = \Sabre\HTTP\encodePath('/' . trim($this->server->getBaseUri() . ($path ? $path . '/' : '') . $name, '/')); + $fullPath = encodePath('/' . trim($this->server->getBaseUri() . ($path ? $path . '/' : '') . $name, '/')); $displayName = isset($file[200]['{DAV:}displayname']) ? $file[200]['{DAV:}displayname'] : $name; @@ -343,11 +346,11 @@ class Browser extends DAV\Browser\Plugin { /** * @brief Creates a form to add new folders and upload files. * - * @param \Sabre\DAV\INode $node + * @param INode $node * @param[in,out] string &$output * @param string $path */ - public function htmlActionsPanel(DAV\INode $node, &$output, $path) { + public function htmlActionsPanel(INode $node, &$output, $path) { if (! $node instanceof DAV\ICollection) { return; } diff --git a/Zotlabs/Storage/CalDAVClient.php b/Zotlabs/Storage/CalDAVClient.php index c1a8db932..8ab34e23f 100644 --- a/Zotlabs/Storage/CalDAVClient.php +++ b/Zotlabs/Storage/CalDAVClient.php @@ -2,76 +2,80 @@ namespace Zotlabs\Storage; -// The Hubzilla CalDAV client will store calendar information in the 'cal' DB table. +// The Hubzilla CalDAV client will store calendar information in the 'cal' DB table. // Event information will remain in the 'event' table. In order to implement CalDAV on top of our // existing system, there is an event table column called vdata. This will hold the "one true record" // of the event in VCALENDAR format. When we receive a foreign event, we will pick out the fields -// of this entry that are important to us and use it to populate the other event table fields. +// of this entry that are important to us and use it to populate the other event table fields. // When we make an event change, it is required that we load this entry as a vobject, make the changes on the // vobject, and then store the result back in event.vdata. This will preserve foreign keys which we // know nothing about. Then we sync this back to the DAV server. -// We still need a DB update to create a 'cal' table entry for our existing events and link these together. -// I'm currently anticipating separating tasks/to-do items from events, so each new account wil get two default calendars. +// We still need a DB update to create a 'cal' table entry for our existing events and link these together. +// I'm currently anticipating separating tasks/to-do items from events, so each new account wil get two default calendars. -// We will eventually provide for magic-auth or cookie login of the CURL process so we won't be required to -// store our hubzilla password. Currently for testing we are using HTTP BASIC-AUTH and must initialise the -// username/password correctly to make the connection. +// We will eventually provide for magic-auth or cookie login of the CURL process so we won't be required to +// store our hubzilla password. Currently for testing we are using HTTP BASIC-AUTH and must initialise the +// username/password correctly to make the connection. -// Repeating events will be awkward because every instance has the same UUID. This would make it difficult to -// search for upcoming events if the initial instance was created (for instance) a few years ago. So the current plan is -// to create event instances for a prescribed time limit from now (perhaps 5-10 years for annual events). +// Repeating events will be awkward because every instance has the same UUID. This would make it difficult to +// search for upcoming events if the initial instance was created (for instance) a few years ago. So the current plan is +// to create event instances for a prescribed time limit from now (perhaps 5-10 years for annual events). // This plan may change. The repurcussions of this decision mean that an edit to a recurring event must // edit all existing instances of the event, and only one unique instance can be used for sync. -// Sabre vobject provides a function to automatically expand recurring events into individual event instances. +// Sabre vobject provides a function to automatically expand recurring events into individual event instances. +class CalDAVClient +{ -class CalDAVClient { + private $username; + private $password; - private $username; - private $password; + private $url; - private $url; + public $filepos = 0; + public $request_data = ''; - public $filepos = 0; - public $request_data = ''; + public function __construct($user, $pass, $url) + { + $this->username = $user; + $this->password = $pass; + $this->url = $url; + } - function __construct($user,$pass,$url) { - $this->username = $user; - $this->password = $pass; - $this->url = $url; + private function set_data($s) + { + $this->request_data = $s; + $this->filepos = 0; + } - } + public function curl_read($ch, $fh, $size) + { - private function set_data($s) { - $this->request_data = $s; - $this->filepos = 0; - } + if ($this->filepos < 0) { + unset($fh); + return ''; + } - public function curl_read($ch,$fh,$size) { + $s = substr($this->request_data, $this->filepos, $size); - if($this->filepos < 0) { - unset($fh); - return ''; - } + if (strlen($s) < $size) { + $this->filepos = (-1); + } else { + $this->filepos = $this->filepos + $size; + } - $s = substr($this->request_data,$this->filepos,$size); + return $s; + } - if(strlen($s) < $size) - $this->filepos = (-1); - else - $this->filepos = $this->filepos + $size; + public function ctag_fetch() + { + $headers = ['Depth: 0', 'Prefer: return-minimal', 'Content-Type: application/xml; charset=utf-8']; - return $s; - } + // recommended ctag fetch by sabre - function ctag_fetch() { - $headers = [ 'Depth: 0', 'Prefer: return-minimal', 'Content-Type: application/xml; charset=utf-8']; - - // recommended ctag fetch by sabre - - $this->set_data(' + $this->set_data(' @@ -80,10 +84,10 @@ class CalDAVClient { '); - // thunderbird uses this - it's a bit more verbose on what capabilities - // are provided by the server + // thunderbird uses this - it's a bit more verbose on what capabilities + // are provided by the server - $this->set_data(' + $this->set_data(' @@ -96,33 +100,36 @@ class CalDAVClient { '); + $auth = $this->username . ':' . $this->password; - $auth = $this->username . ':' . $this->password; + $recurse = 0; - $recurse = 0; + $x = z_fetch_url( + $this->url, + true, + $recurse, + ['headers' => $headers, + 'http_auth' => $auth, + 'custom' => 'PROPFIND', + 'upload' => true, + 'infile' => 3, + 'infilesize' => strlen($this->request_data), + 'readfunc' => [$this, 'curl_read'] + ] + ); - $x = z_fetch_url($this->url,true,$recurse, - [ 'headers' => $headers, - 'http_auth' => $auth, - 'custom' => 'PROPFIND', - 'upload' => true, - 'infile' => 3, - 'infilesize' => strlen($this->request_data), - 'readfunc' => [ $this, 'curl_read' ] - ]); - - return $x; - - } + return $x; + } - function detail_fetch() { - $headers = [ 'Depth: 1', 'Prefer: return-minimal', 'Content-Type: application/xml; charset=utf-8']; + public function detail_fetch() + { + $headers = ['Depth: 1', 'Prefer: return-minimal', 'Content-Type: application/xml; charset=utf-8']; - // this query should return all objects in the given calendar, you can filter it appropriately - // using filter options + // this query should return all objects in the given calendar, you can filter it appropriately + // using filter options - $this->set_data(' + $this->set_data(' @@ -133,25 +140,26 @@ class CalDAVClient { '); - $auth = $this->username . ':' . $this->password; + $auth = $this->username . ':' . $this->password; - $recurse = 0; - $x = z_fetch_url($this->url,true,$recurse, - [ 'headers' => $headers, - 'http_auth' => $auth, - 'custom' => 'REPORT', - 'upload' => true, - 'infile' => 3, - 'infilesize' => strlen($this->request_data), - 'readfunc' => [ $this, 'curl_read' ] - ]); - - - return $x; - - } + $recurse = 0; + $x = z_fetch_url( + $this->url, + true, + $recurse, + ['headers' => $headers, + 'http_auth' => $auth, + 'custom' => 'REPORT', + 'upload' => true, + 'infile' => 3, + 'infilesize' => strlen($this->request_data), + 'readfunc' => [$this, 'curl_read'] + ] + ); + return $x; + } } diff --git a/Zotlabs/Storage/Directory.php b/Zotlabs/Storage/Directory.php index 2883ae35a..ee84dee5d 100644 --- a/Zotlabs/Storage/Directory.php +++ b/Zotlabs/Storage/Directory.php @@ -15,925 +15,936 @@ require_once('include/photos.php'); * * A class that represents a directory. * - * @extends \\Sabre\\DAV\\Node - * @implements \\Sabre\\DAV\\ICollection - * @implements \\Sabre\\DAV\\IQuota + * @extends \Sabre\\DAV\\Node + * @implements \Sabre\\DAV\\ICollection + * @implements \Sabre\\DAV\\IQuota * * @license http://opensource.org/licenses/mit-license.php The MIT License (MIT) */ -class Directory extends DAV\Node implements DAV\ICollection, DAV\IQuota, DAV\IMoveTarget { +class Directory extends DAV\Node implements DAV\ICollection, DAV\IQuota, DAV\IMoveTarget +{ - /** - * @brief The path inside /cloud - * - * @var string $red_path - */ - private $red_path; - private $folder_hash; - /** - * @brief The full path as seen in the browser. - * /cloud + $red_path - * @todo I think this is not used anywhere, we always strip '/cloud' and only use it in debug - * @var string $ext_path - */ - private $ext_path; - private $root_dir = ''; - private $auth; - /** - * @brief The real path on the filesystem. - * The actual path in store/ with the hashed names. - * - * @var string $os_path - */ - private $os_path = ''; + /** + * @brief The path inside /cloud + * + * @var string $red_path + */ + private $red_path; + private $folder_hash; + /** + * @brief The full path as seen in the browser. + * /cloud + $red_path + * @todo I think this is not used anywhere, we always strip '/cloud' and only use it in debug + * @var string $ext_path + */ + private $ext_path; + private $root_dir = ''; + private $auth; + /** + * @brief The real path on the filesystem. + * The actual path in store/ with the hashed names. + * + * @var string $os_path + */ + private $os_path = ''; - /** - * @brief Sets up the directory node, expects a full path. - * - * @param string $ext_path a full path - * @param BasicAuth &$auth_plugin - */ - public function __construct($ext_path, &$auth_plugin) { - // $ext_path = urldecode($ext_path); - logger('directory ' . $ext_path, LOGGER_DATA); - $this->ext_path = $ext_path; - // remove "/cloud" from the beginning of the path - $modulename = App::$module; - $this->red_path = ((strpos($ext_path, '/' . $modulename) === 0) ? substr($ext_path, strlen($modulename) + 1) : $ext_path); - if (! $this->red_path) { - $this->red_path = '/'; - } - $this->auth = $auth_plugin; - $this->folder_hash = ''; - $this->getDir(); + /** + * @brief Sets up the directory node, expects a full path. + * + * @param string $ext_path a full path + * @param BasicAuth &$auth_plugin + */ + public function __construct($ext_path, &$auth_plugin) + { + // $ext_path = urldecode($ext_path); + logger('directory ' . $ext_path, LOGGER_DATA); + $this->ext_path = $ext_path; + // remove "/cloud" from the beginning of the path + $modulename = App::$module; + $this->red_path = ((strpos($ext_path, '/' . $modulename) === 0) ? substr($ext_path, strlen($modulename) + 1) : $ext_path); + if (!$this->red_path) { + $this->red_path = '/'; + } + $this->auth = $auth_plugin; + $this->folder_hash = ''; + $this->getDir(); - if($this->auth->browser) { - $this->auth->browser->set_writeable(); - } - } + if ($this->auth->browser) { + $this->auth->browser->set_writeable(); + } + } - private function log() { - logger('ext_path ' . $this->ext_path, LOGGER_DATA); - logger('os_path ' . $this->os_path, LOGGER_DATA); - logger('red_path ' . $this->red_path, LOGGER_DATA); - } + private function log() + { + logger('ext_path ' . $this->ext_path, LOGGER_DATA); + logger('os_path ' . $this->os_path, LOGGER_DATA); + logger('red_path ' . $this->red_path, LOGGER_DATA); + } - /** - * @brief Returns an array with all the child nodes. - * - * @throw "\Sabre\DAV\Exception\Forbidden" - * @return array \\Sabre\\DAV\\INode[] - */ - public function getChildren() { - logger('children for ' . $this->ext_path, LOGGER_DATA); - $this->log(); + /** + * @brief Returns an array with all the child nodes. + * + * @throw "\Sabre\DAV\Exception\Forbidden" + * @return array \\Sabre\\DAV\\INode[] + */ + public function getChildren() + { + logger('children for ' . $this->ext_path, LOGGER_DATA); + $this->log(); - if (get_config('system', 'block_public') && (! $this->auth->channel_id) && (! $this->auth->observer)) { - throw new DAV\Exception\Forbidden('Permission denied.'); - } + if (get_config('system', 'block_public') && (!$this->auth->channel_id) && (!$this->auth->observer)) { + throw new DAV\Exception\Forbidden('Permission denied.'); + } - if (($this->auth->owner_id) && (! perm_is_allowed($this->auth->owner_id, $this->auth->observer, 'view_storage'))) { - throw new DAV\Exception\Forbidden('Permission denied.'); - } + if (($this->auth->owner_id) && (!perm_is_allowed($this->auth->owner_id, $this->auth->observer, 'view_storage'))) { + throw new DAV\Exception\Forbidden('Permission denied.'); + } - $contents = $this->CollectionData($this->red_path, $this->auth); - return $contents; - } + $contents = $this->CollectionData($this->red_path, $this->auth); + return $contents; + } - /** - * @brief Returns a child by name. - * - * @throw "\Sabre\DAV\Exception\Forbidden" - * @throw "\Sabre\DAV\Exception\NotFound" - * @param string $name - */ - public function getChild($name) { - logger($name, LOGGER_DATA); + /** + * @brief Returns a child by name. + * + * @throw "\Sabre\DAV\Exception\Forbidden" + * @throw "\Sabre\DAV\Exception\NotFound" + * @param string $name + */ + public function getChild($name) + { + logger($name, LOGGER_DATA); - if (get_config('system', 'block_public') && (! $this->auth->channel_id) && (! $this->auth->observer)) { - throw new DAV\Exception\Forbidden('Permission denied.'); - } + if (get_config('system', 'block_public') && (!$this->auth->channel_id) && (!$this->auth->observer)) { + throw new DAV\Exception\Forbidden('Permission denied.'); + } - if (($this->auth->owner_id) && (! perm_is_allowed($this->auth->owner_id, $this->auth->observer, 'view_storage'))) { - throw new DAV\Exception\Forbidden('Permission denied.'); - } + if (($this->auth->owner_id) && (!perm_is_allowed($this->auth->owner_id, $this->auth->observer, 'view_storage'))) { + throw new DAV\Exception\Forbidden('Permission denied.'); + } - $modulename = App::$module; - if ($this->red_path === '/' && $name === $modulename) { - return new Directory('/' . $modulename, $this->auth); - } + $modulename = App::$module; + if ($this->red_path === '/' && $name === $modulename) { + return new Directory('/' . $modulename, $this->auth); + } - $x = $this->FileData($this->ext_path . '/' . $name, $this->auth); - if ($x) { - return $x; - } + $x = $this->FileData($this->ext_path . '/' . $name, $this->auth); + if ($x) { + return $x; + } - throw new DAV\Exception\NotFound('The file with name: ' . $name . ' could not be found.'); - } + throw new DAV\Exception\NotFound('The file with name: ' . $name . ' could not be found.'); + } - /** - * @brief Returns the name of the directory. - * - * @return string - */ - public function getName() { - return (basename($this->red_path)); - } + /** + * @brief Returns the name of the directory. + * + * @return string + */ + public function getName() + { + return (basename($this->red_path)); + } - /** - * @brief Renames the directory. - * - * @todo handle duplicate directory name - * - * @throw "\Sabre\DAV\Exception\Forbidden" - * @param string $name The new name of the directory. - * @return void - */ - public function setName($name) { - logger('old name ' . basename($this->red_path) . ' -> ' . $name, LOGGER_DATA); + /** + * @brief Renames the directory. + * + * @param string $name The new name of the directory. + * @return void + * @todo handle duplicate directory name + * + * @throw "\Sabre\DAV\Exception\Forbidden" + */ + public function setName($name) + { + logger('old name ' . basename($this->red_path) . ' -> ' . $name, LOGGER_DATA); - if ((! $name) || (! $this->auth->owner_id)) { - logger('permission denied ' . $name); - throw new DAV\Exception\Forbidden('Permission denied.'); - } + if ((!$name) || (!$this->auth->owner_id)) { + logger('permission denied ' . $name); + throw new DAV\Exception\Forbidden('Permission denied.'); + } - if (! perm_is_allowed($this->auth->owner_id, $this->auth->observer, 'write_storage')) { - logger('permission denied '. $name); - throw new DAV\Exception\Forbidden('Permission denied.'); - } + if (!perm_is_allowed($this->auth->owner_id, $this->auth->observer, 'write_storage')) { + logger('permission denied ' . $name); + throw new DAV\Exception\Forbidden('Permission denied.'); + } - list($parent_path, ) = \Sabre\Uri\split($this->red_path); - $new_path = $parent_path . '/' . $name; + list($parent_path,) = \Sabre\Uri\split($this->red_path); + $new_path = $parent_path . '/' . $name; - $r = q("UPDATE attach SET filename = '%s' WHERE hash = '%s' AND uid = %d", - dbesc($name), - dbesc($this->folder_hash), - intval($this->auth->owner_id) - ); + $r = q("UPDATE attach SET filename = '%s' WHERE hash = '%s' AND uid = %d", + dbesc($name), + dbesc($this->folder_hash), + intval($this->auth->owner_id) + ); - $x = attach_syspaths($this->auth->owner_id,$this->folder_hash); + $x = attach_syspaths($this->auth->owner_id, $this->folder_hash); - $y = q("update attach set display_path = '%s' where hash = '%s' and uid = %d", - dbesc($x['path']), - dbesc($this->folder_hash), - intval($this->auth->owner_id) - ); + $y = q("update attach set display_path = '%s' where hash = '%s' and uid = %d", + dbesc($x['path']), + dbesc($this->folder_hash), + intval($this->auth->owner_id) + ); - $ch = channelx_by_n($this->auth->owner_id); - if ($ch) { - $sync = attach_export_data($ch, $this->folder_hash); - if ($sync) { - Libsync::build_sync_packet($ch['channel_id'], array('file' => array($sync))); - } - } + $ch = channelx_by_n($this->auth->owner_id); + if ($ch) { + $sync = attach_export_data($ch, $this->folder_hash); + if ($sync) { + Libsync::build_sync_packet($ch['channel_id'], array('file' => array($sync))); + } + } - $this->red_path = $new_path; - } + $this->red_path = $new_path; + } - /** - * @brief Creates a new file in the directory. - * - * Data will either be supplied as a stream resource, or in certain cases - * as a string. Keep in mind that you may have to support either. - * - * After successful creation of the file, you may choose to return the ETag - * of the new file here. - * - * @throw "\Sabre\DAV\Exception\Forbidden" - * @param string $name Name of the file - * @param resource|string $data Initial payload - * @return null|string ETag - */ - public function createFile($name, $data = null) { - logger('create file in directory ' . $name, LOGGER_DEBUG); + /** + * @brief Creates a new file in the directory. + * + * Data will either be supplied as a stream resource, or in certain cases + * as a string. Keep in mind that you may have to support either. + * + * After successful creation of the file, you may choose to return the ETag + * of the new file here. + * + * @throw "\Sabre\DAV\Exception\Forbidden" + * @param string $name Name of the file + * @param resource|string $data Initial payload + * @return null|string ETag + */ + public function createFile($name, $data = null) + { + logger('create file in directory ' . $name, LOGGER_DEBUG); - if (! $this->auth->owner_id) { - logger('permission denied ' . $name); - throw new DAV\Exception\Forbidden('Permission denied.'); - } + if (!$this->auth->owner_id) { + logger('permission denied ' . $name); + throw new DAV\Exception\Forbidden('Permission denied.'); + } - if (! perm_is_allowed($this->auth->owner_id, $this->auth->observer, 'write_storage')) { - logger('permission denied ' . $name); - throw new DAV\Exception\Forbidden('Permission denied.'); - } + if (!perm_is_allowed($this->auth->owner_id, $this->auth->observer, 'write_storage')) { + logger('permission denied ' . $name); + throw new DAV\Exception\Forbidden('Permission denied.'); + } - $mimetype = z_mime_content_type($name); + $mimetype = z_mime_content_type($name); - $channel = channelx_by_n($this->auth->owner_id); + $channel = channelx_by_n($this->auth->owner_id); - if (! $channel) { - logger('no channel'); - throw new DAV\Exception\Forbidden('Permission denied.'); - } + if (!$channel) { + logger('no channel'); + throw new DAV\Exception\Forbidden('Permission denied.'); + } - $filesize = 0; - $hash = new_uuid(); + $filesize = 0; + $hash = new_uuid(); - $f = 'store/' . $this->auth->owner_nick . '/' . (($this->os_path) ? $this->os_path . '/' : '') . $hash; + $f = 'store/' . $this->auth->owner_nick . '/' . (($this->os_path) ? $this->os_path . '/' : '') . $hash; - $direct = null; + $direct = null; - if ($this->folder_hash) { - $r = q("select * from attach where hash = '%s' and is_dir = 1 and uid = %d limit 1", - dbesc($this->folder_hash), - intval($channel['channel_id']) - ); - if ($r) { - $direct = array_shift($r); - } - } + if ($this->folder_hash) { + $r = q("select * from attach where hash = '%s' and is_dir = 1 and uid = %d limit 1", + dbesc($this->folder_hash), + intval($channel['channel_id']) + ); + if ($r) { + $direct = array_shift($r); + } + } - if (($direct) && (($direct['allow_cid']) || ($direct['allow_gid']) || ($direct['deny_cid']) || ($direct['deny_gid']))) { - $allow_cid = $direct['allow_cid']; - $allow_gid = $direct['allow_gid']; - $deny_cid = $direct['deny_cid']; - $deny_gid = $direct['deny_gid']; - } - else { - $allow_cid = $channel['channel_allow_cid']; - $allow_gid = $channel['channel_allow_gid']; - $deny_cid = $channel['channel_deny_cid']; - $deny_gid = $channel['channel_deny_gid']; - } + if (($direct) && (($direct['allow_cid']) || ($direct['allow_gid']) || ($direct['deny_cid']) || ($direct['deny_gid']))) { + $allow_cid = $direct['allow_cid']; + $allow_gid = $direct['allow_gid']; + $deny_cid = $direct['deny_cid']; + $deny_gid = $direct['deny_gid']; + } else { + $allow_cid = $channel['channel_allow_cid']; + $allow_gid = $channel['channel_allow_gid']; + $deny_cid = $channel['channel_deny_cid']; + $deny_gid = $channel['channel_deny_gid']; + } - $created = $edited = datetime_convert(); + $created = $edited = datetime_convert(); - $r = q("INSERT INTO attach ( aid, uid, hash, creator, filename, folder, os_storage, filetype, filesize, revision, is_photo, content, created, edited, os_path, display_path, allow_cid, allow_gid, deny_cid, deny_gid ) + $r = q("INSERT INTO attach ( aid, uid, hash, creator, filename, folder, os_storage, filetype, filesize, revision, is_photo, content, created, edited, os_path, display_path, allow_cid, allow_gid, deny_cid, deny_gid ) VALUES ( %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', %d, %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s' ) ", - intval($channel['channel_account_id']), - intval($channel['channel_id']), - dbesc($hash), - dbesc($this->auth->observer), - dbesc($name), - dbesc($this->folder_hash), - intval(1), - dbesc($mimetype), - intval($filesize), - intval(0), - intval(0), - dbesc($f), - dbesc($created), - dbesc($edited), - '', - '', - dbesc($allow_cid), - dbesc($allow_gid), - dbesc($deny_cid), - dbesc($deny_gid) - ); - - // fetch the actual storage paths - - $xpath = attach_syspaths($this->auth->owner_id, $hash); - - if (is_resource($data)) { - $fp = fopen($f,'wb'); - if ($fp) { - pipe_streams($data,$fp); - fclose($fp); - } - $size = filesize($f); - } - else { - $size = file_put_contents($f, $data); - } - - // delete attach entry if file_put_contents() failed - if ($size === false) { - logger('file_put_contents() failed to ' . $f); - attach_delete($channel['channel_id'], $hash); - return; - } - - $is_photo = 0; - $gis = @getimagesize($f); - logger('getimagesize: ' . print_r($gis,true), LOGGER_DATA); - if (($gis) && supported_imagetype($gis[2])) { - $is_photo = 1; - } - - // If we know it's a photo, over-ride the type in case the source system could not determine what it was - - if ($is_photo) { - q("update attach set filetype = '%s' where hash = '%s' and uid = %d", - dbesc($gis['mime']), - dbesc($hash), - intval($channel['channel_id']) - ); - } - - // updates entry with path and filesize - $d = q("UPDATE attach SET filesize = '%s', os_path = '%s', display_path = '%s', is_photo = %d WHERE hash = '%s' AND uid = %d", - dbesc($size), - dbesc($xpath['os_path']), - dbesc($xpath['path']), - intval($is_photo), - dbesc($hash), - intval($channel['channel_id']) - ); - - // update the parent folder's lastmodified timestamp - $e = q("UPDATE attach SET edited = '%s' WHERE hash = '%s' AND uid = %d", - dbesc($edited), - dbesc($this->folder_hash), - intval($channel['channel_id']) - ); - - $maxfilesize = get_config('system', 'maxfilesize'); - if (($maxfilesize) && ($size > $maxfilesize)) { - logger('system maxfilesize exceeded. Deleting uploaded file.'); - attach_delete($channel['channel_id'], $hash); - return; - } - - // check against service class quota - $limit = engr_units_to_bytes(service_class_fetch($channel['channel_id'], 'attach_upload_limit')); - if ($limit !== false) { - $z = q("SELECT SUM(filesize) AS total FROM attach WHERE aid = %d ", - intval($channel['channel_account_id']) - ); - if (($z) && ($z[0]['total'] + $size > $limit)) { - logger('service class limit exceeded for ' . $channel['channel_name'] . ' total usage is ' . $z[0]['total'] . ' limit is ' . userReadableSize($limit)); - attach_delete($channel['channel_id'], $hash); - return; - } - } - - if ($is_photo) { - $album = ''; - if ($this->folder_hash) { - $f1 = q("select filename, display_path from attach WHERE hash = '%s' AND uid = %d", - dbesc($this->folder_hash), - intval($channel['channel_id']) - ); - if ($f1) { - $album = (($f1[0]['display_path']) ? $f1[0]['display_path'] : $f1[0]['filename']); - } - } - - $args = [ - 'resource_id' => $hash, - 'album' => $album, - 'folder' => $this->folder_hash, - 'os_syspath' => $f, - 'os_path' => $xpath['os_path'], - 'display_path' => $xpath['path'], - 'filename' => $name, - 'getimagesize' => $gis, - 'directory' => $direct - ]; - $p = photo_upload($channel, App::get_observer(), $args); - } - - Run::Summon([ 'Thumbnail' , $hash ]); - - $sync = attach_export_data($channel, $hash); - - if ($sync) { - Libsync::build_sync_packet($channel['channel_id'], array('file' => array($sync))); - } - } - - /** - * @brief Creates a new subdirectory. - * - * @param string $name the directory to create - * @return void - */ - public function createDirectory($name) { - logger('create directory ' . $name, LOGGER_DEBUG); - - if ((! $this->auth->owner_id) || (! perm_is_allowed($this->auth->owner_id, $this->auth->observer, 'write_storage'))) { - throw new DAV\Exception\Forbidden('Permission denied.'); - } - - $channel = channelx_by_n($this->auth->owner_id); - - if ($channel) { - - // When initiated from DAV, set the 'force' flag on attach_mkdir(). This will cause the operation to report success even if the - // folder already exists. - - require_once('include/attach.php'); - $result = attach_mkdir($channel, $this->auth->observer, array('filename' => $name, 'folder' => $this->folder_hash, 'force' => true)); - - if ($result['success']) { - $sync = attach_export_data($channel,$result['data']['hash']); - logger('createDirectory: attach_export_data returns $sync:' . print_r($sync, true), LOGGER_DEBUG); - - if ($sync) { - Libsync::build_sync_packet($channel['channel_id'], array('file' => array($sync))); - } - } - else { - logger('error ' . print_r($result, true), LOGGER_DEBUG); - } - } - } - - /** - * @brief delete directory - */ - public function delete() { - logger('delete file ' . basename($this->red_path), LOGGER_DEBUG); - - if ((! $this->auth->owner_id) || (! perm_is_allowed($this->auth->owner_id, $this->auth->observer, 'write_storage'))) { - throw new DAV\Exception\Forbidden('Permission denied.'); - } - - if ($this->auth->owner_id !== $this->auth->channel_id) { - if (($this->auth->observer !== $this->data['creator']) || intval($this->data['is_dir'])) { - throw new DAV\Exception\Forbidden('Permission denied.'); - } - } - - attach_delete($this->auth->owner_id, $this->folder_hash); - - $channel = channelx_by_n($this->auth->owner_id); - if ($channel) { - $sync = attach_export_data($channel, $this->folder_hash, true); - if ($sync) { - Libsync::build_sync_packet($channel['channel_id'], array('file' => array($sync))); - } - } - } - - - /** - * @brief Checks if a child exists. - * - * @param string $name - * The name to check if it exists. - * @return boolean - */ - public function childExists($name) { - // On /cloud we show a list of available channels. - // @todo what happens if no channels are available? - $modulename = App::$module; - if ($this->red_path === '/' && $name === $modulename) { - //logger('We are at ' $modulename . ' show a channel list', LOGGER_DEBUG); - return true; - } - - $x = $this->FileData($this->ext_path . '/' . $name, $this->auth, true); - //logger('FileData returns: ' . print_r($x, true), LOGGER_DATA); - if ($x) { - return true; - } - return false; - } - - - public function moveInto($targetName,$sourcePath, DAV\INode $sourceNode) { - - if (! $this->auth->owner_id) { - return false; - } - - if(! ($sourceNode->data && $sourceNode->data['hash'])) { - return false; - } - - return attach_move($this->auth->owner_id, $sourceNode->data['hash'], $this->folder_hash); - } - - - /** - * @todo add description of what this function does. - * - * @throw "\Sabre\DAV\Exception\NotFound" - * @return void - */ - function getDir() { - - logger('GetDir: ' . $this->ext_path, LOGGER_DEBUG); - $this->auth->log(); - $modulename = App::$module; - - $file = $this->ext_path; - - $x = strpos($file, '/' . $modulename); - if ($x === 0) { - $file = substr($file, strlen($modulename) + 1); - } - - if ((! $file) || ($file === '/')) { - return; - } - - $file = trim($file, '/'); - $path_arr = explode('/', $file); - - if (! $path_arr) - return; - - logger('paths: ' . print_r($path_arr, true), LOGGER_DATA); - - $channel_name = $path_arr[0]; - - $channel = channelx_by_nick($channel_name); - - if (! $channel) { - throw new DAV\Exception\NotFound('The file with name: ' . $channel_name . ' could not be found.'); - } - - $channel_id = $channel['channel_id']; - $this->auth->owner_id = $channel_id; - $this->auth->owner_nick = $channel_name; - - $path = '/' . $channel_name; - $folder = ''; - $os_path = ''; - - for ($x = 1; $x < count($path_arr); $x++) { - $r = q("select id, hash, filename, flags, is_dir from attach where folder = '%s' and filename = '%s' and uid = %d and is_dir != 0", - dbesc($folder), - dbesc($path_arr[$x]), - intval($channel_id) - ); - if ($r && intval($r[0]['is_dir'])) { - $folder = $r[0]['hash']; - if (strlen($os_path)) { - $os_path .= '/'; - } - $os_path .= $folder; - $path = $path . '/' . $r[0]['filename']; - } - } - $this->folder_hash = $folder; - $this->os_path = $os_path; - } - - /** - * @brief Returns the last modification time for the directory, as a UNIX - * timestamp. - * - * It looks for the last edited file in the folder. If it is an empty folder - * it returns the lastmodified time of the folder itself, to prevent zero - * timestamps. - * - * @return int last modification time in UNIX timestamp - */ - public function getLastModified() { - $r = q("SELECT edited FROM attach WHERE folder = '%s' AND uid = %d ORDER BY edited DESC LIMIT 1", - dbesc($this->folder_hash), - intval($this->auth->owner_id) - ); - if (! $r) { - $r = q("SELECT edited FROM attach WHERE hash = '%s' AND uid = %d LIMIT 1", - dbesc($this->folder_hash), - intval($this->auth->owner_id) - ); - if (! $r) - return ''; - } - return datetime_convert('UTC', 'UTC', $r[0]['edited'], 'U'); - } - - - /** - * @brief Array with all Directory and File DAV\\Node items for the given path. - * - * @param string $file path to a directory - * @param \Zotlabs\Storage\BasicAuth &$auth - * @returns null|array \\Sabre\\DAV\\INode[] - * @throw "\Sabre\DAV\Exception\Forbidden" - * @throw "\Sabre\DAV\Exception\NotFound" - */ - function CollectionData($file, &$auth) { - $ret = []; - - $x = strpos($file, '/cloud'); - if ($x === 0) { - $file = substr($file, 6); - } - - // return a list of channel if we are not inside a channel - if ((! $file) || ($file === '/')) { - return $this->ChannelList($auth); - } - - $file = trim($file, '/'); - $path_arr = explode('/', $file); - - if (! $path_arr) { - return null; - } - - $channel_name = $path_arr[0]; - - $channel = channelx_by_nick($channel_name); - - if (! $channel) { - return null; - } - - $channel_id = $channel['channel_id']; - $perms = permissions_sql($channel_id); - - $auth->owner_id = $channel_id; - - $path = '/' . $channel_name; - - $folder = ''; - $errors = false; - $permission_error = false; - - for ($x = 1; $x < count($path_arr); $x++) { - $r = q("SELECT id, hash, filename, flags, is_dir FROM attach WHERE folder = '%s' AND filename = '%s' AND uid = %d AND is_dir != 0 $perms LIMIT 1", - dbesc($folder), - dbesc($path_arr[$x]), - intval($channel_id) - ); - if (! $r) { - // path wasn't found. Try without permissions to see if it was the result of permissions. - $errors = true; - $r = q("select id, hash, filename, flags, is_dir from attach where folder = '%s' and filename = '%s' and uid = %d and is_dir != 0 limit 1", - dbesc($folder), - basename($path_arr[$x]), - intval($channel_id) - ); - if ($r) { - $permission_error = true; - } - break; - } - - if ($r && intval($r[0]['is_dir'])) { - $folder = $r[0]['hash']; - $path = $path . '/' . $r[0]['filename']; - } - } - - if ($errors) { - if ($permission_error) { - throw new DAV\Exception\Forbidden('Permission denied.'); - } - else { - throw new DAV\Exception\NotFound('A component of the requested file path could not be found.'); - } - } - - // This should no longer be needed since we just returned errors for paths not found - if ($path !== '/' . $file) { - logger("Path mismatch: $path !== /$file"); - return NULL; - } - - $prefix = ''; - - if(! array_key_exists('cloud_sort',$_SESSION)) - $_SESSION['cloud_sort'] = 'name'; - - switch($_SESSION['cloud_sort']) { - case 'size': - $suffix = ' order by is_dir desc, filesize asc '; - break; - // The following provides inconsistent results for directories because we re-calculate the date for directories based on the most recent change - case 'date': - $suffix = ' order by is_dir desc, edited asc '; - break; - case 'name': - default: - $suffix = ' order by is_dir desc, filename asc '; - break; - } - - $r = q("select $prefix id, uid, hash, filename, filetype, filesize, revision, folder, flags, is_dir, created, edited from attach where folder = '%s' and uid = %d $perms $suffix", - dbesc($folder), - intval($channel_id) - ); - - foreach ($r as $rr) { - if (App::$module === 'cloud' && (strpos($rr['filename'],'.') === 0) && (! get_pconfig($channel_id,'system','show_dot_files')) ) { - continue; - } - - // @FIXME I don't think we use revisions currently in attach structures. - // In case we see any in the wild provide a unique filename. This - // name may or may not be accessible - - if ($rr['revision']) { - $rr['filename'] .= '-' . $rr['revision']; - } - - // logger('filename: ' . $rr['filename'], LOGGER_DEBUG); - if (intval($rr['is_dir'])) { - $ret[] = new Directory($path . '/' . $rr['filename'], $auth); - } - else { - $ret[] = new File($path . '/' . $rr['filename'], $rr, $auth); - } - } - - return $ret; - } - - - /** - * @brief Returns an array with viewable channels. - * - * Get a list of Directory objects with all the channels where the visitor - * has view_storage perms. - * - * - * @param BasicAuth &$auth - * @return array Directory[] - */ - function ChannelList(&$auth) { - $ret = []; - - $disabled = intval(get_config('system','cloud_disable_siteroot',true)); - - $r = q("SELECT channel_id, channel_address, profile.publish FROM channel left join profile on profile.uid = channel.channel_id WHERE channel_removed = 0 AND channel_system = 0 AND (channel_pageflags & %d) = 0 and profile.is_default = 1", - intval(PAGE_HIDDEN) - ); - if ($r) { - foreach ($r as $rr) { - if ((perm_is_allowed($rr['channel_id'], $auth->observer, 'view_storage') && $rr['publish']) || $rr['channel_id'] == $this->auth->channel_id) { - logger('found channel: /cloud/' . $rr['channel_address'], LOGGER_DATA); - if ($disabled) { - $conn = q("select abook_id from abook where abook_channel = %d and abook_xchan = '%s' and abook_pending = 0", - intval($rr['channel_id']), - dbesc($auth->observer) - ); - if (! $conn) { - continue; - } - } - - $ret[] = new Directory($rr['channel_address'], $auth); - } - } - } - return $ret; - } - - - /** - * @brief - * - * @param string $file - * path to file or directory - * @param BasicAuth &$auth - * @param boolean $test (optional) enable test mode - * @return File|Directory|boolean|null - * @throw "\Sabre\DAV\Exception\Forbidden" - */ - function FileData($file, &$auth, $test = false) { - logger($file . (($test) ? ' (test mode) ' : ''), LOGGER_DATA); - - $x = strpos($file, '/cloud'); - if ($x === 0) { - $file = substr($file, 6); - } - else { - $x = strpos($file, '/dav'); - if($x === 0) - $file = substr($file, 4); - } - - if ((! $file) || ($file === '/')) { - return new Directory('/', $auth); - } - - $file = trim($file, '/'); - - $path_arr = explode('/', $file); - - if (! $path_arr) - return null; - - $channel_name = $path_arr[0]; - - $r = q("select channel_id from channel where channel_address = '%s' limit 1", - dbesc($channel_name) - ); - - if (! $r) - return null; - - $channel_id = $r[0]['channel_id']; - - $path = '/' . $channel_name; - - $auth->owner_id = $channel_id; - - $permission_error = false; - - $folder = ''; - - require_once('include/security.php'); - $perms = permissions_sql($channel_id); - - $errors = false; - - for ($x = 1; $x < count($path_arr); $x++) { - $r = q("select id, hash, filename, flags, is_dir from attach where folder = '%s' and filename = '%s' and uid = %d and is_dir != 0 $perms", - dbesc($folder), - dbesc($path_arr[$x]), - intval($channel_id) - ); - - if ($r && intval($r[0]['is_dir'])) { - $folder = $r[0]['hash']; - $path = $path . '/' . $r[0]['filename']; - } - if (! $r) { - $r = q("select id, uid, hash, filename, filetype, filesize, revision, folder, flags, is_dir, os_storage, created, edited from attach + intval($channel['channel_account_id']), + intval($channel['channel_id']), + dbesc($hash), + dbesc($this->auth->observer), + dbesc($name), + dbesc($this->folder_hash), + intval(1), + dbesc($mimetype), + intval($filesize), + intval(0), + intval(0), + dbesc($f), + dbesc($created), + dbesc($edited), + '', + '', + dbesc($allow_cid), + dbesc($allow_gid), + dbesc($deny_cid), + dbesc($deny_gid) + ); + + // fetch the actual storage paths + + $xpath = attach_syspaths($this->auth->owner_id, $hash); + + if (is_resource($data)) { + $fp = fopen($f, 'wb'); + if ($fp) { + pipe_streams($data, $fp); + fclose($fp); + } + $size = filesize($f); + } else { + $size = file_put_contents($f, $data); + } + + // delete attach entry if file_put_contents() failed + if ($size === false) { + logger('file_put_contents() failed to ' . $f); + attach_delete($channel['channel_id'], $hash); + return; + } + + $is_photo = 0; + $gis = @getimagesize($f); + logger('getimagesize: ' . print_r($gis, true), LOGGER_DATA); + if (($gis) && supported_imagetype($gis[2])) { + $is_photo = 1; + } + + // If we know it's a photo, over-ride the type in case the source system could not determine what it was + + if ($is_photo) { + q("update attach set filetype = '%s' where hash = '%s' and uid = %d", + dbesc($gis['mime']), + dbesc($hash), + intval($channel['channel_id']) + ); + } + + // updates entry with path and filesize + $d = q("UPDATE attach SET filesize = '%s', os_path = '%s', display_path = '%s', is_photo = %d WHERE hash = '%s' AND uid = %d", + dbesc($size), + dbesc($xpath['os_path']), + dbesc($xpath['path']), + intval($is_photo), + dbesc($hash), + intval($channel['channel_id']) + ); + + // update the parent folder's lastmodified timestamp + $e = q("UPDATE attach SET edited = '%s' WHERE hash = '%s' AND uid = %d", + dbesc($edited), + dbesc($this->folder_hash), + intval($channel['channel_id']) + ); + + $maxfilesize = get_config('system', 'maxfilesize'); + if (($maxfilesize) && ($size > $maxfilesize)) { + logger('system maxfilesize exceeded. Deleting uploaded file.'); + attach_delete($channel['channel_id'], $hash); + return; + } + + // check against service class quota + $limit = engr_units_to_bytes(service_class_fetch($channel['channel_id'], 'attach_upload_limit')); + if ($limit !== false) { + $z = q("SELECT SUM(filesize) AS total FROM attach WHERE aid = %d ", + intval($channel['channel_account_id']) + ); + if (($z) && ($z[0]['total'] + $size > $limit)) { + logger('service class limit exceeded for ' . $channel['channel_name'] . ' total usage is ' . $z[0]['total'] . ' limit is ' . userReadableSize($limit)); + attach_delete($channel['channel_id'], $hash); + return; + } + } + + if ($is_photo) { + $album = ''; + if ($this->folder_hash) { + $f1 = q("select filename, display_path from attach WHERE hash = '%s' AND uid = %d", + dbesc($this->folder_hash), + intval($channel['channel_id']) + ); + if ($f1) { + $album = (($f1[0]['display_path']) ? $f1[0]['display_path'] : $f1[0]['filename']); + } + } + + $args = [ + 'resource_id' => $hash, + 'album' => $album, + 'folder' => $this->folder_hash, + 'os_syspath' => $f, + 'os_path' => $xpath['os_path'], + 'display_path' => $xpath['path'], + 'filename' => $name, + 'getimagesize' => $gis, + 'directory' => $direct + ]; + $p = photo_upload($channel, App::get_observer(), $args); + } + + Run::Summon(['Thumbnail', $hash]); + + $sync = attach_export_data($channel, $hash); + + if ($sync) { + Libsync::build_sync_packet($channel['channel_id'], array('file' => array($sync))); + } + } + + /** + * @brief Creates a new subdirectory. + * + * @param string $name the directory to create + * @return void + */ + public function createDirectory($name) + { + logger('create directory ' . $name, LOGGER_DEBUG); + + if ((!$this->auth->owner_id) || (!perm_is_allowed($this->auth->owner_id, $this->auth->observer, 'write_storage'))) { + throw new DAV\Exception\Forbidden('Permission denied.'); + } + + $channel = channelx_by_n($this->auth->owner_id); + + if ($channel) { + + // When initiated from DAV, set the 'force' flag on attach_mkdir(). This will cause the operation to report success even if the + // folder already exists. + + require_once('include/attach.php'); + $result = attach_mkdir($channel, $this->auth->observer, array('filename' => $name, 'folder' => $this->folder_hash, 'force' => true)); + + if ($result['success']) { + $sync = attach_export_data($channel, $result['data']['hash']); + logger('createDirectory: attach_export_data returns $sync:' . print_r($sync, true), LOGGER_DEBUG); + + if ($sync) { + Libsync::build_sync_packet($channel['channel_id'], array('file' => array($sync))); + } + } else { + logger('error ' . print_r($result, true), LOGGER_DEBUG); + } + } + } + + /** + * @brief delete directory + */ + public function delete() + { + logger('delete file ' . basename($this->red_path), LOGGER_DEBUG); + + if ((!$this->auth->owner_id) || (!perm_is_allowed($this->auth->owner_id, $this->auth->observer, 'write_storage'))) { + throw new DAV\Exception\Forbidden('Permission denied.'); + } + + if ($this->auth->owner_id !== $this->auth->channel_id) { + if (($this->auth->observer !== $this->data['creator']) || intval($this->data['is_dir'])) { + throw new DAV\Exception\Forbidden('Permission denied.'); + } + } + + attach_delete($this->auth->owner_id, $this->folder_hash); + + $channel = channelx_by_n($this->auth->owner_id); + if ($channel) { + $sync = attach_export_data($channel, $this->folder_hash, true); + if ($sync) { + Libsync::build_sync_packet($channel['channel_id'], array('file' => array($sync))); + } + } + } + + + /** + * @brief Checks if a child exists. + * + * @param string $name + * The name to check if it exists. + * @return bool + */ + public function childExists($name) + { + // On /cloud we show a list of available channels. + // @todo what happens if no channels are available? + $modulename = App::$module; + if ($this->red_path === '/' && $name === $modulename) { + //logger('We are at ' $modulename . ' show a channel list', LOGGER_DEBUG); + return true; + } + + $x = $this->FileData($this->ext_path . '/' . $name, $this->auth, true); + //logger('FileData returns: ' . print_r($x, true), LOGGER_DATA); + if ($x) { + return true; + } + return false; + } + + + public function moveInto($targetName, $sourcePath, DAV\INode $sourceNode) + { + + if (!$this->auth->owner_id) { + return false; + } + + if (!($sourceNode->data && $sourceNode->data['hash'])) { + return false; + } + + return attach_move($this->auth->owner_id, $sourceNode->data['hash'], $this->folder_hash); + } + + + /** + * @return void + * @todo add description of what this function does. + * + * @throw "\Sabre\DAV\Exception\NotFound" + */ + public function getDir() + { + + logger('GetDir: ' . $this->ext_path, LOGGER_DEBUG); + $this->auth->log(); + $modulename = App::$module; + + $file = $this->ext_path; + + $x = strpos($file, '/' . $modulename); + if ($x === 0) { + $file = substr($file, strlen($modulename) + 1); + } + + if ((!$file) || ($file === '/')) { + return; + } + + $file = trim($file, '/'); + $path_arr = explode('/', $file); + + if (!$path_arr) + return; + + logger('paths: ' . print_r($path_arr, true), LOGGER_DATA); + + $channel_name = $path_arr[0]; + + $channel = channelx_by_nick($channel_name); + + if (!$channel) { + throw new DAV\Exception\NotFound('The file with name: ' . $channel_name . ' could not be found.'); + } + + $channel_id = $channel['channel_id']; + $this->auth->owner_id = $channel_id; + $this->auth->owner_nick = $channel_name; + + $path = '/' . $channel_name; + $folder = ''; + $os_path = ''; + + for ($x = 1; $x < count($path_arr); $x++) { + $r = q("select id, hash, filename, flags, is_dir from attach where folder = '%s' and filename = '%s' and uid = %d and is_dir != 0", + dbesc($folder), + dbesc($path_arr[$x]), + intval($channel_id) + ); + if ($r && intval($r[0]['is_dir'])) { + $folder = $r[0]['hash']; + if (strlen($os_path)) { + $os_path .= '/'; + } + $os_path .= $folder; + $path = $path . '/' . $r[0]['filename']; + } + } + $this->folder_hash = $folder; + $this->os_path = $os_path; + } + + /** + * @brief Returns the last modification time for the directory, as a UNIX + * timestamp. + * + * It looks for the last edited file in the folder. If it is an empty folder + * it returns the lastmodified time of the folder itself, to prevent zero + * timestamps. + * + * @return int last modification time in UNIX timestamp + */ + public function getLastModified() + { + $r = q("SELECT edited FROM attach WHERE folder = '%s' AND uid = %d ORDER BY edited DESC LIMIT 1", + dbesc($this->folder_hash), + intval($this->auth->owner_id) + ); + if (!$r) { + $r = q("SELECT edited FROM attach WHERE hash = '%s' AND uid = %d LIMIT 1", + dbesc($this->folder_hash), + intval($this->auth->owner_id) + ); + if (!$r) + return ''; + } + return datetime_convert('UTC', 'UTC', $r[0]['edited'], 'U'); + } + + + /** + * @brief Array with all Directory and File DAV\\Node items for the given path. + * + * @param string $file path to a directory + * @param BasicAuth &$auth + * @returns null|array \\Sabre\\DAV\\INode[] + * @throw "\Sabre\DAV\Exception\Forbidden" + * @throw "\Sabre\DAV\Exception\NotFound" + */ + public function CollectionData($file, &$auth) + { + $ret = []; + + $x = strpos($file, '/cloud'); + if ($x === 0) { + $file = substr($file, 6); + } + + // return a list of channel if we are not inside a channel + if ((!$file) || ($file === '/')) { + return $this->ChannelList($auth); + } + + $file = trim($file, '/'); + $path_arr = explode('/', $file); + + if (!$path_arr) { + return null; + } + + $channel_name = $path_arr[0]; + + $channel = channelx_by_nick($channel_name); + + if (!$channel) { + return null; + } + + $channel_id = $channel['channel_id']; + $perms = permissions_sql($channel_id); + + $auth->owner_id = $channel_id; + + $path = '/' . $channel_name; + + $folder = ''; + $errors = false; + $permission_error = false; + + for ($x = 1; $x < count($path_arr); $x++) { + $r = q("SELECT id, hash, filename, flags, is_dir FROM attach WHERE folder = '%s' AND filename = '%s' AND uid = %d AND is_dir != 0 $perms LIMIT 1", + dbesc($folder), + dbesc($path_arr[$x]), + intval($channel_id) + ); + if (!$r) { + // path wasn't found. Try without permissions to see if it was the result of permissions. + $errors = true; + $r = q("select id, hash, filename, flags, is_dir from attach where folder = '%s' and filename = '%s' and uid = %d and is_dir != 0 limit 1", + dbesc($folder), + basename($path_arr[$x]), + intval($channel_id) + ); + if ($r) { + $permission_error = true; + } + break; + } + + if ($r && intval($r[0]['is_dir'])) { + $folder = $r[0]['hash']; + $path = $path . '/' . $r[0]['filename']; + } + } + + if ($errors) { + if ($permission_error) { + throw new DAV\Exception\Forbidden('Permission denied.'); + } else { + throw new DAV\Exception\NotFound('A component of the requested file path could not be found.'); + } + } + + // This should no longer be needed since we just returned errors for paths not found + if ($path !== '/' . $file) { + logger("Path mismatch: $path !== /$file"); + return NULL; + } + + $prefix = ''; + + if (!array_key_exists('cloud_sort', $_SESSION)) + $_SESSION['cloud_sort'] = 'name'; + + switch ($_SESSION['cloud_sort']) { + case 'size': + $suffix = ' order by is_dir desc, filesize asc '; + break; + // The following provides inconsistent results for directories because we re-calculate the date for directories based on the most recent change + case 'date': + $suffix = ' order by is_dir desc, edited asc '; + break; + case 'name': + default: + $suffix = ' order by is_dir desc, filename asc '; + break; + } + + $r = q("select $prefix id, uid, hash, filename, filetype, filesize, revision, folder, flags, is_dir, created, edited from attach where folder = '%s' and uid = %d $perms $suffix", + dbesc($folder), + intval($channel_id) + ); + + foreach ($r as $rr) { + if (App::$module === 'cloud' && (strpos($rr['filename'], '.') === 0) && (!get_pconfig($channel_id, 'system', 'show_dot_files'))) { + continue; + } + + // @FIXME I don't think we use revisions currently in attach structures. + // In case we see any in the wild provide a unique filename. This + // name may or may not be accessible + + if ($rr['revision']) { + $rr['filename'] .= '-' . $rr['revision']; + } + + // logger('filename: ' . $rr['filename'], LOGGER_DEBUG); + if (intval($rr['is_dir'])) { + $ret[] = new Directory($path . '/' . $rr['filename'], $auth); + } else { + $ret[] = new File($path . '/' . $rr['filename'], $rr, $auth); + } + } + + return $ret; + } + + + /** + * @brief Returns an array with viewable channels. + * + * Get a list of Directory objects with all the channels where the visitor + * has view_storage perms. + * + * + * @param BasicAuth &$auth + * @return array Directory[] + */ + public function ChannelList(&$auth) + { + $ret = []; + + $disabled = intval(get_config('system', 'cloud_disable_siteroot', true)); + + $r = q("SELECT channel_id, channel_address, profile.publish FROM channel left join profile on profile.uid = channel.channel_id WHERE channel_removed = 0 AND channel_system = 0 AND (channel_pageflags & %d) = 0 and profile.is_default = 1", + intval(PAGE_HIDDEN) + ); + if ($r) { + foreach ($r as $rr) { + if ((perm_is_allowed($rr['channel_id'], $auth->observer, 'view_storage') && $rr['publish']) || $rr['channel_id'] == $this->auth->channel_id) { + logger('found channel: /cloud/' . $rr['channel_address'], LOGGER_DATA); + if ($disabled) { + $conn = q("select abook_id from abook where abook_channel = %d and abook_xchan = '%s' and abook_pending = 0", + intval($rr['channel_id']), + dbesc($auth->observer) + ); + if (!$conn) { + continue; + } + } + + $ret[] = new Directory($rr['channel_address'], $auth); + } + } + } + return $ret; + } + + + /** + * @brief + * + * @param string $file + * path to file or directory + * @param BasicAuth &$auth + * @param bool $test (optional) enable test mode + * @return File|Directory|bool|null + * @throw "\Sabre\DAV\Exception\Forbidden" + */ + public function FileData($file, &$auth, $test = false) + { + logger($file . (($test) ? ' (test mode) ' : ''), LOGGER_DATA); + + $x = strpos($file, '/cloud'); + if ($x === 0) { + $file = substr($file, 6); + } else { + $x = strpos($file, '/dav'); + if ($x === 0) + $file = substr($file, 4); + } + + if ((!$file) || ($file === '/')) { + return new Directory('/', $auth); + } + + $file = trim($file, '/'); + + $path_arr = explode('/', $file); + + if (!$path_arr) + return null; + + $channel_name = $path_arr[0]; + + $r = q("select channel_id from channel where channel_address = '%s' limit 1", + dbesc($channel_name) + ); + + if (!$r) + return null; + + $channel_id = $r[0]['channel_id']; + + $path = '/' . $channel_name; + + $auth->owner_id = $channel_id; + + $permission_error = false; + + $folder = ''; + + require_once('include/security.php'); + $perms = permissions_sql($channel_id); + + $errors = false; + + for ($x = 1; $x < count($path_arr); $x++) { + $r = q("select id, hash, filename, flags, is_dir from attach where folder = '%s' and filename = '%s' and uid = %d and is_dir != 0 $perms", + dbesc($folder), + dbesc($path_arr[$x]), + intval($channel_id) + ); + + if ($r && intval($r[0]['is_dir'])) { + $folder = $r[0]['hash']; + $path = $path . '/' . $r[0]['filename']; + } + if (!$r) { + $r = q("select id, uid, hash, filename, filetype, filesize, revision, folder, flags, is_dir, os_storage, created, edited from attach where folder = '%s' and filename = '%s' and uid = %d $perms order by filename limit 1", - dbesc($folder), - dbesc(basename($file)), - intval($channel_id) - ); - } - if (! $r) { - $errors = true; - $r = q("select id, uid, hash, filename, filetype, filesize, revision, folder, flags, is_dir, os_storage, created, edited from attach + dbesc($folder), + dbesc(basename($file)), + intval($channel_id) + ); + } + if (!$r) { + $errors = true; + $r = q("select id, uid, hash, filename, filetype, filesize, revision, folder, flags, is_dir, os_storage, created, edited from attach where folder = '%s' and filename = '%s' and uid = %d order by filename limit 1", - dbesc($folder), - dbesc(basename($file)), - intval($channel_id) - ); - if ($r) - $permission_error = true; - } - } + dbesc($folder), + dbesc(basename($file)), + intval($channel_id) + ); + if ($r) + $permission_error = true; + } + } - if ($path === '/' . $file) { - if ($test) - return true; - // final component was a directory. - return new Directory($file, $auth); - } + if ($path === '/' . $file) { + if ($test) + return true; + // final component was a directory. + return new Directory($file, $auth); + } - if ($errors) { - logger('not found ' . $file); - if ($test) - return false; - if ($permission_error) { - logger('permission error ' . $file); - throw new DAV\Exception\Forbidden('Permission denied.'); - } - return; - } + if ($errors) { + logger('not found ' . $file); + if ($test) + return false; + if ($permission_error) { + logger('permission error ' . $file); + throw new DAV\Exception\Forbidden('Permission denied.'); + } + return; + } - if ($r) { - if ($test) - return true; + if ($r) { + if ($test) + return true; - if (intval($r[0]['is_dir'])) { - return new Directory($path . '/' . $r[0]['filename'], $auth); - } - else { - return new File($path . '/' . $r[0]['filename'], $r[0], $auth); - } - } - return false; - } + if (intval($r[0]['is_dir'])) { + return new Directory($path . '/' . $r[0]['filename'], $auth); + } else { + return new File($path . '/' . $r[0]['filename'], $r[0], $auth); + } + } + return false; + } - public function getQuotaInfo() { + public function getQuotaInfo() + { - /** - * Returns the quota information - * - * This method MUST return an array with 2 values, the first being the total used space, - * the second the available space (in bytes) - */ + /** + * Returns the quota information + * + * This method MUST return an array with 2 values, the first being the total used space, + * the second the available space (in bytes) + */ - $used = 0; - $limit = 0; - $free = 0; - - if ($this->auth->owner_id) { - $channel = channelx_by_n($this->auth->owner_id); - if($channel) { - $r = q("SELECT SUM(filesize) AS total FROM attach WHERE aid = %d", - intval($channel['channel_account_id']) - ); - $used = (($r) ? (float) $r[0]['total'] : 0); - $limit = (float) engr_units_to_bytes(service_class_fetch($this->auth->owner_id, 'attach_upload_limit')); - if($limit) { - // Don't let the result go negative - $free = (($limit > $used) ? $limit - $used : 0); - } - } - } + $used = 0; + $limit = 0; + $free = 0; - if(! $limit) { - $free = disk_free_space('store'); - $used = disk_total_space('store') - $free; - } + if ($this->auth->owner_id) { + $channel = channelx_by_n($this->auth->owner_id); + if ($channel) { + $r = q("SELECT SUM(filesize) AS total FROM attach WHERE aid = %d", + intval($channel['channel_account_id']) + ); + $used = (($r) ? (float)$r[0]['total'] : 0); + $limit = (float)engr_units_to_bytes(service_class_fetch($this->auth->owner_id, 'attach_upload_limit')); + if ($limit) { + // Don't let the result go negative + $free = (($limit > $used) ? $limit - $used : 0); + } + } + } - // prevent integer overflow on 32-bit systems + if (!$limit) { + $free = disk_free_space('store'); + $used = disk_total_space('store') - $free; + } - if($used > (float) PHP_INT_MAX) - $used = PHP_INT_MAX; - if($free > (float) PHP_INT_MAX) - $free = PHP_INT_MAX; + // prevent integer overflow on 32-bit systems - return [ (int) $used, (int) $free ]; + if ($used > (float)PHP_INT_MAX) + $used = PHP_INT_MAX; + if ($free > (float)PHP_INT_MAX) + $free = PHP_INT_MAX; - } + return [(int)$used, (int)$free]; + + } } diff --git a/Zotlabs/Storage/File.php b/Zotlabs/Storage/File.php index a345492e4..248a484ff 100644 --- a/Zotlabs/Storage/File.php +++ b/Zotlabs/Storage/File.php @@ -14,8 +14,8 @@ require_once('include/photos.php'); * * It provides all functions to work with files in the project cloud through DAV protocol. * - * @extends \\Sabre\\DAV\\Node - * @implements \\Sabre\\DAV\\IFile + * @extends \Sabre\\DAV\\Node + * @implements \Sabre\\DAV\\IFile * * @license http://opensource.org/licenses/mit-license.php The MIT License (MIT) */ @@ -36,7 +36,7 @@ class File extends DAV\Node implements DAV\IFile { /** * @see \\Sabre\\DAV\\Auth\\Backend\\BackendInterface - * @var \\Zotlabs\\Storage\\BasicAuth $auth + * @var \Zotlabs\\Storage\\BasicAuth $auth */ private $auth; @@ -427,7 +427,7 @@ class File extends DAV\Node implements DAV\IFile { // invoked on a dav-mounted filesystem. By setting system.os_delete_prohibit, one can remove files // via the web interface but from their operating system the filesystem is treated as read-only. - if (get_pconfig($this->auth->owner_id,'system','os_delete_prohibit') && \App::$module == 'dav') { + if (get_pconfig($this->auth->owner_id,'system','os_delete_prohibit') && App::$module == 'dav') { throw new DAV\Exception\Forbidden('Permission denied.'); } diff --git a/Zotlabs/Storage/GitRepo.php b/Zotlabs/Storage/GitRepo.php index e3b1e13d6..261e11ffe 100644 --- a/Zotlabs/Storage/GitRepo.php +++ b/Zotlabs/Storage/GitRepo.php @@ -2,157 +2,170 @@ namespace Zotlabs\Storage; +use PHPGit\Exception\GitException; use PHPGit\Git as PHPGit; - /** * Wrapper class for PHPGit class for git repositories managed by Hubzilla * * @author Andrew Manning */ -class GitRepo { +class GitRepo +{ - public $url = null; - public $name = null; - private $path = null; - private $channel = null; - public $git = null; - private $repoBasePath = null; + public $url = null; + public $name = null; + private $path = null; + private $channel = null; + public $git = null; + private $repoBasePath = null; - function __construct($channel = 'sys', $url = null, $clone = false, $name = null, $path = null) { + public function __construct($channel = 'sys', $url = null, $clone = false, $name = null, $path = null) + { - if ($channel === 'sys' && !is_site_admin()) { - logger('Only admin can use channel sys'); - return null; - } + if ($channel === 'sys' && !is_site_admin()) { + logger('Only admin can use channel sys'); + return null; + } - $this->repoBasePath = 'store/git'; - $this->channel = $channel; - $this->git = new PHPGit(); + $this->repoBasePath = 'store/git'; + $this->channel = $channel; + $this->git = new PHPGit(); - // Allow custom path for repo in the case of , for example - if ($path) { - $this->path = $path; - } else { - $this->path = $this->repoBasePath . "/" . $this->channel . "/" . $this->name; - } + // Allow custom path for repo in the case of , for example + if ($path) { + $this->path = $path; + } else { + $this->path = $this->repoBasePath . "/" . $this->channel . "/" . $this->name; + } - if ($this->isValidGitRepoURL($url)) { - $this->url = $url; - } + if ($this->isValidGitRepoURL($url)) { + $this->url = $url; + } - if ($name) { - $this->name = $name; - } else { - $this->name = $this->getRepoNameFromURL($url); - } - if (!$this->name) { - logger('Error creating GitRepo. No repo name found.'); - return null; - } + if ($name) { + $this->name = $name; + } else { + $this->name = $this->getRepoNameFromURL($url); + } + if (!$this->name) { + logger('Error creating GitRepo. No repo name found.'); + return null; + } - if (is_dir($this->path)) { - // ignore the $url input if it exists - // TODO: Check if the path is either empty or is a valid git repo and error if not - $this->git->setRepository($this->path); - // TODO: get repo metadata - return; - } + if (is_dir($this->path)) { + // ignore the $url input if it exists + // TODO: Check if the path is either empty or is a valid git repo and error if not + $this->git->setRepository($this->path); + // TODO: get repo metadata + return; + } - if ($this->url) { - // create the folder and clone the repo at url to that folder if $clone is true - if ($clone) { - if (mkdir($this->path, 0770, true)) { - $this->git->setRepository($this->path); - if (!$this->cloneRepo()) { - // TODO: throw error - logger('git clone failed: ' . json_encode($this->git)); - } - } else { - logger('git repo path could not be created: ' . json_encode($this->git)); - } - } - } - } - - public function initRepo() { - if(!$this->path) return false; - try { - return $this->git->init($this->path); - } catch (\PHPGit\Exception\GitException $ex) { - return false; - } - } + if ($this->url) { + // create the folder and clone the repo at url to that folder if $clone is true + if ($clone) { + if (mkdir($this->path, 0770, true)) { + $this->git->setRepository($this->path); + if (!$this->cloneRepo()) { + // TODO: throw error + logger('git clone failed: ' . json_encode($this->git)); + } + } else { + logger('git repo path could not be created: ' . json_encode($this->git)); + } + } + } + } - public function pull() { - try { - $success = $this->git->pull(); - } catch (\PHPGit\Exception\GitException $ex) { - return false; - } - return $success; - } + public function initRepo() + { + if (!$this->path) { + return false; + } + try { + return $this->git->init($this->path); + } catch (GitException $ex) { + return false; + } + } - public function getRepoPath() { - return $this->path; - } + public function pull() + { + try { + $success = $this->git->pull(); + } catch (GitException $ex) { + return false; + } + return $success; + } - public function setRepoPath($directory) { - if (is_dir($directory)) { - $this->path->$directory; - $this->git->setRepository($directory); - return true; - } - return false; - } + public function getRepoPath() + { + return $this->path; + } - public function setIdentity($user_name, $user_email) { - // setup user for commit messages - $this->git->config->set("user.name", $user_name, ['global' => false, 'system' => false]); - $this->git->config->set("user.email", $user_email, ['global' => false, 'system' => false]); - } + public function setRepoPath($directory) + { + if (is_dir($directory)) { + $this->path->$directory; + $this->git->setRepository($directory); + return true; + } + return false; + } - public function cloneRepo() { - if (validate_url($this->url) && $this->isValidGitRepoURL($this->url) && is_dir($this->path)) { - return $this->git->clone($this->url, $this->path); - } - } + public function setIdentity($user_name, $user_email) + { + // setup user for commit messages + $this->git->config->set("user.name", $user_name, ['global' => false, 'system' => false]); + $this->git->config->set("user.email", $user_email, ['global' => false, 'system' => false]); + } - public function probeRepo() { - $git = $this->git; - $repo = []; - $repo['remote'] = $git->remote(); - $repo['branches'] = $git->branch(['all' => true]); - $repo['logs'] = $git->log(array('limit' => 50)); - return $repo; - } - - // Commit changes to the repo. Default is to stage all changes and commit everything. - public function commit($msg, $options = []) { - try { - return $this->git->commit($msg, $options); - } catch (\PHPGit\Exception\GitException $ex) { - return false; - } - } + public function cloneRepo() + { + if (validate_url($this->url) && $this->isValidGitRepoURL($this->url) && is_dir($this->path)) { + return $this->git->clone($this->url, $this->path); + } + } - public static function isValidGitRepoURL($url) { - if (validate_url($url) && strrpos(parse_url($url, PHP_URL_PATH), '.')) { - return true; - } else { - return false; - } - } + public function probeRepo() + { + $git = $this->git; + $repo = []; + $repo['remote'] = $git->remote(); + $repo['branches'] = $git->branch(['all' => true]); + $repo['logs'] = $git->log(array('limit' => 50)); + return $repo; + } - public static function getRepoNameFromURL($url) { - $urlpath = parse_url($url, PHP_URL_PATH); - $lastslash = strrpos($urlpath, '/') + 1; - $gitext = strrpos($urlpath, '.'); - if ($gitext) { - return substr($urlpath, $lastslash, $gitext - $lastslash); - } else { - return null; - } - } + // Commit changes to the repo. Default is to stage all changes and commit everything. + public function commit($msg, $options = []) + { + try { + return $this->git->commit($msg, $options); + } catch (GitException $ex) { + return false; + } + } + public static function isValidGitRepoURL($url) + { + if (validate_url($url) && strrpos(parse_url($url, PHP_URL_PATH), '.')) { + return true; + } else { + return false; + } + } + + public static function getRepoNameFromURL($url) + { + $urlpath = parse_url($url, PHP_URL_PATH); + $lastslash = strrpos($urlpath, '/') + 1; + $gitext = strrpos($urlpath, '.'); + if ($gitext) { + return substr($urlpath, $lastslash, $gitext - $lastslash); + } else { + return null; + } + } } diff --git a/Zotlabs/Storage/ZotOauth2Pdo.php b/Zotlabs/Storage/ZotOauth2Pdo.php index b2c3ce228..45a7abef0 100644 --- a/Zotlabs/Storage/ZotOauth2Pdo.php +++ b/Zotlabs/Storage/ZotOauth2Pdo.php @@ -2,9 +2,12 @@ namespace Zotlabs\Storage; -class ZotOauth2Pdo extends \OAuth2\Storage\Pdo { - public function getConfig() +use OAuth2\Storage\Pdo; + +class ZotOauth2Pdo extends Pdo +{ + public function getConfig() { - return $this->config; + return $this->config; } } diff --git a/Zotlabs/Text/Tagadelic.php b/Zotlabs/Text/Tagadelic.php index 8b898b3cb..634e0debb 100644 --- a/Zotlabs/Text/Tagadelic.php +++ b/Zotlabs/Text/Tagadelic.php @@ -2,45 +2,46 @@ namespace Zotlabs\Text; +class Tagadelic +{ -class Tagadelic { + public static function calc($arr) + { - static public function calc($arr) { + $tags = []; + $min = 1e9; + $max = -1e9; - $tags = []; - $min = 1e9; - $max = -1e9; + $x = 0; + if (! $arr) { + return []; + } - $x = 0; - if (! $arr) { - return []; - } + foreach ($arr as $rr) { + $tags[$x][0] = $rr['term']; + $tags[$x][1] = log($rr['total']); + $tags[$x][2] = 0; + $min = min($min, $tags[$x][1]); + $max = max($max, $tags[$x][1]); + $x++; + } - foreach ($arr as $rr) { - $tags[$x][0] = $rr['term']; - $tags[$x][1] = log($rr['total']); - $tags[$x][2] = 0; - $min = min($min,$tags[$x][1]); - $max = max($max,$tags[$x][1]); - $x ++; - } + usort($tags, 'self::tags_sort'); - usort($tags,'self::tags_sort'); + $range = max(.01, $max - $min) * 1.0001; - $range = max(.01, $max - $min) * 1.0001; + for ($x = 0; $x < count($tags); $x++) { + $tags[$x][2] = 1 + floor(9 * ($tags[$x][1] - $min) / $range); + } - for ($x = 0; $x < count($tags); $x ++) { - $tags[$x][2] = 1 + floor(9 * ($tags[$x][1] - $min) / $range); - } + return $tags; + } - return $tags; - } - - static public function tags_sort($a,$b) { - if (strtolower($a[0]) === strtolower($b[0])) { - return 0; - } - return((strtolower($a[0]) < strtolower($b[0])) ? -1 : 1); - } - -} \ No newline at end of file + public static function tags_sort($a, $b) + { + if (strtolower($a[0]) === strtolower($b[0])) { + return 0; + } + return((strtolower($a[0]) < strtolower($b[0])) ? -1 : 1); + } +} diff --git a/Zotlabs/Thumbs/Epubthumb.php b/Zotlabs/Thumbs/Epubthumb.php index 3f283dfa0..3ceddbb41 100644 --- a/Zotlabs/Thumbs/Epubthumb.php +++ b/Zotlabs/Thumbs/Epubthumb.php @@ -10,52 +10,53 @@ use Epub; * @brief Thumbnail creation for epub files. * */ +class Epubthumb +{ -class Epubthumb { + /** + * @brief Match for application/epub+zip. + * + * @param string $type MimeType + * @return bool + */ - /** - * @brief Match for application/epub+zip. - * - * @param string $type MimeType - * @return boolean - */ + public function Match($type) + { + return (($type === 'application/epub+zip') ? true : false); + } - function Match($type) { - return(($type === 'application/epub+zip') ? true : false ); - } + /** + * @brief + * + * @param array $attach + * @param number $preview_style unused + * @param number $height (optional) default 300 + * @param number $width (optional) default 300 + */ - /** - * @brief - * - * @param array $attach - * @param number $preview_style unused - * @param number $height (optional) default 300 - * @param number $width (optional) default 300 - */ - - function Thumb($attach, $preview_style, $height = 300, $width = 300) { + public function Thumb($attach, $preview_style, $height = 300, $width = 300) + { - $photo = false; + $photo = false; - $ep = new EPub(dbunescbin($attach['content'])); - $data = $ep->Cover(); + $ep = new EPub(dbunescbin($attach['content'])); + $data = $ep->Cover(); - if ($data['found']) { - $photo = $data['data']; - } + if ($data['found']) { + $photo = $data['data']; + } - if ($photo) { - $image = imagecreatefromstring($photo); - $dest = imagecreatetruecolor($width, $height); - $srcwidth = imagesx($image); - $srcheight = imagesy($image); + if ($photo) { + $image = imagecreatefromstring($photo); + $dest = imagecreatetruecolor($width, $height); + $srcwidth = imagesx($image); + $srcheight = imagesy($image); - imagealphablending($dest, false); - imagesavealpha($dest, true); - imagecopyresampled($dest, $image, 0, 0, 0, 0, $width, $height, $srcwidth, $srcheight); - imagedestroy($image); - imagejpeg($dest, dbunescbin($attach['content']) . '.thumb'); - } - } + imagealphablending($dest, false); + imagesavealpha($dest, true); + imagecopyresampled($dest, $image, 0, 0, 0, 0, $width, $height, $srcwidth, $srcheight); + imagedestroy($image); + imagejpeg($dest, dbunescbin($attach['content']) . '.thumb'); + } + } } - diff --git a/Zotlabs/Thumbs/Mp3audio.php b/Zotlabs/Thumbs/Mp3audio.php index 71260381f..bccd20b94 100644 --- a/Zotlabs/Thumbs/Mp3audio.php +++ b/Zotlabs/Thumbs/Mp3audio.php @@ -8,40 +8,42 @@ require_once('library/php-id3/PhpId3/Id3Tags.php'); use PhpId3\Id3TagsReader; -class Mp3audio { +class Mp3audio +{ - function Match($type) { - return(($type === 'audio/mpeg') ? true : false ); - } + public function Match($type) + { + return (($type === 'audio/mpeg') ? true : false); + } - function Thumb($attach,$preview_style,$height = 300, $width = 300) { + public function Thumb($attach, $preview_style, $height = 300, $width = 300) + { - $fh = @fopen(dbunescbin($attach['content']),'rb'); - if ($fh === false) { - return; - } - $id3 = new Id3TagsReader($fh); - $id3->readAllTags(); + $fh = @fopen(dbunescbin($attach['content']), 'rb'); + if ($fh === false) { + return; + } + $id3 = new Id3TagsReader($fh); + $id3->readAllTags(); - $image = $id3->getImage(); - if (is_array($image)) { - $photo = $image[1]; - } + $image = $id3->getImage(); + if (is_array($image)) { + $photo = $image[1]; + } + + fclose($fh); - fclose($fh); - if ($photo) { - $image = imagecreatefromstring($photo); - $dest = imagecreatetruecolor( $width, $height ); - $srcwidth = imagesx($image); - $srcheight = imagesy($image); + $image = imagecreatefromstring($photo); + $dest = imagecreatetruecolor($width, $height); + $srcwidth = imagesx($image); + $srcheight = imagesy($image); - imagealphablending($dest, false); - imagesavealpha($dest, true); - imagecopyresampled($dest, $image, 0, 0, 0, 0, $width, $height, $srcwidth, $srcheight); + imagealphablending($dest, false); + imagesavealpha($dest, true); + imagecopyresampled($dest, $image, 0, 0, 0, 0, $width, $height, $srcwidth, $srcheight); imagedestroy($image); - imagejpeg($dest,dbunescbin($attach['content']) . '.thumb'); - } - } + imagejpeg($dest, dbunescbin($attach['content']) . '.thumb'); + } + } } - diff --git a/Zotlabs/Thumbs/Pdf.php b/Zotlabs/Thumbs/Pdf.php index 53b7d22f8..899bc0e52 100644 --- a/Zotlabs/Thumbs/Pdf.php +++ b/Zotlabs/Thumbs/Pdf.php @@ -2,48 +2,48 @@ namespace Zotlabs\Thumbs; +class Pdf +{ -class Pdf { + public function Match($type) + { + return (($type === 'application/pdf') ? true : false); + } - function Match($type) { - return(($type === 'application/pdf') ? true : false ); - } + public function Thumb($attach, $preview_style, $height = 300, $width = 300) + { - function Thumb($attach,$preview_style,$height = 300, $width = 300) { + $photo = false; - $photo = false; + $file = dbunescbin($attach['content']); + $tmpfile = $file . '.pdf'; + $outfile = $file . '.jpg'; - $file = dbunescbin($attach['content']); - $tmpfile = $file . '.pdf'; - $outfile = $file . '.jpg'; + $istream = fopen($file, 'rb'); + $ostream = fopen($tmpfile, 'wb'); + if ($istream && $ostream) { + pipe_streams($istream, $ostream); + fclose($istream); + fclose($ostream); + } - $istream = fopen($file,'rb'); - $ostream = fopen($tmpfile,'wb'); - if ($istream && $ostream) { - pipe_streams($istream,$ostream); - fclose($istream); - fclose($ostream); - } - - $imagick_path = get_config('system','imagick_convert_path'); - if ($imagick_path && @file_exists($imagick_path)) { - $cmd = $imagick_path . ' ' . escapeshellarg(PROJECT_BASE . '/' . $tmpfile . '[0]') . ' -resize ' . $width . 'x' . $height . ' ' . escapeshellarg(PROJECT_BASE . '/' . $outfile); - // logger('imagick thumbnail command: ' . $cmd); - for ($x = 0; $x < 4; $x ++) { - exec($cmd); - if (! file_exists($outfile)) { - logger('imagick scale failed. Retrying.'); - continue; - } - } - if (! file_exists($outfile)) { - logger('imagick scale failed.'); - } - else { - @rename($outfile,$file . '.thumb'); - } - } - @unlink($tmpfile); - } + $imagick_path = get_config('system', 'imagick_convert_path'); + if ($imagick_path && @file_exists($imagick_path)) { + $cmd = $imagick_path . ' ' . escapeshellarg(PROJECT_BASE . '/' . $tmpfile . '[0]') . ' -resize ' . $width . 'x' . $height . ' ' . escapeshellarg(PROJECT_BASE . '/' . $outfile); + // logger('imagick thumbnail command: ' . $cmd); + for ($x = 0; $x < 4; $x++) { + exec($cmd); + if (!file_exists($outfile)) { + logger('imagick scale failed. Retrying.'); + continue; + } + } + if (!file_exists($outfile)) { + logger('imagick scale failed.'); + } else { + @rename($outfile, $file . '.thumb'); + } + } + @unlink($tmpfile); + } } - diff --git a/Zotlabs/Thumbs/Text.php b/Zotlabs/Thumbs/Text.php index 0f6632238..4846adc11 100644 --- a/Zotlabs/Thumbs/Text.php +++ b/Zotlabs/Thumbs/Text.php @@ -2,48 +2,50 @@ namespace Zotlabs\Thumbs; +class Text +{ -class Text { + public function MatchDefault($type) + { + return (($type === 'text') ? true : false); + } - function MatchDefault($type) { - return(($type === 'text') ? true : false ); - } + public function Thumb($attach, $preview_style, $height = 300, $width = 300) + { - function Thumb($attach,$preview_style,$height = 300, $width = 300) { + $stream = @fopen(dbunescbin($attach['content']), 'rb'); + if ($stream) { + $content = trim(stream_get_contents($stream, 4096)); + $content = str_replace("\r", '', $content); + $content_a = explode("\n", $content); + } + if ($content_a) { + $fsize = 4; + $lsize = 8; + $image = imagecreate($width, $height); + imagecolorallocate($image, 255, 255, 255); + $colour = imagecolorallocate($image, 0, 0, 0); + $border = imagecolorallocate($image, 208, 208, 208); - $stream = @fopen(dbunescbin($attach['content']),'rb'); - if ($stream) { - $content = trim(stream_get_contents($stream,4096)); - $content = str_replace("\r",'',$content); - $content_a = explode("\n",$content); - } - if ($content_a) { - $fsize = 4; - $lsize = 8; - $image = imagecreate($width,$height); - imagecolorallocate($image,255,255,255); - $colour = imagecolorallocate($image,0,0,0); - $border = imagecolorallocate($image,208,208,208); + $x1 = 0; + $y1 = 0; + $x2 = ImageSX($image) - 1; + $y2 = ImageSY($image) - 1; - $x1 = 0; - $y1 = 0; - $x2 = ImageSX($image) - 1; - $y2 = ImageSY($image) - 1; + for ($i = 0; $i < 2; $i++) { + ImageRectangle($image, $x1++, $y1++, $x2--, $y2--, $border); + } - for ($i = 0; $i < 2; $i++) { - ImageRectangle($image, $x1++, $y1++, $x2--, $y2--, $border); - } - - foreach ($content_a as $l => $t) { - $l = $l + 1; - $x = 3; - $y = ($l * $lsize) + 3 - $fsize; - imagestring($image,1,$x,$y,$t,$colour); - if (($l * $lsize) >= $height) { - break; - } - } - imagejpeg($image,dbunescbin($attach['content']) . '.thumb'); - } - } -} \ No newline at end of file + foreach ($content_a as $l => $t) { + $l = $l + 1; + $x = 3; + $y = ($l * $lsize) + 3 - $fsize; + imagestring($image, 1, $x, $y, $t, $colour); + if (($l * $lsize) >= $height) { + break; + } + } + imagejpeg($image, dbunescbin($attach['content']) . '.thumb'); + } + } +} diff --git a/Zotlabs/Thumbs/Video.php b/Zotlabs/Thumbs/Video.php index cf20c561b..9ab5a12d1 100644 --- a/Zotlabs/Thumbs/Video.php +++ b/Zotlabs/Thumbs/Video.php @@ -2,67 +2,66 @@ namespace Zotlabs\Thumbs; +class Video +{ -class Video { + public function MatchDefault($type) + { + return (($type === 'video') ? true : false); + } - function MatchDefault($type) { - return(($type === 'video') ? true : false ); - } + public function Thumb($attach, $preview_style, $height = 300, $width = 300) + { - function Thumb($attach,$preview_style,$height = 300, $width = 300) { + $photo = false; - $photo = false; - - $t = explode('/',$attach['filetype']); - if ($t[1]) { - $extension = '.' . $t[1]; - } - else { - return; - } + $t = explode('/', $attach['filetype']); + if ($t[1]) { + $extension = '.' . $t[1]; + } else { + return; + } - $file = dbunescbin($attach['content']); - $tmpfile = $file . $extension; - $outfile = $file . '.jpg'; + $file = dbunescbin($attach['content']); + $tmpfile = $file . $extension; + $outfile = $file . '.jpg'; - $istream = fopen($file,'rb'); - $ostream = fopen($tmpfile,'wb'); - if ($istream && $ostream) { - pipe_streams($istream,$ostream); - fclose($istream); - fclose($ostream); - } + $istream = fopen($file, 'rb'); + $ostream = fopen($tmpfile, 'wb'); + if ($istream && $ostream) { + pipe_streams($istream, $ostream); + fclose($istream); + fclose($ostream); + } - /* - * Note: imagick convert may try to call 'ffmpeg' (or other conversion utilities) under - * the covers for this particular operation. If this is not installed or not in the path - * for the web server user, errors may be reported in the web server logs. - */ + /* + * Note: imagick convert may try to call 'ffmpeg' (or other conversion utilities) under + * the covers for this particular operation. If this is not installed or not in the path + * for the web server user, errors may be reported in the web server logs. + */ - $ffmpeg = trim(shell_exec('which ffmpeg')); - if (! $ffmpeg) { - logger('ffmpeg not found in path. Video thumbnails may fail.'); - } + $ffmpeg = trim(shell_exec('which ffmpeg')); + if (!$ffmpeg) { + logger('ffmpeg not found in path. Video thumbnails may fail.'); + } - $imagick_path = get_config('system','imagick_convert_path'); - if ($imagick_path && @file_exists($imagick_path)) { - $cmd = $imagick_path . ' ' . escapeshellarg(PROJECT_BASE . '/' . $tmpfile . '[0]') . ' -resize ' . $width . 'x' . $height . ' ' . escapeshellarg(PROJECT_BASE . '/' . $outfile); - // logger('imagick thumbnail command: ' . $cmd); + $imagick_path = get_config('system', 'imagick_convert_path'); + if ($imagick_path && @file_exists($imagick_path)) { + $cmd = $imagick_path . ' ' . escapeshellarg(PROJECT_BASE . '/' . $tmpfile . '[0]') . ' -resize ' . $width . 'x' . $height . ' ' . escapeshellarg(PROJECT_BASE . '/' . $outfile); + // logger('imagick thumbnail command: ' . $cmd); - /** @scrutinizer ignore-unhandled */ - @exec($cmd); + /** @scrutinizer ignore-unhandled */ + @exec($cmd); - if (! file_exists($outfile)) { - logger('imagick scale failed.'); - } - else { - @rename($outfile,$file . '.thumb'); - } - } - - @unlink($tmpfile); - } + if (!file_exists($outfile)) { + logger('imagick scale failed.'); + } else { + @rename($outfile, $file . '.thumb'); + } + } + + @unlink($tmpfile); + } } - diff --git a/Zotlabs/Update/_1000.php b/Zotlabs/Update/_1000.php index 02787db38..d6862ae8b 100644 --- a/Zotlabs/Update/_1000.php +++ b/Zotlabs/Update/_1000.php @@ -2,14 +2,14 @@ namespace Zotlabs\Update; -class _1000 { -function run() { - $r = q("ALTER TABLE `channel` ADD `channel_a_delegate` TINYINT( 3 ) UNSIGNED NOT NULL DEFAULT '0', ADD INDEX ( `channel_a_delegate` )"); - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; +class _1000 +{ + public function run() + { + $r = q("ALTER TABLE `channel` ADD `channel_a_delegate` TINYINT( 3 ) UNSIGNED NOT NULL DEFAULT '0', ADD INDEX ( `channel_a_delegate` )"); + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1001.php b/Zotlabs/Update/_1001.php index 9acc96373..998382c9a 100644 --- a/Zotlabs/Update/_1001.php +++ b/Zotlabs/Update/_1001.php @@ -2,9 +2,11 @@ namespace Zotlabs\Update; -class _1001 { -function run() { - $r = q("CREATE TABLE if not exists `verify` ( +class _1001 +{ + public function run() + { + $r = q("CREATE TABLE if not exists `verify` ( `id` INT(10) UNSIGNED NOT NULL , `channel` INT(10) UNSIGNED NOT NULL DEFAULT '0', `type` CHAR( 32 ) NOT NULL DEFAULT '', @@ -14,14 +16,12 @@ function run() { PRIMARY KEY ( `id` ) ) ENGINE = MYISAM DEFAULT CHARSET=utf8"); - $r2 = q("alter table `verify` add index (`channel`), add index (`type`), add index (`token`), + $r2 = q("alter table `verify` add index (`channel`), add index (`type`), add index (`token`), add index (`meta`), add index (`created`)"); - if($r && $r2) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + if ($r && $r2) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1002.php b/Zotlabs/Update/_1002.php index 1f42449c6..a1aca8685 100644 --- a/Zotlabs/Update/_1002.php +++ b/Zotlabs/Update/_1002.php @@ -2,19 +2,19 @@ namespace Zotlabs\Update; -class _1002 { -function run() { - $r = q("ALTER TABLE `event` CHANGE `account` `aid` INT( 10 ) UNSIGNED NOT NULL DEFAULT '0'"); - $r2 = q("alter table `event` drop index `account`, add index (`aid`)"); +class _1002 +{ + public function run() + { + $r = q("ALTER TABLE `event` CHANGE `account` `aid` INT( 10 ) UNSIGNED NOT NULL DEFAULT '0'"); + $r2 = q("alter table `event` drop index `account`, add index (`aid`)"); - q("drop table contact"); - q("drop table deliverq"); + q("drop table contact"); + q("drop table deliverq"); - if($r && $r2) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + if ($r && $r2) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1003.php b/Zotlabs/Update/_1003.php index 18d99f985..96e2a56f7 100644 --- a/Zotlabs/Update/_1003.php +++ b/Zotlabs/Update/_1003.php @@ -2,14 +2,15 @@ namespace Zotlabs\Update; -class _1003 { -function run() { - $r = q("ALTER TABLE `xchan` ADD `xchan_flags` INT UNSIGNED NOT NULL DEFAULT '0' AFTER `xchan_network` , +class _1003 +{ + public function run() + { + $r = q("ALTER TABLE `xchan` ADD `xchan_flags` INT UNSIGNED NOT NULL DEFAULT '0' AFTER `xchan_network` , ADD INDEX ( `xchan_flags` ) "); - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1004.php b/Zotlabs/Update/_1004.php index a86f5d824..ce210c173 100644 --- a/Zotlabs/Update/_1004.php +++ b/Zotlabs/Update/_1004.php @@ -2,9 +2,11 @@ namespace Zotlabs\Update; -class _1004 { -function run() { - $r = q("CREATE TABLE if not exists `site` ( +class _1004 +{ + public function run() + { + $r = q("CREATE TABLE if not exists `site` ( `site_url` CHAR( 255 ) NOT NULL , `site_flags` INT NOT NULL DEFAULT '0', `site_update` DATETIME NOT NULL DEFAULT '0001-01-01 00:00:00', @@ -12,13 +14,11 @@ function run() { PRIMARY KEY ( `site_url` ) ) ENGINE = MYISAM DEFAULT CHARSET=utf8"); - $r2 = q("alter table site add index (site_flags), add index (site_update), add index (site_directory) "); + $r2 = q("alter table site add index (site_flags), add index (site_update), add index (site_directory) "); - if($r && $r2) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + if ($r && $r2) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1005.php b/Zotlabs/Update/_1005.php index ee8621a8d..2f59966ed 100644 --- a/Zotlabs/Update/_1005.php +++ b/Zotlabs/Update/_1005.php @@ -2,12 +2,12 @@ namespace Zotlabs\Update; -class _1005 { -function run() { - q("drop table guid"); - q("drop table `notify-threads`"); - return UPDATE_SUCCESS; +class _1005 +{ + public function run() + { + q("drop table guid"); + q("drop table `notify-threads`"); + return UPDATE_SUCCESS; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1006.php b/Zotlabs/Update/_1006.php index 43a2bb951..00ba22b89 100644 --- a/Zotlabs/Update/_1006.php +++ b/Zotlabs/Update/_1006.php @@ -2,10 +2,12 @@ namespace Zotlabs\Update; -class _1006 { -function run() { +class _1006 +{ + public function run() + { - $r = q("CREATE TABLE IF NOT EXISTS `xprof` ( + $r = q("CREATE TABLE IF NOT EXISTS `xprof` ( `xprof_hash` char(255) NOT NULL, `xprof_desc` char(255) NOT NULL DEFAULT '', `xprof_dob` char(12) NOT NULL DEFAULT '', @@ -28,18 +30,16 @@ function run() { KEY `xprof_country` (`xprof_country`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8"); - $r2 = q("CREATE TABLE IF NOT EXISTS `xtag` ( + $r2 = q("CREATE TABLE IF NOT EXISTS `xtag` ( `xtag_hash` char(255) NOT NULL, `xtag_term` char(255) NOT NULL DEFAULT '', PRIMARY KEY (`xtag_hash`), KEY `xtag_term` (`xtag_term`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8"); - if($r && $r2) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + if ($r && $r2) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1007.php b/Zotlabs/Update/_1007.php index eb52c5bc5..cb4e17476 100644 --- a/Zotlabs/Update/_1007.php +++ b/Zotlabs/Update/_1007.php @@ -2,14 +2,15 @@ namespace Zotlabs\Update; -class _1007 { -function run() { - $r = q("ALTER TABLE `channel` ADD `channel_r_storage` INT UNSIGNED NOT NULL DEFAULT '128', ADD `channel_w_storage` INT UNSIGNED NOT NULL DEFAULT '128', add index ( channel_r_storage ), add index ( channel_w_storage )"); +class _1007 +{ + public function run() + { + $r = q("ALTER TABLE `channel` ADD `channel_r_storage` INT UNSIGNED NOT NULL DEFAULT '128', ADD `channel_w_storage` INT UNSIGNED NOT NULL DEFAULT '128', add index ( channel_r_storage ), add index ( channel_w_storage )"); - if($r && $r2) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + if ($r && $r2) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1008.php b/Zotlabs/Update/_1008.php index d5db24bc0..12054c83a 100644 --- a/Zotlabs/Update/_1008.php +++ b/Zotlabs/Update/_1008.php @@ -2,14 +2,15 @@ namespace Zotlabs\Update; -class _1008 { -function run() { - $r = q("alter table profile drop prv_keywords, CHANGE `pub_keywords` `keywords` TEXT CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, drop index pub_keywords"); +class _1008 +{ + public function run() + { + $r = q("alter table profile drop prv_keywords, CHANGE `pub_keywords` `keywords` TEXT CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, drop index pub_keywords"); - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1009.php b/Zotlabs/Update/_1009.php index afb0d5e96..22bfe3d0e 100644 --- a/Zotlabs/Update/_1009.php +++ b/Zotlabs/Update/_1009.php @@ -2,14 +2,14 @@ namespace Zotlabs\Update; -class _1009 { -function run() { - $r = q("ALTER TABLE `xprof` ADD `xprof_keywords` TEXT NOT NULL"); - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; +class _1009 +{ + public function run() + { + $r = q("ALTER TABLE `xprof` ADD `xprof_keywords` TEXT NOT NULL"); + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1010.php b/Zotlabs/Update/_1010.php index 92405cce4..b12b73f96 100644 --- a/Zotlabs/Update/_1010.php +++ b/Zotlabs/Update/_1010.php @@ -2,17 +2,18 @@ namespace Zotlabs\Update; -class _1010 { -function run() { - $r = q("ALTER TABLE `abook` ADD `abook_dob` DATETIME NOT NULL DEFAULT '0001-01-01 00:00:00' AFTER `abook_connnected` , +class _1010 +{ + public function run() + { + $r = q("ALTER TABLE `abook` ADD `abook_dob` DATETIME NOT NULL DEFAULT '0001-01-01 00:00:00' AFTER `abook_connnected` , ADD INDEX ( `abook_dob` )"); - $r2 = q("ALTER TABLE `profile` ADD `dob_tz` CHAR( 255 ) NOT NULL DEFAULT 'UTC' AFTER `dob`"); + $r2 = q("ALTER TABLE `profile` ADD `dob_tz` CHAR( 255 ) NOT NULL DEFAULT 'UTC' AFTER `dob`"); - if($r && $r2) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + if ($r && $r2) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1011.php b/Zotlabs/Update/_1011.php index b6841a9fe..e752e352f 100644 --- a/Zotlabs/Update/_1011.php +++ b/Zotlabs/Update/_1011.php @@ -2,15 +2,16 @@ namespace Zotlabs\Update; -class _1011 { -function run() { - $r = q("ALTER TABLE `item` ADD `expires` DATETIME NOT NULL DEFAULT '0001-01-01 00:00:00' AFTER `edited` , +class _1011 +{ + public function run() + { + $r = q("ALTER TABLE `item` ADD `expires` DATETIME NOT NULL DEFAULT '0001-01-01 00:00:00' AFTER `edited` , ADD INDEX ( `expires` )"); - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1012.php b/Zotlabs/Update/_1012.php index 3ae5caf65..ad0f97b40 100644 --- a/Zotlabs/Update/_1012.php +++ b/Zotlabs/Update/_1012.php @@ -2,15 +2,16 @@ namespace Zotlabs\Update; -class _1012 { -function run() { - $r = q("ALTER TABLE `xchan` ADD `xchan_connurl` CHAR( 255 ) NOT NULL DEFAULT '' AFTER `xchan_url` , +class _1012 +{ + public function run() + { + $r = q("ALTER TABLE `xchan` ADD `xchan_connurl` CHAR( 255 ) NOT NULL DEFAULT '' AFTER `xchan_url` , ADD INDEX ( `xchan_connurl` )"); - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1013.php b/Zotlabs/Update/_1013.php index 68fc9071d..1e60bf622 100644 --- a/Zotlabs/Update/_1013.php +++ b/Zotlabs/Update/_1013.php @@ -2,20 +2,21 @@ namespace Zotlabs\Update; -class _1013 { -function run() { - $r = q("CREATE TABLE if not exists `xlink` ( +class _1013 +{ + public function run() + { + $r = q("CREATE TABLE if not exists `xlink` ( `xlink_id` INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY , `xlink_xchan` CHAR( 255 ) NOT NULL DEFAULT '', `xlink_link` CHAR( 255 ) NOT NULL DEFAULT '', `xlink_updated` DATETIME NOT NULL DEFAULT '0001-01-01 00:00:00' ) ENGINE = MYISAM DEFAULT CHARSET=utf8"); - $r2 = q("alter table xlink add index ( xlink_xchan ), add index ( xlink_link ), add index ( xlink_updated ) "); - if($r && $r2) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + $r2 = q("alter table xlink add index ( xlink_xchan ), add index ( xlink_link ), add index ( xlink_updated ) "); + if ($r && $r2) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1014.php b/Zotlabs/Update/_1014.php index e9b3a89e0..b4eef8c43 100644 --- a/Zotlabs/Update/_1014.php +++ b/Zotlabs/Update/_1014.php @@ -2,13 +2,14 @@ namespace Zotlabs\Update; -class _1014 { -function run() { - $r = q("ALTER TABLE `verify` CHANGE `id` `id` INT( 10 ) UNSIGNED NOT NULL AUTO_INCREMENT"); - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; +class _1014 +{ + public function run() + { + $r = q("ALTER TABLE `verify` CHANGE `id` `id` INT( 10 ) UNSIGNED NOT NULL AUTO_INCREMENT"); + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1015.php b/Zotlabs/Update/_1015.php index d9d2d2f27..3d24c486b 100644 --- a/Zotlabs/Update/_1015.php +++ b/Zotlabs/Update/_1015.php @@ -2,18 +2,18 @@ namespace Zotlabs\Update; -class _1015 { -function run() { - $r = q("ALTER TABLE `channel` ADD `channel_r_pages` INT UNSIGNED NOT NULL DEFAULT '128', +class _1015 +{ + public function run() + { + $r = q("ALTER TABLE `channel` ADD `channel_r_pages` INT UNSIGNED NOT NULL DEFAULT '128', ADD `channel_w_pages` INT UNSIGNED NOT NULL DEFAULT '128'"); - $r2 = q("ALTER TABLE `channel` ADD INDEX ( `channel_r_pages` ) , ADD INDEX ( `channel_w_pages` ) "); + $r2 = q("ALTER TABLE `channel` ADD INDEX ( `channel_r_pages` ) , ADD INDEX ( `channel_w_pages` ) "); - if($r && $r2) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + if ($r && $r2) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1016.php b/Zotlabs/Update/_1016.php index 93f71a474..19ec91430 100644 --- a/Zotlabs/Update/_1016.php +++ b/Zotlabs/Update/_1016.php @@ -2,10 +2,12 @@ namespace Zotlabs\Update; -class _1016 { -function run() { +class _1016 +{ + public function run() + { - $r = q("CREATE TABLE IF NOT EXISTS `menu` ( + $r = q("CREATE TABLE IF NOT EXISTS `menu` ( `menu_id` int(10) unsigned NOT NULL AUTO_INCREMENT, `menu_channel_id` int(10) unsigned NOT NULL DEFAULT '0', `menu_desc` char(255) NOT NULL DEFAULT '', @@ -13,7 +15,7 @@ function run() { KEY `menu_channel_id` (`menu_channel_id`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 "); - $r2 = q("CREATE TABLE IF NOT EXISTS `menu_item` ( + $r2 = q("CREATE TABLE IF NOT EXISTS `menu_item` ( `mitem_id` int(10) unsigned NOT NULL AUTO_INCREMENT, `mitem_link` char(255) NOT NULL DEFAULT '', `mitem_desc` char(255) NOT NULL DEFAULT '', @@ -30,10 +32,9 @@ function run() { ) ENGINE=MyISAM DEFAULT CHARSET=utf8 "); - if($r && $r2) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + if ($r && $r2) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1017.php b/Zotlabs/Update/_1017.php index 429eda67b..8bc40eff8 100644 --- a/Zotlabs/Update/_1017.php +++ b/Zotlabs/Update/_1017.php @@ -2,13 +2,14 @@ namespace Zotlabs\Update; -class _1017 { -function run() { - $r = q("ALTER TABLE `event` CHANGE `cid` `event_xchan` CHAR( 255 ) NOT NULL DEFAULT '', ADD INDEX ( `event_xchan` ), drop index cid "); - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; +class _1017 +{ + public function run() + { + $r = q("ALTER TABLE `event` CHANGE `cid` `event_xchan` CHAR( 255 ) NOT NULL DEFAULT '', ADD INDEX ( `event_xchan` ), drop index cid "); + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1018.php b/Zotlabs/Update/_1018.php index 546ebffca..5254ba3b9 100644 --- a/Zotlabs/Update/_1018.php +++ b/Zotlabs/Update/_1018.php @@ -2,15 +2,15 @@ namespace Zotlabs\Update; -class _1018 { -function run() { - $r = q("ALTER TABLE `event` ADD `event_hash` CHAR( 255 ) NOT NULL DEFAULT '' AFTER `event_xchan` , +class _1018 +{ + public function run() + { + $r = q("ALTER TABLE `event` ADD `event_hash` CHAR( 255 ) NOT NULL DEFAULT '' AFTER `event_xchan` , ADD INDEX ( `event_hash` )"); - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1019.php b/Zotlabs/Update/_1019.php index a7103c98c..0ada7141a 100644 --- a/Zotlabs/Update/_1019.php +++ b/Zotlabs/Update/_1019.php @@ -2,13 +2,14 @@ namespace Zotlabs\Update; -class _1019 { -function run() { - $r = q("ALTER TABLE `event` DROP `message_id` "); - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; +class _1019 +{ + public function run() + { + $r = q("ALTER TABLE `event` DROP `message_id` "); + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1020.php b/Zotlabs/Update/_1020.php index c898f31c2..a51e44c3e 100644 --- a/Zotlabs/Update/_1020.php +++ b/Zotlabs/Update/_1020.php @@ -2,13 +2,14 @@ namespace Zotlabs\Update; -class _1020 { -function run() { - $r = q("alter table photo drop `contact-id`, drop guid, drop index `resource-id`, add index ( `resource_id` )"); - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; +class _1020 +{ + public function run() + { + $r = q("alter table photo drop `contact-id`, drop guid, drop index `resource-id`, add index ( `resource_id` )"); + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1021.php b/Zotlabs/Update/_1021.php index 72203fadb..0bbb2c3bf 100644 --- a/Zotlabs/Update/_1021.php +++ b/Zotlabs/Update/_1021.php @@ -2,16 +2,17 @@ namespace Zotlabs\Update; -class _1021 { -function run() { +class _1021 +{ + public function run() + { - $r = q("ALTER TABLE `abook` CHANGE `abook_connnected` `abook_connected` DATETIME NOT NULL DEFAULT '0001-01-01 00:00:00', + $r = q("ALTER TABLE `abook` CHANGE `abook_connnected` `abook_connected` DATETIME NOT NULL DEFAULT '0001-01-01 00:00:00', drop index `abook_connnected`, add index ( `abook_connected` ) "); - - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1022.php b/Zotlabs/Update/_1022.php index 7e13250c4..c6e2c1f0c 100644 --- a/Zotlabs/Update/_1022.php +++ b/Zotlabs/Update/_1022.php @@ -2,13 +2,14 @@ namespace Zotlabs\Update; -class _1022 { -function run() { - $r = q("alter table attach add index ( filename ), add index ( filetype ), add index ( filesize ), add index ( created ), add index ( edited ) "); - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; +class _1022 +{ + public function run() + { + $r = q("alter table attach add index ( filename ), add index ( filetype ), add index ( filesize ), add index ( created ), add index ( edited ) "); + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1023.php b/Zotlabs/Update/_1023.php index 0a8dd2f00..e57b2ab1b 100644 --- a/Zotlabs/Update/_1023.php +++ b/Zotlabs/Update/_1023.php @@ -2,13 +2,14 @@ namespace Zotlabs\Update; -class _1023 { -function run() { - $r = q("ALTER TABLE `item` ADD `revision` INT UNSIGNED NOT NULL DEFAULT '0' AFTER `lang` , add index ( revision ) "); - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; +class _1023 +{ + public function run() + { + $r = q("ALTER TABLE `item` ADD `revision` INT UNSIGNED NOT NULL DEFAULT '0' AFTER `lang` , add index ( revision ) "); + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1024.php b/Zotlabs/Update/_1024.php index 004d8967a..b1c60b2e7 100644 --- a/Zotlabs/Update/_1024.php +++ b/Zotlabs/Update/_1024.php @@ -2,14 +2,15 @@ namespace Zotlabs\Update; -class _1024 { -function run() { - $r = q("ALTER TABLE `attach` ADD `revision` INT UNSIGNED NOT NULL DEFAULT '0' AFTER `filesize` , +class _1024 +{ + public function run() + { + $r = q("ALTER TABLE `attach` ADD `revision` INT UNSIGNED NOT NULL DEFAULT '0' AFTER `filesize` , ADD INDEX ( `revision` ) "); - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1025.php b/Zotlabs/Update/_1025.php index d498233b3..021b79913 100644 --- a/Zotlabs/Update/_1025.php +++ b/Zotlabs/Update/_1025.php @@ -2,14 +2,15 @@ namespace Zotlabs\Update; -class _1025 { -function run() { - $r = q("ALTER TABLE `attach` ADD `folder` CHAR( 64 ) NOT NULL DEFAULT '' AFTER `revision` , +class _1025 +{ + public function run() + { + $r = q("ALTER TABLE `attach` ADD `folder` CHAR( 64 ) NOT NULL DEFAULT '' AFTER `revision` , ADD `flags` INT UNSIGNED NOT NULL DEFAULT '0' AFTER `folder` , add index ( folder ), add index ( flags )"); - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1026.php b/Zotlabs/Update/_1026.php index d57fdec37..0fb95fb21 100644 --- a/Zotlabs/Update/_1026.php +++ b/Zotlabs/Update/_1026.php @@ -2,15 +2,15 @@ namespace Zotlabs\Update; -class _1026 { -function run() { - $r = q("ALTER TABLE `item` ADD `mimetype` CHAR( 255 ) NOT NULL DEFAULT '' AFTER `author_xchan` , +class _1026 +{ + public function run() + { + $r = q("ALTER TABLE `item` ADD `mimetype` CHAR( 255 ) NOT NULL DEFAULT '' AFTER `author_xchan` , ADD INDEX ( `mimetype` )"); - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1027.php b/Zotlabs/Update/_1027.php index 04d09d202..912f987f2 100644 --- a/Zotlabs/Update/_1027.php +++ b/Zotlabs/Update/_1027.php @@ -2,14 +2,15 @@ namespace Zotlabs\Update; -class _1027 { -function run() { - $r = q("ALTER TABLE `abook` ADD `abook_rating` INT NOT NULL DEFAULT '0' AFTER `abook_closeness` , +class _1027 +{ + public function run() + { + $r = q("ALTER TABLE `abook` ADD `abook_rating` INT NOT NULL DEFAULT '0' AFTER `abook_closeness` , ADD INDEX ( `abook_rating` )"); - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1028.php b/Zotlabs/Update/_1028.php index a58784a01..8ee118c69 100644 --- a/Zotlabs/Update/_1028.php +++ b/Zotlabs/Update/_1028.php @@ -2,14 +2,15 @@ namespace Zotlabs\Update; -class _1028 { -function run() { - $r = q("ALTER TABLE `xlink` ADD `xlink_rating` INT NOT NULL DEFAULT '0' AFTER `xlink_link` , +class _1028 +{ + public function run() + { + $r = q("ALTER TABLE `xlink` ADD `xlink_rating` INT NOT NULL DEFAULT '0' AFTER `xlink_link` , ADD INDEX ( `xlink_rating` ) "); - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1029.php b/Zotlabs/Update/_1029.php index 7072647cb..9866447eb 100644 --- a/Zotlabs/Update/_1029.php +++ b/Zotlabs/Update/_1029.php @@ -2,14 +2,15 @@ namespace Zotlabs\Update; -class _1029 { -function run() { - $r = q("ALTER TABLE `channel` ADD `channel_deleted` DATETIME NOT NULL DEFAULT '0001-01-01 00:00:00' AFTER `channel_pageflags` , +class _1029 +{ + public function run() + { + $r = q("ALTER TABLE `channel` ADD `channel_deleted` DATETIME NOT NULL DEFAULT '0001-01-01 00:00:00' AFTER `channel_pageflags` , ADD INDEX ( `channel_deleted` ) "); - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1030.php b/Zotlabs/Update/_1030.php index 7aebde49e..e769a395b 100644 --- a/Zotlabs/Update/_1030.php +++ b/Zotlabs/Update/_1030.php @@ -2,9 +2,11 @@ namespace Zotlabs\Update; -class _1030 { -function run() { - $r = q("CREATE TABLE IF NOT EXISTS `issue` ( +class _1030 +{ + public function run() + { + $r = q("CREATE TABLE IF NOT EXISTS `issue` ( `issue_id` INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY , `issue_created` DATETIME NOT NULL DEFAULT '0001-01-01 00:00:00', `issue_updated` DATETIME NOT NULL DEFAULT '0001-01-01 00:00:00', @@ -20,10 +22,9 @@ KEY `issue_status` (`issue_status`), KEY `issue_component` (`issue_component`) ) ENGINE = MYISAM DEFAULT CHARSET=utf8"); - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1031.php b/Zotlabs/Update/_1031.php index 4463c9e04..330e27fc9 100644 --- a/Zotlabs/Update/_1031.php +++ b/Zotlabs/Update/_1031.php @@ -2,15 +2,15 @@ namespace Zotlabs\Update; -class _1031 { -function run() { - $r = q("ALTER TABLE `account` ADD `account_external` CHAR( 255 ) NOT NULL DEFAULT '' AFTER `account_email` , +class _1031 +{ + public function run() + { + $r = q("ALTER TABLE `account` ADD `account_external` CHAR( 255 ) NOT NULL DEFAULT '' AFTER `account_email` , ADD INDEX ( `account_external` )"); - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1032.php b/Zotlabs/Update/_1032.php index a9fbeca9e..e518500ca 100644 --- a/Zotlabs/Update/_1032.php +++ b/Zotlabs/Update/_1032.php @@ -2,9 +2,11 @@ namespace Zotlabs\Update; -class _1032 { -function run() { - $r = q("CREATE TABLE if not exists `xign` ( +class _1032 +{ + public function run() + { + $r = q("CREATE TABLE if not exists `xign` ( `id` INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY , `uid` INT NOT NULL DEFAULT '0', `xchan` CHAR( 255 ) NOT NULL DEFAULT '', @@ -12,10 +14,9 @@ KEY `uid` (`uid`), KEY `xchan` (`xchan`) ) ENGINE = MYISAM DEFAULT CHARSET = utf8"); - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1033.php b/Zotlabs/Update/_1033.php index a201e1d1f..c0eaf53b3 100644 --- a/Zotlabs/Update/_1033.php +++ b/Zotlabs/Update/_1033.php @@ -2,9 +2,11 @@ namespace Zotlabs\Update; -class _1033 { -function run() { - $r = q("CREATE TABLE if not exists `shares` ( +class _1033 +{ + public function run() + { + $r = q("CREATE TABLE if not exists `shares` ( `share_id` INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY , `share_type` INT NOT NULL DEFAULT '0', `share_target` INT UNSIGNED NOT NULL DEFAULT '0', @@ -14,16 +16,15 @@ KEY `share_target` (`share_target`), KEY `share_xchan` (`share_xchan`) ) ENGINE = MYISAM DEFAULT CHARSET = utf8"); - // if these fail don't bother reporting it + // if these fail don't bother reporting it - q("drop table gcign"); - q("drop table gcontact"); - q("drop table glink"); + q("drop table gcign"); + q("drop table gcontact"); + q("drop table glink"); - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1034.php b/Zotlabs/Update/_1034.php index 646182cc0..adbe0789f 100644 --- a/Zotlabs/Update/_1034.php +++ b/Zotlabs/Update/_1034.php @@ -2,19 +2,20 @@ namespace Zotlabs\Update; -class _1034 { -function run() { - $r = q("CREATE TABLE if not exists `updates` ( +class _1034 +{ + public function run() + { + $r = q("CREATE TABLE if not exists `updates` ( `ud_hash` CHAR( 128 ) NOT NULL , `ud_date` DATETIME NOT NULL DEFAULT '0001-01-01 00:00:00', PRIMARY KEY ( `ud_hash` ), KEY `ud_date` ( `ud_date` ) ) ENGINE = MYISAM DEFAULT CHARSET = utf8"); - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1035.php b/Zotlabs/Update/_1035.php index 44e95189d..d280610fe 100644 --- a/Zotlabs/Update/_1035.php +++ b/Zotlabs/Update/_1035.php @@ -2,9 +2,11 @@ namespace Zotlabs\Update; -class _1035 { -function run() { - $r = q("CREATE TABLE if not exists `xconfig` ( +class _1035 +{ + public function run() + { + $r = q("CREATE TABLE if not exists `xconfig` ( `id` INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY , `xchan` CHAR( 255 ) NOT NULL , `cat` CHAR( 255 ) NOT NULL , @@ -15,10 +17,9 @@ KEY `cat` ( `cat` ), KEY `k` ( `k` ) ) ENGINE = MYISAM DEFAULT CHARSET = utf8"); - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1036.php b/Zotlabs/Update/_1036.php index 700a82e12..c33f267e6 100644 --- a/Zotlabs/Update/_1036.php +++ b/Zotlabs/Update/_1036.php @@ -2,15 +2,14 @@ namespace Zotlabs\Update; -class _1036 { -function run() { - $r = q("ALTER TABLE `profile` ADD `channels` TEXT NOT NULL AFTER `contact` "); - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; - +class _1036 +{ + public function run() + { + $r = q("ALTER TABLE `profile` ADD `channels` TEXT NOT NULL AFTER `contact` "); + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1037.php b/Zotlabs/Update/_1037.php index a1ad20aa8..541f7c080 100644 --- a/Zotlabs/Update/_1037.php +++ b/Zotlabs/Update/_1037.php @@ -2,9 +2,11 @@ namespace Zotlabs\Update; -class _1037 { -function run() { - $r1 = q("ALTER TABLE `item` CHANGE `uri` `mid` CHAR( 255 ) CHARACTER SET ascii COLLATE ascii_general_ci NOT NULL DEFAULT '', +class _1037 +{ + public function run() + { + $r1 = q("ALTER TABLE `item` CHANGE `uri` `mid` CHAR( 255 ) CHARACTER SET ascii COLLATE ascii_general_ci NOT NULL DEFAULT '', CHANGE `parent_uri` `parent_mid` CHAR( 255 ) CHARACTER SET ascii COLLATE ascii_general_ci NOT NULL DEFAULT '', DROP INDEX `uri` , ADD INDEX `mid` ( `mid` ), @@ -13,18 +15,16 @@ ADD INDEX `parent_mid` ( `parent_mid` ), DROP INDEX `uid_uri` , ADD INDEX `uid_mid` ( `mid` , `uid` ) "); - $r2 = q("ALTER TABLE `mail` CHANGE `uri` `mid` CHAR( 255 ) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL , + $r2 = q("ALTER TABLE `mail` CHANGE `uri` `mid` CHAR( 255 ) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL , CHANGE `parent_uri` `parent_mid` CHAR( 255 ) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL , DROP INDEX `uri` , ADD INDEX `mid` ( `mid` ), DROP INDEX `parent_uri` , ADD INDEX `parent_mid` ( `parent_mid` ) "); - if($r1 && $r2) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + if ($r1 && $r2) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1038.php b/Zotlabs/Update/_1038.php index b4a86f41e..a8398aa0c 100644 --- a/Zotlabs/Update/_1038.php +++ b/Zotlabs/Update/_1038.php @@ -2,16 +2,15 @@ namespace Zotlabs\Update; -class _1038 { -function run() { - $r = q("ALTER TABLE `manage` CHANGE `mid` `xchan` CHAR( 255 ) NOT NULL DEFAULT '', drop index `mid`, ADD INDEX ( `xchan` )"); - - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; +class _1038 +{ + public function run() + { + $r = q("ALTER TABLE `manage` CHANGE `mid` `xchan` CHAR( 255 ) NOT NULL DEFAULT '', drop index `mid`, ADD INDEX ( `xchan` )"); + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1039.php b/Zotlabs/Update/_1039.php index 9214cdc4a..f1e652abb 100644 --- a/Zotlabs/Update/_1039.php +++ b/Zotlabs/Update/_1039.php @@ -2,15 +2,15 @@ namespace Zotlabs\Update; -class _1039 { -function run() { - $r = q("ALTER TABLE `channel` CHANGE `channel_default_gid` `channel_default_group` CHAR( 255 ) NOT NULL DEFAULT ''"); - - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; +class _1039 +{ + public function run() + { + $r = q("ALTER TABLE `channel` CHANGE `channel_default_gid` `channel_default_group` CHAR( 255 ) NOT NULL DEFAULT ''"); + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1040.php b/Zotlabs/Update/_1040.php index 77dc75a48..eea74bc76 100644 --- a/Zotlabs/Update/_1040.php +++ b/Zotlabs/Update/_1040.php @@ -2,15 +2,16 @@ namespace Zotlabs\Update; -class _1040 { -function run() { - $r1 = q("ALTER TABLE `session` CHANGE `expire` `expire` BIGINT UNSIGNED NOT NULL "); - $r2 = q("ALTER TABLE `tokens` CHANGE `expires` `expires` BIGINT UNSIGNED NOT NULL "); +class _1040 +{ + public function run() + { + $r1 = q("ALTER TABLE `session` CHANGE `expire` `expire` BIGINT UNSIGNED NOT NULL "); + $r2 = q("ALTER TABLE `tokens` CHANGE `expires` `expires` BIGINT UNSIGNED NOT NULL "); - if($r1 && $r2) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + if ($r1 && $r2) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1041.php b/Zotlabs/Update/_1041.php index 4ed436e61..727f0d8a4 100644 --- a/Zotlabs/Update/_1041.php +++ b/Zotlabs/Update/_1041.php @@ -2,15 +2,15 @@ namespace Zotlabs\Update; -class _1041 { -function run() { - $r = q("ALTER TABLE `outq` ADD `outq_driver` CHAR( 32 ) NOT NULL DEFAULT '' AFTER `outq_channel` "); +class _1041 +{ + public function run() + { + $r = q("ALTER TABLE `outq` ADD `outq_driver` CHAR( 32 ) NOT NULL DEFAULT '' AFTER `outq_channel` "); - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1042.php b/Zotlabs/Update/_1042.php index 56298b6a3..95dc70ab2 100644 --- a/Zotlabs/Update/_1042.php +++ b/Zotlabs/Update/_1042.php @@ -2,15 +2,15 @@ namespace Zotlabs\Update; -class _1042 { -function run() { - $r = q("ALTER TABLE `hubloc` ADD `hubloc_updated` DATETIME NOT NULL DEFAULT '0001-01-01 00:00:00', +class _1042 +{ + public function run() + { + $r = q("ALTER TABLE `hubloc` ADD `hubloc_updated` DATETIME NOT NULL DEFAULT '0001-01-01 00:00:00', ADD `hubloc_connected` DATETIME NOT NULL DEFAULT '0001-01-01 00:00:00', ADD INDEX ( `hubloc_updated` ), ADD INDEX ( `hubloc_connected` )"); - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1043.php b/Zotlabs/Update/_1043.php index b28203731..54ec99c45 100644 --- a/Zotlabs/Update/_1043.php +++ b/Zotlabs/Update/_1043.php @@ -2,14 +2,15 @@ namespace Zotlabs\Update; -class _1043 { -function run() { - $r = q("ALTER TABLE `item` ADD `comment_policy` CHAR( 255 ) NOT NULL DEFAULT '' AFTER `coord` , +class _1043 +{ + public function run() + { + $r = q("ALTER TABLE `item` ADD `comment_policy` CHAR( 255 ) NOT NULL DEFAULT '' AFTER `coord` , ADD INDEX ( `comment_policy` ) "); - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1044.php b/Zotlabs/Update/_1044.php index fc7e5438f..2d16feaef 100644 --- a/Zotlabs/Update/_1044.php +++ b/Zotlabs/Update/_1044.php @@ -2,14 +2,15 @@ namespace Zotlabs\Update; -class _1044 { -function run() { - $r = q("ALTER TABLE `term` ADD `imgurl` CHAR( 255 ) NOT NULL , +class _1044 +{ + public function run() + { + $r = q("ALTER TABLE `term` ADD `imgurl` CHAR( 255 ) NOT NULL , ADD INDEX ( `imgurl` ) "); - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1045.php b/Zotlabs/Update/_1045.php index 73570c316..2a317cfe5 100644 --- a/Zotlabs/Update/_1045.php +++ b/Zotlabs/Update/_1045.php @@ -2,14 +2,15 @@ namespace Zotlabs\Update; -class _1045 { -function run() { - $r = q("ALTER TABLE `site` ADD `site_register` INT NOT NULL DEFAULT '0', +class _1045 +{ + public function run() + { + $r = q("ALTER TABLE `site` ADD `site_register` INT NOT NULL DEFAULT '0', ADD INDEX ( `site_register` ) "); - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1046.php b/Zotlabs/Update/_1046.php index 219d53e17..70c26faa4 100644 --- a/Zotlabs/Update/_1046.php +++ b/Zotlabs/Update/_1046.php @@ -2,14 +2,15 @@ namespace Zotlabs\Update; -class _1046 { -function run() { - $r = q("ALTER TABLE `term` ADD `term_hash` CHAR( 255 ) NOT NULL DEFAULT '', +class _1046 +{ + public function run() + { + $r = q("ALTER TABLE `term` ADD `term_hash` CHAR( 255 ) NOT NULL DEFAULT '', ADD INDEX ( `term_hash` ) "); - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1047.php b/Zotlabs/Update/_1047.php index 930c13eb8..525b90773 100644 --- a/Zotlabs/Update/_1047.php +++ b/Zotlabs/Update/_1047.php @@ -2,14 +2,15 @@ namespace Zotlabs\Update; -class _1047 { -function run() { - $r = q("ALTER TABLE `xprof` ADD `xprof_age` TINYINT( 3 ) UNSIGNED NOT NULL DEFAULT '0' AFTER `xprof_hash` , +class _1047 +{ + public function run() + { + $r = q("ALTER TABLE `xprof` ADD `xprof_age` TINYINT( 3 ) UNSIGNED NOT NULL DEFAULT '0' AFTER `xprof_hash` , ADD INDEX ( `xprof_age` ) "); - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1048.php b/Zotlabs/Update/_1048.php index 20c8931d7..bf4877cfb 100644 --- a/Zotlabs/Update/_1048.php +++ b/Zotlabs/Update/_1048.php @@ -2,9 +2,11 @@ namespace Zotlabs\Update; -class _1048 { -function run() { - $r = q("CREATE TABLE IF NOT EXISTS `obj` ( +class _1048 +{ + public function run() + { + $r = q("CREATE TABLE IF NOT EXISTS `obj` ( `obj_id` int(10) unsigned NOT NULL AUTO_INCREMENT, `obj_page` char(64) NOT NULL DEFAULT '', `obj_verb` char(255) NOT NULL DEFAULT '', @@ -19,11 +21,9 @@ function run() { KEY `obj_obj` (`obj_obj`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 "); - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1049.php b/Zotlabs/Update/_1049.php index 622847727..cd13e5ebc 100644 --- a/Zotlabs/Update/_1049.php +++ b/Zotlabs/Update/_1049.php @@ -2,14 +2,15 @@ namespace Zotlabs\Update; -class _1049 { -function run() { - $r = q("ALTER TABLE `term` ADD `parent_hash` CHAR( 255 ) NOT NULL DEFAULT '' AFTER `term_hash` , ADD INDEX ( `parent_hash` ) "); +class _1049 +{ + public function run() + { + $r = q("ALTER TABLE `term` ADD `parent_hash` CHAR( 255 ) NOT NULL DEFAULT '' AFTER `term_hash` , ADD INDEX ( `parent_hash` ) "); - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1050.php b/Zotlabs/Update/_1050.php index 1939d346f..3823c3260 100644 --- a/Zotlabs/Update/_1050.php +++ b/Zotlabs/Update/_1050.php @@ -2,14 +2,15 @@ namespace Zotlabs\Update; -class _1050 { -function run() { - $r = q("ALTER TABLE `xtag` DROP PRIMARY KEY , ADD `xtag_id` INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY FIRST , ADD INDEX ( `xtag_hash` ) "); +class _1050 +{ + public function run() + { + $r = q("ALTER TABLE `xtag` DROP PRIMARY KEY , ADD `xtag_id` INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY FIRST , ADD INDEX ( `xtag_hash` ) "); - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1051.php b/Zotlabs/Update/_1051.php index 041b51f21..efa725a01 100644 --- a/Zotlabs/Update/_1051.php +++ b/Zotlabs/Update/_1051.php @@ -2,15 +2,15 @@ namespace Zotlabs\Update; -class _1051 { -function run() { - $r = q("ALTER TABLE `photo` ADD `photo_flags` INT UNSIGNED NOT NULL DEFAULT '0' AFTER `profile` , ADD INDEX ( `photo_flags` ) "); +class _1051 +{ + public function run() + { + $r = q("ALTER TABLE `photo` ADD `photo_flags` INT UNSIGNED NOT NULL DEFAULT '0' AFTER `profile` , ADD INDEX ( `photo_flags` ) "); - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1052.php b/Zotlabs/Update/_1052.php index 05addf21f..099ba547f 100644 --- a/Zotlabs/Update/_1052.php +++ b/Zotlabs/Update/_1052.php @@ -2,13 +2,14 @@ namespace Zotlabs\Update; -class _1052 { -function run() { - $r = q("ALTER TABLE `channel` ADD UNIQUE (`channel_address`) "); - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; +class _1052 +{ + public function run() + { + $r = q("ALTER TABLE `channel` ADD UNIQUE (`channel_address`) "); + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1053.php b/Zotlabs/Update/_1053.php index 4752dca62..5e0f743e3 100644 --- a/Zotlabs/Update/_1053.php +++ b/Zotlabs/Update/_1053.php @@ -2,13 +2,14 @@ namespace Zotlabs\Update; -class _1053 { -function run() { - $r = q("ALTER TABLE `profile` ADD `chandesc` TEXT NOT NULL DEFAULT '' AFTER `pdesc` "); - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; +class _1053 +{ + public function run() + { + $r = q("ALTER TABLE `profile` ADD `chandesc` TEXT NOT NULL DEFAULT '' AFTER `pdesc` "); + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1054.php b/Zotlabs/Update/_1054.php index f4fc2eb5b..155cf1026 100644 --- a/Zotlabs/Update/_1054.php +++ b/Zotlabs/Update/_1054.php @@ -2,13 +2,14 @@ namespace Zotlabs\Update; -class _1054 { -function run() { - $r = q("ALTER TABLE `item` CHANGE `title` `title` TEXT CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' "); - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; +class _1054 +{ + public function run() + { + $r = q("ALTER TABLE `item` CHANGE `title` `title` TEXT CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' "); + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1055.php b/Zotlabs/Update/_1055.php index 6b3a4c1d9..b4c520557 100644 --- a/Zotlabs/Update/_1055.php +++ b/Zotlabs/Update/_1055.php @@ -2,13 +2,14 @@ namespace Zotlabs\Update; -class _1055 { -function run() { - $r = q("ALTER TABLE `mail` CHANGE `title` `title` TEXT CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' "); - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; +class _1055 +{ + public function run() + { + $r = q("ALTER TABLE `mail` CHANGE `title` `title` TEXT CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' "); + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1056.php b/Zotlabs/Update/_1056.php index 6c0510f88..edc286add 100644 --- a/Zotlabs/Update/_1056.php +++ b/Zotlabs/Update/_1056.php @@ -2,14 +2,15 @@ namespace Zotlabs\Update; -class _1056 { -function run() { - $r = q("ALTER TABLE `xchan` ADD `xchan_instance_url` CHAR( 255 ) NOT NULL DEFAULT '' AFTER `xchan_network` , +class _1056 +{ + public function run() + { + $r = q("ALTER TABLE `xchan` ADD `xchan_instance_url` CHAR( 255 ) NOT NULL DEFAULT '' AFTER `xchan_network` , ADD INDEX ( `xchan_instance_url` ) "); - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1057.php b/Zotlabs/Update/_1057.php index 081e82d1f..f4b395210 100644 --- a/Zotlabs/Update/_1057.php +++ b/Zotlabs/Update/_1057.php @@ -2,13 +2,14 @@ namespace Zotlabs\Update; -class _1057 { -function run() { - $r = q("drop table intro"); - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; +class _1057 +{ + public function run() + { + $r = q("drop table intro"); + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1058.php b/Zotlabs/Update/_1058.php index 8e3ffdd6a..9c5057e3c 100644 --- a/Zotlabs/Update/_1058.php +++ b/Zotlabs/Update/_1058.php @@ -2,18 +2,19 @@ namespace Zotlabs\Update; -class _1058 { -function run() { - $r1 = q("ALTER TABLE `menu` ADD `menu_name` CHAR( 255 ) NOT NULL DEFAULT '' AFTER `menu_channel_id` , +class _1058 +{ + public function run() + { + $r1 = q("ALTER TABLE `menu` ADD `menu_name` CHAR( 255 ) NOT NULL DEFAULT '' AFTER `menu_channel_id` , ADD INDEX ( `menu_name` ) "); - $r2 = q("ALTER TABLE `menu_item` ADD `mitem_flags` INT NOT NULL DEFAULT '0' AFTER `mitem_desc` , + $r2 = q("ALTER TABLE `menu_item` ADD `mitem_flags` INT NOT NULL DEFAULT '0' AFTER `mitem_desc` , ADD INDEX ( `mitem_flags` ) "); - if($r1 && $r2) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + if ($r1 && $r2) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1059.php b/Zotlabs/Update/_1059.php index e035efb2c..7c6b83ae5 100644 --- a/Zotlabs/Update/_1059.php +++ b/Zotlabs/Update/_1059.php @@ -2,13 +2,14 @@ namespace Zotlabs\Update; -class _1059 { -function run() { - $r = q("ALTER TABLE `mail` ADD `attach` MEDIUMTEXT NOT NULL DEFAULT '' AFTER `body` "); - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; +class _1059 +{ + public function run() + { + $r = q("ALTER TABLE `mail` ADD `attach` MEDIUMTEXT NOT NULL DEFAULT '' AFTER `body` "); + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1060.php b/Zotlabs/Update/_1060.php index 5182b803a..cd638d00c 100644 --- a/Zotlabs/Update/_1060.php +++ b/Zotlabs/Update/_1060.php @@ -2,10 +2,12 @@ namespace Zotlabs\Update; -class _1060 { -function run() { +class _1060 +{ + public function run() + { - $r = q("CREATE TABLE IF NOT EXISTS `vote` ( + $r = q("CREATE TABLE IF NOT EXISTS `vote` ( `vote_id` int(10) unsigned NOT NULL AUTO_INCREMENT, `vote_poll` int(11) NOT NULL DEFAULT '0', `vote_element` int(11) NOT NULL DEFAULT '0', @@ -15,10 +17,9 @@ function run() { UNIQUE KEY `vote_vote` (`vote_poll`,`vote_element`,`vote_xchan`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 "); - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1061.php b/Zotlabs/Update/_1061.php index e7af5bcb1..ffa06c2e0 100644 --- a/Zotlabs/Update/_1061.php +++ b/Zotlabs/Update/_1061.php @@ -2,14 +2,15 @@ namespace Zotlabs\Update; -class _1061 { -function run() { - $r = q("ALTER TABLE `vote` ADD INDEX ( `vote_poll` ), ADD INDEX ( `vote_element` ) "); +class _1061 +{ + public function run() + { + $r = q("ALTER TABLE `vote` ADD INDEX ( `vote_poll` ), ADD INDEX ( `vote_element` ) "); - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1062.php b/Zotlabs/Update/_1062.php index 39aa80e8a..81fb75341 100644 --- a/Zotlabs/Update/_1062.php +++ b/Zotlabs/Update/_1062.php @@ -2,9 +2,11 @@ namespace Zotlabs\Update; -class _1062 { -function run() { - $r1 = q("CREATE TABLE IF NOT EXISTS `poll` ( +class _1062 +{ + public function run() + { + $r1 = q("CREATE TABLE IF NOT EXISTS `poll` ( `poll_id` INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY , `poll_channel` INT UNSIGNED NOT NULL DEFAULT '0', `poll_desc` TEXT NOT NULL DEFAULT '', @@ -15,7 +17,7 @@ KEY `poll_flags` (`poll_flags`), KEY `poll_votes` (`poll_votes`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 "); - $r2 = q("CREATE TABLE IF NOT EXISTS `poll_elm` ( + $r2 = q("CREATE TABLE IF NOT EXISTS `poll_elm` ( `pelm_id` INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY , `pelm_poll` INT UNSIGNED NOT NULL DEFAULT '0', `pelm_desc` TEXT NOT NULL DEFAULT '', @@ -25,10 +27,9 @@ KEY `pelm_poll` (`pelm_poll`), KEY `pelm_result` (`pelm_result`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 "); - if($r1 && $r2) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + if ($r1 && $r2) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1063.php b/Zotlabs/Update/_1063.php index 04d523df6..0cd846c9a 100644 --- a/Zotlabs/Update/_1063.php +++ b/Zotlabs/Update/_1063.php @@ -2,15 +2,16 @@ namespace Zotlabs\Update; -class _1063 { -function run() { - $r = q("ALTER TABLE `xchan` ADD `xchan_follow` CHAR( 255 ) NOT NULL DEFAULT '' AFTER `xchan_connurl` , +class _1063 +{ + public function run() + { + $r = q("ALTER TABLE `xchan` ADD `xchan_follow` CHAR( 255 ) NOT NULL DEFAULT '' AFTER `xchan_connurl` , ADD `xchan_connpage` CHAR( 255 ) NOT NULL DEFAULT '' AFTER `xchan_follow` , ADD INDEX ( `xchan_follow` ), ADD INDEX ( `xchan_connpage`) "); - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1064.php b/Zotlabs/Update/_1064.php index 295dd96c5..21ced526d 100644 --- a/Zotlabs/Update/_1064.php +++ b/Zotlabs/Update/_1064.php @@ -2,14 +2,15 @@ namespace Zotlabs\Update; -class _1064 { -function run() { - $r = q("ALTER TABLE `updates` ADD `ud_guid` CHAR( 255 ) NOT NULL DEFAULT '' AFTER `ud_hash` , +class _1064 +{ + public function run() + { + $r = q("ALTER TABLE `updates` ADD `ud_guid` CHAR( 255 ) NOT NULL DEFAULT '' AFTER `ud_hash` , ADD INDEX ( `ud_guid` )"); - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1065.php b/Zotlabs/Update/_1065.php index 0a3e9082e..d143007ce 100644 --- a/Zotlabs/Update/_1065.php +++ b/Zotlabs/Update/_1065.php @@ -2,14 +2,15 @@ namespace Zotlabs\Update; -class _1065 { -function run() { - $r = q("ALTER TABLE `item` DROP `wall`, ADD `layout_mid` CHAR( 255 ) NOT NULL DEFAULT '' AFTER `target` , +class _1065 +{ + public function run() + { + $r = q("ALTER TABLE `item` DROP `wall`, ADD `layout_mid` CHAR( 255 ) NOT NULL DEFAULT '' AFTER `target` , ADD INDEX ( `layout_mid` ) "); - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1066.php b/Zotlabs/Update/_1066.php index e7d3fbbb4..9ff357c4e 100644 --- a/Zotlabs/Update/_1066.php +++ b/Zotlabs/Update/_1066.php @@ -2,14 +2,15 @@ namespace Zotlabs\Update; -class _1066 { -function run() { - $r = q("ALTER TABLE `site` ADD `site_access` INT NOT NULL DEFAULT '0' AFTER `site_url` , +class _1066 +{ + public function run() + { + $r = q("ALTER TABLE `site` ADD `site_access` INT NOT NULL DEFAULT '0' AFTER `site_url` , ADD INDEX ( `site_access` )"); - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1067.php b/Zotlabs/Update/_1067.php index 94782a15e..d7f7a60e3 100644 --- a/Zotlabs/Update/_1067.php +++ b/Zotlabs/Update/_1067.php @@ -2,13 +2,14 @@ namespace Zotlabs\Update; -class _1067 { -function run() { - $r = q("ALTER TABLE `updates` DROP PRIMARY KEY , ADD `ud_id` INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY FIRST, ADD INDEX ( `ud_hash` ) "); - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; +class _1067 +{ + public function run() + { + $r = q("ALTER TABLE `updates` DROP PRIMARY KEY , ADD `ud_id` INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY FIRST, ADD INDEX ( `ud_hash` ) "); + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1068.php b/Zotlabs/Update/_1068.php index e2a70684f..9ada57d0e 100644 --- a/Zotlabs/Update/_1068.php +++ b/Zotlabs/Update/_1068.php @@ -2,13 +2,14 @@ namespace Zotlabs\Update; -class _1068 { -function run(){ +class _1068 +{ + public function run() + { $r = q("ALTER TABLE `hubloc` ADD `hubloc_status` INT UNSIGNED NOT NULL DEFAULT '0' AFTER `hubloc_flags` , ADD INDEX ( `hubloc_status` )"); - if($r) - return UPDATE_SUCCESS; + if ($r) { + return UPDATE_SUCCESS; + } return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1069.php b/Zotlabs/Update/_1069.php index 437c7ffef..1787d5869 100644 --- a/Zotlabs/Update/_1069.php +++ b/Zotlabs/Update/_1069.php @@ -2,14 +2,15 @@ namespace Zotlabs\Update; -class _1069 { -function run() { - $r = q("ALTER TABLE `site` ADD `site_sellpage` CHAR( 255 ) NOT NULL DEFAULT '', +class _1069 +{ + public function run() + { + $r = q("ALTER TABLE `site` ADD `site_sellpage` CHAR( 255 ) NOT NULL DEFAULT '', ADD INDEX ( `site_sellpage` )"); - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1070.php b/Zotlabs/Update/_1070.php index 2ec03f752..b21ef2a62 100644 --- a/Zotlabs/Update/_1070.php +++ b/Zotlabs/Update/_1070.php @@ -2,14 +2,15 @@ namespace Zotlabs\Update; -class _1070 { -function run() { - $r = q("ALTER TABLE `updates` ADD `ud_flags` INT NOT NULL DEFAULT '0', +class _1070 +{ + public function run() + { + $r = q("ALTER TABLE `updates` ADD `ud_flags` INT NOT NULL DEFAULT '0', ADD INDEX ( `ud_flags` )"); - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1071.php b/Zotlabs/Update/_1071.php index f4e627e7f..8348616de 100644 --- a/Zotlabs/Update/_1071.php +++ b/Zotlabs/Update/_1071.php @@ -2,14 +2,15 @@ namespace Zotlabs\Update; -class _1071 { -function run() { - $r = q("ALTER TABLE `updates` ADD `ud_addr` CHAR( 255 ) NOT NULL DEFAULT '', +class _1071 +{ + public function run() + { + $r = q("ALTER TABLE `updates` ADD `ud_addr` CHAR( 255 ) NOT NULL DEFAULT '', ADD INDEX ( `ud_addr` ) "); - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1072.php b/Zotlabs/Update/_1072.php index 191893547..45ef72eb3 100644 --- a/Zotlabs/Update/_1072.php +++ b/Zotlabs/Update/_1072.php @@ -2,16 +2,16 @@ namespace Zotlabs\Update; -class _1072 { -function run() { - $r = q("ALTER TABLE `xtag` ADD `xtag_flags` INT NOT NULL DEFAULT '0', +class _1072 +{ + public function run() + { + $r = q("ALTER TABLE `xtag` ADD `xtag_flags` INT NOT NULL DEFAULT '0', ADD INDEX ( `xtag_flags` ) "); - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1073.php b/Zotlabs/Update/_1073.php index f238d7a36..9673a1877 100644 --- a/Zotlabs/Update/_1073.php +++ b/Zotlabs/Update/_1073.php @@ -2,9 +2,11 @@ namespace Zotlabs\Update; -class _1073 { -function run() { - $r1 = q("CREATE TABLE IF NOT EXISTS `source` ( +class _1073 +{ + public function run() + { + $r1 = q("CREATE TABLE IF NOT EXISTS `source` ( `src_id` INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY , `src_channel_id` INT UNSIGNED NOT NULL DEFAULT '0', `src_channel_xchan` CHAR( 255 ) NOT NULL DEFAULT '', @@ -12,12 +14,11 @@ function run() { `src_patt` MEDIUMTEXT NOT NULL DEFAULT '' ) ENGINE=MyISAM DEFAULT CHARSET=utf8 "); - $r2 = q("ALTER TABLE `source` ADD INDEX ( `src_channel_id` ), ADD INDEX ( `src_channel_xchan` ), ADD INDEX ( `src_xchan` ) "); + $r2 = q("ALTER TABLE `source` ADD INDEX ( `src_channel_id` ), ADD INDEX ( `src_channel_xchan` ), ADD INDEX ( `src_xchan` ) "); - if($r1 && $r2) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + if ($r1 && $r2) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1074.php b/Zotlabs/Update/_1074.php index d331b935a..1a56ae740 100644 --- a/Zotlabs/Update/_1074.php +++ b/Zotlabs/Update/_1074.php @@ -2,18 +2,18 @@ namespace Zotlabs\Update; -class _1074 { -function run() { - $r1 = q("ALTER TABLE `site` ADD `site_sync` DATETIME NOT NULL AFTER `site_update` "); +class _1074 +{ + public function run() + { + $r1 = q("ALTER TABLE `site` ADD `site_sync` DATETIME NOT NULL AFTER `site_update` "); - $r2 = q("ALTER TABLE `updates` ADD `ud_last` DATETIME NOT NULL DEFAULT '0001-01-01 00:00:00' AFTER `ud_date` , + $r2 = q("ALTER TABLE `updates` ADD `ud_last` DATETIME NOT NULL DEFAULT '0001-01-01 00:00:00' AFTER `ud_date` , ADD INDEX ( `ud_last` ) "); - if($r1 && $r2) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + if ($r1 && $r2) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1075.php b/Zotlabs/Update/_1075.php index ac7c48f71..95c416888 100644 --- a/Zotlabs/Update/_1075.php +++ b/Zotlabs/Update/_1075.php @@ -2,15 +2,16 @@ namespace Zotlabs\Update; -class _1075 { -function run() { - $r = q("ALTER TABLE `channel` ADD `channel_a_republish` INT UNSIGNED NOT NULL DEFAULT '128', +class _1075 +{ + public function run() + { + $r = q("ALTER TABLE `channel` ADD `channel_a_republish` INT UNSIGNED NOT NULL DEFAULT '128', ADD INDEX ( `channel_a_republish` )"); - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1076.php b/Zotlabs/Update/_1076.php index c8c631822..a1a5008e6 100644 --- a/Zotlabs/Update/_1076.php +++ b/Zotlabs/Update/_1076.php @@ -2,13 +2,14 @@ namespace Zotlabs\Update; -class _1076 { -function run() { - $r = q("ALTER TABLE `item` CHANGE `inform` `sig` TEXT CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' "); - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; +class _1076 +{ + public function run() + { + $r = q("ALTER TABLE `item` CHANGE `inform` `sig` TEXT CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' "); + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1077.php b/Zotlabs/Update/_1077.php index 3c23affbc..0ed1e4461 100644 --- a/Zotlabs/Update/_1077.php +++ b/Zotlabs/Update/_1077.php @@ -2,13 +2,14 @@ namespace Zotlabs\Update; -class _1077 { -function run() { - $r = q("ALTER TABLE `item` ADD `source_xchan` CHAR( 255 ) NOT NULL DEFAULT '' AFTER `author_xchan` "); - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; +class _1077 +{ + public function run() + { + $r = q("ALTER TABLE `item` ADD `source_xchan` CHAR( 255 ) NOT NULL DEFAULT '' AFTER `author_xchan` "); + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1078.php b/Zotlabs/Update/_1078.php index c832a1c2a..06e4c84e8 100644 --- a/Zotlabs/Update/_1078.php +++ b/Zotlabs/Update/_1078.php @@ -2,14 +2,15 @@ namespace Zotlabs\Update; -class _1078 { -function run() { - $r = q("ALTER TABLE `channel` ADD `channel_dirdate` DATETIME NOT NULL DEFAULT '0001-01-01 00:00:00' AFTER `channel_pageflags` , ADD INDEX ( `channel_dirdate` )"); +class _1078 +{ + public function run() + { + $r = q("ALTER TABLE `channel` ADD `channel_dirdate` DATETIME NOT NULL DEFAULT '0001-01-01 00:00:00' AFTER `channel_pageflags` , ADD INDEX ( `channel_dirdate` )"); - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1079.php b/Zotlabs/Update/_1079.php index b10e2e7ae..d966a1edb 100644 --- a/Zotlabs/Update/_1079.php +++ b/Zotlabs/Update/_1079.php @@ -2,13 +2,14 @@ namespace Zotlabs\Update; -class _1079 { -function run() { - $r = q("ALTER TABLE `site` ADD `site_location` CHAR( 255 ) NOT NULL DEFAULT ''"); - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; +class _1079 +{ + public function run() + { + $r = q("ALTER TABLE `site` ADD `site_location` CHAR( 255 ) NOT NULL DEFAULT ''"); + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1080.php b/Zotlabs/Update/_1080.php index ab9c4c2f8..72012140c 100644 --- a/Zotlabs/Update/_1080.php +++ b/Zotlabs/Update/_1080.php @@ -2,14 +2,15 @@ namespace Zotlabs\Update; -class _1080 { -function run() { - $r = q("ALTER TABLE `mail` ADD `expires` DATETIME NOT NULL DEFAULT '0001-01-01 00:00:00', +class _1080 +{ + public function run() + { + $r = q("ALTER TABLE `mail` ADD `expires` DATETIME NOT NULL DEFAULT '0001-01-01 00:00:00', ADD INDEX ( `expires` ) "); - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1081.php b/Zotlabs/Update/_1081.php index 521a39893..8611f1f87 100644 --- a/Zotlabs/Update/_1081.php +++ b/Zotlabs/Update/_1081.php @@ -2,13 +2,14 @@ namespace Zotlabs\Update; -class _1081 { -function run() { - $r = q("DROP TABLE `queue` "); - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; +class _1081 +{ + public function run() + { + $r = q("DROP TABLE `queue` "); + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1082.php b/Zotlabs/Update/_1082.php index 8ce8d20c1..3cd2e2683 100644 --- a/Zotlabs/Update/_1082.php +++ b/Zotlabs/Update/_1082.php @@ -2,13 +2,14 @@ namespace Zotlabs\Update; -class _1082 { -function run() { - $r = q("DROP TABLE `challenge` "); - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; +class _1082 +{ + public function run() + { + $r = q("DROP TABLE `challenge` "); + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1083.php b/Zotlabs/Update/_1083.php index becb2b702..90dd19376 100644 --- a/Zotlabs/Update/_1083.php +++ b/Zotlabs/Update/_1083.php @@ -2,14 +2,15 @@ namespace Zotlabs\Update; -class _1083 { -function run() { - $r = q("ALTER TABLE `notify` ADD `aid` INT NOT NULL AFTER `msg` , +class _1083 +{ + public function run() + { + $r = q("ALTER TABLE `notify` ADD `aid` INT NOT NULL AFTER `msg` , ADD INDEX ( `aid` )"); - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1084.php b/Zotlabs/Update/_1084.php index 621d0346d..ca139cf62 100644 --- a/Zotlabs/Update/_1084.php +++ b/Zotlabs/Update/_1084.php @@ -2,11 +2,13 @@ namespace Zotlabs\Update; -class _1084 { -function run() { +class _1084 +{ + public function run() + { - $r = q("CREATE TABLE if not exists `sys_perms` ( + $r = q("CREATE TABLE if not exists `sys_perms` ( `id` INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY , `cat` CHAR( 255 ) NOT NULL , `k` CHAR( 255 ) NOT NULL , @@ -14,11 +16,9 @@ function run() { `public_perm` TINYINT( 1 ) UNSIGNED NOT NULL ) ENGINE = MYISAM DEFAULT CHARSET = utf8"); - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; - + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1085.php b/Zotlabs/Update/_1085.php index f50e56319..d86bc0596 100644 --- a/Zotlabs/Update/_1085.php +++ b/Zotlabs/Update/_1085.php @@ -2,19 +2,19 @@ namespace Zotlabs\Update; -class _1085 { -function run() { - $r1 = q("ALTER TABLE `photo` CHANGE `desc` `description` TEXT CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL "); +class _1085 +{ + public function run() + { + $r1 = q("ALTER TABLE `photo` CHANGE `desc` `description` TEXT CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL "); - $r2 = q("RENAME TABLE `group` TO `groups`"); + $r2 = q("RENAME TABLE `group` TO `groups`"); - $r3 = q("ALTER TABLE `event` CHANGE `desc` `description` TEXT CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL "); - - if($r1 && $r2 && $r3) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + $r3 = q("ALTER TABLE `event` CHANGE `desc` `description` TEXT CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL "); + if ($r1 && $r2 && $r3) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1086.php b/Zotlabs/Update/_1086.php index 1b034daac..2273c10f3 100644 --- a/Zotlabs/Update/_1086.php +++ b/Zotlabs/Update/_1086.php @@ -2,14 +2,15 @@ namespace Zotlabs\Update; -class _1086 { -function run() { - $r = q("ALTER TABLE `account` ADD `account_level` INT UNSIGNED NOT NULL DEFAULT '0', +class _1086 +{ + public function run() + { + $r = q("ALTER TABLE `account` ADD `account_level` INT UNSIGNED NOT NULL DEFAULT '0', ADD INDEX ( `account_level` )"); - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1087.php b/Zotlabs/Update/_1087.php index 839c16d91..764b95806 100644 --- a/Zotlabs/Update/_1087.php +++ b/Zotlabs/Update/_1087.php @@ -2,16 +2,17 @@ namespace Zotlabs\Update; -class _1087 { -function run() { - $r = q("ALTER TABLE `xprof` ADD `xprof_about` TEXT NOT NULL DEFAULT '', +class _1087 +{ + public function run() + { + $r = q("ALTER TABLE `xprof` ADD `xprof_about` TEXT NOT NULL DEFAULT '', ADD `xprof_homepage` CHAR( 255 ) NOT NULL DEFAULT '', ADD `xprof_hometown` CHAR( 255 ) NOT NULL DEFAULT '', ADD INDEX ( `xprof_hometown` )"); - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1088.php b/Zotlabs/Update/_1088.php index 5f478d0a1..469a1d9b7 100644 --- a/Zotlabs/Update/_1088.php +++ b/Zotlabs/Update/_1088.php @@ -2,16 +2,17 @@ namespace Zotlabs\Update; -class _1088 { -function run() { - $r = q("ALTER TABLE `obj` ADD `allow_cid` MEDIUMTEXT NOT NULL DEFAULT '', +class _1088 +{ + public function run() + { + $r = q("ALTER TABLE `obj` ADD `allow_cid` MEDIUMTEXT NOT NULL DEFAULT '', ADD `allow_gid` MEDIUMTEXT NOT NULL DEFAULT '', ADD `deny_cid` MEDIUMTEXT NOT NULL DEFAULT '', ADD `deny_gid` MEDIUMTEXT NOT NULL DEFAULT ''"); - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1089.php b/Zotlabs/Update/_1089.php index 0a8a1d55b..5f178694a 100644 --- a/Zotlabs/Update/_1089.php +++ b/Zotlabs/Update/_1089.php @@ -2,15 +2,15 @@ namespace Zotlabs\Update; -class _1089 { -function run() { - $r = q("ALTER TABLE `attach` ADD `creator` CHAR( 128 ) NOT NULL DEFAULT '' AFTER `hash` , +class _1089 +{ + public function run() + { + $r = q("ALTER TABLE `attach` ADD `creator` CHAR( 128 ) NOT NULL DEFAULT '' AFTER `hash` , ADD INDEX ( `creator` ) "); - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1090.php b/Zotlabs/Update/_1090.php index efa7db7c2..3939d4a2a 100644 --- a/Zotlabs/Update/_1090.php +++ b/Zotlabs/Update/_1090.php @@ -2,14 +2,15 @@ namespace Zotlabs\Update; -class _1090 { -function run() { - $r = q("ALTER TABLE `menu` ADD `menu_flags` INT NOT NULL DEFAULT '0', +class _1090 +{ + public function run() + { + $r = q("ALTER TABLE `menu` ADD `menu_flags` INT NOT NULL DEFAULT '0', ADD INDEX ( `menu_flags` )"); - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1091.php b/Zotlabs/Update/_1091.php index 0b287b45f..46b59ea96 100644 --- a/Zotlabs/Update/_1091.php +++ b/Zotlabs/Update/_1091.php @@ -2,12 +2,12 @@ namespace Zotlabs\Update; -class _1091 { -function run() { - @os_mkdir('cache/smarty3',STORAGE_DEFAULT_PERMISSIONS,true); - @file_put_contents('cache/locks',''); - return UPDATE_SUCCESS; +class _1091 +{ + public function run() + { + @os_mkdir('cache/smarty3', STORAGE_DEFAULT_PERMISSIONS, true); + @file_put_contents('cache/locks', ''); + return UPDATE_SUCCESS; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1092.php b/Zotlabs/Update/_1092.php index 897c0c20b..a7f4483e5 100644 --- a/Zotlabs/Update/_1092.php +++ b/Zotlabs/Update/_1092.php @@ -2,9 +2,11 @@ namespace Zotlabs\Update; -class _1092 { -function run() { - $r1 = q("CREATE TABLE IF NOT EXISTS `chat` ( +class _1092 +{ + public function run() + { + $r1 = q("CREATE TABLE IF NOT EXISTS `chat` ( `chat_id` int(10) unsigned NOT NULL AUTO_INCREMENT, `chat_room` int(10) unsigned NOT NULL DEFAULT '0', `chat_xchan` char(255) NOT NULL DEFAULT '', @@ -16,7 +18,7 @@ function run() { KEY `created` (`created`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8"); - $r2 = q("CREATE TABLE IF NOT EXISTS `chatpresence` ( + $r2 = q("CREATE TABLE IF NOT EXISTS `chatpresence` ( `cp_id` int(10) unsigned NOT NULL AUTO_INCREMENT, `cp_room` int(10) unsigned NOT NULL DEFAULT '0', `cp_xchan` char(255) NOT NULL DEFAULT '', @@ -29,7 +31,7 @@ function run() { KEY `cp_status` (`cp_status`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8"); - $r3 = q("CREATE TABLE IF NOT EXISTS `chatroom` ( + $r3 = q("CREATE TABLE IF NOT EXISTS `chatroom` ( `cr_id` int(10) unsigned NOT NULL AUTO_INCREMENT, `cr_aid` int(10) unsigned NOT NULL DEFAULT '0', `cr_uid` int(10) unsigned NOT NULL DEFAULT '0', @@ -49,13 +51,9 @@ function run() { ) ENGINE=MyISAM DEFAULT CHARSET=utf8"); - if($r1 && $r2 && $r3) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + if ($r1 && $r2 && $r3) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - - - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1093.php b/Zotlabs/Update/_1093.php index eb692e0b7..ba61bf590 100644 --- a/Zotlabs/Update/_1093.php +++ b/Zotlabs/Update/_1093.php @@ -2,13 +2,14 @@ namespace Zotlabs\Update; -class _1093 { -function run() { - $r = q("ALTER TABLE `chatpresence` ADD `cp_client` CHAR( 128 ) NOT NULL DEFAULT ''"); - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; +class _1093 +{ + public function run() + { + $r = q("ALTER TABLE `chatpresence` ADD `cp_client` CHAR( 128 ) NOT NULL DEFAULT ''"); + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1094.php b/Zotlabs/Update/_1094.php index c502c7a06..bbe7a95da 100644 --- a/Zotlabs/Update/_1094.php +++ b/Zotlabs/Update/_1094.php @@ -2,14 +2,15 @@ namespace Zotlabs\Update; -class _1094 { -function run() { - $r = q("ALTER TABLE `chatroom` ADD `cr_expire` INT UNSIGNED NOT NULL DEFAULT '0' AFTER `cr_edited` , +class _1094 +{ + public function run() + { + $r = q("ALTER TABLE `chatroom` ADD `cr_expire` INT UNSIGNED NOT NULL DEFAULT '0' AFTER `cr_edited` , ADD INDEX ( `cr_expire` )"); - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1095.php b/Zotlabs/Update/_1095.php index 0f11fda62..f4b0105f0 100644 --- a/Zotlabs/Update/_1095.php +++ b/Zotlabs/Update/_1095.php @@ -2,14 +2,15 @@ namespace Zotlabs\Update; -class _1095 { -function run() { - $r = q("ALTER TABLE `channel` ADD `channel_a_bookmark` INT UNSIGNED NOT NULL DEFAULT '128', +class _1095 +{ + public function run() + { + $r = q("ALTER TABLE `channel` ADD `channel_a_bookmark` INT UNSIGNED NOT NULL DEFAULT '128', ADD INDEX ( `channel_a_bookmark` )"); - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1096.php b/Zotlabs/Update/_1096.php index 67abb9ee2..e70db08b1 100644 --- a/Zotlabs/Update/_1096.php +++ b/Zotlabs/Update/_1096.php @@ -2,13 +2,14 @@ namespace Zotlabs\Update; -class _1096 { -function run() { - $r = q("ALTER TABLE `account` CHANGE `account_level` `account_level` INT( 10 ) UNSIGNED NOT NULL DEFAULT '0'"); - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; +class _1096 +{ + public function run() + { + $r = q("ALTER TABLE `account` CHANGE `account_level` `account_level` INT( 10 ) UNSIGNED NOT NULL DEFAULT '0'"); + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1097.php b/Zotlabs/Update/_1097.php index bfe04cf5f..5370e6ec6 100644 --- a/Zotlabs/Update/_1097.php +++ b/Zotlabs/Update/_1097.php @@ -2,23 +2,23 @@ namespace Zotlabs\Update; -class _1097 { -function run() { +class _1097 +{ + public function run() + { - // fix some mangled hublocs from a bug long ago + // fix some mangled hublocs from a bug long ago - $r = q("select hubloc_id, hubloc_addr from hubloc where hubloc_addr like '%%/%%'"); - if($r) { - foreach($r as $rr) { - q("update hubloc set hubloc_addr = '%s' where hubloc_id = %d", - dbesc(substr($rr['hubloc_addr'],0,strpos($rr['hubloc_addr'],'/'))), - intval($rr['hubloc_id']) - ); - } - } - return UPDATE_SUCCESS; - + $r = q("select hubloc_id, hubloc_addr from hubloc where hubloc_addr like '%%/%%'"); + if ($r) { + foreach ($r as $rr) { + q( + "update hubloc set hubloc_addr = '%s' where hubloc_id = %d", + dbesc(substr($rr['hubloc_addr'], 0, strpos($rr['hubloc_addr'], '/'))), + intval($rr['hubloc_id']) + ); + } + } + return UPDATE_SUCCESS; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1098.php b/Zotlabs/Update/_1098.php index e74ba640c..708042bff 100644 --- a/Zotlabs/Update/_1098.php +++ b/Zotlabs/Update/_1098.php @@ -2,24 +2,25 @@ namespace Zotlabs\Update; -class _1098 { -function run() { - $r = q("ALTER TABLE `channel` CHANGE `channel_r_stream` `channel_r_stream` INT( 10 ) UNSIGNED NOT NULL DEFAULT '0'"); - $r2 = q("ALTER TABLE `channel` CHANGE `channel_r_profile` `channel_r_profile` INT( 10 ) UNSIGNED NOT NULL DEFAULT '0'"); - $r3 = q("ALTER TABLE `channel` CHANGE `channel_r_photos` `channel_r_photos` INT( 10 ) UNSIGNED NOT NULL DEFAULT '0'"); - $r4 = q("ALTER TABLE `channel` CHANGE `channel_r_abook` `channel_r_abook` INT( 10 ) UNSIGNED NOT NULL DEFAULT '0'"); - $r4 = q("ALTER TABLE `channel` CHANGE `channel_w_stream` `channel_w_stream` INT( 10 ) UNSIGNED NOT NULL DEFAULT '0'"); - $r5 = q("ALTER TABLE `channel` CHANGE `channel_w_wall` `channel_w_wall` INT( 10 ) UNSIGNED NOT NULL DEFAULT '0'"); - $r6 = q("ALTER TABLE `channel` CHANGE `channel_w_tagwall` `channel_w_tagwall` INT( 10 ) UNSIGNED NOT NULL DEFAULT '0'"); - $r7 = q("ALTER TABLE `channel` CHANGE `channel_w_comment` `channel_w_comment` INT( 10 ) UNSIGNED NOT NULL DEFAULT '0'"); - $r8 = q("ALTER TABLE `channel` CHANGE `channel_w_mail` `channel_w_mail` INT( 10 ) UNSIGNED NOT NULL DEFAULT '0'"); - $r9 = q("ALTER TABLE `channel` CHANGE `channel_w_photos` `channel_w_photos` INT( 10 ) UNSIGNED NOT NULL DEFAULT '0'"); - $r10 = q("ALTER TABLE `channel` CHANGE `channel_w_chat` `channel_w_chat` INT( 10 ) UNSIGNED NOT NULL DEFAULT '0'"); - $r11 = q("ALTER TABLE `channel` CHANGE `channel_a_delegate` `channel_a_delegate` INT( 10 ) UNSIGNED NOT NULL DEFAULT '0'"); - if($r && $r2 && $r3 && $r3 && $r5 && $r6 && $r7 && $r8 && $r9 && $r9 && $r10 && $r11) - return UPDATE_SUCCESS; - return UPDATE_FAILED; +class _1098 +{ + public function run() + { + $r = q("ALTER TABLE `channel` CHANGE `channel_r_stream` `channel_r_stream` INT( 10 ) UNSIGNED NOT NULL DEFAULT '0'"); + $r2 = q("ALTER TABLE `channel` CHANGE `channel_r_profile` `channel_r_profile` INT( 10 ) UNSIGNED NOT NULL DEFAULT '0'"); + $r3 = q("ALTER TABLE `channel` CHANGE `channel_r_photos` `channel_r_photos` INT( 10 ) UNSIGNED NOT NULL DEFAULT '0'"); + $r4 = q("ALTER TABLE `channel` CHANGE `channel_r_abook` `channel_r_abook` INT( 10 ) UNSIGNED NOT NULL DEFAULT '0'"); + $r4 = q("ALTER TABLE `channel` CHANGE `channel_w_stream` `channel_w_stream` INT( 10 ) UNSIGNED NOT NULL DEFAULT '0'"); + $r5 = q("ALTER TABLE `channel` CHANGE `channel_w_wall` `channel_w_wall` INT( 10 ) UNSIGNED NOT NULL DEFAULT '0'"); + $r6 = q("ALTER TABLE `channel` CHANGE `channel_w_tagwall` `channel_w_tagwall` INT( 10 ) UNSIGNED NOT NULL DEFAULT '0'"); + $r7 = q("ALTER TABLE `channel` CHANGE `channel_w_comment` `channel_w_comment` INT( 10 ) UNSIGNED NOT NULL DEFAULT '0'"); + $r8 = q("ALTER TABLE `channel` CHANGE `channel_w_mail` `channel_w_mail` INT( 10 ) UNSIGNED NOT NULL DEFAULT '0'"); + $r9 = q("ALTER TABLE `channel` CHANGE `channel_w_photos` `channel_w_photos` INT( 10 ) UNSIGNED NOT NULL DEFAULT '0'"); + $r10 = q("ALTER TABLE `channel` CHANGE `channel_w_chat` `channel_w_chat` INT( 10 ) UNSIGNED NOT NULL DEFAULT '0'"); + $r11 = q("ALTER TABLE `channel` CHANGE `channel_a_delegate` `channel_a_delegate` INT( 10 ) UNSIGNED NOT NULL DEFAULT '0'"); + if ($r && $r2 && $r3 && $r3 && $r5 && $r6 && $r7 && $r8 && $r9 && $r9 && $r10 && $r11) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1099.php b/Zotlabs/Update/_1099.php index a49e38c7c..79446e7f4 100644 --- a/Zotlabs/Update/_1099.php +++ b/Zotlabs/Update/_1099.php @@ -2,9 +2,11 @@ namespace Zotlabs\Update; -class _1099 { -function run() { - $r = q("CREATE TABLE IF NOT EXISTS `xchat` ( +class _1099 +{ + public function run() + { + $r = q("CREATE TABLE IF NOT EXISTS `xchat` ( `xchat_id` int(10) unsigned NOT NULL AUTO_INCREMENT, `xchat_url` char(255) NOT NULL DEFAULT '', `xchat_desc` char(255) NOT NULL DEFAULT '', @@ -15,10 +17,9 @@ function run() { KEY `xchat_xchan` (`xchat_xchan`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 "); - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1100.php b/Zotlabs/Update/_1100.php index ffcd7dc1d..dcf294bf9 100644 --- a/Zotlabs/Update/_1100.php +++ b/Zotlabs/Update/_1100.php @@ -2,15 +2,15 @@ namespace Zotlabs\Update; -class _1100 { -function run() { - $r = q("ALTER TABLE `xchat` ADD `xchat_edited` DATETIME NOT NULL DEFAULT '0001-01-01 00:00:00', +class _1100 +{ + public function run() + { + $r = q("ALTER TABLE `xchat` ADD `xchat_edited` DATETIME NOT NULL DEFAULT '0001-01-01 00:00:00', ADD INDEX ( `xchat_edited` ) "); - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1101.php b/Zotlabs/Update/_1101.php index 709b6fa0a..95f6f6ac3 100644 --- a/Zotlabs/Update/_1101.php +++ b/Zotlabs/Update/_1101.php @@ -2,12 +2,12 @@ namespace Zotlabs\Update; -class _1101 { -function run() { - $r = q("update updates set ud_flags = 2 where ud_flags = (-1)"); - $r = q("update updates set ud_flags = 0 where ud_flags = 4096"); - return UPDATE_SUCCESS; +class _1101 +{ + public function run() + { + $r = q("update updates set ud_flags = 2 where ud_flags = (-1)"); + $r = q("update updates set ud_flags = 0 where ud_flags = 4096"); + return UPDATE_SUCCESS; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1102.php b/Zotlabs/Update/_1102.php index d25979b1f..aefdfd70d 100644 --- a/Zotlabs/Update/_1102.php +++ b/Zotlabs/Update/_1102.php @@ -2,15 +2,16 @@ namespace Zotlabs\Update; -class _1102 { -function run() { - $r = q("update abook set abook_flags = (abook_flags - %d) +class _1102 +{ + public function run() + { + $r = q( + "update abook set abook_flags = (abook_flags - %d) where ( abook_flags & %d)", - intval(ABOOK_FLAG_UNCONNECTED), - intval(ABOOK_FLAG_UNCONNECTED) - ); - return UPDATE_SUCCESS; + intval(ABOOK_FLAG_UNCONNECTED), + intval(ABOOK_FLAG_UNCONNECTED) + ); + return UPDATE_SUCCESS; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1103.php b/Zotlabs/Update/_1103.php index 3f9a9286c..0372fd1c0 100644 --- a/Zotlabs/Update/_1103.php +++ b/Zotlabs/Update/_1103.php @@ -2,13 +2,14 @@ namespace Zotlabs\Update; -class _1103 { -function run() { - $x = curl_version(); - if(stristr($x['ssl_version'],'openssl')) - set_config('system','curl_ssl_ciphers','ALL:!eNULL'); - return UPDATE_SUCCESS; +class _1103 +{ + public function run() + { + $x = curl_version(); + if (stristr($x['ssl_version'], 'openssl')) { + set_config('system', 'curl_ssl_ciphers', 'ALL:!eNULL'); + } + return UPDATE_SUCCESS; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1104.php b/Zotlabs/Update/_1104.php index 0d299095b..0601f715e 100644 --- a/Zotlabs/Update/_1104.php +++ b/Zotlabs/Update/_1104.php @@ -2,13 +2,14 @@ namespace Zotlabs\Update; -class _1104 { -function run() { - $r = q("ALTER TABLE `item` ADD `route` TEXT NOT NULL DEFAULT '' AFTER `postopts` "); - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; +class _1104 +{ + public function run() + { + $r = q("ALTER TABLE `item` ADD `route` TEXT NOT NULL DEFAULT '' AFTER `postopts` "); + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1105.php b/Zotlabs/Update/_1105.php index a96600150..0c44d0980 100644 --- a/Zotlabs/Update/_1105.php +++ b/Zotlabs/Update/_1105.php @@ -2,14 +2,15 @@ namespace Zotlabs\Update; -class _1105 { -function run() { - $r = q("ALTER TABLE `site` ADD `site_pull` DATETIME NOT NULL DEFAULT '0001-01-01 00:00:00' AFTER `site_update` , +class _1105 +{ + public function run() + { + $r = q("ALTER TABLE `site` ADD `site_pull` DATETIME NOT NULL DEFAULT '0001-01-01 00:00:00' AFTER `site_update` , CHANGE `site_sync` `site_sync` DATETIME NOT NULL DEFAULT '0001-01-01 00:00:00', ADD INDEX ( `site_pull` ) "); - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1106.php b/Zotlabs/Update/_1106.php index 7e13d8993..2d8adffee 100644 --- a/Zotlabs/Update/_1106.php +++ b/Zotlabs/Update/_1106.php @@ -2,13 +2,14 @@ namespace Zotlabs\Update; -class _1106 { -function run() { - $r = q("ALTER TABLE `notify` CHANGE `parent` `parent` CHAR( 255 ) NOT NULL DEFAULT ''"); - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; +class _1106 +{ + public function run() + { + $r = q("ALTER TABLE `notify` CHANGE `parent` `parent` CHAR( 255 ) NOT NULL DEFAULT ''"); + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1107.php b/Zotlabs/Update/_1107.php index bcac6cf19..37e7f0f00 100644 --- a/Zotlabs/Update/_1107.php +++ b/Zotlabs/Update/_1107.php @@ -2,9 +2,11 @@ namespace Zotlabs\Update; -class _1107 { -function run() { - $r = q("CREATE TABLE IF NOT EXISTS `app` ( +class _1107 +{ + public function run() + { + $r = q("CREATE TABLE IF NOT EXISTS `app` ( `id` int(11) NOT NULL AUTO_INCREMENT, `app_id` char(64) NOT NULL DEFAULT '', `app_sig` char(255) NOT NULL DEFAULT '', @@ -24,11 +26,9 @@ function run() { KEY `app_channel` (`app_channel`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 "); - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1108.php b/Zotlabs/Update/_1108.php index d9d9b0c18..3fe3a7dae 100644 --- a/Zotlabs/Update/_1108.php +++ b/Zotlabs/Update/_1108.php @@ -2,16 +2,17 @@ namespace Zotlabs\Update; -class _1108 { -function run() { - $r = q("ALTER TABLE `app` ADD `app_addr` CHAR( 255 ) NOT NULL DEFAULT '', +class _1108 +{ + public function run() + { + $r = q("ALTER TABLE `app` ADD `app_addr` CHAR( 255 ) NOT NULL DEFAULT '', ADD `app_price` CHAR( 255 ) NOT NULL DEFAULT '', ADD `app_page` CHAR( 255 ) NOT NULL DEFAULT '', ADD INDEX ( `app_price` )"); - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1109.php b/Zotlabs/Update/_1109.php index 6a0aed28f..0f7ea1552 100644 --- a/Zotlabs/Update/_1109.php +++ b/Zotlabs/Update/_1109.php @@ -2,15 +2,16 @@ namespace Zotlabs\Update; -class _1109 { -function run() { - $r = q("ALTER TABLE `app` CHANGE `app_id` `app_id` CHAR( 255 ) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT ''"); - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; -} +class _1109 +{ + public function run() + { + $r = q("ALTER TABLE `app` CHANGE `app_id` `app_id` CHAR( 255 ) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT ''"); + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } // We ended up with an extra zero in the name for 1108, so do it over and ignore the result. - - -} \ No newline at end of file +} diff --git a/Zotlabs/Update/_1110.php b/Zotlabs/Update/_1110.php index 62bcbd0c8..54108a5d2 100644 --- a/Zotlabs/Update/_1110.php +++ b/Zotlabs/Update/_1110.php @@ -2,16 +2,15 @@ namespace Zotlabs\Update; -class _1110 { -function run() { - $r = q("ALTER TABLE `app` ADD `app_addr` CHAR( 255 ) NOT NULL DEFAULT '', +class _1110 +{ + public function run() + { + $r = q("ALTER TABLE `app` ADD `app_addr` CHAR( 255 ) NOT NULL DEFAULT '', ADD `app_price` CHAR( 255 ) NOT NULL DEFAULT '', ADD `app_page` CHAR( 255 ) NOT NULL DEFAULT '', ADD INDEX ( `app_price` )"); - return UPDATE_SUCCESS; - + return UPDATE_SUCCESS; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1111.php b/Zotlabs/Update/_1111.php index a3080b465..35141a8b3 100644 --- a/Zotlabs/Update/_1111.php +++ b/Zotlabs/Update/_1111.php @@ -2,13 +2,14 @@ namespace Zotlabs\Update; -class _1111 { -function run() { - $r = q("ALTER TABLE `app` ADD `app_requires` CHAR( 255 ) NOT NULL DEFAULT '' "); - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; +class _1111 +{ + public function run() + { + $r = q("ALTER TABLE `app` ADD `app_requires` CHAR( 255 ) NOT NULL DEFAULT '' "); + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1112.php b/Zotlabs/Update/_1112.php index e81780519..235bc125f 100644 --- a/Zotlabs/Update/_1112.php +++ b/Zotlabs/Update/_1112.php @@ -2,9 +2,11 @@ namespace Zotlabs\Update; -class _1112 { -function run() { - $r = q("CREATE TABLE IF NOT EXISTS `likes` ( +class _1112 +{ + public function run() + { + $r = q("CREATE TABLE IF NOT EXISTS `likes` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `liker` char(128) NOT NULL DEFAULT '', `likee` char(128) NOT NULL DEFAULT '', @@ -20,10 +22,9 @@ function run() { KEY `target_type` (`target_type`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8"); - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1113.php b/Zotlabs/Update/_1113.php index 0a726dcc3..d456bb222 100644 --- a/Zotlabs/Update/_1113.php +++ b/Zotlabs/Update/_1113.php @@ -2,15 +2,16 @@ namespace Zotlabs\Update; -class _1113 { -function run() { - $r = q("ALTER TABLE `likes` ADD `channel_id` INT UNSIGNED NOT NULL DEFAULT '0' AFTER `id` , +class _1113 +{ + public function run() + { + $r = q("ALTER TABLE `likes` ADD `channel_id` INT UNSIGNED NOT NULL DEFAULT '0' AFTER `id` , CHANGE `iid` `iid` INT( 10 ) UNSIGNED NOT NULL DEFAULT '0', ADD INDEX ( `channel_id` )"); - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1114.php b/Zotlabs/Update/_1114.php index 5b564d513..da9482c14 100644 --- a/Zotlabs/Update/_1114.php +++ b/Zotlabs/Update/_1114.php @@ -2,14 +2,15 @@ namespace Zotlabs\Update; -class _1114 { -function run() { - $r = q("ALTER TABLE `likes` ADD `target_id` CHAR( 128 ) NOT NULL DEFAULT '' AFTER `target_type` , +class _1114 +{ + public function run() + { + $r = q("ALTER TABLE `likes` ADD `target_id` CHAR( 128 ) NOT NULL DEFAULT '' AFTER `target_type` , ADD INDEX ( `target_id` )"); - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1115.php b/Zotlabs/Update/_1115.php index 4edf63b85..af5ae4a2a 100644 --- a/Zotlabs/Update/_1115.php +++ b/Zotlabs/Update/_1115.php @@ -2,15 +2,15 @@ namespace Zotlabs\Update; -class _1115 { -function run() { +class _1115 +{ + public function run() + { - // Introducing email verification. Mark all existing accounts as verified or they - // won't be able to login. + // Introducing email verification. Mark all existing accounts as verified or they + // won't be able to login. - $r = q("update account set account_flags = (account_flags ^ 1) where (account_flags & 1) "); - return UPDATE_SUCCESS; + $r = q("update account set account_flags = (account_flags ^ 1) where (account_flags & 1) "); + return UPDATE_SUCCESS; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1116.php b/Zotlabs/Update/_1116.php index 3d6d30f09..8ae1725a4 100644 --- a/Zotlabs/Update/_1116.php +++ b/Zotlabs/Update/_1116.php @@ -2,11 +2,11 @@ namespace Zotlabs\Update; -class _1116 { -function run() { - @os_mkdir('cache/smarty3',STORAGE_DEFAULT_PERMISSIONS,true); - return UPDATE_SUCCESS; -} - - -} \ No newline at end of file +class _1116 +{ + public function run() + { + @os_mkdir('cache/smarty3', STORAGE_DEFAULT_PERMISSIONS, true); + return UPDATE_SUCCESS; + } +} diff --git a/Zotlabs/Update/_1117.php b/Zotlabs/Update/_1117.php index cc8edb3c9..76437808a 100644 --- a/Zotlabs/Update/_1117.php +++ b/Zotlabs/Update/_1117.php @@ -2,15 +2,15 @@ namespace Zotlabs\Update; -class _1117 { -function run() { - $r = q("ALTER TABLE `channel` CHANGE `channel_a_bookmark` `channel_w_like` INT( 10 ) UNSIGNED NOT NULL DEFAULT '128', +class _1117 +{ + public function run() + { + $r = q("ALTER TABLE `channel` CHANGE `channel_a_bookmark` `channel_w_like` INT( 10 ) UNSIGNED NOT NULL DEFAULT '128', DROP INDEX `channel_a_bookmark` , ADD INDEX `channel_w_like` ( `channel_w_like` ) "); - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; - + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1118.php b/Zotlabs/Update/_1118.php index 22cfc2357..0f40442a5 100644 --- a/Zotlabs/Update/_1118.php +++ b/Zotlabs/Update/_1118.php @@ -2,15 +2,15 @@ namespace Zotlabs\Update; -class _1118 { -function run() { - $r = q("ALTER TABLE `account` ADD `account_password_changed` DATETIME NOT NULL DEFAULT '0001-01-01 00:00:00', +class _1118 +{ + public function run() + { + $r = q("ALTER TABLE `account` ADD `account_password_changed` DATETIME NOT NULL DEFAULT '0001-01-01 00:00:00', ADD INDEX ( `account_password_changed` )"); - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1119.php b/Zotlabs/Update/_1119.php index ccf03e45a..50f925be1 100644 --- a/Zotlabs/Update/_1119.php +++ b/Zotlabs/Update/_1119.php @@ -2,9 +2,11 @@ namespace Zotlabs\Update; -class _1119 { -function run() { - $r1 = q("CREATE TABLE IF NOT EXISTS `profdef` ( +class _1119 +{ + public function run() + { + $r1 = q("CREATE TABLE IF NOT EXISTS `profdef` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `field_name` char(255) NOT NULL DEFAULT '', `field_type` char(16) NOT NULL DEFAULT '', @@ -14,7 +16,7 @@ function run() { KEY `field_name` (`field_name`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8"); - $r2 = q("CREATE TABLE IF NOT EXISTS `profext` ( + $r2 = q("CREATE TABLE IF NOT EXISTS `profext` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `channel_id` int(10) unsigned NOT NULL DEFAULT '0', `hash` char(255) NOT NULL DEFAULT '', @@ -26,10 +28,9 @@ function run() { KEY `k` (`k`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8"); - if($r1 && $r2) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + if ($r1 && $r2) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1120.php b/Zotlabs/Update/_1120.php index c1f7e98d7..1acab424b 100644 --- a/Zotlabs/Update/_1120.php +++ b/Zotlabs/Update/_1120.php @@ -2,15 +2,15 @@ namespace Zotlabs\Update; -class _1120 { -function run() { - $r = q("ALTER TABLE `item` ADD `public_policy` CHAR( 255 ) NOT NULL DEFAULT '' AFTER `coord` , +class _1120 +{ + public function run() + { + $r = q("ALTER TABLE `item` ADD `public_policy` CHAR( 255 ) NOT NULL DEFAULT '' AFTER `coord` , ADD INDEX ( `public_policy` )"); - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1121.php b/Zotlabs/Update/_1121.php index c5ff00694..ae3215858 100644 --- a/Zotlabs/Update/_1121.php +++ b/Zotlabs/Update/_1121.php @@ -2,15 +2,15 @@ namespace Zotlabs\Update; -class _1121 { -function run() { - $r = q("ALTER TABLE `site` ADD `site_realm` CHAR( 255 ) NOT NULL DEFAULT '', +class _1121 +{ + public function run() + { + $r = q("ALTER TABLE `site` ADD `site_realm` CHAR( 255 ) NOT NULL DEFAULT '', ADD INDEX ( `site_realm` )"); - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1122.php b/Zotlabs/Update/_1122.php index 903e3aaeb..36746ded8 100644 --- a/Zotlabs/Update/_1122.php +++ b/Zotlabs/Update/_1122.php @@ -2,15 +2,17 @@ namespace Zotlabs\Update; -class _1122 { -function run() { - $r = q("update site set site_realm = '%s' where true", - dbesc(DIRECTORY_REALM) - ); - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; +class _1122 +{ + public function run() + { + $r = q( + "update site set site_realm = '%s' where true", + dbesc(DIRECTORY_REALM) + ); + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1123.php b/Zotlabs/Update/_1123.php index 96096beac..a5b08618d 100644 --- a/Zotlabs/Update/_1123.php +++ b/Zotlabs/Update/_1123.php @@ -2,16 +2,17 @@ namespace Zotlabs\Update; -class _1123 { -function run() { - $r1 = q("ALTER TABLE `hubloc` ADD `hubloc_network` CHAR( 32 ) NOT NULL DEFAULT '' AFTER `hubloc_addr` , +class _1123 +{ + public function run() + { + $r1 = q("ALTER TABLE `hubloc` ADD `hubloc_network` CHAR( 32 ) NOT NULL DEFAULT '' AFTER `hubloc_addr` , ADD INDEX ( `hubloc_network` )"); - $r2 = q("update hubloc set hubloc_network = 'zot' where true"); + $r2 = q("update hubloc set hubloc_network = 'zot' where true"); - if($r1 && $r2) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + if ($r1 && $r2) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1124.php b/Zotlabs/Update/_1124.php index 1320b8b55..ca3693579 100644 --- a/Zotlabs/Update/_1124.php +++ b/Zotlabs/Update/_1124.php @@ -2,9 +2,11 @@ namespace Zotlabs\Update; -class _1124 { -function run() { - $r1 = q("CREATE TABLE IF NOT EXISTS `sign` ( +class _1124 +{ + public function run() + { + $r1 = q("CREATE TABLE IF NOT EXISTS `sign` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `iid` int(10) unsigned NOT NULL DEFAULT '0', `retract_iid` int(10) unsigned NOT NULL DEFAULT '0', @@ -16,7 +18,7 @@ function run() { KEY `retract_iid` (`retract_iid`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 "); - $r2 = q("CREATE TABLE IF NOT EXISTS `conv` ( + $r2 = q("CREATE TABLE IF NOT EXISTS `conv` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `guid` char(255) NOT NULL, `recips` mediumtext NOT NULL, @@ -30,12 +32,9 @@ function run() { KEY `updated` (`updated`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 "); - if($r1 && $r2) - return UPDATE_SUCCESS; - return UPDATE_FAILED; - - + if ($r1 && $r2) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1125.php b/Zotlabs/Update/_1125.php index 03f9640a4..4881c2f31 100644 --- a/Zotlabs/Update/_1125.php +++ b/Zotlabs/Update/_1125.php @@ -2,15 +2,14 @@ namespace Zotlabs\Update; -class _1125 { -function run() { - $r = q("ALTER TABLE `profdef` ADD `field_inputs` MEDIUMTEXT NOT NULL DEFAULT ''"); - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; - +class _1125 +{ + public function run() + { + $r = q("ALTER TABLE `profdef` ADD `field_inputs` MEDIUMTEXT NOT NULL DEFAULT ''"); + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1126.php b/Zotlabs/Update/_1126.php index f657cc6b2..3a9e40211 100644 --- a/Zotlabs/Update/_1126.php +++ b/Zotlabs/Update/_1126.php @@ -2,15 +2,15 @@ namespace Zotlabs\Update; -class _1126 { -function run() { - $r = q("ALTER TABLE `mail` ADD `convid` INT UNSIGNED NOT NULL DEFAULT '0' AFTER `id` , +class _1126 +{ + public function run() + { + $r = q("ALTER TABLE `mail` ADD `convid` INT UNSIGNED NOT NULL DEFAULT '0' AFTER `id` , ADD INDEX ( `convid` )"); - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; - + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1127.php b/Zotlabs/Update/_1127.php index 8dec64ae4..ff5789f12 100644 --- a/Zotlabs/Update/_1127.php +++ b/Zotlabs/Update/_1127.php @@ -2,15 +2,15 @@ namespace Zotlabs\Update; -class _1127 { -function run() { - $r = q("ALTER TABLE `item` ADD `comments_closed` DATETIME NOT NULL DEFAULT '0001-01-01 00:00:00' AFTER `changed` , +class _1127 +{ + public function run() + { + $r = q("ALTER TABLE `item` ADD `comments_closed` DATETIME NOT NULL DEFAULT '0001-01-01 00:00:00' AFTER `changed` , ADD INDEX ( `comments_closed` ), ADD INDEX ( `changed` ) "); - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; - + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1128.php b/Zotlabs/Update/_1128.php index 8ee6d225e..63fb0de97 100644 --- a/Zotlabs/Update/_1128.php +++ b/Zotlabs/Update/_1128.php @@ -2,14 +2,14 @@ namespace Zotlabs\Update; -class _1128 { -function run() { - $r = q("ALTER TABLE `item` ADD `diaspora_meta` MEDIUMTEXT NOT NULL DEFAULT '' AFTER `sig` "); - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; - +class _1128 +{ + public function run() + { + $r = q("ALTER TABLE `item` ADD `diaspora_meta` MEDIUMTEXT NOT NULL DEFAULT '' AFTER `sig` "); + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1129.php b/Zotlabs/Update/_1129.php index 76bd155ee..f5e13db1f 100644 --- a/Zotlabs/Update/_1129.php +++ b/Zotlabs/Update/_1129.php @@ -2,13 +2,14 @@ namespace Zotlabs\Update; -class _1129 { -function run() { - $r = q("update hubloc set hubloc_network = 'zot' where hubloc_network = ''"); - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; +class _1129 +{ + public function run() + { + $r = q("update hubloc set hubloc_network = 'zot' where hubloc_network = ''"); + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1130.php b/Zotlabs/Update/_1130.php index 1322a0b13..7b1049097 100644 --- a/Zotlabs/Update/_1130.php +++ b/Zotlabs/Update/_1130.php @@ -2,27 +2,29 @@ namespace Zotlabs\Update; -class _1130 { -function run() { - $myperms = PERMS_R_STREAM|PERMS_R_PROFILE|PERMS_R_PHOTOS|PERMS_R_ABOOK - |PERMS_W_STREAM|PERMS_W_WALL|PERMS_W_COMMENT|PERMS_W_MAIL|PERMS_W_CHAT - |PERMS_R_STORAGE|PERMS_R_PAGES|PERMS_W_LIKE; +class _1130 +{ + public function run() + { + $myperms = PERMS_R_STREAM | PERMS_R_PROFILE | PERMS_R_PHOTOS | PERMS_R_ABOOK + | PERMS_W_STREAM | PERMS_W_WALL | PERMS_W_COMMENT | PERMS_W_MAIL | PERMS_W_CHAT + | PERMS_R_STORAGE | PERMS_R_PAGES | PERMS_W_LIKE; - $r = q("select abook_channel, abook_my_perms from abook where (abook_flags & %d) and abook_my_perms != 0", - intval(ABOOK_FLAG_SELF) - ); - if($r) { - foreach($r as $rr) { - set_pconfig($rr['abook_channel'],'system','autoperms',$rr['abook_my_perms']); - } - } - $r = q("update abook set abook_my_perms = %d where (abook_flags & %d) and abook_my_perms = 0", - intval($myperms), - intval(ABOOK_FLAG_SELF) - ); + $r = q( + "select abook_channel, abook_my_perms from abook where (abook_flags & %d) and abook_my_perms != 0", + intval(ABOOK_FLAG_SELF) + ); + if ($r) { + foreach ($r as $rr) { + set_pconfig($rr['abook_channel'], 'system', 'autoperms', $rr['abook_my_perms']); + } + } + $r = q( + "update abook set abook_my_perms = %d where (abook_flags & %d) and abook_my_perms = 0", + intval($myperms), + intval(ABOOK_FLAG_SELF) + ); - return UPDATE_SUCCESS; + return UPDATE_SUCCESS; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1131.php b/Zotlabs/Update/_1131.php index 72e968f67..c2569f8c2 100644 --- a/Zotlabs/Update/_1131.php +++ b/Zotlabs/Update/_1131.php @@ -2,19 +2,20 @@ namespace Zotlabs\Update; -class _1131 { -function run() { - if(ACTIVE_DBTYPE == DBTYPE_POSTGRES) // make sure this gets skipped for anyone who hasn't run it yet, it will fail on pg - return UPDATE_SUCCESS; - - $r1 = q("ALTER TABLE `abook` ADD `abook_rating_text` TEXT NOT NULL DEFAULT '' AFTER `abook_rating` "); - $r2 = q("ALTER TABLE `xlink` ADD `xlink_rating_text` TEXT NOT NULL DEFAULT '' AFTER `xlink_rating` "); +class _1131 +{ + public function run() + { + if (ACTIVE_DBTYPE == DBTYPE_POSTGRES) { // make sure this gets skipped for anyone who hasn't run it yet, it will fail on pg + return UPDATE_SUCCESS; + } - if($r1 && $r2) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + $r1 = q("ALTER TABLE `abook` ADD `abook_rating_text` TEXT NOT NULL DEFAULT '' AFTER `abook_rating` "); + $r2 = q("ALTER TABLE `xlink` ADD `xlink_rating_text` TEXT NOT NULL DEFAULT '' AFTER `xlink_rating` "); + if ($r1 && $r2) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1132.php b/Zotlabs/Update/_1132.php index 9e085a351..bb54c3616 100644 --- a/Zotlabs/Update/_1132.php +++ b/Zotlabs/Update/_1132.php @@ -2,16 +2,17 @@ namespace Zotlabs\Update; -class _1132 { -function run() { - if(ACTIVE_DBTYPE == DBTYPE_POSTGRES) { // correct previous failed update - $r1 = q("ALTER TABLE abook ADD abook_rating_text TEXT NOT NULL DEFAULT '' "); - $r2 = q("ALTER TABLE xlink ADD xlink_rating_text TEXT NOT NULL DEFAULT '' "); - if(!$r1 || !$r2) - return UPDATE_FAILED; - } - return UPDATE_SUCCESS; +class _1132 +{ + public function run() + { + if (ACTIVE_DBTYPE == DBTYPE_POSTGRES) { // correct previous failed update + $r1 = q("ALTER TABLE abook ADD abook_rating_text TEXT NOT NULL DEFAULT '' "); + $r2 = q("ALTER TABLE xlink ADD xlink_rating_text TEXT NOT NULL DEFAULT '' "); + if (!$r1 || !$r2) { + return UPDATE_FAILED; + } + } + return UPDATE_SUCCESS; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1133.php b/Zotlabs/Update/_1133.php index 3820ef1ee..65f7437b5 100644 --- a/Zotlabs/Update/_1133.php +++ b/Zotlabs/Update/_1133.php @@ -2,23 +2,25 @@ namespace Zotlabs\Update; -class _1133 { -function run() { - if(ACTIVE_DBTYPE == DBTYPE_POSTGRES) { - $r1 = q("CREATE TABLE xperm ( +class _1133 +{ + public function run() + { + if (ACTIVE_DBTYPE == DBTYPE_POSTGRES) { + $r1 = q("CREATE TABLE xperm ( xp_id serial NOT NULL, xp_client varchar( 20 ) NOT NULL DEFAULT '', xp_channel bigint NOT NULL DEFAULT '0', xp_perm varchar( 64 ) NOT NULL DEFAULT '', PRIMARY KEY (xp_id) )"); - $r2 = 0; - foreach(array('xp_client', 'xp_channel', 'xp_perm') as $fld) - $r2 += ((q("create index $fld on xperm ($fld)") == false) ? 0 : 1); - - $r = (($r1 && $r2) ? true : false); - } - else { - $r = q("CREATE TABLE IF NOT EXISTS `xperm` ( + $r2 = 0; + foreach (array('xp_client', 'xp_channel', 'xp_perm') as $fld) { + $r2 += ((q("create index $fld on xperm ($fld)") == false) ? 0 : 1); + } + + $r = (($r1 && $r2) ? true : false); + } else { + $r = q("CREATE TABLE IF NOT EXISTS `xperm` ( `xp_id` INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY , `xp_client` VARCHAR( 20 ) NOT NULL DEFAULT '', `xp_channel` INT UNSIGNED NOT NULL DEFAULT '0', @@ -27,12 +29,10 @@ function run() { KEY `xp_channel` (`xp_channel`), KEY `xp_perm` (`xp_perm`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 "); - } - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; - + } + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1134.php b/Zotlabs/Update/_1134.php index d52bab044..2b59de8c9 100644 --- a/Zotlabs/Update/_1134.php +++ b/Zotlabs/Update/_1134.php @@ -2,19 +2,20 @@ namespace Zotlabs\Update; -class _1134 { -function run() { - if(ACTIVE_DBTYPE == DBTYPE_POSTGRES) { - $r1 = q("ALTER TABLE xlink ADD xlink_static numeric(1) NOT NULL DEFAULT '0' "); - $r2 = q("create index xlink_static on xlink ( xlink_static ) "); - $r = $r1 && $r2; - } - else - $r = q("ALTER TABLE xlink ADD xlink_static TINYINT( 1 ) NOT NULL DEFAULT '0', ADD INDEX ( xlink_static ) "); - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; +class _1134 +{ + public function run() + { + if (ACTIVE_DBTYPE == DBTYPE_POSTGRES) { + $r1 = q("ALTER TABLE xlink ADD xlink_static numeric(1) NOT NULL DEFAULT '0' "); + $r2 = q("create index xlink_static on xlink ( xlink_static ) "); + $r = $r1 && $r2; + } else { + $r = q("ALTER TABLE xlink ADD xlink_static TINYINT( 1 ) NOT NULL DEFAULT '0', ADD INDEX ( xlink_static ) "); + } + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1135.php b/Zotlabs/Update/_1135.php index 92a3e04b6..0ebca6f32 100644 --- a/Zotlabs/Update/_1135.php +++ b/Zotlabs/Update/_1135.php @@ -2,13 +2,14 @@ namespace Zotlabs\Update; -class _1135 { -function run() { - $r = q("ALTER TABLE xlink ADD xlink_sig TEXT NOT NULL DEFAULT ''"); - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; +class _1135 +{ + public function run() + { + $r = q("ALTER TABLE xlink ADD xlink_sig TEXT NOT NULL DEFAULT ''"); + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1136.php b/Zotlabs/Update/_1136.php index 0c1691fcb..2e0fd3bac 100644 --- a/Zotlabs/Update/_1136.php +++ b/Zotlabs/Update/_1136.php @@ -2,16 +2,17 @@ namespace Zotlabs\Update; -class _1136 { -function run() { - $r1 = q("alter table item add item_unseen smallint not null default '0' "); - $r2 = q("create index item_unseen on item ( item_unseen ) "); - $r3 = q("update item set item_unseen = 1 where ( item_flags & 2 ) > 0 "); +class _1136 +{ + public function run() + { + $r1 = q("alter table item add item_unseen smallint not null default '0' "); + $r2 = q("create index item_unseen on item ( item_unseen ) "); + $r3 = q("update item set item_unseen = 1 where ( item_flags & 2 ) > 0 "); - if($r1 && $r2 && $r3) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + if ($r1 && $r2 && $r3) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1137.php b/Zotlabs/Update/_1137.php index ab11fe3db..d2ec91c55 100644 --- a/Zotlabs/Update/_1137.php +++ b/Zotlabs/Update/_1137.php @@ -2,15 +2,15 @@ namespace Zotlabs\Update; -class _1137 { -function run() { - $r1 = q("alter table site add site_valid smallint not null default '0' "); - $r2 = q("create index site_valid on site ( site_valid ) "); - if($r1 && $r2) - return UPDATE_SUCCESS; - return UPDATE_FAILED; +class _1137 +{ + public function run() + { + $r1 = q("alter table site add site_valid smallint not null default '0' "); + $r2 = q("create index site_valid on site ( site_valid ) "); + if ($r1 && $r2) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1138.php b/Zotlabs/Update/_1138.php index b2eac12af..60b57aac4 100644 --- a/Zotlabs/Update/_1138.php +++ b/Zotlabs/Update/_1138.php @@ -2,14 +2,15 @@ namespace Zotlabs\Update; -class _1138 { -function run() { - $r1 = q("alter table outq add outq_priority smallint not null default '0' "); - $r2 = q("create index outq_priority on outq ( outq_priority ) "); - if($r1 && $r2) - return UPDATE_SUCCESS; - return UPDATE_FAILED; +class _1138 +{ + public function run() + { + $r1 = q("alter table outq add outq_priority smallint not null default '0' "); + $r2 = q("create index outq_priority on outq ( outq_priority ) "); + if ($r1 && $r2) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1139.php b/Zotlabs/Update/_1139.php index aa4863fc1..e667040c2 100644 --- a/Zotlabs/Update/_1139.php +++ b/Zotlabs/Update/_1139.php @@ -2,20 +2,20 @@ namespace Zotlabs\Update; -class _1139 { -function run() { - if(ACTIVE_DBTYPE == DBTYPE_POSTGRES) { - $r1 = q("ALTER TABLE channel ADD channel_lastpost timestamp NOT NULL DEFAULT '0001-01-01 00:00:00'"); - $r2 = q("create index channel_lastpost on channel ( channel_lastpost ) "); - $r = $r1 && $r2; - } - else - $r = q("ALTER TABLE `channel` ADD `channel_lastpost` DATETIME NOT NULL DEFAULT '0001-01-01 00:00:00' AFTER `channel_dirdate` , ADD INDEX ( `channel_lastpost` ) "); - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; - +class _1139 +{ + public function run() + { + if (ACTIVE_DBTYPE == DBTYPE_POSTGRES) { + $r1 = q("ALTER TABLE channel ADD channel_lastpost timestamp NOT NULL DEFAULT '0001-01-01 00:00:00'"); + $r2 = q("create index channel_lastpost on channel ( channel_lastpost ) "); + $r = $r1 && $r2; + } else { + $r = q("ALTER TABLE `channel` ADD `channel_lastpost` DATETIME NOT NULL DEFAULT '0001-01-01 00:00:00' AFTER `channel_dirdate` , ADD INDEX ( `channel_lastpost` ) "); + } + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1140.php b/Zotlabs/Update/_1140.php index 5b4c8f801..67840fd6e 100644 --- a/Zotlabs/Update/_1140.php +++ b/Zotlabs/Update/_1140.php @@ -2,26 +2,28 @@ namespace Zotlabs\Update; -class _1140 { -function run() { - $r = q("select * from clients where true"); - $x = false; - if($r) { - foreach($r as $rr) { - $m = q("INSERT INTO xperm (xp_client, xp_channel, xp_perm) VALUES ('%s', %d, '%s') ", - dbesc($rr['client_id']), - intval($rr['uid']), - dbesc('all') - ); - if(! $m) - $x = true; - } - } - if($x) - return UPDATE_FAILED; - return UPDATE_SUCCESS; +class _1140 +{ + public function run() + { + $r = q("select * from clients where true"); + $x = false; + if ($r) { + foreach ($r as $rr) { + $m = q( + "INSERT INTO xperm (xp_client, xp_channel, xp_perm) VALUES ('%s', %d, '%s') ", + dbesc($rr['client_id']), + intval($rr['uid']), + dbesc('all') + ); + if (!$m) { + $x = true; + } + } + } + if ($x) { + return UPDATE_FAILED; + } + return UPDATE_SUCCESS; + } } - - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1141.php b/Zotlabs/Update/_1141.php index e1f84f469..5c00756e2 100644 --- a/Zotlabs/Update/_1141.php +++ b/Zotlabs/Update/_1141.php @@ -2,29 +2,30 @@ namespace Zotlabs\Update; -class _1141 { -function run() { - if(ACTIVE_DBTYPE == DBTYPE_POSTGRES) { - $r1 = q("ALTER TABLE menu ADD menu_created timestamp NOT NULL DEFAULT '0001-01-01 00:00:00', ADD menu_edited timestamp NOT NULL DEFAULT '0001-01-01 00:00:00'"); - $r2 = q("create index menu_created on menu ( menu_created ) "); - $r3 = q("create index menu_edited on menu ( menu_edited ) "); - $r = $r1 && $r2; - } - else - $r = q("ALTER TABLE menu ADD menu_created DATETIME NOT NULL DEFAULT '0001-01-01 00:00:00', ADD menu_edited DATETIME NOT NULL DEFAULT '0001-01-01 00:00:00', ADD INDEX ( menu_created ), ADD INDEX ( menu_edited ) "); +class _1141 +{ + public function run() + { + if (ACTIVE_DBTYPE == DBTYPE_POSTGRES) { + $r1 = q("ALTER TABLE menu ADD menu_created timestamp NOT NULL DEFAULT '0001-01-01 00:00:00', ADD menu_edited timestamp NOT NULL DEFAULT '0001-01-01 00:00:00'"); + $r2 = q("create index menu_created on menu ( menu_created ) "); + $r3 = q("create index menu_edited on menu ( menu_edited ) "); + $r = $r1 && $r2; + } else { + $r = q("ALTER TABLE menu ADD menu_created DATETIME NOT NULL DEFAULT '0001-01-01 00:00:00', ADD menu_edited DATETIME NOT NULL DEFAULT '0001-01-01 00:00:00', ADD INDEX ( menu_created ), ADD INDEX ( menu_edited ) "); + } - $t = datetime_convert(); - q("update menu set menu_created = '%s', menu_edited = '%s' where true", - dbesc($t), - dbesc($t) - ); + $t = datetime_convert(); + q( + "update menu set menu_created = '%s', menu_edited = '%s' where true", + dbesc($t), + dbesc($t) + ); - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; - + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1142.php b/Zotlabs/Update/_1142.php index a8ffcc182..84ca4ef7d 100644 --- a/Zotlabs/Update/_1142.php +++ b/Zotlabs/Update/_1142.php @@ -2,17 +2,16 @@ namespace Zotlabs\Update; -class _1142 { -function run() { - - $r1 = q("alter table site add site_dead smallint not null default '0' "); - $r2 = q("create index site_dead on site ( site_dead ) "); - if($r1 && $r2) - return UPDATE_SUCCESS; - return UPDATE_FAILED; - +class _1142 +{ + public function run() + { + $r1 = q("alter table site add site_dead smallint not null default '0' "); + $r2 = q("create index site_dead on site ( site_dead ) "); + if ($r1 && $r2) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1143.php b/Zotlabs/Update/_1143.php index 99f23af61..2dabaadd0 100644 --- a/Zotlabs/Update/_1143.php +++ b/Zotlabs/Update/_1143.php @@ -2,16 +2,16 @@ namespace Zotlabs\Update; -class _1143 { -function run() { - - $r1 = q("ALTER TABLE abook ADD abook_incl TEXT NOT NULL DEFAULT ''"); - $r2 = q("ALTER TABLE abook ADD abook_excl TEXT NOT NULL DEFAULT '' "); - if($r1 && $r2) - return UPDATE_SUCCESS; - return UPDATE_FAILED; +class _1143 +{ + public function run() + { + $r1 = q("ALTER TABLE abook ADD abook_incl TEXT NOT NULL DEFAULT ''"); + $r2 = q("ALTER TABLE abook ADD abook_excl TEXT NOT NULL DEFAULT '' "); + if ($r1 && $r2) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1144.php b/Zotlabs/Update/_1144.php index 848897de7..b8730bb0e 100644 --- a/Zotlabs/Update/_1144.php +++ b/Zotlabs/Update/_1144.php @@ -2,26 +2,28 @@ namespace Zotlabs\Update; -class _1144 { -function run() { - $r = q("select flags, id from attach where flags != 0"); - if($r) { - foreach($r as $rr) { - if($rr['flags'] & 1) { - q("update attach set is_dir = 1 where id = %d", - intval($rr['id']) - ); - } - if($rr['flags'] & 2) { - q("update attach set os_storage = 1 where id = %d", - intval($rr['id']) - ); - } - } - } +class _1144 +{ + public function run() + { + $r = q("select flags, id from attach where flags != 0"); + if ($r) { + foreach ($r as $rr) { + if ($rr['flags'] & 1) { + q( + "update attach set is_dir = 1 where id = %d", + intval($rr['id']) + ); + } + if ($rr['flags'] & 2) { + q( + "update attach set os_storage = 1 where id = %d", + intval($rr['id']) + ); + } + } + } - return UPDATE_SUCCESS; + return UPDATE_SUCCESS; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1145.php b/Zotlabs/Update/_1145.php index 5102acc8a..54c2a55e1 100644 --- a/Zotlabs/Update/_1145.php +++ b/Zotlabs/Update/_1145.php @@ -2,29 +2,28 @@ namespace Zotlabs\Update; -class _1145 { -function run() { +class _1145 +{ + public function run() + { - if(ACTIVE_DBTYPE == DBTYPE_POSTGRES) { - $r1 = q("ALTER TABLE event ADD event_status char(255) NOT NULL DEFAULT '', + if (ACTIVE_DBTYPE == DBTYPE_POSTGRES) { + $r1 = q("ALTER TABLE event ADD event_status char(255) NOT NULL DEFAULT '', ADD event_status_date timestamp NOT NULL DEFAULT '0001-01-01 00:00:00', ADD event_percent SMALLINT NOT NULL DEFAULT '0', ADD event_repeat TEXT NOT NULL DEFAULT '' "); - $r2 = q("create index event_status on event ( event_status )"); - $r = $r1 && $r2; - } - else { - $r = q("ALTER TABLE `event` ADD `event_status` CHAR( 255 ) NOT NULL DEFAULT '', + $r2 = q("create index event_status on event ( event_status )"); + $r = $r1 && $r2; + } else { + $r = q("ALTER TABLE `event` ADD `event_status` CHAR( 255 ) NOT NULL DEFAULT '', ADD `event_status_date` DATETIME NOT NULL DEFAULT '0001-01-01 00:00:00', ADD `event_percent` SMALLINT NOT NULL DEFAULT '0', ADD `event_repeat` TEXT NOT NULL DEFAULT '', ADD INDEX ( `event_status` ) "); - } - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; - + } + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1146.php b/Zotlabs/Update/_1146.php index 92a26adaf..e61f2db81 100644 --- a/Zotlabs/Update/_1146.php +++ b/Zotlabs/Update/_1146.php @@ -2,15 +2,16 @@ namespace Zotlabs\Update; -class _1146 { -function run() { +class _1146 +{ + public function run() + { - $r1 = q("alter table event add event_sequence smallint not null default '0' "); - $r2 = q("create index event_sequence on event ( event_sequence ) "); - if($r1 && $r2) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + $r1 = q("alter table event add event_sequence smallint not null default '0' "); + $r2 = q("create index event_sequence on event ( event_sequence ) "); + if ($r1 && $r2) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1147.php b/Zotlabs/Update/_1147.php index b8f6716f7..62c90e717 100644 --- a/Zotlabs/Update/_1147.php +++ b/Zotlabs/Update/_1147.php @@ -2,15 +2,16 @@ namespace Zotlabs\Update; -class _1147 { -function run() { +class _1147 +{ + public function run() + { - $r1 = q("alter table event add event_priority smallint not null default '0' "); - $r2 = q("create index event_priority on event ( event_priority ) "); - if($r1 && $r2) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + $r1 = q("alter table event add event_priority smallint not null default '0' "); + $r2 = q("create index event_priority on event ( event_priority ) "); + if ($r1 && $r2) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1148.php b/Zotlabs/Update/_1148.php index ed9a4d93a..475150609 100644 --- a/Zotlabs/Update/_1148.php +++ b/Zotlabs/Update/_1148.php @@ -2,16 +2,16 @@ namespace Zotlabs\Update; -class _1148 { -function run() { - $r1 = q("alter table likes add i_mid char(255) not null default '' "); - $r2 = q("create index i_mid on likes ( i_mid ) "); - - if($r1 && $r2) - return UPDATE_SUCCESS; - return UPDATE_FAILED; +class _1148 +{ + public function run() + { + $r1 = q("alter table likes add i_mid char(255) not null default '' "); + $r2 = q("create index i_mid on likes ( i_mid ) "); + if ($r1 && $r2) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1149.php b/Zotlabs/Update/_1149.php index f45bd995b..c3c265253 100644 --- a/Zotlabs/Update/_1149.php +++ b/Zotlabs/Update/_1149.php @@ -2,34 +2,33 @@ namespace Zotlabs\Update; -class _1149 { -function run() { - if(ACTIVE_DBTYPE == DBTYPE_POSTGRES) { - $r1 = q("ALTER TABLE obj ADD obj_term CHAR( 255 ) NOT NULL DEFAULT '', +class _1149 +{ + public function run() + { + if (ACTIVE_DBTYPE == DBTYPE_POSTGRES) { + $r1 = q("ALTER TABLE obj ADD obj_term CHAR( 255 ) NOT NULL DEFAULT '', ADD obj_url CHAR( 255 ) NOT NULL DEFAULT '', ADD obj_imgurl CHAR( 255 ) NOT NULL DEFAULT '', ADD obj_created timestamp NOT NULL DEFAULT '0001-01-01 00:00:00', ADD obj_edited timestamp NOT NULL DEFAULT '0001-01-01 00:00:00' "); - } - else { - $r1 = q("ALTER TABLE obj ADD obj_term CHAR( 255 ) NOT NULL DEFAULT '', + } else { + $r1 = q("ALTER TABLE obj ADD obj_term CHAR( 255 ) NOT NULL DEFAULT '', ADD obj_url CHAR( 255 ) NOT NULL DEFAULT '', ADD obj_imgurl CHAR( 255 ) NOT NULL DEFAULT '', ADD obj_created DATETIME NOT NULL DEFAULT '0001-01-01 00:00:00', ADD obj_edited DATETIME NOT NULL DEFAULT '0001-01-01 00:00:00' "); - } - - $r2 = q("create index obj_term on obj ( obj_term ) "); - $r3 = q("create index obj_url on obj ( obj_url ) "); - $r4 = q("create index obj_imgurl on obj ( obj_imgurl ) "); - $r5 = q("create index obj_created on obj ( obj_created ) "); - $r6 = q("create index obj_edited on obj ( obj_edited ) "); - $r = $r1 && $r2 && $r3 && $r4 && $r5 && $r6; - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + } + $r2 = q("create index obj_term on obj ( obj_term ) "); + $r3 = q("create index obj_url on obj ( obj_url ) "); + $r4 = q("create index obj_imgurl on obj ( obj_imgurl ) "); + $r5 = q("create index obj_created on obj ( obj_created ) "); + $r6 = q("create index obj_edited on obj ( obj_edited ) "); + $r = $r1 && $r2 && $r3 && $r4 && $r5 && $r6; + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1150.php b/Zotlabs/Update/_1150.php index 896fc75e3..a45bf48f2 100644 --- a/Zotlabs/Update/_1150.php +++ b/Zotlabs/Update/_1150.php @@ -2,27 +2,26 @@ namespace Zotlabs\Update; -class _1150 { -function run() { +class _1150 +{ + public function run() + { - if(ACTIVE_DBTYPE == DBTYPE_POSTGRES) { - $r1 = q("ALTER TABLE app ADD app_created timestamp NOT NULL DEFAULT '0001-01-01 00:00:00', + if (ACTIVE_DBTYPE == DBTYPE_POSTGRES) { + $r1 = q("ALTER TABLE app ADD app_created timestamp NOT NULL DEFAULT '0001-01-01 00:00:00', ADD app_edited timestamp NOT NULL DEFAULT '0001-01-01 00:00:00' "); - } - else { - $r1 = q("ALTER TABLE app ADD app_created DATETIME NOT NULL DEFAULT '0001-01-01 00:00:00', + } else { + $r1 = q("ALTER TABLE app ADD app_created DATETIME NOT NULL DEFAULT '0001-01-01 00:00:00', ADD app_edited DATETIME NOT NULL DEFAULT '0001-01-01 00:00:00' "); - } + } - $r2 = q("create index app_created on app ( app_created ) "); - $r3 = q("create index app_edited on app ( app_edited ) "); - - $r = $r1 && $r2 && $r3; - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + $r2 = q("create index app_created on app ( app_created ) "); + $r3 = q("create index app_edited on app ( app_edited ) "); + $r = $r1 && $r2 && $r3; + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1151.php b/Zotlabs/Update/_1151.php index d14baabb1..57c153428 100644 --- a/Zotlabs/Update/_1151.php +++ b/Zotlabs/Update/_1151.php @@ -2,23 +2,23 @@ namespace Zotlabs\Update; -class _1151 { -function run() { +class _1151 +{ + public function run() + { - $r3 = q("select likes.*, item.mid from likes left join item on likes.iid = item.id"); - if($r3) { - foreach($r3 as $rr) { - q("update likes set i_mid = '%s' where id = $d", - dbesc($rr['mid']), - intval($rr['id']) - ); - } - } + $r3 = q("select likes.*, item.mid from likes left join item on likes.iid = item.id"); + if ($r3) { + foreach ($r3 as $rr) { + q( + "update likes set i_mid = '%s' where id = $d", + dbesc($rr['mid']), + intval($rr['id']) + ); + } + } - return UPDATE_SUCCESS; - + return UPDATE_SUCCESS; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1152.php b/Zotlabs/Update/_1152.php index 54393d590..34e3486f5 100644 --- a/Zotlabs/Update/_1152.php +++ b/Zotlabs/Update/_1152.php @@ -2,12 +2,13 @@ namespace Zotlabs\Update; -class _1152 { -function run() { +class _1152 +{ + public function run() + { - if(ACTIVE_DBTYPE == DBTYPE_POSTGRES) { - - $r1 = q("CREATE TABLE IF NOT EXISTS \"dreport\" ( + if (ACTIVE_DBTYPE == DBTYPE_POSTGRES) { + $r1 = q("CREATE TABLE IF NOT EXISTS \"dreport\" ( \"dreport_id\" serial NOT NULL, \"dreport_channel\" int(11) NOT NULL DEFAULT '0', \"dreport_mid\" char(255) NOT NULL DEFAULT '', @@ -18,17 +19,15 @@ function run() { \"dreport_xchan\" char(255) NOT NULL DEFAULT '', PRIMARY KEY (\"dreport_id\") "); - $r2 = q("create index \"dreport_mid\" on dreport (\"dreport_mid\") "); - $r3 = q("create index \"dreport_site\" on dreport (\"dreport_site\") "); - $r4 = q("create index \"dreport_time\" on dreport (\"dreport_time\") "); - $r5 = q("create index \"dreport_xchan\" on dreport (\"dreport_xchan\") "); - $r6 = q("create index \"dreport_channel\" on dreport (\"dreport_channel\") "); + $r2 = q("create index \"dreport_mid\" on dreport (\"dreport_mid\") "); + $r3 = q("create index \"dreport_site\" on dreport (\"dreport_site\") "); + $r4 = q("create index \"dreport_time\" on dreport (\"dreport_time\") "); + $r5 = q("create index \"dreport_xchan\" on dreport (\"dreport_xchan\") "); + $r6 = q("create index \"dreport_channel\" on dreport (\"dreport_channel\") "); - $r = $r1 && $r2 && $r3 && $r4 && $r5 && $r6; - - } - else { - $r = q("CREATE TABLE IF NOT EXISTS `dreport` ( + $r = $r1 && $r2 && $r3 && $r4 && $r5 && $r6; + } else { + $r = q("CREATE TABLE IF NOT EXISTS `dreport` ( `dreport_id` int(11) NOT NULL AUTO_INCREMENT, `dreport_channel` int(11) NOT NULL DEFAULT '0', `dreport_mid` char(255) NOT NULL DEFAULT '', @@ -44,14 +43,11 @@ function run() { KEY `dreport_xchan` (`dreport_xchan`), KEY `dreport_channel` (`dreport_channel`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 "); + } - } - - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; - + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1153.php b/Zotlabs/Update/_1153.php index cf8d451b4..f913f3154 100644 --- a/Zotlabs/Update/_1153.php +++ b/Zotlabs/Update/_1153.php @@ -2,17 +2,16 @@ namespace Zotlabs\Update; -class _1153 { -function run() { - - $r1 = q("ALTER TABLE dreport ADD dreport_queue CHAR( 255 ) NOT NULL DEFAULT '' "); - $r2 = q("create index dreport_queue on dreport ( dreport_queue) "); - if($r1 && $r2) - return UPDATE_SUCCESS; - return UPDATE_FAILED; - +class _1153 +{ + public function run() + { + $r1 = q("ALTER TABLE dreport ADD dreport_queue CHAR( 255 ) NOT NULL DEFAULT '' "); + $r2 = q("create index dreport_queue on dreport ( dreport_queue) "); + if ($r1 && $r2) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1154.php b/Zotlabs/Update/_1154.php index 6e92f525d..b69985191 100644 --- a/Zotlabs/Update/_1154.php +++ b/Zotlabs/Update/_1154.php @@ -2,16 +2,15 @@ namespace Zotlabs\Update; -class _1154 { -function run() { - - $r = q("ALTER TABLE event ADD event_vdata text NOT NULL "); - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; +class _1154 +{ + public function run() + { + $r = q("ALTER TABLE event ADD event_vdata text NOT NULL "); + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1155.php b/Zotlabs/Update/_1155.php index 8f2553b3e..dded454ce 100644 --- a/Zotlabs/Update/_1155.php +++ b/Zotlabs/Update/_1155.php @@ -2,16 +2,16 @@ namespace Zotlabs\Update; -class _1155 { -function run() { +class _1155 +{ + public function run() + { - $r1 = q("alter table site add site_type smallint not null default '0' "); - $r2 = q("create index site_type on site ( site_type ) "); - if($r1 && $r2) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + $r1 = q("alter table site add site_type smallint not null default '0' "); + $r2 = q("create index site_type on site ( site_type ) "); + if ($r1 && $r2) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1156.php b/Zotlabs/Update/_1156.php index 536a219d8..b748876a2 100644 --- a/Zotlabs/Update/_1156.php +++ b/Zotlabs/Update/_1156.php @@ -2,27 +2,29 @@ namespace Zotlabs\Update; -class _1156 { -function run() { - $r1 = q("ALTER TABLE mail ADD conv_guid CHAR( 255 ) NOT NULL DEFAULT '' "); - $r2 = q("create index conv_guid on mail ( conv_guid ) "); +class _1156 +{ + public function run() + { + $r1 = q("ALTER TABLE mail ADD conv_guid CHAR( 255 ) NOT NULL DEFAULT '' "); + $r2 = q("create index conv_guid on mail ( conv_guid ) "); - $r3 = q("select mail.id, mail.convid, conv.guid from mail left join conv on mail.convid = conv.id where true"); - if($r3) { - foreach($r3 as $rr) { - if($rr['convid']) { - q("update mail set conv_guid = '%s' where id = %d", - dbesc($rr['guid']), - intval($rr['id']) - ); - } - } - } - - if($r1 && $r2) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + $r3 = q("select mail.id, mail.convid, conv.guid from mail left join conv on mail.convid = conv.id where true"); + if ($r3) { + foreach ($r3 as $rr) { + if ($rr['convid']) { + q( + "update mail set conv_guid = '%s' where id = %d", + dbesc($rr['guid']), + intval($rr['id']) + ); + } + } + } + + if ($r1 && $r2) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1157.php b/Zotlabs/Update/_1157.php index 01f452f32..8715f31f0 100644 --- a/Zotlabs/Update/_1157.php +++ b/Zotlabs/Update/_1157.php @@ -2,16 +2,15 @@ namespace Zotlabs\Update; -class _1157 { -function run() { - $r1 = q("alter table site add site_project char(255) not null default '' "); - $r2 = q("create index site_project on site ( site_project ) "); - if($r1 && $r2) - return UPDATE_SUCCESS; - return UPDATE_FAILED; - +class _1157 +{ + public function run() + { + $r1 = q("alter table site add site_project char(255) not null default '' "); + $r2 = q("create index site_project on site ( site_project ) "); + if ($r1 && $r2) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1158.php b/Zotlabs/Update/_1158.php index 89ac3daa1..0903a2722 100644 --- a/Zotlabs/Update/_1158.php +++ b/Zotlabs/Update/_1158.php @@ -2,21 +2,21 @@ namespace Zotlabs\Update; -class _1158 { -function run() { - $r = q("select attach.id, attach.data, channel_address from attach left join channel on attach.uid = channel_id where os_storage = 1 and not attach.data like '%%store%%' "); - if($r) { - foreach($r as $rr) { - $has_slash = ((substr($rr['data'],0,1) === '/') ? true : false); - q("update attach set data = '%s' where id = %d", - dbesc('store/' . $rr['channel_address']. (($has_slash) ? '' : '/' . $rr['data'])), - dbesc($rr['id']) - ); - } - } - return UPDATE_SUCCESS; +class _1158 +{ + public function run() + { + $r = q("select attach.id, attach.data, channel_address from attach left join channel on attach.uid = channel_id where os_storage = 1 and not attach.data like '%%store%%' "); + if ($r) { + foreach ($r as $rr) { + $has_slash = ((substr($rr['data'], 0, 1) === '/') ? true : false); + q( + "update attach set data = '%s' where id = %d", + dbesc('store/' . $rr['channel_address'] . (($has_slash) ? '' : '/' . $rr['data'])), + dbesc($rr['id']) + ); + } + } + return UPDATE_SUCCESS; + } } - - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1159.php b/Zotlabs/Update/_1159.php index 1445092ad..25e3a68f5 100644 --- a/Zotlabs/Update/_1159.php +++ b/Zotlabs/Update/_1159.php @@ -2,24 +2,24 @@ namespace Zotlabs\Update; -class _1159 { -function run() { - $r = q("select attach.id, attach.data, attach.hash, channel_address from attach left join channel on attach.uid = channel_id where os_storage = 1 "); - if($r) { - foreach($r as $rr) { - $x = dbunescbin($rr['data']); - $has_slash = (($x === 'store/' . $rr['channel_address'] . '/') ? true : false); - if(($x === 'store/' . $rr['channel_address']) || ($has_slash)) { - q("update attach set data = '%s' where id = %d", - dbesc('store/' . $rr['channel_address']. (($has_slash) ? '' : '/' . $rr['hash'])), - dbesc($rr['id']) - ); - } - } - } - return UPDATE_SUCCESS; +class _1159 +{ + public function run() + { + $r = q("select attach.id, attach.data, attach.hash, channel_address from attach left join channel on attach.uid = channel_id where os_storage = 1 "); + if ($r) { + foreach ($r as $rr) { + $x = dbunescbin($rr['data']); + $has_slash = (($x === 'store/' . $rr['channel_address'] . '/') ? true : false); + if (($x === 'store/' . $rr['channel_address']) || ($has_slash)) { + q( + "update attach set data = '%s' where id = %d", + dbesc('store/' . $rr['channel_address'] . (($has_slash) ? '' : '/' . $rr['hash'])), + dbesc($rr['id']) + ); + } + } + } + return UPDATE_SUCCESS; + } } - - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1160.php b/Zotlabs/Update/_1160.php index b7b67076f..9e61ec5c3 100644 --- a/Zotlabs/Update/_1160.php +++ b/Zotlabs/Update/_1160.php @@ -2,13 +2,14 @@ namespace Zotlabs\Update; -class _1160 { -function run() { - $r = q("alter table abook add abook_instance text not null default '' "); - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; +class _1160 +{ + public function run() + { + $r = q("alter table abook add abook_instance text not null default '' "); + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1161.php b/Zotlabs/Update/_1161.php index 870eb7ed6..465e7e24d 100644 --- a/Zotlabs/Update/_1161.php +++ b/Zotlabs/Update/_1161.php @@ -2,11 +2,13 @@ namespace Zotlabs\Update; -class _1161 { -function run() { +class _1161 +{ + public function run() + { - if(ACTIVE_DBTYPE == DBTYPE_POSTGRES) { - $r1 = q("CREATE TABLE \"iconfig\" ( + if (ACTIVE_DBTYPE == DBTYPE_POSTGRES) { + $r1 = q("CREATE TABLE \"iconfig\" ( \"id\" serial NOT NULL, \"iid\" bigint NOT NULL DEFAULT '0', \"cat\" text NOT NULL DEFAULT '', @@ -14,13 +16,12 @@ function run() { \"v\" text NOT NULL DEFAULT '', PRIMARY_KEY(\"id\") ) "); -$r2 = q("create index \"iconfig_iid\" on iconfig (\"iid\") ");; -$r3 = q("create index \"iconfig_cat\" on iconfig (\"cat\") "); -$r4 = q("create index \"iconfig_k\" on iconfig (\"k\") "); - $r = $r1 && $r2 && $r3 && $r4; - } - else { - $r = q("CREATE TABLE IF NOT EXISTS `iconfig` ( + $r2 = q("create index \"iconfig_iid\" on iconfig (\"iid\") "); + $r3 = q("create index \"iconfig_cat\" on iconfig (\"cat\") "); + $r4 = q("create index \"iconfig_k\" on iconfig (\"k\") "); + $r = $r1 && $r2 && $r3 && $r4; + } else { + $r = q("CREATE TABLE IF NOT EXISTS `iconfig` ( `id` int(11) NOT NULL AUTO_INCREMENT, `iid` int(11) NOT NULL DEFAULT '0', `cat` char(255) NOT NULL DEFAULT '', @@ -31,13 +32,11 @@ $r4 = q("create index \"iconfig_k\" on iconfig (\"k\") "); KEY `cat` (`cat`), KEY `k` (`k`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 "); + } - } - - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1162.php b/Zotlabs/Update/_1162.php index 683ea9802..231acecef 100644 --- a/Zotlabs/Update/_1162.php +++ b/Zotlabs/Update/_1162.php @@ -2,18 +2,20 @@ namespace Zotlabs\Update; -class _1162 { -function run() { - $r1 = q("alter table iconfig add sharing int not null default '0' "); +class _1162 +{ + public function run() + { + $r1 = q("alter table iconfig add sharing int not null default '0' "); - if(ACTIVE_DBTYPE == DBTYPE_POSTGRES) - $r2 = q("create index \"iconfig_sharing\" on iconfig (\"sharing\") "); - else - $r2 = q("alter table iconfig add index ( sharing ) "); - if($r1 && $r2) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + if (ACTIVE_DBTYPE == DBTYPE_POSTGRES) { + $r2 = q("create index \"iconfig_sharing\" on iconfig (\"sharing\") "); + } else { + $r2 = q("alter table iconfig add index ( sharing ) "); + } + if ($r1 && $r2) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1163.php b/Zotlabs/Update/_1163.php index 376447aea..45dc471cd 100644 --- a/Zotlabs/Update/_1163.php +++ b/Zotlabs/Update/_1163.php @@ -2,21 +2,21 @@ namespace Zotlabs\Update; -class _1163 { -function run() { +class _1163 +{ + public function run() + { - if(ACTIVE_DBTYPE == DBTYPE_POSTGRES) { - $r1 = q("alter table channel add channel_moved text not null default '' "); - $r2 = q("create index \"channel_channel_moved\" on channel (\"channel_moved\") "); - } - else { - $r1 = q("alter table channel add channel_moved char(255) not null default '' "); - $r2 = q("alter table channel add index ( channel_moved ) "); - } - if($r1 && $r2) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + if (ACTIVE_DBTYPE == DBTYPE_POSTGRES) { + $r1 = q("alter table channel add channel_moved text not null default '' "); + $r2 = q("create index \"channel_channel_moved\" on channel (\"channel_moved\") "); + } else { + $r1 = q("alter table channel add channel_moved char(255) not null default '' "); + $r2 = q("alter table channel add index ( channel_moved ) "); + } + if ($r1 && $r2) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1164.php b/Zotlabs/Update/_1164.php index bad9193d1..8d8f2071d 100644 --- a/Zotlabs/Update/_1164.php +++ b/Zotlabs/Update/_1164.php @@ -2,11 +2,13 @@ namespace Zotlabs\Update; -class _1164 { -function run() { +class _1164 +{ + public function run() + { - if(ACTIVE_DBTYPE == DBTYPE_POSTGRES) { - $r1 = q("CREATE TABLE \"abconfig\" ( + if (ACTIVE_DBTYPE == DBTYPE_POSTGRES) { + $r1 = q("CREATE TABLE \"abconfig\" ( \"id\" serial NOT NULL, \"chan\" text NOT NULL, \"xchan\" text NOT NULL, @@ -14,14 +16,13 @@ function run() { \"k\" text NOT NULL, \"v\" text NOT NULL, PRIMARY KEY (\"id\") "); - $r2 = q("create index \"abconfig_chan\" on abconfig (\"chan\") "); - $r3 = q("create index \"abconfig_xchan\" on abconfig (\"xchan\") "); - $r4 = q("create index \"abconfig_cat\" on abconfig (\"cat\") "); - $r5 = q("create index \"abconfig_k\" on abconfig (\"k\") "); - $r = $r1 && $r2 && $r3 && $r4 && $r5; - } - else { - $r = q("CREATE TABLE IF NOT EXISTS `abconfig` ( + $r2 = q("create index \"abconfig_chan\" on abconfig (\"chan\") "); + $r3 = q("create index \"abconfig_xchan\" on abconfig (\"xchan\") "); + $r4 = q("create index \"abconfig_cat\" on abconfig (\"cat\") "); + $r5 = q("create index \"abconfig_k\" on abconfig (\"k\") "); + $r = $r1 && $r2 && $r3 && $r4 && $r5; + } else { + $r = q("CREATE TABLE IF NOT EXISTS `abconfig` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `chan` char(255) NOT NULL DEFAULT '', `xchan` char(255) NOT NULL DEFAULT '', @@ -34,12 +35,10 @@ function run() { KEY `cat` (`cat`), KEY `k` (`k`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 "); - - } - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + } + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1165.php b/Zotlabs/Update/_1165.php index fcea38dc8..bf8a144e2 100644 --- a/Zotlabs/Update/_1165.php +++ b/Zotlabs/Update/_1165.php @@ -2,19 +2,21 @@ namespace Zotlabs\Update; -class _1165 { -function run() { +class _1165 +{ + public function run() + { - $r1 = q("alter table hook add hook_version int not null default '0' "); + $r1 = q("alter table hook add hook_version int not null default '0' "); - if(ACTIVE_DBTYPE == DBTYPE_POSTGRES) - $r2 = q("create index \"hook_version_idx\" on hook (\"hook_version\") "); - else - $r2 = q("alter table hook add index ( hook_version ) "); - if($r1 && $r2) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + if (ACTIVE_DBTYPE == DBTYPE_POSTGRES) { + $r2 = q("create index \"hook_version_idx\" on hook (\"hook_version\") "); + } else { + $r2 = q("alter table hook add index ( hook_version ) "); + } + if ($r1 && $r2) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1166.php b/Zotlabs/Update/_1166.php index e320d5bc0..b0a88ddfb 100644 --- a/Zotlabs/Update/_1166.php +++ b/Zotlabs/Update/_1166.php @@ -2,14 +2,15 @@ namespace Zotlabs\Update; -class _1166 { -function run() { +class _1166 +{ + public function run() + { - $r = q("alter table source add src_tag text not null default '' "); - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + $r = q("alter table source add src_tag text not null default '' "); + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1167.php b/Zotlabs/Update/_1167.php index b5510a21f..2c8e311da 100644 --- a/Zotlabs/Update/_1167.php +++ b/Zotlabs/Update/_1167.php @@ -2,25 +2,25 @@ namespace Zotlabs\Update; -class _1167 { -function run() { +class _1167 +{ + public function run() + { - $r1 = q("alter table app add app_deleted int not null default '0' "); - $r2 = q("alter table app add app_system int not null default '0' "); + $r1 = q("alter table app add app_deleted int not null default '0' "); + $r2 = q("alter table app add app_system int not null default '0' "); - if(ACTIVE_DBTYPE == DBTYPE_POSTGRES) { - $r3 = q("create index \"app_deleted_idx\" on app (\"app_deleted\") "); - $r4 = q("create index \"app_system_idx\" on app (\"app_system\") "); - } - else { - $r3 = q("alter table app add index ( app_deleted ) "); - $r4 = q("alter table app add index ( app_system ) "); - } + if (ACTIVE_DBTYPE == DBTYPE_POSTGRES) { + $r3 = q("create index \"app_deleted_idx\" on app (\"app_deleted\") "); + $r4 = q("create index \"app_system_idx\" on app (\"app_system\") "); + } else { + $r3 = q("alter table app add index ( app_deleted ) "); + $r4 = q("alter table app add index ( app_system ) "); + } - if($r1 && $r2 && $r3 && $r4) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + if ($r1 && $r2 && $r3 && $r4) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1168.php b/Zotlabs/Update/_1168.php index cf7d2689a..941c5f94f 100644 --- a/Zotlabs/Update/_1168.php +++ b/Zotlabs/Update/_1168.php @@ -2,22 +2,22 @@ namespace Zotlabs\Update; -class _1168 { -function run() { +class _1168 +{ + public function run() + { - $r1 = q("alter table obj add obj_quantity int not null default '0' "); + $r1 = q("alter table obj add obj_quantity int not null default '0' "); - if(ACTIVE_DBTYPE == DBTYPE_POSTGRES) { - $r2 = q("create index \"obj_quantity_idx\" on obj (\"obj_quantity\") "); - } - else { - $r2 = q("alter table obj add index ( obj_quantity ) "); - } + if (ACTIVE_DBTYPE == DBTYPE_POSTGRES) { + $r2 = q("create index \"obj_quantity_idx\" on obj (\"obj_quantity\") "); + } else { + $r2 = q("alter table obj add index ( obj_quantity ) "); + } - if($r1 && $r2) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + if ($r1 && $r2) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1169.php b/Zotlabs/Update/_1169.php index cfe4dbcd8..b8bdcb32a 100644 --- a/Zotlabs/Update/_1169.php +++ b/Zotlabs/Update/_1169.php @@ -2,26 +2,24 @@ namespace Zotlabs\Update; -class _1169 { -function run() { +class _1169 +{ + public function run() + { - if(ACTIVE_DBTYPE == DBTYPE_POSTGRES) { - $r1 = q("ALTER TABLE `addon` CHANGE `timestamp` `tstamp` numeric( 20 ) UNSIGNED NOT NULL DEFAULT '0' "); - $r2 = q("ALTER TABLE `addon` CHANGE `name` `aname` text NOT NULL DEFAULT '' "); - $r3 = q("ALTER TABLE `hook` CHANGE `function` `fn` text NOT NULL DEFAULT '' "); + if (ACTIVE_DBTYPE == DBTYPE_POSTGRES) { + $r1 = q("ALTER TABLE `addon` CHANGE `timestamp` `tstamp` numeric( 20 ) UNSIGNED NOT NULL DEFAULT '0' "); + $r2 = q("ALTER TABLE `addon` CHANGE `name` `aname` text NOT NULL DEFAULT '' "); + $r3 = q("ALTER TABLE `hook` CHANGE `function` `fn` text NOT NULL DEFAULT '' "); + } else { + $r1 = q("ALTER TABLE `addon` CHANGE `timestamp` `tstamp` BIGINT( 20 ) UNSIGNED NOT NULL DEFAULT '0' "); + $r2 = q("ALTER TABLE `addon` CHANGE `name` `aname` CHAR(255) NOT NULL DEFAULT '' "); + $r3 = q("ALTER TABLE `hook` CHANGE `function` `fn` CHAR(255) NOT NULL DEFAULT '' "); + } - } - else { - $r1 = q("ALTER TABLE `addon` CHANGE `timestamp` `tstamp` BIGINT( 20 ) UNSIGNED NOT NULL DEFAULT '0' "); - $r2 = q("ALTER TABLE `addon` CHANGE `name` `aname` CHAR(255) NOT NULL DEFAULT '' "); - $r3 = q("ALTER TABLE `hook` CHANGE `function` `fn` CHAR(255) NOT NULL DEFAULT '' "); - } - - if($r1 && $r2 && $r3) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + if ($r1 && $r2 && $r3) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1170.php b/Zotlabs/Update/_1170.php index 215aeebc7..324ac8339 100644 --- a/Zotlabs/Update/_1170.php +++ b/Zotlabs/Update/_1170.php @@ -2,20 +2,20 @@ namespace Zotlabs\Update; -class _1170 { -function run() { +class _1170 +{ + public function run() + { - $r1 = q("drop table fcontact"); - $r2 = q("drop table ffinder"); - $r3 = q("drop table fserver"); - $r4 = q("drop table fsuggest"); - $r5 = q("drop table spam"); - - if($r1 && $r2 && $r3 && $r4 && $r5) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + $r1 = q("drop table fcontact"); + $r2 = q("drop table ffinder"); + $r3 = q("drop table fserver"); + $r4 = q("drop table fsuggest"); + $r5 = q("drop table spam"); + if ($r1 && $r2 && $r3 && $r4 && $r5) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1171.php b/Zotlabs/Update/_1171.php index 5cbf0ca21..0035f773a 100644 --- a/Zotlabs/Update/_1171.php +++ b/Zotlabs/Update/_1171.php @@ -2,23 +2,21 @@ namespace Zotlabs\Update; -class _1171 { -function run() { - - $r1 = q("ALTER TABLE verify CHANGE `type` `vtype` varchar(32) NOT NULL DEFAULT '' "); - $r2 = q("ALTER TABLE tokens CHANGE `scope` `auth_scope` varchar(512) NOT NULL DEFAULT '' "); - $r3 = q("ALTER TABLE auth_codes CHANGE `scope` `auth_scope` varchar(512) NOT NULL DEFAULT '' "); - $r4 = q("ALTER TABLE clients CHANGE `name` `clname` TEXT "); - $r5 = q("ALTER TABLE session CHANGE `data` `sess_data` TEXT NOT NULL "); - $r6 = q("ALTER TABLE register CHANGE `language` `lang` varchar(16) NOT NULL DEFAULT '' "); - - if($r1 && $r2 && $r3 && $r4 && $r5 && $r6) - return UPDATE_SUCCESS; - return UPDATE_FAILED; - +class _1171 +{ + public function run() + { + $r1 = q("ALTER TABLE verify CHANGE `type` `vtype` varchar(32) NOT NULL DEFAULT '' "); + $r2 = q("ALTER TABLE tokens CHANGE `scope` `auth_scope` varchar(512) NOT NULL DEFAULT '' "); + $r3 = q("ALTER TABLE auth_codes CHANGE `scope` `auth_scope` varchar(512) NOT NULL DEFAULT '' "); + $r4 = q("ALTER TABLE clients CHANGE `name` `clname` TEXT "); + $r5 = q("ALTER TABLE session CHANGE `data` `sess_data` TEXT NOT NULL "); + $r6 = q("ALTER TABLE register CHANGE `language` `lang` varchar(16) NOT NULL DEFAULT '' "); + if ($r1 && $r2 && $r3 && $r4 && $r5 && $r6) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1172.php b/Zotlabs/Update/_1172.php index 117e87fa1..2c54af05e 100644 --- a/Zotlabs/Update/_1172.php +++ b/Zotlabs/Update/_1172.php @@ -2,28 +2,27 @@ namespace Zotlabs\Update; -class _1172 { -function run() { +class _1172 +{ + public function run() + { - $r1 = q("ALTER TABLE term CHANGE `type` `ttype` int(3) NOT NULL DEFAULT '0' "); - - if(ACTIVE_DBTYPE == DBTYPE_POSTGRES) { - $r2 = q("ALTER TABLE groups CHANGE `name` `gname` TEXT NOT NULL "); - $r3 = q("ALTER TABLE profile CHANGE `name` `fullname` TEXT NOT NULL "); - $r4 = q("ALTER TABLE profile CHANGE `with` `partner` TEXT NOT NULL "); - $r5 = q("ALTER TABLE profile CHANGE `work` `employment` TEXT NOT NULL "); - } - else { - $r2 = q("ALTER TABLE groups CHANGE `name` `gname` char(255) NOT NULL DEFAULT '' "); - $r3 = q("ALTER TABLE profile CHANGE `name` `fullname` char(255) NOT NULL DEFAULT '' "); - $r4 = q("ALTER TABLE profile CHANGE `with` `partner` char(255) NOT NULL DEFAULT '' "); - $r5 = q("ALTER TABLE profile CHANGE `work` `employment` TEXT NOT NULL "); - } - if($r1 && $r2 && $r3 && $r4 && $r5) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + $r1 = q("ALTER TABLE term CHANGE `type` `ttype` int(3) NOT NULL DEFAULT '0' "); + if (ACTIVE_DBTYPE == DBTYPE_POSTGRES) { + $r2 = q("ALTER TABLE groups CHANGE `name` `gname` TEXT NOT NULL "); + $r3 = q("ALTER TABLE profile CHANGE `name` `fullname` TEXT NOT NULL "); + $r4 = q("ALTER TABLE profile CHANGE `with` `partner` TEXT NOT NULL "); + $r5 = q("ALTER TABLE profile CHANGE `work` `employment` TEXT NOT NULL "); + } else { + $r2 = q("ALTER TABLE groups CHANGE `name` `gname` char(255) NOT NULL DEFAULT '' "); + $r3 = q("ALTER TABLE profile CHANGE `name` `fullname` char(255) NOT NULL DEFAULT '' "); + $r4 = q("ALTER TABLE profile CHANGE `with` `partner` char(255) NOT NULL DEFAULT '' "); + $r5 = q("ALTER TABLE profile CHANGE `work` `employment` TEXT NOT NULL "); + } + if ($r1 && $r2 && $r3 && $r4 && $r5) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1173.php b/Zotlabs/Update/_1173.php index 7c0d76c73..3d42f5038 100644 --- a/Zotlabs/Update/_1173.php +++ b/Zotlabs/Update/_1173.php @@ -2,26 +2,25 @@ namespace Zotlabs\Update; -class _1173 { -function run() { +class _1173 +{ + public function run() + { - if(ACTIVE_DBTYPE == DBTYPE_POSTGRES) { - $r1 = q("ALTER TABLE notify CHANGE `name` `xname` TEXT NOT NULL "); - $r2 = q("ALTER TABLE notify CHANGE `date` `created` timestamp NOT NULL DEFAULT '0001-01-01 00:00:00' "); - $r3 = q("ALTER TABLE notify CHANGE `type` `ntype` numeric(3) NOT NULL DEFAULT '0' "); - } - else { - $r1 = q("ALTER TABLE notify CHANGE `name` `xname` char(255) NOT NULL DEFAULT '' "); - $r2 = q("ALTER TABLE notify CHANGE `date` `created` DATETIME NOT NULL DEFAULT '0001-01-01 00:00:00' "); - $r3 = q("ALTER TABLE notify CHANGE `type` `ntype` smallint(3) NOT NULL DEFAULT '0' "); - } - - if($r1 && $r2 && $r3) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + if (ACTIVE_DBTYPE == DBTYPE_POSTGRES) { + $r1 = q("ALTER TABLE notify CHANGE `name` `xname` TEXT NOT NULL "); + $r2 = q("ALTER TABLE notify CHANGE `date` `created` timestamp NOT NULL DEFAULT '0001-01-01 00:00:00' "); + $r3 = q("ALTER TABLE notify CHANGE `type` `ntype` numeric(3) NOT NULL DEFAULT '0' "); + } else { + $r1 = q("ALTER TABLE notify CHANGE `name` `xname` char(255) NOT NULL DEFAULT '' "); + $r2 = q("ALTER TABLE notify CHANGE `date` `created` DATETIME NOT NULL DEFAULT '0001-01-01 00:00:00' "); + $r3 = q("ALTER TABLE notify CHANGE `type` `ntype` smallint(3) NOT NULL DEFAULT '0' "); + } + if ($r1 && $r2 && $r3) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1174.php b/Zotlabs/Update/_1174.php index 0d68b2d65..dedb31eaa 100644 --- a/Zotlabs/Update/_1174.php +++ b/Zotlabs/Update/_1174.php @@ -2,31 +2,30 @@ namespace Zotlabs\Update; -class _1174 { -function run() { +class _1174 +{ + public function run() + { - if(ACTIVE_DBTYPE == DBTYPE_POSTGRES) { - $r1 = q("ALTER TABLE event CHANGE `type` `etype` varchar(255) NOT NULL DEFAULT '' "); - $r2 = q("ALTER TABLE event CHANGE `start` `dtstart` timestamp NOT NULL DEFAULT '0001-01-01 00:00:00' "); - $r3 = q("ALTER TABLE event CHANGE `finish` `dtend` timestamp NOT NULL DEFAULT '0001-01-01 00:00:00' "); - $r4 = q("ALTER TABLE event CHANGE `ignore` `dismissed` numeric(1) NOT NULL DEFAULT '0' "); - $r5 = q("ALTER TABLE attach CHANGE `data` `content` bytea NOT NULL "); - $r6 = q("ALTER TABLE photo CHANGE `data` `content` bytea NOT NULL "); - } - else { - $r1 = q("ALTER TABLE event CHANGE `type` `etype` char(255) NOT NULL DEFAULT '' "); - $r2 = q("ALTER TABLE event CHANGE `start` `dtstart` DATETIME NOT NULL DEFAULT '0001-01-01 00:00:00' "); - $r3 = q("ALTER TABLE event CHANGE `finish` `dtend` DATETIME NOT NULL DEFAULT '0001-01-01 00:00:00' "); - $r4 = q("ALTER TABLE event CHANGE `ignore` `dismissed` tinyint(1) NOT NULL DEFAULT '0' "); - $r5 = q("ALTER TABLE attach CHANGE `data` `content` longblob NOT NULL "); - $r6 = q("ALTER TABLE photo CHANGE `data` `content` mediumblob NOT NULL "); - } - - if($r1 && $r2 && $r3 && $r4 && $r5 && $r6) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + if (ACTIVE_DBTYPE == DBTYPE_POSTGRES) { + $r1 = q("ALTER TABLE event CHANGE `type` `etype` varchar(255) NOT NULL DEFAULT '' "); + $r2 = q("ALTER TABLE event CHANGE `start` `dtstart` timestamp NOT NULL DEFAULT '0001-01-01 00:00:00' "); + $r3 = q("ALTER TABLE event CHANGE `finish` `dtend` timestamp NOT NULL DEFAULT '0001-01-01 00:00:00' "); + $r4 = q("ALTER TABLE event CHANGE `ignore` `dismissed` numeric(1) NOT NULL DEFAULT '0' "); + $r5 = q("ALTER TABLE attach CHANGE `data` `content` bytea NOT NULL "); + $r6 = q("ALTER TABLE photo CHANGE `data` `content` bytea NOT NULL "); + } else { + $r1 = q("ALTER TABLE event CHANGE `type` `etype` char(255) NOT NULL DEFAULT '' "); + $r2 = q("ALTER TABLE event CHANGE `start` `dtstart` DATETIME NOT NULL DEFAULT '0001-01-01 00:00:00' "); + $r3 = q("ALTER TABLE event CHANGE `finish` `dtend` DATETIME NOT NULL DEFAULT '0001-01-01 00:00:00' "); + $r4 = q("ALTER TABLE event CHANGE `ignore` `dismissed` tinyint(1) NOT NULL DEFAULT '0' "); + $r5 = q("ALTER TABLE attach CHANGE `data` `content` longblob NOT NULL "); + $r6 = q("ALTER TABLE photo CHANGE `data` `content` mediumblob NOT NULL "); + } + if ($r1 && $r2 && $r3 && $r4 && $r5 && $r6) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1175.php b/Zotlabs/Update/_1175.php index 188586084..d9530014b 100644 --- a/Zotlabs/Update/_1175.php +++ b/Zotlabs/Update/_1175.php @@ -2,30 +2,26 @@ namespace Zotlabs\Update; -class _1175 { -function run() { +class _1175 +{ + public function run() + { - if(ACTIVE_DBTYPE == DBTYPE_POSTGRES) { - $r1 = q("ALTER TABLE item CHANGE `object` `obj` text NOT NULL"); - $r2 = q("ALTER TABLE photo CHANGE `size` `filesize` bigint NOT NULL DEFAULT '0' "); - $r3 = q("ALTER TABLE photo CHANGE `scale` `imgscale` numeric(3) NOT NULL DEFAULT '0' "); - $r4 = q("ALTER TABLE photo CHANGE `type` `mimetype` varchar(128) NOT NULL DEFAULT 'image/jpeg' "); - - } - else { - $r1 = q("ALTER TABLE item CHANGE `object` `obj` text NOT NULL"); - $r2 = q("ALTER TABLE photo CHANGE `size` `filesize` int(10) unsigned NOT NULL DEFAULT '0' "); - $r3 = q("ALTER TABLE photo CHANGE `scale` `imgscale` tinyint(3) unsigned NOT NULL DEFAULT '0' "); - $r4 = q("ALTER TABLE photo CHANGE `type` `mimetype` char(128) NOT NULL DEFAULT 'image/jpeg' "); - - } - - if($r1 && $r2 && $r3 && $r4) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + if (ACTIVE_DBTYPE == DBTYPE_POSTGRES) { + $r1 = q("ALTER TABLE item CHANGE `object` `obj` text NOT NULL"); + $r2 = q("ALTER TABLE photo CHANGE `size` `filesize` bigint NOT NULL DEFAULT '0' "); + $r3 = q("ALTER TABLE photo CHANGE `scale` `imgscale` numeric(3) NOT NULL DEFAULT '0' "); + $r4 = q("ALTER TABLE photo CHANGE `type` `mimetype` varchar(128) NOT NULL DEFAULT 'image/jpeg' "); + } else { + $r1 = q("ALTER TABLE item CHANGE `object` `obj` text NOT NULL"); + $r2 = q("ALTER TABLE photo CHANGE `size` `filesize` int(10) unsigned NOT NULL DEFAULT '0' "); + $r3 = q("ALTER TABLE photo CHANGE `scale` `imgscale` tinyint(3) unsigned NOT NULL DEFAULT '0' "); + $r4 = q("ALTER TABLE photo CHANGE `type` `mimetype` char(128) NOT NULL DEFAULT 'image/jpeg' "); + } + if ($r1 && $r2 && $r3 && $r4) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1176.php b/Zotlabs/Update/_1176.php index 8c02ee436..988ac2145 100644 --- a/Zotlabs/Update/_1176.php +++ b/Zotlabs/Update/_1176.php @@ -2,18 +2,19 @@ namespace Zotlabs\Update; -class _1176 { -function run() { +use Zotlabs\Lib\IConfig; - $r = q("select * from item_id where true"); - if($r) { - foreach($r as $rr) { - \Zotlabs\Lib\IConfig::Set($rr['iid'],'system',$rr['service'],$rr['sid'],true); - } - } - return UPDATE_SUCCESS; +class _1176 +{ + public function run() + { + $r = q("select * from item_id where true"); + if ($r) { + foreach ($r as $rr) { + IConfig::Set($rr['iid'], 'system', $rr['service'], $rr['sid'], true); + } + } + return UPDATE_SUCCESS; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1177.php b/Zotlabs/Update/_1177.php index 119e48ee8..f30d45922 100644 --- a/Zotlabs/Update/_1177.php +++ b/Zotlabs/Update/_1177.php @@ -2,14 +2,16 @@ namespace Zotlabs\Update; -class _1177 { -function run() { +class _1177 +{ + public function run() + { - if(ACTIVE_DBTYPE == DBTYPE_POSTGRES) { - $r1 = q("alter table event add cal_id bigint NOT NULL DEFAULT '0'"); - $r2 = q("create index \"event_cal_idx\" on event (\"cal_id\") "); + if (ACTIVE_DBTYPE == DBTYPE_POSTGRES) { + $r1 = q("alter table event add cal_id bigint NOT NULL DEFAULT '0'"); + $r2 = q("create index \"event_cal_idx\" on event (\"cal_id\") "); - $r3 = q("CREATE TABLE \"cal\" ( + $r3 = q("CREATE TABLE \"cal\" ( \"cal_id\" serial NOT NULL, \"cal_aid\" bigint NOT NULL DEFAULT '0', \"cal_uid\" bigint NOT NULL DEFAULT '0', @@ -22,18 +24,17 @@ function run() { \"synctoken\" text NOT NULL, \"cal_types\" text NOT NULL, PRIMARY KEY (\"cal_id\") "); - $r4 = q("create index \"cal_hash_idx\" on cal (\"cal_hash\") "); - $r5 = q("create index \"cal_name_idx\" on cal (\"cal_name\") "); - $r6 = q("create index \"cal_types_idx\" on cal (\"cal_types\") "); - $r7 = q("create index \"cal_aid_idx\" on cal (\"cal_aid\") "); - $r8 = q("create index \"cal_uid_idx\" on cal (\"cal_uid\") "); - $r = $r1 && $r2 && $r3 && $r4 && $r5 && $r6 && $r7 && $r8; - } - else { - $r1 = q("alter table event add cal_id int(10) unsigned NOT NULL DEFAULT '0', + $r4 = q("create index \"cal_hash_idx\" on cal (\"cal_hash\") "); + $r5 = q("create index \"cal_name_idx\" on cal (\"cal_name\") "); + $r6 = q("create index \"cal_types_idx\" on cal (\"cal_types\") "); + $r7 = q("create index \"cal_aid_idx\" on cal (\"cal_aid\") "); + $r8 = q("create index \"cal_uid_idx\" on cal (\"cal_uid\") "); + $r = $r1 && $r2 && $r3 && $r4 && $r5 && $r6 && $r7 && $r8; + } else { + $r1 = q("alter table event add cal_id int(10) unsigned NOT NULL DEFAULT '0', add index ( cal_id ) "); - $r2 = q("CREATE TABLE IF NOT EXISTS `cal` ( + $r2 = q("CREATE TABLE IF NOT EXISTS `cal` ( `cal_id` int(10) unsigned NOT NULL AUTO_INCREMENT, `cal_aid` int(10) unsigned NOT NULL DEFAULT '0', `cal_uid` int(10) unsigned NOT NULL DEFAULT '0', @@ -53,14 +54,12 @@ function run() { KEY `cal_types` (`cal_types`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 "); - $r = $r1 && $r2; - } + $r = $r1 && $r2; + } - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1178.php b/Zotlabs/Update/_1178.php index 97c42a16d..680128234 100644 --- a/Zotlabs/Update/_1178.php +++ b/Zotlabs/Update/_1178.php @@ -2,41 +2,43 @@ namespace Zotlabs\Update; -class _1178 { -function run() { +class _1178 +{ + public function run() + { - $c2 = null; + $c2 = null; - $c1 = q("SELECT channel_id, channel_hash from channel where true"); - if($c1) { - $c2 = q("SELECT id, chan from abconfig where true"); - if($c2) { - for($x = 0; $x < count($c2); $x ++) { - foreach($c1 as $c) { - if($c['channel_hash'] == $c2[$x]['chan']) { - $c2[$x]['chan'] = $c['channel_id']; - break; - } - } - } - } - } + $c1 = q("SELECT channel_id, channel_hash from channel where true"); + if ($c1) { + $c2 = q("SELECT id, chan from abconfig where true"); + if ($c2) { + for ($x = 0; $x < count($c2); $x++) { + foreach ($c1 as $c) { + if ($c['channel_hash'] == $c2[$x]['chan']) { + $c2[$x]['chan'] = $c['channel_id']; + break; + } + } + } + } + } - $r1 = q("ALTER TABLE abconfig CHANGE chan chan int(10) unsigned NOT NULL DEFAULT '0' "); + $r1 = q("ALTER TABLE abconfig CHANGE chan chan int(10) unsigned NOT NULL DEFAULT '0' "); - if($c2) { - foreach($c2 as $c) { - q("UPDATE abconfig SET chan = %d where id = %d", - intval($c['chan']), - intval($c['id']) - ); - } - } + if ($c2) { + foreach ($c2 as $c) { + q( + "UPDATE abconfig SET chan = %d where id = %d", + intval($c['chan']), + intval($c['id']) + ); + } + } - if($r1) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + if ($r1) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1179.php b/Zotlabs/Update/_1179.php index 44227c8d3..aadd56cc6 100644 --- a/Zotlabs/Update/_1179.php +++ b/Zotlabs/Update/_1179.php @@ -2,11 +2,13 @@ namespace Zotlabs\Update; -class _1179 { -function run() { +class _1179 +{ + public function run() + { - if(ACTIVE_DBTYPE == DBTYPE_POSTGRES) { - $r1 = q("CREATE TABLE atoken ( + if (ACTIVE_DBTYPE == DBTYPE_POSTGRES) { + $r1 = q("CREATE TABLE atoken ( atoken_id serial NOT NULL, atoken_aid bigint NOT NULL DEFAULT 0, atoken_uid bigint NOT NULL DEFAULT 0, @@ -14,17 +16,15 @@ function run() { atoken_token varchar(255) NOT NULL DEFAULT '', atoken_expires timestamp NOT NULL DEFAULT '0001-01-01 00:00:00', PRIMARY KEY (atoken_id)) "); - $r2 = q("create index atoken_aid on atoken (atoken_aid)"); - $r3 = q("create index atoken_uid on atoken (atoken_uid)"); - $r4 = q("create index atoken_name on atoken (atoken_name)"); - $r5 = q("create index atoken_token on atoken (atoken_token)"); - $r6 = q("create index atoken_expires on atoken (atoken_expires)"); + $r2 = q("create index atoken_aid on atoken (atoken_aid)"); + $r3 = q("create index atoken_uid on atoken (atoken_uid)"); + $r4 = q("create index atoken_name on atoken (atoken_name)"); + $r5 = q("create index atoken_token on atoken (atoken_token)"); + $r6 = q("create index atoken_expires on atoken (atoken_expires)"); - $r = $r1 && $r2 && $r3 && $r4 && $r5 && $r6; - - } - else { - $r = q("CREATE TABLE IF NOT EXISTS `atoken` ( + $r = $r1 && $r2 && $r3 && $r4 && $r5 && $r6; + } else { + $r = q("CREATE TABLE IF NOT EXISTS `atoken` ( `atoken_id` int(11) NOT NULL AUTO_INCREMENT, `atoken_aid` int(11) NOT NULL DEFAULT 0, `atoken_uid` int(11) NOT NULL DEFAULT 0, @@ -38,12 +38,10 @@ function run() { KEY `atoken_token` (`atoken_token`), KEY `atoken_expires` (`atoken_expires`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 "); - } - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; - + } + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1180.php b/Zotlabs/Update/_1180.php index f02f46231..782fe58a2 100644 --- a/Zotlabs/Update/_1180.php +++ b/Zotlabs/Update/_1180.php @@ -2,31 +2,32 @@ namespace Zotlabs\Update; -class _1180 { -function run() { +class _1180 +{ + public function run() + { - require_once('include/perm_upgrade.php'); + require_once('include/perm_upgrade.php'); - $r1 = q("select * from channel where true"); - if($r1) { - foreach($r1 as $rr) { - perm_limits_upgrade($rr); - autoperms_upgrade($rr); - } - } + $r1 = q("select * from channel where true"); + if ($r1) { + foreach ($r1 as $rr) { + perm_limits_upgrade($rr); + autoperms_upgrade($rr); + } + } - $r2 = q("select * from abook where true"); - if($r2) { - foreach($r2 as $rr) { - perm_abook_upgrade($rr); - } - } - - $r = $r1 && $r2; - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + $r2 = q("select * from abook where true"); + if ($r2) { + foreach ($r2 as $rr) { + perm_abook_upgrade($rr); + } + } + + $r = $r1 && $r2; + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1181.php b/Zotlabs/Update/_1181.php index 3366efde1..1d47149fd 100644 --- a/Zotlabs/Update/_1181.php +++ b/Zotlabs/Update/_1181.php @@ -2,13 +2,13 @@ namespace Zotlabs\Update; -class _1181 { -function run() { -// if(\Zotlabs\Lib\System::get_server_role() == 'pro') { -// q("update account set account_level = 5 where true"); -// } - return UPDATE_SUCCESS; +class _1181 +{ + public function run() + { +// if(\Zotlabs\Lib\System::get_server_role() == 'pro') { +// q("update account set account_level = 5 where true"); +// } + return UPDATE_SUCCESS; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1182.php b/Zotlabs/Update/_1182.php index d652f432e..a38ae73d2 100644 --- a/Zotlabs/Update/_1182.php +++ b/Zotlabs/Update/_1182.php @@ -2,16 +2,16 @@ namespace Zotlabs\Update; -class _1182 { -function run() { +class _1182 +{ + public function run() + { - $r1 = q("alter table site add site_version varchar(32) not null default '' "); + $r1 = q("alter table site add site_version varchar(32) not null default '' "); - if($r1) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + if ($r1) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1183.php b/Zotlabs/Update/_1183.php index 6e9f89271..0c07dee33 100644 --- a/Zotlabs/Update/_1183.php +++ b/Zotlabs/Update/_1183.php @@ -2,24 +2,24 @@ namespace Zotlabs\Update; -class _1183 { -function run() { +class _1183 +{ + public function run() + { - if(ACTIVE_DBTYPE == DBTYPE_POSTGRES) { - $r1 = q("alter table hook ALTER COLUMN priority TYPE smallint"); - $r2 = q("alter table hook ALTER COLUMN priority SET NOT NULL"); - $r3 = q("alter table hook ALTER COLUMN priority SET DEFAULT '0'"); - $r1 = $r1 && $r2 && $r3; - } - else { - $r1 = q("alter table hook CHANGE priority priority smallint NOT NULL DEFAULT '0' "); - } - $r2 = q("create index priority_idx on hook (priority)"); + if (ACTIVE_DBTYPE == DBTYPE_POSTGRES) { + $r1 = q("alter table hook ALTER COLUMN priority TYPE smallint"); + $r2 = q("alter table hook ALTER COLUMN priority SET NOT NULL"); + $r3 = q("alter table hook ALTER COLUMN priority SET DEFAULT '0'"); + $r1 = $r1 && $r2 && $r3; + } else { + $r1 = q("alter table hook CHANGE priority priority smallint NOT NULL DEFAULT '0' "); + } + $r2 = q("create index priority_idx on hook (priority)"); - if($r1 && $r2) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + if ($r1 && $r2) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1184.php b/Zotlabs/Update/_1184.php index e8c37888c..2ef55b777 100644 --- a/Zotlabs/Update/_1184.php +++ b/Zotlabs/Update/_1184.php @@ -2,15 +2,16 @@ namespace Zotlabs\Update; -class _1184 { -function run() { +class _1184 +{ + public function run() + { - $r1 = q("alter table site add site_crypto text not null default '' "); + $r1 = q("alter table site add site_crypto text not null default '' "); - if($r1) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + if ($r1) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1185.php b/Zotlabs/Update/_1185.php index c7f4f1bd4..543e29c6f 100644 --- a/Zotlabs/Update/_1185.php +++ b/Zotlabs/Update/_1185.php @@ -2,15 +2,16 @@ namespace Zotlabs\Update; -class _1185 { -function run() { +class _1185 +{ + public function run() + { - $r1 = q("alter table app add app_plugin text not null default '' "); + $r1 = q("alter table app add app_plugin text not null default '' "); - if($r1) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + if ($r1) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1186.php b/Zotlabs/Update/_1186.php index a9c4f0e36..bb50efa4b 100644 --- a/Zotlabs/Update/_1186.php +++ b/Zotlabs/Update/_1186.php @@ -2,17 +2,16 @@ namespace Zotlabs\Update; -class _1186 { -function run() { - - $r1 = q("alter table profile add profile_vcard text not null"); - - if($r1) - return UPDATE_SUCCESS; - return UPDATE_FAILED; +class _1186 +{ + public function run() + { + $r1 = q("alter table profile add profile_vcard text not null"); + if ($r1) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1187.php b/Zotlabs/Update/_1187.php index 0fce5aa05..de4e069d2 100644 --- a/Zotlabs/Update/_1187.php +++ b/Zotlabs/Update/_1187.php @@ -2,23 +2,21 @@ namespace Zotlabs\Update; -class _1187 { -function run() { - - if(ACTIVE_DBTYPE == DBTYPE_POSTGRES) { - $r1 = q("alter table outq add outq_scheduled timestamp not null default '0001-01-01 00:00:00' "); - } - else { - $r1 = q("alter table outq add outq_scheduled datetime not null default '0001-01-01 00:00:00' "); - } - $r2 = q("create index outq_scheduled_idx on outq (outq_scheduled)"); - - if($r1 && $r2) - return UPDATE_SUCCESS; - return UPDATE_FAILED; +class _1187 +{ + public function run() + { + if (ACTIVE_DBTYPE == DBTYPE_POSTGRES) { + $r1 = q("alter table outq add outq_scheduled timestamp not null default '0001-01-01 00:00:00' "); + } else { + $r1 = q("alter table outq add outq_scheduled datetime not null default '0001-01-01 00:00:00' "); + } + $r2 = q("create index outq_scheduled_idx on outq (outq_scheduled)"); + if ($r1 && $r2) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1188.php b/Zotlabs/Update/_1188.php index eb0335aab..1bc0f5e8f 100644 --- a/Zotlabs/Update/_1188.php +++ b/Zotlabs/Update/_1188.php @@ -2,17 +2,17 @@ namespace Zotlabs\Update; -class _1188 { -function run() { +class _1188 +{ + public function run() + { - $r1 = q("alter table channel add channel_password varchar(255) not null default '' "); - $r2 = q("alter table channel add channel_salt varchar(255) not null default '' "); - - if($r1 && $r2) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + $r1 = q("alter table channel add channel_password varchar(255) not null default '' "); + $r2 = q("alter table channel add channel_salt varchar(255) not null default '' "); + if ($r1 && $r2) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1189.php b/Zotlabs/Update/_1189.php index 5cd180fbd..f50fb524d 100644 --- a/Zotlabs/Update/_1189.php +++ b/Zotlabs/Update/_1189.php @@ -2,22 +2,21 @@ namespace Zotlabs\Update; -class _1189 { -function run() { +class _1189 +{ + public function run() + { - $r1 = q("alter table mail add mail_mimetype varchar(64) not null default 'text/bbcode' "); - - if(ACTIVE_DBTYPE == DBTYPE_POSTGRES) { - $r2 = q("alter table mail add mail_raw smallint not null default 0 "); - } - else { - $r2 = q("alter table mail add mail_raw tinyint(4) not null default 0 "); - } - if($r1 && $r2) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + $r1 = q("alter table mail add mail_mimetype varchar(64) not null default 'text/bbcode' "); + if (ACTIVE_DBTYPE == DBTYPE_POSTGRES) { + $r2 = q("alter table mail add mail_raw smallint not null default 0 "); + } else { + $r2 = q("alter table mail add mail_raw tinyint(4) not null default 0 "); + } + if ($r1 && $r2) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1190.php b/Zotlabs/Update/_1190.php index 9321b82b8..4a5fa82e7 100644 --- a/Zotlabs/Update/_1190.php +++ b/Zotlabs/Update/_1190.php @@ -2,16 +2,17 @@ namespace Zotlabs\Update; -class _1190 { -function run() { - $r1 = q("alter table abook add abook_not_here smallint not null default 0 "); +class _1190 +{ + public function run() + { + $r1 = q("alter table abook add abook_not_here smallint not null default 0 "); - $r2 = q("create index abook_not_here on abook (abook_not_here)"); + $r2 = q("create index abook_not_here on abook (abook_not_here)"); - if($r1 && $r2) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + if ($r1 && $r2) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1191.php b/Zotlabs/Update/_1191.php index 015b9a2ca..567e7ea17 100644 --- a/Zotlabs/Update/_1191.php +++ b/Zotlabs/Update/_1191.php @@ -2,31 +2,31 @@ namespace Zotlabs\Update; -class _1191 { -function run() { +class _1191 +{ + public function run() + { - $r = q("SELECT 1 FROM principals LIMIT 1"); + $r = q("SELECT 1 FROM principals LIMIT 1"); - if($r !== false) { - return UPDATE_SUCCESS; - } - else { - if(ACTIVE_DBTYPE == DBTYPE_POSTGRES) { - $r1 = q("CREATE TABLE addressbooks ( + if ($r !== false) { + return UPDATE_SUCCESS; + } else { + if (ACTIVE_DBTYPE == DBTYPE_POSTGRES) { + $r1 = q("CREATE TABLE addressbooks ( id SERIAL NOT NULL, principaluri VARCHAR(255), displayname VARCHAR(255), uri VARCHAR(200), description TEXT, synctoken INTEGER NOT NULL DEFAULT 1 - );" - ); + );"); - $r2 = q("ALTER TABLE ONLY addressbooks ADD CONSTRAINT addressbooks_pkey PRIMARY KEY (id);"); + $r2 = q("ALTER TABLE ONLY addressbooks ADD CONSTRAINT addressbooks_pkey PRIMARY KEY (id);"); - $r3 = q("CREATE UNIQUE INDEX addressbooks_ukey ON addressbooks USING btree (principaluri, uri);"); + $r3 = q("CREATE UNIQUE INDEX addressbooks_ukey ON addressbooks USING btree (principaluri, uri);"); - $r4 = q("CREATE TABLE cards ( + $r4 = q("CREATE TABLE cards ( id SERIAL NOT NULL, addressbookid INTEGER NOT NULL, carddata BYTEA, @@ -34,27 +34,25 @@ function run() { lastmodified INTEGER, etag VARCHAR(32), size INTEGER NOT NULL - );" - ); + );"); - $r5 = q("ALTER TABLE ONLY cards ADD CONSTRAINT cards_pkey PRIMARY KEY (id);"); + $r5 = q("ALTER TABLE ONLY cards ADD CONSTRAINT cards_pkey PRIMARY KEY (id);"); - $r6 = q("CREATE UNIQUE INDEX cards_ukey ON cards USING btree (addressbookid, uri);"); + $r6 = q("CREATE UNIQUE INDEX cards_ukey ON cards USING btree (addressbookid, uri);"); - $r7 = q("CREATE TABLE addressbookchanges ( + $r7 = q("CREATE TABLE addressbookchanges ( id SERIAL NOT NULL, uri VARCHAR(200) NOT NULL, synctoken INTEGER NOT NULL, addressbookid INTEGER NOT NULL, operation SMALLINT NOT NULL - );" - ); + );"); - $r8 = q("ALTER TABLE ONLY addressbookchanges ADD CONSTRAINT addressbookchanges_pkey PRIMARY KEY (id);"); + $r8 = q("ALTER TABLE ONLY addressbookchanges ADD CONSTRAINT addressbookchanges_pkey PRIMARY KEY (id);"); - $r9 = q("CREATE INDEX addressbookchanges_addressbookid_synctoken_ix ON addressbookchanges USING btree (addressbookid, synctoken);"); + $r9 = q("CREATE INDEX addressbookchanges_addressbookid_synctoken_ix ON addressbookchanges USING btree (addressbookid, synctoken);"); - $r10 = q("CREATE TABLE calendarobjects ( + $r10 = q("CREATE TABLE calendarobjects ( id SERIAL NOT NULL, calendardata BYTEA, uri VARCHAR(200), @@ -66,23 +64,21 @@ function run() { firstoccurence INTEGER, lastoccurence INTEGER, uid VARCHAR(200) - );" - ); + );"); - $r11 = q("ALTER TABLE ONLY calendarobjects ADD CONSTRAINT calendarobjects_pkey PRIMARY KEY (id);"); + $r11 = q("ALTER TABLE ONLY calendarobjects ADD CONSTRAINT calendarobjects_pkey PRIMARY KEY (id);"); - $r12 = q("CREATE UNIQUE INDEX calendarobjects_ukey ON calendarobjects USING btree (calendarid, uri);"); + $r12 = q("CREATE UNIQUE INDEX calendarobjects_ukey ON calendarobjects USING btree (calendarid, uri);"); - $r13 = q("CREATE TABLE calendars ( + $r13 = q("CREATE TABLE calendars ( id SERIAL NOT NULL, synctoken INTEGER NOT NULL DEFAULT 1, components VARCHAR(21) - );" - ); + );"); - $r14 = q("ALTER TABLE ONLY calendars ADD CONSTRAINT calendars_pkey PRIMARY KEY (id);"); + $r14 = q("ALTER TABLE ONLY calendars ADD CONSTRAINT calendars_pkey PRIMARY KEY (id);"); - $r15 = q("CREATE TABLE calendarinstances ( + $r15 = q("CREATE TABLE calendarinstances ( id SERIAL NOT NULL, calendarid INTEGER NOT NULL, principaluri VARCHAR(100), @@ -97,18 +93,17 @@ function run() { share_href VARCHAR(100), share_displayname VARCHAR(100), share_invitestatus SMALLINT NOT NULL DEFAULT '2' -- '1 = noresponse, 2 = accepted, 3 = declined, 4 = invalid' - );" - ); + );"); - $r16 = q("ALTER TABLE ONLY calendarinstances ADD CONSTRAINT calendarinstances_pkey PRIMARY KEY (id);"); + $r16 = q("ALTER TABLE ONLY calendarinstances ADD CONSTRAINT calendarinstances_pkey PRIMARY KEY (id);"); - $r17 = q("CREATE UNIQUE INDEX calendarinstances_principaluri_uri ON calendarinstances USING btree (principaluri, uri);"); + $r17 = q("CREATE UNIQUE INDEX calendarinstances_principaluri_uri ON calendarinstances USING btree (principaluri, uri);"); - $r18 = q("CREATE UNIQUE INDEX calendarinstances_principaluri_calendarid ON calendarinstances USING btree (principaluri, calendarid);"); + $r18 = q("CREATE UNIQUE INDEX calendarinstances_principaluri_calendarid ON calendarinstances USING btree (principaluri, calendarid);"); - $r19 = q("CREATE UNIQUE INDEX calendarinstances_principaluri_share_href ON calendarinstances USING btree (principaluri, share_href);"); + $r19 = q("CREATE UNIQUE INDEX calendarinstances_principaluri_share_href ON calendarinstances USING btree (principaluri, share_href);"); - $r20 = q("CREATE TABLE calendarsubscriptions ( + $r20 = q("CREATE TABLE calendarsubscriptions ( id SERIAL NOT NULL, uri VARCHAR(200) NOT NULL, principaluri VARCHAR(100) NOT NULL, @@ -121,27 +116,25 @@ function run() { stripalarms SMALLINT NULL, stripattachments SMALLINT NULL, lastmodified INTEGER - );" - ); + );"); - $r21 = q("ALTER TABLE ONLY calendarsubscriptions ADD CONSTRAINT calendarsubscriptions_pkey PRIMARY KEY (id);"); + $r21 = q("ALTER TABLE ONLY calendarsubscriptions ADD CONSTRAINT calendarsubscriptions_pkey PRIMARY KEY (id);"); - $r22 = q("CREATE UNIQUE INDEX calendarsubscriptions_ukey ON calendarsubscriptions USING btree (principaluri, uri);"); + $r22 = q("CREATE UNIQUE INDEX calendarsubscriptions_ukey ON calendarsubscriptions USING btree (principaluri, uri);"); - $r23 = q("CREATE TABLE calendarchanges ( + $r23 = q("CREATE TABLE calendarchanges ( id SERIAL NOT NULL, uri VARCHAR(200) NOT NULL, synctoken INTEGER NOT NULL, calendarid INTEGER NOT NULL, operation SMALLINT NOT NULL DEFAULT 0 - );" - ); + );"); - $r24 = q("ALTER TABLE ONLY calendarchanges ADD CONSTRAINT calendarchanges_pkey PRIMARY KEY (id);"); + $r24 = q("ALTER TABLE ONLY calendarchanges ADD CONSTRAINT calendarchanges_pkey PRIMARY KEY (id);"); - $r25 = q("CREATE INDEX calendarchanges_calendarid_synctoken_ix ON calendarchanges USING btree (calendarid, synctoken);"); + $r25 = q("CREATE INDEX calendarchanges_calendarid_synctoken_ix ON calendarchanges USING btree (calendarid, synctoken);"); - $r26 = q("CREATE TABLE schedulingobjects ( + $r26 = q("CREATE TABLE schedulingobjects ( id SERIAL NOT NULL, principaluri VARCHAR(255), calendardata BYTEA, @@ -149,10 +142,9 @@ function run() { lastmodified INTEGER, etag VARCHAR(32), size INTEGER NOT NULL - );" - ); + );"); - $r27 = q("CREATE TABLE locks ( + $r27 = q("CREATE TABLE locks ( id SERIAL NOT NULL, owner VARCHAR(100), timeout INTEGER, @@ -161,74 +153,69 @@ function run() { scope SMALLINT, depth SMALLINT, uri TEXT - );" - ); + );"); - $r28 = q("ALTER TABLE ONLY locks ADD CONSTRAINT locks_pkey PRIMARY KEY (id);"); + $r28 = q("ALTER TABLE ONLY locks ADD CONSTRAINT locks_pkey PRIMARY KEY (id);"); - $r29 = q("CREATE INDEX locks_token_ix ON locks USING btree (token);"); + $r29 = q("CREATE INDEX locks_token_ix ON locks USING btree (token);"); - $r30 = q("CREATE INDEX locks_uri_ix ON locks USING btree (uri);"); + $r30 = q("CREATE INDEX locks_uri_ix ON locks USING btree (uri);"); - $r31 = q("CREATE TABLE principals ( + $r31 = q("CREATE TABLE principals ( id SERIAL NOT NULL, uri VARCHAR(200) NOT NULL, email VARCHAR(80), displayname VARCHAR(80) - );" - ); + );"); - $r32 = q("ALTER TABLE ONLY principals ADD CONSTRAINT principals_pkey PRIMARY KEY (id);"); + $r32 = q("ALTER TABLE ONLY principals ADD CONSTRAINT principals_pkey PRIMARY KEY (id);"); - $r33 = q("CREATE UNIQUE INDEX principals_ukey ON principals USING btree (uri);"); + $r33 = q("CREATE UNIQUE INDEX principals_ukey ON principals USING btree (uri);"); - $r34 = q("CREATE TABLE groupmembers ( + $r34 = q("CREATE TABLE groupmembers ( id SERIAL NOT NULL, principal_id INTEGER NOT NULL, member_id INTEGER NOT NULL - );" - ); + );"); - $r35 = q("ALTER TABLE ONLY groupmembers ADD CONSTRAINT groupmembers_pkey PRIMARY KEY (id);"); + $r35 = q("ALTER TABLE ONLY groupmembers ADD CONSTRAINT groupmembers_pkey PRIMARY KEY (id);"); - $r36 = q("CREATE UNIQUE INDEX groupmembers_ukey ON groupmembers USING btree (principal_id, member_id);"); + $r36 = q("CREATE UNIQUE INDEX groupmembers_ukey ON groupmembers USING btree (principal_id, member_id);"); - $r37 = q("CREATE TABLE propertystorage ( + $r37 = q("CREATE TABLE propertystorage ( id SERIAL NOT NULL, path VARCHAR(1024) NOT NULL, name VARCHAR(100) NOT NULL, valuetype INT, value BYTEA - );" - ); + );"); - $r38 = q("ALTER TABLE ONLY propertystorage ADD CONSTRAINT propertystorage_pkey PRIMARY KEY (id);"); + $r38 = q("ALTER TABLE ONLY propertystorage ADD CONSTRAINT propertystorage_pkey PRIMARY KEY (id);"); - $r39 = q("CREATE UNIQUE INDEX propertystorage_ukey ON propertystorage (path, name);"); + $r39 = q("CREATE UNIQUE INDEX propertystorage_ukey ON propertystorage (path, name);"); - $r40 = q("CREATE TABLE users ( + $r40 = q("CREATE TABLE users ( id SERIAL NOT NULL, username VARCHAR(50), digesta1 VARCHAR(32) - );" - ); + );"); - $r41 = q("ALTER TABLE ONLY users ADD CONSTRAINT users_pkey PRIMARY KEY (id);"); + $r41 = q("ALTER TABLE ONLY users ADD CONSTRAINT users_pkey PRIMARY KEY (id);"); - $r42 = q("CREATE UNIQUE INDEX users_ukey ON users USING btree (username);"); + $r42 = q("CREATE UNIQUE INDEX users_ukey ON users USING btree (username);"); - if( - $r1 && $r2 && $r3 && $r4 && $r5 && $r6 && $r7 && $r8 && $r9 && $r10 - && $r11 && $r12 && $r13 && $r14 && $r15 && $r16 && $r17 && $r18 && $r19 && $r20 - && $r21 && $r22 && $r23 && $r24 && $r25 && $r26 && $r27 && $r28 && $r29 && $r30 - && $r31 && $r32 && $r33 && $r34 && $r35 && $r36 && $r37 && $r38 && $r39 && $r40 - && $r41 && $r42 - ) - return UPDATE_SUCCESS; - return UPDATE_FAILED; - } - else { - $r1 = q("CREATE TABLE if not exists addressbooks ( + if ( + $r1 && $r2 && $r3 && $r4 && $r5 && $r6 && $r7 && $r8 && $r9 && $r10 + && $r11 && $r12 && $r13 && $r14 && $r15 && $r16 && $r17 && $r18 && $r19 && $r20 + && $r21 && $r22 && $r23 && $r24 && $r25 && $r26 && $r27 && $r28 && $r29 && $r30 + && $r31 && $r32 && $r33 && $r34 && $r35 && $r36 && $r37 && $r38 && $r39 && $r40 + && $r41 && $r42 + ) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } else { + $r1 = q("CREATE TABLE if not exists addressbooks ( id INT(11) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT, principaluri VARBINARY(255), displayname VARCHAR(255), @@ -236,10 +223,9 @@ function run() { description TEXT, synctoken INT(11) UNSIGNED NOT NULL DEFAULT '1', UNIQUE(principaluri(100), uri(100)) - ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;" - ); + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;"); - $r2 = q("CREATE TABLE if not exists cards ( + $r2 = q("CREATE TABLE if not exists cards ( id INT(11) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT, addressbookid INT(11) UNSIGNED NOT NULL, carddata MEDIUMBLOB, @@ -247,20 +233,18 @@ function run() { lastmodified INT(11) UNSIGNED, etag VARBINARY(32), size INT(11) UNSIGNED NOT NULL - ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;" - ); + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;"); - $r3 = q("CREATE TABLE if not exists addressbookchanges ( + $r3 = q("CREATE TABLE if not exists addressbookchanges ( id INT(11) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT, uri VARBINARY(200) NOT NULL, synctoken INT(11) UNSIGNED NOT NULL, addressbookid INT(11) UNSIGNED NOT NULL, operation TINYINT(1) NOT NULL, INDEX addressbookid_synctoken (addressbookid, synctoken) - ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;" - ); + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;"); - $r4 = q("CREATE TABLE if not exists calendarobjects ( + $r4 = q("CREATE TABLE if not exists calendarobjects ( id INT(11) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT, calendardata MEDIUMBLOB, uri VARBINARY(200), @@ -274,17 +258,15 @@ function run() { uid VARBINARY(200), UNIQUE(calendarid, uri), INDEX calendarid_time (calendarid, firstoccurence) - ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;" - ); + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;"); - $r5 = q("CREATE TABLE if not exists calendars ( + $r5 = q("CREATE TABLE if not exists calendars ( id INTEGER UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT, synctoken INTEGER UNSIGNED NOT NULL DEFAULT '1', components VARBINARY(21) - ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;" - ); + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;"); - $r6 = q("CREATE TABLE if not exists calendarinstances ( + $r6 = q("CREATE TABLE if not exists calendarinstances ( id INTEGER UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT, calendarid INTEGER UNSIGNED NOT NULL, principaluri VARBINARY(100), @@ -302,20 +284,18 @@ function run() { UNIQUE(principaluri, uri), UNIQUE(calendarid, principaluri), UNIQUE(calendarid, share_href) - ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;" - ); + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;"); - $r7 = q("CREATE TABLE if not exists calendarchanges ( + $r7 = q("CREATE TABLE if not exists calendarchanges ( id INT(11) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT, uri VARBINARY(200) NOT NULL, synctoken INT(11) UNSIGNED NOT NULL, calendarid INT(11) UNSIGNED NOT NULL, operation TINYINT(1) NOT NULL, INDEX calendarid_synctoken (calendarid, synctoken) - ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;" - ); + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;"); - $r8 = q("CREATE TABLE if not exists calendarsubscriptions ( + $r8 = q("CREATE TABLE if not exists calendarsubscriptions ( id INT(11) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT, uri VARBINARY(200) NOT NULL, principaluri VARBINARY(100) NOT NULL, @@ -329,10 +309,9 @@ function run() { stripattachments TINYINT(1) NULL, lastmodified INT(11) UNSIGNED, UNIQUE(principaluri, uri) - ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;" - ); + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;"); - $r9 = q("CREATE TABLE if not exists schedulingobjects ( + $r9 = q("CREATE TABLE if not exists schedulingobjects ( id INT(11) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT, principaluri VARBINARY(255), calendardata MEDIUMBLOB, @@ -340,10 +319,9 @@ function run() { lastmodified INT(11) UNSIGNED, etag VARBINARY(32), size INT(11) UNSIGNED NOT NULL - ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;" - ); + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;"); - $r10 = q("CREATE TABLE if not exists locks ( + $r10 = q("CREATE TABLE if not exists locks ( id INTEGER UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT, owner VARCHAR(100), timeout INTEGER UNSIGNED, @@ -354,46 +332,41 @@ function run() { uri VARBINARY(1000), INDEX(token), INDEX(uri(100)) - ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;" - ); + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;"); - $r11 = q("CREATE TABLE if not exists principals ( + $r11 = q("CREATE TABLE if not exists principals ( id INTEGER UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT, uri VARBINARY(200) NOT NULL, email VARBINARY(80), displayname VARCHAR(80), UNIQUE(uri) - ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;" - ); + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;"); - $r12 = q("CREATE TABLE if not exists groupmembers ( + $r12 = q("CREATE TABLE if not exists groupmembers ( id INTEGER UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT, principal_id INTEGER UNSIGNED NOT NULL, member_id INTEGER UNSIGNED NOT NULL, UNIQUE(principal_id, member_id) - ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;" - ); + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;"); - $r13 = q("CREATE TABLE if not exists propertystorage ( + $r13 = q("CREATE TABLE if not exists propertystorage ( id INT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT, path VARBINARY(1024) NOT NULL, name VARBINARY(100) NOT NULL, valuetype INT UNSIGNED, value MEDIUMBLOB - ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;" - ); + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;"); - $r14 = q("CREATE UNIQUE INDEX path_property ON propertystorage (path(600), name(100));"); + $r14 = q("CREATE UNIQUE INDEX path_property ON propertystorage (path(600), name(100));"); - $r15 = q("CREATE TABLE if not exists users ( + $r15 = q("CREATE TABLE if not exists users ( id INTEGER UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT, username VARBINARY(50), digesta1 VARBINARY(32), UNIQUE(username) - ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;" - ); + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;"); - $r16 = q("CREATE TABLE if not exists calendarinstances ( + $r16 = q("CREATE TABLE if not exists calendarinstances ( id INTEGER UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT, calendarid INTEGER UNSIGNED NOT NULL, principaluri VARBINARY(100), @@ -411,15 +384,13 @@ function run() { UNIQUE(principaluri, uri), UNIQUE(calendarid, principaluri), UNIQUE(calendarid, share_href) - ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;" - ); + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;"); - if($r1 && $r2 && $r3 && $r4 && $r5 && $r6 && $r7 && $r8 && $r9 && $r10 && $r11 && $r12 && $r13 && $r14 && $r15 && $r16) - return UPDATE_SUCCESS; - return UPDATE_FAILED; - } - } + if ($r1 && $r2 && $r3 && $r4 && $r5 && $r6 && $r7 && $r8 && $r9 && $r10 && $r11 && $r12 && $r13 && $r14 && $r15 && $r16) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } + } + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1192.php b/Zotlabs/Update/_1192.php index f2445da05..9c4d6bfb8 100644 --- a/Zotlabs/Update/_1192.php +++ b/Zotlabs/Update/_1192.php @@ -2,20 +2,20 @@ namespace Zotlabs\Update; -class _1192 { -function run() { +class _1192 +{ + public function run() + { - if(ACTIVE_DBTYPE == DBTYPE_POSTGRES) { - $r1 = q("CREATE INDEX item_obj_type ON item (obj_type)"); - } - else { - $r1 = q("ALTER TABLE item ADD INDEX (obj_type)"); - } + if (ACTIVE_DBTYPE == DBTYPE_POSTGRES) { + $r1 = q("CREATE INDEX item_obj_type ON item (obj_type)"); + } else { + $r1 = q("ALTER TABLE item ADD INDEX (obj_type)"); + } - if($r1) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + if ($r1) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1193.php b/Zotlabs/Update/_1193.php index 267e16da1..9b8299b9f 100644 --- a/Zotlabs/Update/_1193.php +++ b/Zotlabs/Update/_1193.php @@ -2,21 +2,20 @@ namespace Zotlabs\Update; -class _1193 { -function run() { +class _1193 +{ + public function run() + { - if(ACTIVE_DBTYPE == DBTYPE_POSTGRES) { - $r1 = q("CREATE INDEX item_uid_unseen ON item (uid, item_unseen)"); - } - else { - $r1 = q("ALTER TABLE item ADD INDEX uid_item_unseen (uid, item_unseen)"); - } + if (ACTIVE_DBTYPE == DBTYPE_POSTGRES) { + $r1 = q("CREATE INDEX item_uid_unseen ON item (uid, item_unseen)"); + } else { + $r1 = q("ALTER TABLE item ADD INDEX uid_item_unseen (uid, item_unseen)"); + } - if($r1) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + if ($r1) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1194.php b/Zotlabs/Update/_1194.php index 916723eaa..bb1801c68 100644 --- a/Zotlabs/Update/_1194.php +++ b/Zotlabs/Update/_1194.php @@ -2,21 +2,22 @@ namespace Zotlabs\Update; -class _1194 { -function run() { - $r = q("select id, resource_id from item where resource_type = 'nwiki'"); - if($r) { - foreach($r as $rv) { - $mimetype = get_iconfig($rv['id'],'wiki','mimeType'); - q("update item set mimetype = '%s' where resource_type = 'nwikipage' and resource_id = '%s'", - dbesc($mimetype), - dbesc($rv['resource_id']) - ); - } - } +class _1194 +{ + public function run() + { + $r = q("select id, resource_id from item where resource_type = 'nwiki'"); + if ($r) { + foreach ($r as $rv) { + $mimetype = get_iconfig($rv['id'], 'wiki', 'mimeType'); + q( + "update item set mimetype = '%s' where resource_type = 'nwikipage' and resource_id = '%s'", + dbesc($mimetype), + dbesc($rv['resource_id']) + ); + } + } - return UPDATE_SUCCESS; + return UPDATE_SUCCESS; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1195.php b/Zotlabs/Update/_1195.php index 248e9a34a..535441d8b 100644 --- a/Zotlabs/Update/_1195.php +++ b/Zotlabs/Update/_1195.php @@ -2,20 +2,20 @@ namespace Zotlabs\Update; -class _1195 { -function run() { +class _1195 +{ + public function run() + { - if(ACTIVE_DBTYPE == DBTYPE_POSTGRES) { - $r1 = q("CREATE INDEX item_resource_id ON item (resource_id)"); - } - else { - $r1 = q("ALTER TABLE item ADD INDEX (resource_id)"); - } + if (ACTIVE_DBTYPE == DBTYPE_POSTGRES) { + $r1 = q("CREATE INDEX item_resource_id ON item (resource_id)"); + } else { + $r1 = q("ALTER TABLE item ADD INDEX (resource_id)"); + } - if($r1) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + if ($r1) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1196.php b/Zotlabs/Update/_1196.php index 720252bf8..13f1a36af 100644 --- a/Zotlabs/Update/_1196.php +++ b/Zotlabs/Update/_1196.php @@ -2,11 +2,13 @@ namespace Zotlabs\Update; -class _1196 { -function run() { +class _1196 +{ + public function run() + { - if(ACTIVE_DBTYPE == DBTYPE_POSTGRES) { - $r1 = q("CREATE TABLE \"pchan\" ( + if (ACTIVE_DBTYPE == DBTYPE_POSTGRES) { + $r1 = q("CREATE TABLE \"pchan\" ( \"pchan_id\" serial NOT NULL, \"pchan_guid\" text NOT NULL, \"pchan_hash\" text NOT NULL, @@ -15,15 +17,14 @@ function run() { PRIMARY KEY (\"pchan_id\") )"); - $r2 = q("create index \"pchan_guid\" on pchan (\"pchan_guid\")"); - $r3 = q("create index \"pchan_hash\" on pchan (\"pchan_hash\")"); + $r2 = q("create index \"pchan_guid\" on pchan (\"pchan_guid\")"); + $r3 = q("create index \"pchan_hash\" on pchan (\"pchan_hash\")"); - if($r1 && $r2 && $r3) { - return UPDATE_SUCCESS; - } - } - else { - $r1 = q("CREATE TABLE IF NOT EXISTS pchan ( + if ($r1 && $r2 && $r3) { + return UPDATE_SUCCESS; + } + } else { + $r1 = q("CREATE TABLE IF NOT EXISTS pchan ( pchan_id int(11) UNSIGNED NOT NULL AUTO_INCREMENT, pchan_guid char(191) NOT NULL DEFAULT '', pchan_hash char(191) NOT NULL DEFAULT '', @@ -33,13 +34,11 @@ function run() { KEY pchan_guid (pchan_guid), KEY pchan_hash (pchan_hash) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4"); - if($r1) { - return UPDATE_SUCCESS; - } - } + if ($r1) { + return UPDATE_SUCCESS; + } + } - return UPDATE_FAILED; + return UPDATE_FAILED; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1197.php b/Zotlabs/Update/_1197.php index 72e4ca59d..6ac50b329 100644 --- a/Zotlabs/Update/_1197.php +++ b/Zotlabs/Update/_1197.php @@ -2,16 +2,16 @@ namespace Zotlabs\Update; -class _1197 { -function run() { +class _1197 +{ + public function run() + { - $r = q("select diaspora_meta from item where true limit 1"); - if($r) { - $r = q("ALTER TABLE item DROP diaspora_meta"); - } + $r = q("select diaspora_meta from item where true limit 1"); + if ($r) { + $r = q("ALTER TABLE item DROP diaspora_meta"); + } - return UPDATE_SUCCESS; + return UPDATE_SUCCESS; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1198.php b/Zotlabs/Update/_1198.php index d188c94f6..80b2a18c1 100644 --- a/Zotlabs/Update/_1198.php +++ b/Zotlabs/Update/_1198.php @@ -2,11 +2,13 @@ namespace Zotlabs\Update; -class _1198 { -function run() { +class _1198 +{ + public function run() + { - if(ACTIVE_DBTYPE == DBTYPE_MYSQL) { - $r = q("ALTER TABLE item + if (ACTIVE_DBTYPE == DBTYPE_MYSQL) { + $r = q("ALTER TABLE item DROP INDEX item_blocked, DROP INDEX item_unpublished, DROP INDEX item_deleted, @@ -15,10 +17,8 @@ function run() { DROP INDEX item_pending_remove, DROP INDEX item_type "); - } - - return UPDATE_SUCCESS; -} - + } + return UPDATE_SUCCESS; + } } diff --git a/Zotlabs/Update/_1199.php b/Zotlabs/Update/_1199.php index 7ab03b073..76e517f21 100644 --- a/Zotlabs/Update/_1199.php +++ b/Zotlabs/Update/_1199.php @@ -2,18 +2,18 @@ namespace Zotlabs\Update; -class _1199 { -function run() { +class _1199 +{ + public function run() + { - if(ACTIVE_DBTYPE == DBTYPE_MYSQL) { - $r = q("ALTER TABLE item + if (ACTIVE_DBTYPE == DBTYPE_MYSQL) { + $r = q("ALTER TABLE item DROP INDEX uid, ADD INDEX (item_type) "); - } + } - return UPDATE_SUCCESS; + return UPDATE_SUCCESS; + } } - - -} \ No newline at end of file diff --git a/Zotlabs/Update/_1200.php b/Zotlabs/Update/_1200.php index 9f7bfb152..31baf5d8b 100644 --- a/Zotlabs/Update/_1200.php +++ b/Zotlabs/Update/_1200.php @@ -2,23 +2,23 @@ namespace Zotlabs\Update; -class _1200 { -function run() { +class _1200 +{ + public function run() + { - if(ACTIVE_DBTYPE == DBTYPE_MYSQL) { - $r = q("ALTER TABLE item + if (ACTIVE_DBTYPE == DBTYPE_MYSQL) { + $r = q("ALTER TABLE item DROP INDEX item_type, ADD INDEX uid_item_type (uid, item_type) "); - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; - } - else { - return UPDATE_SUCCESS; - } - -} - + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } else { + return UPDATE_SUCCESS; + } + } } diff --git a/Zotlabs/Update/_1201.php b/Zotlabs/Update/_1201.php index 920a7401e..a1efe8bba 100644 --- a/Zotlabs/Update/_1201.php +++ b/Zotlabs/Update/_1201.php @@ -2,26 +2,26 @@ namespace Zotlabs\Update; -class _1201 { +class _1201 +{ - function run() { + public function run() + { - if(ACTIVE_DBTYPE == DBTYPE_MYSQL) { - $r = q("ALTER TABLE item + if (ACTIVE_DBTYPE == DBTYPE_MYSQL) { + $r = q("ALTER TABLE item DROP INDEX item_thread_top, ADD INDEX uid_item_thread_top (uid, item_thread_top), ADD INDEX uid_item_blocked (uid, item_blocked), ADD INDEX item_deleted_pending_remove_changed (item_deleted, item_pending_remove, changed) "); - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; - } - else { - return UPDATE_SUCCESS; - } - - } - + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } else { + return UPDATE_SUCCESS; + } + } } diff --git a/Zotlabs/Update/_1202.php b/Zotlabs/Update/_1202.php index c9ccd157b..b9e8fcd4e 100644 --- a/Zotlabs/Update/_1202.php +++ b/Zotlabs/Update/_1202.php @@ -2,14 +2,15 @@ namespace Zotlabs\Update; -class _1202 { +class _1202 +{ - function run() { + public function run() + { - // empty update in order to make the DB_UPDATE_VERSION equal to the current maximum update function - // rather than being one greater than the last known update + // empty update in order to make the DB_UPDATE_VERSION equal to the current maximum update function + // rather than being one greater than the last known update - return UPDATE_SUCCESS; - - } + return UPDATE_SUCCESS; + } } diff --git a/Zotlabs/Update/_1203.php b/Zotlabs/Update/_1203.php index 2968d209d..7888aff38 100644 --- a/Zotlabs/Update/_1203.php +++ b/Zotlabs/Update/_1203.php @@ -2,12 +2,14 @@ namespace Zotlabs\Update; -class _1203 { +class _1203 +{ - function run() { + public function run() + { - if(ACTIVE_DBTYPE == DBTYPE_MYSQL) { - $r = q("ALTER TABLE item + if (ACTIVE_DBTYPE == DBTYPE_MYSQL) { + $r = q("ALTER TABLE item DROP INDEX item_wall, DROP INDEX item_starred, DROP INDEX item_retained, @@ -16,14 +18,12 @@ class _1203 { ADD INDEX uid_item_retained (uid, item_retained) "); - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; - } - else { - return UPDATE_SUCCESS; - } - - } - + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } else { + return UPDATE_SUCCESS; + } + } } diff --git a/Zotlabs/Update/_1204.php b/Zotlabs/Update/_1204.php index 0b9204b9b..3bd9a7e3a 100644 --- a/Zotlabs/Update/_1204.php +++ b/Zotlabs/Update/_1204.php @@ -2,33 +2,33 @@ namespace Zotlabs\Update; -class _1204 { +class _1204 +{ - function run() { + public function run() + { - if(ACTIVE_DBTYPE == DBTYPE_POSTGRES) { - $r1 = q("ALTER TABLE poll ADD poll_guid text NOT NULL"); - $r2 = q("create index \"poll_guid_idx\" on poll (\"poll_guid\")"); - $r3 = q("ALTER TABLE poll_elm ADD pelm_guid text NOT NULL"); - $r4 = q("create index \"pelm_guid_idx\" on poll_elm (\"pelm_guid\")"); - $r5 = q("ALTER TABLE vote ADD vote_guid text NOT NULL"); - $r6 = q("create index \"vote_guid_idx\" on vote (\"vote_guid\")"); + if (ACTIVE_DBTYPE == DBTYPE_POSTGRES) { + $r1 = q("ALTER TABLE poll ADD poll_guid text NOT NULL"); + $r2 = q("create index \"poll_guid_idx\" on poll (\"poll_guid\")"); + $r3 = q("ALTER TABLE poll_elm ADD pelm_guid text NOT NULL"); + $r4 = q("create index \"pelm_guid_idx\" on poll_elm (\"pelm_guid\")"); + $r5 = q("ALTER TABLE vote ADD vote_guid text NOT NULL"); + $r6 = q("create index \"vote_guid_idx\" on vote (\"vote_guid\")"); - $r = ($r1 && $r2 && $r3 && $r4 && $r5 && $r6); - } - else { - $r1 = q("ALTER TABLE `poll` ADD `poll_guid` VARCHAR(191) NOT NULL, ADD INDEX `poll_guid` (`poll_guid`) "); - $r2 = q("ALTER TABLE `poll_elm` ADD `pelm_guid` VARCHAR(191) NOT NULL, ADD INDEX `pelm_guid` (`pelm_guid`) "); - $r3 = q("ALTER TABLE `vote` ADD `vote_guid` VARCHAR(191) NOT NULL, ADD INDEX `vote_guid` (`vote_guid`) "); + $r = ($r1 && $r2 && $r3 && $r4 && $r5 && $r6); + } else { + $r1 = q("ALTER TABLE `poll` ADD `poll_guid` VARCHAR(191) NOT NULL, ADD INDEX `poll_guid` (`poll_guid`) "); + $r2 = q("ALTER TABLE `poll_elm` ADD `pelm_guid` VARCHAR(191) NOT NULL, ADD INDEX `pelm_guid` (`pelm_guid`) "); + $r3 = q("ALTER TABLE `vote` ADD `vote_guid` VARCHAR(191) NOT NULL, ADD INDEX `vote_guid` (`vote_guid`) "); - $r = ($r1 && $r2 && $r3); - } - - if($r) - return UPDATE_SUCCESS; + $r = ($r1 && $r2 && $r3); + } - return UPDATE_FAILED; - - } + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } diff --git a/Zotlabs/Update/_1205.php b/Zotlabs/Update/_1205.php index 968833726..475ba3f4b 100644 --- a/Zotlabs/Update/_1205.php +++ b/Zotlabs/Update/_1205.php @@ -2,37 +2,36 @@ namespace Zotlabs\Update; -class _1205 { +class _1205 +{ - function run() { + public function run() + { - if(ACTIVE_DBTYPE == DBTYPE_MYSQL) { + if (ACTIVE_DBTYPE == DBTYPE_MYSQL) { + q("ALTER TABLE item DROP INDEX title"); + q("ALTER TABLE item DROP INDEX body"); + q("ALTER TABLE item DROP INDEX allow_cid"); + q("ALTER TABLE item DROP INDEX allow_gid"); + q("ALTER TABLE item DROP INDEX deny_cid"); + q("ALTER TABLE item DROP INDEX deny_gid"); + q("ALTER TABLE item DROP INDEX item_flags"); + q("ALTER TABLE item DROP INDEX item_restrict"); + q("ALTER TABLE item DROP INDEX aid"); - q("ALTER TABLE item DROP INDEX title"); - q("ALTER TABLE item DROP INDEX body"); - q("ALTER TABLE item DROP INDEX allow_cid"); - q("ALTER TABLE item DROP INDEX allow_gid"); - q("ALTER TABLE item DROP INDEX deny_cid"); - q("ALTER TABLE item DROP INDEX deny_gid"); - q("ALTER TABLE item DROP INDEX item_flags"); - q("ALTER TABLE item DROP INDEX item_restrict"); - q("ALTER TABLE item DROP INDEX aid"); - - $r = q("ALTER TABLE item + $r = q("ALTER TABLE item DROP INDEX item_private, ADD INDEX uid_item_private (uid, item_private), ADD INDEX item_wall (item_wall), ADD INDEX item_pending_remove_changed (item_pending_remove, changed) "); - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; - } - else { - return UPDATE_SUCCESS; - } - - } - + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } else { + return UPDATE_SUCCESS; + } + } } diff --git a/Zotlabs/Update/_1206.php b/Zotlabs/Update/_1206.php index 351d53ff6..3b368e2bd 100644 --- a/Zotlabs/Update/_1206.php +++ b/Zotlabs/Update/_1206.php @@ -2,23 +2,23 @@ namespace Zotlabs\Update; -class _1206 { +class _1206 +{ - function run() { + public function run() + { - if(ACTIVE_DBTYPE == DBTYPE_MYSQL) { - $r = q("ALTER TABLE item + if (ACTIVE_DBTYPE == DBTYPE_MYSQL) { + $r = q("ALTER TABLE item ADD INDEX uid_resource_type (uid, resource_type) "); - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; - } - else { - return UPDATE_SUCCESS; - } - - } - + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } else { + return UPDATE_SUCCESS; + } + } } diff --git a/Zotlabs/Update/_1207.php b/Zotlabs/Update/_1207.php index f53bc46ae..309d54718 100644 --- a/Zotlabs/Update/_1207.php +++ b/Zotlabs/Update/_1207.php @@ -2,23 +2,23 @@ namespace Zotlabs\Update; -class _1207 { +class _1207 +{ - function run() { + public function run() + { - if(ACTIVE_DBTYPE == DBTYPE_MYSQL) { - $r = q("ALTER TABLE item + if (ACTIVE_DBTYPE == DBTYPE_MYSQL) { + $r = q("ALTER TABLE item DROP INDEX resource_type "); - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; - } - else { - return UPDATE_SUCCESS; - } - - } - + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } else { + return UPDATE_SUCCESS; + } + } } diff --git a/Zotlabs/Update/_1208.php b/Zotlabs/Update/_1208.php index 840252694..19df1565c 100644 --- a/Zotlabs/Update/_1208.php +++ b/Zotlabs/Update/_1208.php @@ -2,25 +2,25 @@ namespace Zotlabs\Update; -class _1208 { +class _1208 +{ - function run() { + public function run() + { - if(ACTIVE_DBTYPE == DBTYPE_POSTGRES) { - $r1 = q("ALTER TABLE poll ADD poll_author text NOT NULL"); - $r2 = q("create index \"poll_author_idx\" on poll (\"poll_author\") "); + if (ACTIVE_DBTYPE == DBTYPE_POSTGRES) { + $r1 = q("ALTER TABLE poll ADD poll_author text NOT NULL"); + $r2 = q("create index \"poll_author_idx\" on poll (\"poll_author\") "); - $r = ($r1 && $r2); - } - else { - $r = q("ALTER TABLE `poll` ADD `poll_author` VARCHAR(191) NOT NULL AFTER `poll_votes`, + $r = ($r1 && $r2); + } else { + $r = q("ALTER TABLE `poll` ADD `poll_author` VARCHAR(191) NOT NULL AFTER `poll_votes`, ADD INDEX `poll_author` (`poll_author`)"); - } - - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; - - } + } + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } diff --git a/Zotlabs/Update/_1209.php b/Zotlabs/Update/_1209.php index dc95c3166..f09838368 100644 --- a/Zotlabs/Update/_1209.php +++ b/Zotlabs/Update/_1209.php @@ -2,25 +2,25 @@ namespace Zotlabs\Update; -class _1209 { +class _1209 +{ - function run() { + public function run() + { - if(ACTIVE_DBTYPE == DBTYPE_POSTGRES) { - $r1 = q("ALTER TABLE poll_elm ADD pelm_order numeric(6) NOT NULL DEFAULT '0' "); - $r2 = q("create index \"pelm_order_idx\" on poll_elm (\"pelm_order\")"); + if (ACTIVE_DBTYPE == DBTYPE_POSTGRES) { + $r1 = q("ALTER TABLE poll_elm ADD pelm_order numeric(6) NOT NULL DEFAULT '0' "); + $r2 = q("create index \"pelm_order_idx\" on poll_elm (\"pelm_order\")"); - $r = ($r1 && $r2); - } - else { - $r = q("ALTER TABLE `poll_elm` ADD `pelm_order` int(11) NOT NULL DEFAULT 0, + $r = ($r1 && $r2); + } else { + $r = q("ALTER TABLE `poll_elm` ADD `pelm_order` int(11) NOT NULL DEFAULT 0, ADD INDEX `pelm_order` (`pelm_order`)"); - } - - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; - - } + } + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } diff --git a/Zotlabs/Update/_1210.php b/Zotlabs/Update/_1210.php index 813e3fe82..8944faff6 100644 --- a/Zotlabs/Update/_1210.php +++ b/Zotlabs/Update/_1210.php @@ -2,11 +2,13 @@ namespace Zotlabs\Update; -class _1210 { +class _1210 +{ - function run() { + public function run() + { - $sql = "CREATE TABLE oauth_clients ( + $sql = "CREATE TABLE oauth_clients ( client_id VARCHAR(80) NOT NULL, client_secret VARCHAR(80), redirect_uri VARCHAR(2000), @@ -58,21 +60,20 @@ CREATE TABLE oauth_jwt ( ); "; - $arr = explode(';', $sql); - $errors = 0; - foreach($arr as $a) { - if(strlen(trim($a))) { - $r = dbq(trim($a)); - if(! $r) { - $errors ++; - } - } - } - - if(! $errors) - return UPDATE_SUCCESS; - return UPDATE_FAILED; - - } + $arr = explode(';', $sql); + $errors = 0; + foreach ($arr as $a) { + if (strlen(trim($a))) { + $r = dbq(trim($a)); + if (!$r) { + $errors++; + } + } + } + if (!$errors) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } diff --git a/Zotlabs/Update/_1211.php b/Zotlabs/Update/_1211.php index 26e25536d..9ffa0147c 100644 --- a/Zotlabs/Update/_1211.php +++ b/Zotlabs/Update/_1211.php @@ -2,25 +2,25 @@ namespace Zotlabs\Update; -class _1211 { +class _1211 +{ - function run() { + public function run() + { - if(ACTIVE_DBTYPE == DBTYPE_POSTGRES) { - $r1 = q("ALTER TABLE channel ADD channel_active timestamp NOT NULL DEFAULT '0001-01-01 00:00:00' "); - $r2 = q("create index \"channel_active_idx\" on channel (\"channel_active\")"); + if (ACTIVE_DBTYPE == DBTYPE_POSTGRES) { + $r1 = q("ALTER TABLE channel ADD channel_active timestamp NOT NULL DEFAULT '0001-01-01 00:00:00' "); + $r2 = q("create index \"channel_active_idx\" on channel (\"channel_active\")"); - $r = ($r1 && $r2); - } - else { - $r = q("ALTER TABLE `channel` ADD `channel_active` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' , + $r = ($r1 && $r2); + } else { + $r = q("ALTER TABLE `channel` ADD `channel_active` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' , ADD INDEX `channel_active` (`channel_active`)"); - } - - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; - - } + } + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } diff --git a/Zotlabs/Update/_1212.php b/Zotlabs/Update/_1212.php index f15ba8a71..4e10f2071 100644 --- a/Zotlabs/Update/_1212.php +++ b/Zotlabs/Update/_1212.php @@ -2,25 +2,27 @@ namespace Zotlabs\Update; -class _1212 { +use Zotlabs\Access\PermissionRoles; - function run() { +class _1212 +{ - $r = q("select channel_id from channel where true"); - if($r) { - foreach($r as $rv) { - $role = get_pconfig($rv['channel_id'],'system','permissions_role'); - if($role !== 'custom') { - $role_permissions = \Zotlabs\Access\PermissionRoles::role_perms($role); - if(array_key_exists('limits',$role_permissions) && array_key_exists('post_comments',$role_permissions['limits'])) { - set_pconfig($rv['channel_id'],'perm_limits','post_comments',$role_permissions['limits']['post_comments']); - } - } - } - } + public function run() + { - return UPDATE_SUCCESS; - - } + $r = q("select channel_id from channel where true"); + if ($r) { + foreach ($r as $rv) { + $role = get_pconfig($rv['channel_id'], 'system', 'permissions_role'); + if ($role !== 'custom') { + $role_permissions = PermissionRoles::role_perms($role); + if (array_key_exists('limits', $role_permissions) && array_key_exists('post_comments', $role_permissions['limits'])) { + set_pconfig($rv['channel_id'], 'perm_limits', 'post_comments', $role_permissions['limits']['post_comments']); + } + } + } + } + return UPDATE_SUCCESS; + } } diff --git a/Zotlabs/Update/_1213.php b/Zotlabs/Update/_1213.php index d396829a6..3aa37a29a 100644 --- a/Zotlabs/Update/_1213.php +++ b/Zotlabs/Update/_1213.php @@ -2,27 +2,27 @@ namespace Zotlabs\Update; -class _1213 { +class _1213 +{ - function run() { - if(ACTIVE_DBTYPE == DBTYPE_MYSQL) { - q("START TRANSACTION"); + public function run() + { + if (ACTIVE_DBTYPE == DBTYPE_MYSQL) { + q("START TRANSACTION"); - $r = q("ALTER TABLE abconfig + $r = q("ALTER TABLE abconfig DROP INDEX chan, DROP INDEX xchan, ADD INDEX chan_xchan (chan, xchan) "); - if($r) { - q("COMMIT"); - return UPDATE_SUCCESS; - } - else { - q("ROLLBACK"); - return UPDATE_FAILED; - } - } - } - + if ($r) { + q("COMMIT"); + return UPDATE_SUCCESS; + } else { + q("ROLLBACK"); + return UPDATE_FAILED; + } + } + } } diff --git a/Zotlabs/Update/_1214.php b/Zotlabs/Update/_1214.php index 06e5d96ed..5d7fe4d22 100644 --- a/Zotlabs/Update/_1214.php +++ b/Zotlabs/Update/_1214.php @@ -2,56 +2,53 @@ namespace Zotlabs\Update; -class _1214 { +class _1214 +{ - function run() { - if(ACTIVE_DBTYPE == DBTYPE_POSTGRES) { - q("START TRANSACTION"); + public function run() + { + if (ACTIVE_DBTYPE == DBTYPE_POSTGRES) { + q("START TRANSACTION"); - $r1 = q("ALTER TABLE oauth_clients ALTER COLUMN user_id TYPE bigint USING user_id::bigint"); - $r2 = q("ALTER TABLE oauth_clients ALTER COLUMN user_id SET NOT NULL"); - $r3 = q("ALTER TABLE oauth_clients ALTER COLUMN user_id SET DEFAULT 0"); + $r1 = q("ALTER TABLE oauth_clients ALTER COLUMN user_id TYPE bigint USING user_id::bigint"); + $r2 = q("ALTER TABLE oauth_clients ALTER COLUMN user_id SET NOT NULL"); + $r3 = q("ALTER TABLE oauth_clients ALTER COLUMN user_id SET DEFAULT 0"); - $r4 = q("ALTER TABLE oauth_access_tokens ALTER COLUMN user_id TYPE bigint USING user_id::bigint"); - $r5 = q("ALTER TABLE oauth_access_tokens ALTER COLUMN user_id SET NOT NULL"); - $r6 = q("ALTER TABLE oauth_access_tokens ALTER COLUMN user_id SET DEFAULT 0"); + $r4 = q("ALTER TABLE oauth_access_tokens ALTER COLUMN user_id TYPE bigint USING user_id::bigint"); + $r5 = q("ALTER TABLE oauth_access_tokens ALTER COLUMN user_id SET NOT NULL"); + $r6 = q("ALTER TABLE oauth_access_tokens ALTER COLUMN user_id SET DEFAULT 0"); - $r7 = q("ALTER TABLE oauth_authorization_codes ALTER COLUMN user_id TYPE bigint USING user_id::bigint"); - $r8 = q("ALTER TABLE oauth_authorization_codes ALTER COLUMN user_id SET NOT NULL"); - $r9 = q("ALTER TABLE oauth_authorization_codes ALTER COLUMN user_id SET DEFAULT 0"); + $r7 = q("ALTER TABLE oauth_authorization_codes ALTER COLUMN user_id TYPE bigint USING user_id::bigint"); + $r8 = q("ALTER TABLE oauth_authorization_codes ALTER COLUMN user_id SET NOT NULL"); + $r9 = q("ALTER TABLE oauth_authorization_codes ALTER COLUMN user_id SET DEFAULT 0"); - $r10 = q("ALTER TABLE oauth_refresh_tokens ALTER COLUMN user_id TYPE bigint USING user_id::bigint"); - $r11 = q("ALTER TABLE oauth_refresh_tokens ALTER COLUMN user_id SET NOT NULL"); - $r12 = q("ALTER TABLE oauth_refresh_tokens ALTER COLUMN user_id SET DEFAULT 0"); + $r10 = q("ALTER TABLE oauth_refresh_tokens ALTER COLUMN user_id TYPE bigint USING user_id::bigint"); + $r11 = q("ALTER TABLE oauth_refresh_tokens ALTER COLUMN user_id SET NOT NULL"); + $r12 = q("ALTER TABLE oauth_refresh_tokens ALTER COLUMN user_id SET DEFAULT 0"); - if($r1 && $r2 && $r3 && $r4 && $r5 && $r6 && $r7 && $r8 && $r9 && $r10 && $r11 && $r12) { - q("COMMIT"); - return UPDATE_SUCCESS; - } - else { - q("ROLLBACK"); - return UPDATE_FAILED; - } - } - else { - q("START TRANSACTION"); + if ($r1 && $r2 && $r3 && $r4 && $r5 && $r6 && $r7 && $r8 && $r9 && $r10 && $r11 && $r12) { + q("COMMIT"); + return UPDATE_SUCCESS; + } else { + q("ROLLBACK"); + return UPDATE_FAILED; + } + } else { + q("START TRANSACTION"); - $r1 = q("ALTER TABLE oauth_clients MODIFY COLUMN user_id int(10) unsigned NOT NULL DEFAULT 0"); - $r2 = q("ALTER TABLE oauth_access_tokens MODIFY COLUMN user_id int(10) unsigned NOT NULL DEFAULT 0"); - $r3 = q("ALTER TABLE oauth_authorization_codes MODIFY COLUMN user_id int(10) unsigned NOT NULL DEFAULT 0"); - $r4 = q("ALTER TABLE oauth_refresh_tokens MODIFY COLUMN user_id int(10) unsigned NOT NULL DEFAULT 0"); - - if($r1 && $r2 && $r3 && $r4) { - q("COMMIT"); - return UPDATE_SUCCESS; - } - else { - q("ROLLBACK"); - return UPDATE_FAILED; - } - - } - } + $r1 = q("ALTER TABLE oauth_clients MODIFY COLUMN user_id int(10) unsigned NOT NULL DEFAULT 0"); + $r2 = q("ALTER TABLE oauth_access_tokens MODIFY COLUMN user_id int(10) unsigned NOT NULL DEFAULT 0"); + $r3 = q("ALTER TABLE oauth_authorization_codes MODIFY COLUMN user_id int(10) unsigned NOT NULL DEFAULT 0"); + $r4 = q("ALTER TABLE oauth_refresh_tokens MODIFY COLUMN user_id int(10) unsigned NOT NULL DEFAULT 0"); + if ($r1 && $r2 && $r3 && $r4) { + q("COMMIT"); + return UPDATE_SUCCESS; + } else { + q("ROLLBACK"); + return UPDATE_FAILED; + } + } + } } diff --git a/Zotlabs/Update/_1215.php b/Zotlabs/Update/_1215.php index 3acaab587..c007645c0 100644 --- a/Zotlabs/Update/_1215.php +++ b/Zotlabs/Update/_1215.php @@ -2,25 +2,25 @@ namespace Zotlabs\Update; -class _1215 { +class _1215 +{ - function run() { - q("START TRANSACTION"); + public function run() + { + q("START TRANSACTION"); - // this will fix mastodon hubloc_url - $r1 = q("UPDATE hubloc SET hubloc_url = LEFT(hubloc_url, POSITION('/users' IN hubloc_url)-1) WHERE POSITION('/users' IN hubloc_url)>0"); + // this will fix mastodon hubloc_url + $r1 = q("UPDATE hubloc SET hubloc_url = LEFT(hubloc_url, POSITION('/users' IN hubloc_url)-1) WHERE POSITION('/users' IN hubloc_url)>0"); - // this will fix peertube hubloc_url - $r2 = q("UPDATE hubloc SET hubloc_url = LEFT(hubloc_url, POSITION('/account' IN hubloc_url)-1) WHERE POSITION('/account' IN hubloc_url)>0"); - - if($r1 && $r2) { - q("COMMIT"); - return UPDATE_SUCCESS; - } - else { - q("ROLLBACK"); - return UPDATE_FAILED; - } - } + // this will fix peertube hubloc_url + $r2 = q("UPDATE hubloc SET hubloc_url = LEFT(hubloc_url, POSITION('/account' IN hubloc_url)-1) WHERE POSITION('/account' IN hubloc_url)>0"); + if ($r1 && $r2) { + q("COMMIT"); + return UPDATE_SUCCESS; + } else { + q("ROLLBACK"); + return UPDATE_FAILED; + } + } } diff --git a/Zotlabs/Update/_1216.php b/Zotlabs/Update/_1216.php index 69f2be15a..b42442734 100644 --- a/Zotlabs/Update/_1216.php +++ b/Zotlabs/Update/_1216.php @@ -2,18 +2,18 @@ namespace Zotlabs\Update; -class _1216 { +class _1216 +{ - function run() { + public function run() + { - $r = dbq("UPDATE xchan set xchan_name = 'unknown' where xchan_name like '%<%' "); - - if($r) { - return UPDATE_SUCCESS; - } - else { - return UPDATE_FAILED; - } - } + $r = dbq("UPDATE xchan set xchan_name = 'unknown' where xchan_name like '%<%' "); + if ($r) { + return UPDATE_SUCCESS; + } else { + return UPDATE_FAILED; + } + } } diff --git a/Zotlabs/Update/_1217.php b/Zotlabs/Update/_1217.php index 15d2d06b3..5d5d26fd0 100644 --- a/Zotlabs/Update/_1217.php +++ b/Zotlabs/Update/_1217.php @@ -2,21 +2,20 @@ namespace Zotlabs\Update; -class _1217 { +class _1217 +{ - function run() { - if(ACTIVE_DBTYPE == DBTYPE_POSTGRES) { - $r = q("ALTER TABLE app ADD app_options smallint NOT NULL DEFAULT '0' "); - } - else { - $r = q("ALTER TABLE app ADD app_options int(11) NOT NULL DEFAULT 0 "); - - } + public function run() + { + if (ACTIVE_DBTYPE == DBTYPE_POSTGRES) { + $r = q("ALTER TABLE app ADD app_options smallint NOT NULL DEFAULT '0' "); + } else { + $r = q("ALTER TABLE app ADD app_options int(11) NOT NULL DEFAULT 0 "); + } - if($r) { - return UPDATE_SUCCESS; - } - return UPDATE_FAILED; - } + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } - diff --git a/Zotlabs/Update/_1218.php b/Zotlabs/Update/_1218.php index 07c7dba20..fb4284cb0 100644 --- a/Zotlabs/Update/_1218.php +++ b/Zotlabs/Update/_1218.php @@ -2,30 +2,31 @@ namespace Zotlabs\Update; -class _1218 { +class _1218 +{ - function run() { + public function run() + { - if(ACTIVE_DBTYPE == DBTYPE_POSTGRES) { - $r1 = q("ALTER TABLE hubloc add hubloc_id_url text NOT NULL DEFAULT ''"); - $r2 = q("create index \"hubloc_id_url\" on hubloc (\"hubloc_id_url\")"); - $r3 = q("ALTER TABLE hubloc add hubloc_site_id text NOT NULL DEFAULT ''"); - $r4 = q("create index \"hubloc_site_id\" on hubloc (\"hubloc_site_id\")"); + if (ACTIVE_DBTYPE == DBTYPE_POSTGRES) { + $r1 = q("ALTER TABLE hubloc add hubloc_id_url text NOT NULL DEFAULT ''"); + $r2 = q("create index \"hubloc_id_url\" on hubloc (\"hubloc_id_url\")"); + $r3 = q("ALTER TABLE hubloc add hubloc_site_id text NOT NULL DEFAULT ''"); + $r4 = q("create index \"hubloc_site_id\" on hubloc (\"hubloc_site_id\")"); - $r = $r1 && $r2 && $r3 && $r4; - } + $r = $r1 && $r2 && $r3 && $r4; + } - if(ACTIVE_DBTYPE == DBTYPE_MYSQL) { - $r1 = q("ALTER TABLE hubloc add hubloc_id_url varchar(191) NOT NULL, ADD INDEX hubloc_id_url (hubloc_id_url)"); - $r2 = q("ALTER TABLE hubloc add hubloc_site_id varchar(191) NOT NULL, ADD INDEX hubloc_site_id (hubloc_site_id)"); + if (ACTIVE_DBTYPE == DBTYPE_MYSQL) { + $r1 = q("ALTER TABLE hubloc add hubloc_id_url varchar(191) NOT NULL, ADD INDEX hubloc_id_url (hubloc_id_url)"); + $r2 = q("ALTER TABLE hubloc add hubloc_site_id varchar(191) NOT NULL, ADD INDEX hubloc_site_id (hubloc_site_id)"); - $r = $r1 && $r2; - } - - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; - - } + $r = $r1 && $r2; + } + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } diff --git a/Zotlabs/Update/_1219.php b/Zotlabs/Update/_1219.php index be2534001..831ccda10 100644 --- a/Zotlabs/Update/_1219.php +++ b/Zotlabs/Update/_1219.php @@ -2,25 +2,26 @@ namespace Zotlabs\Update; -class _1219 { +class _1219 +{ - function run() { - q("START TRANSACTION"); + public function run() + { + q("START TRANSACTION"); - $r = q("DELETE FROM xchan WHERE + $r = q( + "DELETE FROM xchan WHERE xchan_hash like '%s' AND xchan_network = 'activitypub'", - dbesc(z_root()) . '%' - ); - - if($r) { - q("COMMIT"); - return UPDATE_SUCCESS; - } - else { - q("ROLLBACK"); - return UPDATE_FAILED; - } - } + dbesc(z_root()) . '%' + ); + if ($r) { + q("COMMIT"); + return UPDATE_SUCCESS; + } else { + q("ROLLBACK"); + return UPDATE_FAILED; + } + } } diff --git a/Zotlabs/Update/_1220.php b/Zotlabs/Update/_1220.php index 6ce09c16b..24dcf6129 100644 --- a/Zotlabs/Update/_1220.php +++ b/Zotlabs/Update/_1220.php @@ -2,12 +2,14 @@ namespace Zotlabs\Update; -class _1220 { +class _1220 +{ - function run() { + public function run() + { - if(ACTIVE_DBTYPE == DBTYPE_POSTGRES) { - $r1 = q("CREATE TABLE listeners ( + if (ACTIVE_DBTYPE == DBTYPE_POSTGRES) { + $r1 = q("CREATE TABLE listeners ( id serial NOT NULL, target_id text NOT NULL, portable_id text NOT NULL, @@ -15,16 +17,15 @@ class _1220 { PRIMARY KEY (id) )"); - $r2 = q("create index \"target_id_idx\" on listeners (\"target_id\")"); - $r3 = q("create index \"portable_id_idx\" on listeners (\"portable_id\")"); - $r4 = q("create index \"ltype_idx\" on listeners (\"ltype\")"); + $r2 = q("create index \"target_id_idx\" on listeners (\"target_id\")"); + $r3 = q("create index \"portable_id_idx\" on listeners (\"portable_id\")"); + $r4 = q("create index \"ltype_idx\" on listeners (\"ltype\")"); - $r = $r1 && $r2 && $r3 && $r4; + $r = $r1 && $r2 && $r3 && $r4; + } - } - - if(ACTIVE_DBTYPE == DBTYPE_MYSQL) { - $r = q("CREATE TABLE IF NOT EXISTS listeners ( + if (ACTIVE_DBTYPE == DBTYPE_MYSQL) { + $r = q("CREATE TABLE IF NOT EXISTS listeners ( id int(11) NOT NULL AUTO_INCREMENT, target_id varchar(191) NOT NULL DEFAULT '', portable_id varchar(191) NOT NULL DEFAULT '', @@ -34,14 +35,11 @@ class _1220 { KEY portable_id (portable_id), KEY ltype (ltype) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4"); + } - } - - if($r) { - return UPDATE_SUCCESS; - } - return UPDATE_FAILED; - - } - + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } diff --git a/Zotlabs/Update/_1221.php b/Zotlabs/Update/_1221.php index 252fcaa90..0f7aecea8 100644 --- a/Zotlabs/Update/_1221.php +++ b/Zotlabs/Update/_1221.php @@ -2,19 +2,19 @@ namespace Zotlabs\Update; -class _1221 { +class _1221 +{ - function run() { + public function run() + { - $r1 = q("ALTER table " . TQUOT . 'groups' . TQUOT . " rename to pgrp "); - $r2 = q("ALTER table " . TQUOT . 'group_member' . TQUOT . " rename to pgrp_member "); + $r1 = q("ALTER table " . TQUOT . 'groups' . TQUOT . " rename to pgrp "); + $r2 = q("ALTER table " . TQUOT . 'group_member' . TQUOT . " rename to pgrp_member "); - if($r1 && $r2) { - return UPDATE_SUCCESS; - } - return UPDATE_FAILED; - - } - + if ($r1 && $r2) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } diff --git a/Zotlabs/Update/_1222.php b/Zotlabs/Update/_1222.php index 1db49edff..2b33d1060 100644 --- a/Zotlabs/Update/_1222.php +++ b/Zotlabs/Update/_1222.php @@ -2,18 +2,18 @@ namespace Zotlabs\Update; -class _1222 { +class _1222 +{ - function run() { + public function run() + { - $r = dbq("UPDATE hubloc set hubloc_id_url = hubloc_hash where hubloc_id_url = '' "); - - if($r) { - return UPDATE_SUCCESS; - } - else { - return UPDATE_FAILED; - } - } + $r = dbq("UPDATE hubloc set hubloc_id_url = hubloc_hash where hubloc_id_url = '' "); + if ($r) { + return UPDATE_SUCCESS; + } else { + return UPDATE_FAILED; + } + } } diff --git a/Zotlabs/Update/_1223.php b/Zotlabs/Update/_1223.php index bc50b385d..3e77d3a75 100644 --- a/Zotlabs/Update/_1223.php +++ b/Zotlabs/Update/_1223.php @@ -2,32 +2,37 @@ namespace Zotlabs\Update; -class _1223 { - - function run() { - foreach( [ 'abconfig','config','pconfig','xconfig','iconfig' ] as $tbl) { - while(1) { - $r = q("select id, v from %s where v like '%s' limit 100 ", - dbesc($tbl), - dbesc('a:%') - ); - if(! $r) { - break; - } - foreach($r as $rv) { - $s = unserialize($rv['v']); - if($s && is_array($s)) - $s = serialise($s); - else - $s = $rv['v']; - q("update %s set v = '%s' where id = %d", - dbesc($tbl), - dbesc($s), - dbesc($rv['id']) - ); - } - } - } - return UPDATE_SUCCESS; - } +class _1223 +{ + + public function run() + { + foreach (['abconfig', 'config', 'pconfig', 'xconfig', 'iconfig'] as $tbl) { + while (1) { + $r = q( + "select id, v from %s where v like '%s' limit 100 ", + dbesc($tbl), + dbesc('a:%') + ); + if (!$r) { + break; + } + foreach ($r as $rv) { + $s = unserialize($rv['v']); + if ($s && is_array($s)) { + $s = serialise($s); + } else { + $s = $rv['v']; + } + q( + "update %s set v = '%s' where id = %d", + dbesc($tbl), + dbesc($s), + dbesc($rv['id']) + ); + } + } + } + return UPDATE_SUCCESS; + } } diff --git a/Zotlabs/Update/_1224.php b/Zotlabs/Update/_1224.php index cdbf1a52f..df1949190 100644 --- a/Zotlabs/Update/_1224.php +++ b/Zotlabs/Update/_1224.php @@ -2,11 +2,12 @@ namespace Zotlabs\Update; -class _1224 { - - function run() { - q("update abook set abook_closeness = 80 where abook_closeness = 0 and abook_self = 0"); - return UPDATE_SUCCESS; +class _1224 +{ - } -} \ No newline at end of file + public function run() + { + q("update abook set abook_closeness = 80 where abook_closeness = 0 and abook_self = 0"); + return UPDATE_SUCCESS; + } +} diff --git a/Zotlabs/Update/_1225.php b/Zotlabs/Update/_1225.php index f387dbfc1..c0ededf95 100644 --- a/Zotlabs/Update/_1225.php +++ b/Zotlabs/Update/_1225.php @@ -4,22 +4,24 @@ namespace Zotlabs\Update; use Zotlabs\Lib\Apps; -class _1225 { - - function run() { - q("delete from app where app_channel = 0"); +class _1225 +{ - $apps = Apps::get_system_apps(false); + public function run() + { + q("delete from app where app_channel = 0"); - if($apps) { - foreach($apps as $app) { - $app['uid'] = 0; - $app['guid'] = hash('whirlpool',$app['name']); - $app['system'] = 1; - Apps::app_install(0,$app); - } - } + $apps = Apps::get_system_apps(false); - return UPDATE_SUCCESS; - } -} \ No newline at end of file + if ($apps) { + foreach ($apps as $app) { + $app['uid'] = 0; + $app['guid'] = hash('whirlpool', $app['name']); + $app['system'] = 1; + Apps::app_install(0, $app); + } + } + + return UPDATE_SUCCESS; + } +} diff --git a/Zotlabs/Update/_1226.php b/Zotlabs/Update/_1226.php index c112683d5..188a6afb4 100644 --- a/Zotlabs/Update/_1226.php +++ b/Zotlabs/Update/_1226.php @@ -2,21 +2,21 @@ namespace Zotlabs\Update; +class _1226 +{ -class _1226 { - - function run() { + public function run() + { - if(ACTIVE_DBTYPE == DBTYPE_POSTGRES) { - $r = q("alter table item add item_level bigint NOT NULL DEFAULT '0'"); - } - else { - $r = q("alter table item add item_level int(10) NOT NULL DEFAULT 0 "); - } + if (ACTIVE_DBTYPE == DBTYPE_POSTGRES) { + $r = q("alter table item add item_level bigint NOT NULL DEFAULT '0'"); + } else { + $r = q("alter table item add item_level int(10) NOT NULL DEFAULT 0 "); + } - if($r) { - return UPDATE_SUCCESS; - } - return UPDATE_FAILED; - } -} \ No newline at end of file + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } +} diff --git a/Zotlabs/Update/_1227.php b/Zotlabs/Update/_1227.php index 9fedd7083..f2a143373 100644 --- a/Zotlabs/Update/_1227.php +++ b/Zotlabs/Update/_1227.php @@ -2,31 +2,30 @@ namespace Zotlabs\Update; +class _1227 +{ -class _1227 { + public function run() + { - function run() { + q("START TRANSACTION"); - q("START TRANSACTION"); + if (ACTIVE_DBTYPE == DBTYPE_POSTGRES) { + $r1 = q("ALTER TABLE item ADD uuid text NOT NULL DEFAULT '' "); + $r2 = q("create index \"uuid_idx\" on item (\"uuid\")"); - if(ACTIVE_DBTYPE == DBTYPE_POSTGRES) { - $r1 = q("ALTER TABLE item ADD uuid text NOT NULL DEFAULT '' "); - $r2 = q("create index \"uuid_idx\" on item (\"uuid\")"); - - $r = ($r1 && $r2); - } - else { - $r = q("ALTER TABLE `item` ADD `uuid` char(191) NOT NULL DEFAULT '' , + $r = ($r1 && $r2); + } else { + $r = q("ALTER TABLE `item` ADD `uuid` char(191) NOT NULL DEFAULT '' , ADD INDEX `uuid` (`uuid`)"); - } + } - if($r) { - q("COMMIT"); - return UPDATE_SUCCESS; - } - - q("ROLLBACK"); - return UPDATE_FAILED; - } + if ($r) { + q("COMMIT"); + return UPDATE_SUCCESS; + } + q("ROLLBACK"); + return UPDATE_FAILED; + } } diff --git a/Zotlabs/Update/_1228.php b/Zotlabs/Update/_1228.php index 525eab402..310e5edb6 100644 --- a/Zotlabs/Update/_1228.php +++ b/Zotlabs/Update/_1228.php @@ -4,19 +4,19 @@ namespace Zotlabs\Update; use Zotlabs\Lib\PConfig; -class _1228 { +class _1228 +{ - function run() { + public function run() + { - $r = q("select channel_id from channel where true"); - if ($r) { - foreach ($r as $rv) { - PConfig::Set($rv['channel_id'],'perm_limits','moderated', (string) PERMS_SPECIFIC); - } - } - - return UPDATE_SUCCESS; - - } + $r = q("select channel_id from channel where true"); + if ($r) { + foreach ($r as $rv) { + PConfig::Set($rv['channel_id'], 'perm_limits', 'moderated', (string)PERMS_SPECIFIC); + } + } + return UPDATE_SUCCESS; + } } diff --git a/Zotlabs/Update/_1229.php b/Zotlabs/Update/_1229.php index a8fbd1cb1..52c45b12f 100644 --- a/Zotlabs/Update/_1229.php +++ b/Zotlabs/Update/_1229.php @@ -2,22 +2,21 @@ namespace Zotlabs\Update; -class _1229 { +class _1229 +{ - function run() { + public function run() + { - if(ACTIVE_DBTYPE == DBTYPE_POSTGRES) { - $r = q("ALTER TABLE " . TQUOT . 'xchan' . TQUOT . " RENAME COLUMN xchan_pubforum to xchan_type "); - } - else { - $r = q("ALTER TABLE " . TQUOT . 'xchan' . TQUOT . " CHANGE xchan_pubforum xchan_type tinyint(1) NOT NULL default 0 "); - } - - if($r) { - return UPDATE_SUCCESS; - } - return UPDATE_FAILED; - - } + if (ACTIVE_DBTYPE == DBTYPE_POSTGRES) { + $r = q("ALTER TABLE " . TQUOT . 'xchan' . TQUOT . " RENAME COLUMN xchan_pubforum to xchan_type "); + } else { + $r = q("ALTER TABLE " . TQUOT . 'xchan' . TQUOT . " CHANGE xchan_pubforum xchan_type tinyint(1) NOT NULL default 0 "); + } + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } diff --git a/Zotlabs/Update/_1230.php b/Zotlabs/Update/_1230.php index 824d9be6a..7566793d5 100644 --- a/Zotlabs/Update/_1230.php +++ b/Zotlabs/Update/_1230.php @@ -2,18 +2,18 @@ namespace Zotlabs\Update; -class _1230 { +class _1230 +{ - function run() { + public function run() + { - $r1 = q("ALTER TABLE " . TQUOT . 'xchan' . TQUOT . " DROP COLUMN xchan_instance_url "); - $r2 = q("ALTER TABLE " . TQUOT . 'xchan' . TQUOT . " DROP COLUMN xchan_flags "); - - if($r1 && $r2) { - return UPDATE_SUCCESS; - } - return UPDATE_FAILED; - - } + $r1 = q("ALTER TABLE " . TQUOT . 'xchan' . TQUOT . " DROP COLUMN xchan_instance_url "); + $r2 = q("ALTER TABLE " . TQUOT . 'xchan' . TQUOT . " DROP COLUMN xchan_flags "); + if ($r1 && $r2) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } } diff --git a/Zotlabs/Update/_1231.php b/Zotlabs/Update/_1231.php index beec7ac72..e5ac851e3 100644 --- a/Zotlabs/Update/_1231.php +++ b/Zotlabs/Update/_1231.php @@ -2,31 +2,30 @@ namespace Zotlabs\Update; +class _1231 +{ -class _1231 { + public function run() + { - function run() { + q("START TRANSACTION"); - q("START TRANSACTION"); + if (ACTIVE_DBTYPE == DBTYPE_POSTGRES) { + $r1 = q("ALTER TABLE channel ADD channel_parent text NOT NULL DEFAULT '' "); + $r2 = q("create index \"channel_parent\" on channel (\"channel_parent\")"); - if(ACTIVE_DBTYPE == DBTYPE_POSTGRES) { - $r1 = q("ALTER TABLE channel ADD channel_parent text NOT NULL DEFAULT '' "); - $r2 = q("create index \"channel_parent\" on channel (\"channel_parent\")"); - - $r = ($r1 && $r2); - } - else { - $r = q("ALTER TABLE `channel` ADD `channel_parent` char(191) NOT NULL DEFAULT '' , + $r = ($r1 && $r2); + } else { + $r = q("ALTER TABLE `channel` ADD `channel_parent` char(191) NOT NULL DEFAULT '' , ADD INDEX `channel_parent` (`channel_parent`)"); - } + } - if($r) { - q("COMMIT"); - return UPDATE_SUCCESS; - } - - q("ROLLBACK"); - return UPDATE_FAILED; - } + if ($r) { + q("COMMIT"); + return UPDATE_SUCCESS; + } + q("ROLLBACK"); + return UPDATE_FAILED; + } } diff --git a/Zotlabs/Update/_1232.php b/Zotlabs/Update/_1232.php index 89236a8c5..9f88bf504 100644 --- a/Zotlabs/Update/_1232.php +++ b/Zotlabs/Update/_1232.php @@ -2,31 +2,30 @@ namespace Zotlabs\Update; -class _1232 { +class _1232 +{ - function run() { - - q("START TRANSACTION"); + public function run() + { - if(ACTIVE_DBTYPE == DBTYPE_POSTGRES) { - $r1 = q("ALTER TABLE photo ADD expires timestamp NOT NULL DEFAULT '0001-01-01 00:00:00' "); - $r2 = q("create index \"photo_expires_idx\" on photo (\"expires\")"); + q("START TRANSACTION"); - $r = ($r1 && $r2); - } - else { - $r = q("ALTER TABLE `photo` ADD `expires` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' , + if (ACTIVE_DBTYPE == DBTYPE_POSTGRES) { + $r1 = q("ALTER TABLE photo ADD expires timestamp NOT NULL DEFAULT '0001-01-01 00:00:00' "); + $r2 = q("create index \"photo_expires_idx\" on photo (\"expires\")"); + + $r = ($r1 && $r2); + } else { + $r = q("ALTER TABLE `photo` ADD `expires` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' , ADD INDEX `expires` (`expires`)"); - } + } - if($r) { - q("COMMIT"); - return UPDATE_SUCCESS; - } - - q("ROLLBACK"); - return UPDATE_FAILED; - - } + if ($r) { + q("COMMIT"); + return UPDATE_SUCCESS; + } + q("ROLLBACK"); + return UPDATE_FAILED; + } } diff --git a/Zotlabs/Update/_1233.php b/Zotlabs/Update/_1233.php index 392fd3590..55f96f754 100644 --- a/Zotlabs/Update/_1233.php +++ b/Zotlabs/Update/_1233.php @@ -2,33 +2,34 @@ namespace Zotlabs\Update; -class _1233 { +class _1233 +{ - function run() { - - q("START TRANSACTION"); + public function run() + { - $r = q("ALTER TABLE abook ADD abook_censor INT UNSIGNED NOT NULL DEFAULT '0' "); + q("START TRANSACTION"); - if($r) { - q("COMMIT"); - return UPDATE_SUCCESS; - } + $r = q("ALTER TABLE abook ADD abook_censor INT UNSIGNED NOT NULL DEFAULT '0' "); - q("ROLLBACK"); - return UPDATE_FAILED; + if ($r) { + q("COMMIT"); + return UPDATE_SUCCESS; + } - } + q("ROLLBACK"); + return UPDATE_FAILED; + } - function verify() { + public function verify() + { - $columns = db_columns('abook'); + $columns = db_columns('abook'); - if(in_array('abook_censor',$columns)) { - return true; - } - - return false; - } + if (in_array('abook_censor', $columns)) { + return true; + } + return false; + } } diff --git a/Zotlabs/Update/_1234.php b/Zotlabs/Update/_1234.php index 229669d14..0765a90f0 100644 --- a/Zotlabs/Update/_1234.php +++ b/Zotlabs/Update/_1234.php @@ -2,33 +2,34 @@ namespace Zotlabs\Update; -class _1234 { +class _1234 +{ - function run() { - - q("START TRANSACTION"); + public function run() + { - $r = q("ALTER TABLE oauth_clients ADD client_name VARCHAR(80) "); + q("START TRANSACTION"); - if($r) { - q("COMMIT"); - return UPDATE_SUCCESS; - } + $r = q("ALTER TABLE oauth_clients ADD client_name VARCHAR(80) "); - q("ROLLBACK"); - return UPDATE_FAILED; + if ($r) { + q("COMMIT"); + return UPDATE_SUCCESS; + } - } + q("ROLLBACK"); + return UPDATE_FAILED; + } - function verify() { + public function verify() + { - $columns = db_columns('oauth_clients'); + $columns = db_columns('oauth_clients'); - if(in_array('client_name',$columns)) { - return true; - } - - return false; - } + if (in_array('client_name', $columns)) { + return true; + } + return false; + } } diff --git a/Zotlabs/Update/_1235.php b/Zotlabs/Update/_1235.php index 414306b93..55a501087 100644 --- a/Zotlabs/Update/_1235.php +++ b/Zotlabs/Update/_1235.php @@ -2,28 +2,29 @@ namespace Zotlabs\Update; -class _1235 { +class _1235 +{ - function run() { + public function run() + { - $r = q("ALTER TABLE item add replyto text NOT NULL DEFAULT ''"); + $r = q("ALTER TABLE item add replyto text NOT NULL DEFAULT ''"); - if($r) - return UPDATE_SUCCESS; - return UPDATE_FAILED; + if ($r) { + return UPDATE_SUCCESS; + } + return UPDATE_FAILED; + } - } + public function verify() + { - function verify() { + $columns = db_columns('item'); - $columns = db_columns('item'); - - if(in_array('replyto',$columns)) { - return true; - } - - return false; - - } + if (in_array('replyto', $columns)) { + return true; + } + return false; + } } diff --git a/Zotlabs/Update/_1236.php b/Zotlabs/Update/_1236.php index 8535abb63..8b974a25e 100644 --- a/Zotlabs/Update/_1236.php +++ b/Zotlabs/Update/_1236.php @@ -2,42 +2,42 @@ namespace Zotlabs\Update; -class _1236 { +class _1236 +{ - function run() { - - q("START TRANSACTION"); + public function run() + { - if(ACTIVE_DBTYPE == DBTYPE_POSTGRES) { - $r1 = q("ALTER TABLE abook ADD abook_alias text NOT NULL"); - $r2 = q("create index \"abook_alias_idx\" on photo (\"abook_alias\")"); + q("START TRANSACTION"); - $r = ($r1 && $r2); - } - else { - $r = q("ALTER TABLE `abook` ADD `abook_alias` char(191) NOT NULL DEFAULT '' , + if (ACTIVE_DBTYPE == DBTYPE_POSTGRES) { + $r1 = q("ALTER TABLE abook ADD abook_alias text NOT NULL"); + $r2 = q("create index \"abook_alias_idx\" on photo (\"abook_alias\")"); + + $r = ($r1 && $r2); + } else { + $r = q("ALTER TABLE `abook` ADD `abook_alias` char(191) NOT NULL DEFAULT '' , ADD INDEX `abook_alias` (`abook_alias`)"); - } + } - if($r) { - q("COMMIT"); - return UPDATE_SUCCESS; - } + if ($r) { + q("COMMIT"); + return UPDATE_SUCCESS; + } - q("ROLLBACK"); - return UPDATE_FAILED; + q("ROLLBACK"); + return UPDATE_FAILED; + } - } + public function verify() + { - function verify() { + $columns = db_columns('abook'); - $columns = db_columns('abook'); + if (in_array('abook_alias', $columns)) { + return true; + } - if(in_array('abook_alias',$columns)) { - return true; - } - - return false; - } - + return false; + } } diff --git a/Zotlabs/Update/_1237.php b/Zotlabs/Update/_1237.php index fc3fe7120..ab7334872 100644 --- a/Zotlabs/Update/_1237.php +++ b/Zotlabs/Update/_1237.php @@ -2,22 +2,27 @@ namespace Zotlabs\Update; -class _1237 { +class _1237 +{ - function run() { - q("update app set app_url = '%s' where app_url = '%s' ", - dbesc(z_root() . '/stream'), - dbesc(z_root() . '/network') - ); - q("update app set app_url = '%s' where app_url = '%s' ", - dbesc('$baseurl/stream'), - dbesc('$baseurl/network') - ); + public function run() + { + q( + "update app set app_url = '%s' where app_url = '%s' ", + dbesc(z_root() . '/stream'), + dbesc(z_root() . '/network') + ); + q( + "update app set app_url = '%s' where app_url = '%s' ", + dbesc('$baseurl/stream'), + dbesc('$baseurl/network') + ); - return UPDATE_SUCCESS; - } + return UPDATE_SUCCESS; + } - function verify() { - return true; - } + public function verify() + { + return true; + } } diff --git a/Zotlabs/Update/_1238.php b/Zotlabs/Update/_1238.php index e5c13a191..62ecb7aa9 100644 --- a/Zotlabs/Update/_1238.php +++ b/Zotlabs/Update/_1238.php @@ -2,40 +2,39 @@ namespace Zotlabs\Update; -class _1238 { +class _1238 +{ - function run() { + public function run() + { - $r1 = q("CREATE TABLE " . TQUOT . 'block' . TQUOT . " ( + $r1 = q("CREATE TABLE " . TQUOT . 'block' . TQUOT . " ( block_id int(10) UNSIGNED NOT NULL, block_channel_id int(10) UNSIGNED NOT NULL, block_entity text NOT NULL, block_type int(11) NOT NULL, block_comment mediumtext NOT NULL) "); - $r2 = q("ALTER TABLE " . TQUOT . 'block' . TQUOT . " + $r2 = q("ALTER TABLE " . TQUOT . 'block' . TQUOT . " ADD PRIMARY KEY (block_id), ADD KEY block_channel_id (block_channel_id), ADD KEY block_entity (block_entity(191)), ADD KEY block_type (block_type) "); - $r3 = q("ALTER TABLE " . TQUOT . 'block' . TQUOT . " + $r3 = q("ALTER TABLE " . TQUOT . 'block' . TQUOT . " MODIFY block_id int(10) UNSIGNED NOT NULL AUTO_INCREMENT "); - - return (($r1 && $r2 && $r3) ? UPDATE_SUCCESS : UPDATE_FAILED); - } + return (($r1 && $r2 && $r3) ? UPDATE_SUCCESS : UPDATE_FAILED); + } - function verify() { - - $columns = db_columns('block'); - - if(in_array('block_id',$columns)) { - return true; - } - - return false; - } + public function verify() + { + $columns = db_columns('block'); + if (in_array('block_id', $columns)) { + return true; + } + return false; + } } diff --git a/Zotlabs/Update/_1239.php b/Zotlabs/Update/_1239.php index ae22ec6d8..acf4f767f 100644 --- a/Zotlabs/Update/_1239.php +++ b/Zotlabs/Update/_1239.php @@ -4,42 +4,44 @@ namespace Zotlabs\Update; use Zotlabs\Lib\LibBlock; -class _1239 { +class _1239 +{ - function run() { - $r = q("select channel_id from channel where true"); - if ($r) { - foreach ($r as $rv) { - $a = get_pconfig($rv['channel_id'],'system','blocked'); - if ($a) { - $list = explode(',',$a); - if ($list) { - foreach ($list as $l) { - if (trim($l)) { - LibBlock::store( [ - 'block_channel_id' => intval($rv['channel_id']), - 'block_type' => 0, - 'block_entity' => trim($l), - 'block_comment' => t('Added by superblock') - ]); - } - } - } - del_pconfig($rv['channel_id'],'system','blocked'); - } - } - } - return UPDATE_SUCCESS; - } + public function run() + { + $r = q("select channel_id from channel where true"); + if ($r) { + foreach ($r as $rv) { + $a = get_pconfig($rv['channel_id'], 'system', 'blocked'); + if ($a) { + $list = explode(',', $a); + if ($list) { + foreach ($list as $l) { + if (trim($l)) { + LibBlock::store([ + 'block_channel_id' => intval($rv['channel_id']), + 'block_type' => 0, + 'block_entity' => trim($l), + 'block_comment' => t('Added by superblock') + ]); + } + } + } + del_pconfig($rv['channel_id'], 'system', 'blocked'); + } + } + } + return UPDATE_SUCCESS; + } - function verify() { - - $r = q("select * from pconfig where cat = 'system' and k = 'blocked'"); - if ($r) { - return false; - } - return true; - } + public function verify() + { + $r = q("select * from pconfig where cat = 'system' and k = 'blocked'"); + if ($r) { + return false; + } + return true; + } } diff --git a/Zotlabs/Update/_1240.php b/Zotlabs/Update/_1240.php index d66f97088..b2e4aeafa 100644 --- a/Zotlabs/Update/_1240.php +++ b/Zotlabs/Update/_1240.php @@ -4,38 +4,40 @@ namespace Zotlabs\Update; use Zotlabs\Lib\Config; -class _1240 { +class _1240 +{ - function run() { + public function run() + { -Config::Set('system','allowed_sites',Config::Get('system','whitelisted_sites','')); - Config::Delete('system','whitelisted_sites'); - Config::Set('system','denied_sites',Config::Get('system','blacklisted_sites','')); - Config::Delete('system','blacklisted_sites'); + Config::Set('system', 'allowed_sites', Config::Get('system', 'whitelisted_sites', '')); + Config::Delete('system', 'whitelisted_sites'); + Config::Set('system', 'denied_sites', Config::Get('system', 'blacklisted_sites', '')); + Config::Delete('system', 'blacklisted_sites'); - Config::Set('system','pubstream_allowed_sites',Config::Get('system','pubstream_whitelisted_sites','')); - Config::Delete('system','pubstream_whitelisted_sites'); - Config::Set('system','pubstream_denied_sites',Config::Get('system','pubstream_blacklisted_sites','')); - Config::Delete('system','pubstream_blacklisted_sites'); + Config::Set('system', 'pubstream_allowed_sites', Config::Get('system', 'pubstream_whitelisted_sites', '')); + Config::Delete('system', 'pubstream_whitelisted_sites'); + Config::Set('system', 'pubstream_denied_sites', Config::Get('system', 'pubstream_blacklisted_sites', '')); + Config::Delete('system', 'pubstream_blacklisted_sites'); - Config::Set('system','allowed_sites',Config::Get('system','whitelisted_sites','')); - Config::Delete('system','whitelisted_sites'); - Config::Set('system','denied_sites',Config::Get('system','blacklisted_sites','')); - Config::Delete('system','blacklisted_sites'); + Config::Set('system', 'allowed_sites', Config::Get('system', 'whitelisted_sites', '')); + Config::Delete('system', 'whitelisted_sites'); + Config::Set('system', 'denied_sites', Config::Get('system', 'blacklisted_sites', '')); + Config::Delete('system', 'blacklisted_sites'); - Config::Set('system','pubstream_allowed_sites',Config::Get('system','pubstream_whitelisted_sites','')); - Config::Delete('system','pubstream_whitelisted_sites'); - Config::Set('system','pubstream_denied_sites',Config::Get('system','pubstream_blacklisted_sites','')); - Config::Delete('system','pubstream_blacklisted_sites'); - return UPDATE_SUCCESS; - } - - function verify() { - if (Config::Get('system','blacklisted_sites') !== null) { - return false; - } - return true; - } + Config::Set('system', 'pubstream_allowed_sites', Config::Get('system', 'pubstream_whitelisted_sites', '')); + Config::Delete('system', 'pubstream_whitelisted_sites'); + Config::Set('system', 'pubstream_denied_sites', Config::Get('system', 'pubstream_blacklisted_sites', '')); + Config::Delete('system', 'pubstream_blacklisted_sites'); + return UPDATE_SUCCESS; + } -} \ No newline at end of file + public function verify() + { + if (Config::Get('system', 'blacklisted_sites') !== null) { + return false; + } + return true; + } +} diff --git a/Zotlabs/Update/_1241.php b/Zotlabs/Update/_1241.php index de8a847f3..47def7751 100644 --- a/Zotlabs/Update/_1241.php +++ b/Zotlabs/Update/_1241.php @@ -2,41 +2,41 @@ namespace Zotlabs\Update; -class _1241 { +class _1241 +{ - function run() { - q("START TRANSACTION"); + public function run() + { + q("START TRANSACTION"); - if(ACTIVE_DBTYPE == DBTYPE_POSTGRES) { - $r1 = q("ALTER TABLE pgrp ADD \"rule\" text NOT NULL DEFAULT '' "); - $r2 = q("create index \"group_rule_idx\" on pgrp (\"rule\")"); + if (ACTIVE_DBTYPE == DBTYPE_POSTGRES) { + $r1 = q("ALTER TABLE pgrp ADD \"rule\" text NOT NULL DEFAULT '' "); + $r2 = q("create index \"group_rule_idx\" on pgrp (\"rule\")"); - $r = ($r1 && $r2); - } - else { - $r = q("ALTER TABLE `pgrp` ADD `rule` char(191) NOT NULL DEFAULT '' , + $r = ($r1 && $r2); + } else { + $r = q("ALTER TABLE `pgrp` ADD `rule` char(191) NOT NULL DEFAULT '' , ADD INDEX `rule` (`rule`)"); - } + } - if($r) { - q("COMMIT"); - return UPDATE_SUCCESS; - } + if ($r) { + q("COMMIT"); + return UPDATE_SUCCESS; + } - q("ROLLBACK"); - return UPDATE_FAILED; - } + q("ROLLBACK"); + return UPDATE_FAILED; + } - function verify() { + public function verify() + { - $columns = db_columns('pgrp'); - - if(in_array('rule',$columns)) { - return true; - } - - return false; - } + $columns = db_columns('pgrp'); + if (in_array('rule', $columns)) { + return true; + } + return false; + } } diff --git a/Zotlabs/Update/_1242.php b/Zotlabs/Update/_1242.php index 425d0f58c..bb10772c0 100644 --- a/Zotlabs/Update/_1242.php +++ b/Zotlabs/Update/_1242.php @@ -2,45 +2,42 @@ namespace Zotlabs\Update; -class _1242 { +class _1242 +{ - function run() { - - q("START TRANSACTION"); + public function run() + { - if(ACTIVE_DBTYPE == DBTYPE_POSTGRES) { - $r1 = q("ALTER TABLE xchan ADD xchan_updated timestamp NOT NULL DEFAULT '0001-01-01 00:00:00' "); - $r2 = q("create index \"xchan_updated_idx\" on xchan (\"xchan_updated\")"); + q("START TRANSACTION"); - $r = ($r1 && $r2); - } - else { - $r = q("ALTER TABLE `xchan` ADD `xchan_updated` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' , + if (ACTIVE_DBTYPE == DBTYPE_POSTGRES) { + $r1 = q("ALTER TABLE xchan ADD xchan_updated timestamp NOT NULL DEFAULT '0001-01-01 00:00:00' "); + $r2 = q("create index \"xchan_updated_idx\" on xchan (\"xchan_updated\")"); + + $r = ($r1 && $r2); + } else { + $r = q("ALTER TABLE `xchan` ADD `xchan_updated` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' , ADD INDEX `xchan_updated` (`xchan_updated`)"); - } + } - if($r) { - q("COMMIT"); - return UPDATE_SUCCESS; - } + if ($r) { + q("COMMIT"); + return UPDATE_SUCCESS; + } - q("ROLLBACK"); - return UPDATE_FAILED; - - } - - function verify() { - - $columns = db_columns('xchan'); - - if(in_array('xchan_updated',$columns)) { - return true; - } - - return false; - } + q("ROLLBACK"); + return UPDATE_FAILED; + } + public function verify() + { + $columns = db_columns('xchan'); + if (in_array('xchan_updated', $columns)) { + return true; + } + return false; + } } diff --git a/Zotlabs/Update/_1243.php b/Zotlabs/Update/_1243.php index 137c60c81..3983c7ed9 100644 --- a/Zotlabs/Update/_1243.php +++ b/Zotlabs/Update/_1243.php @@ -2,47 +2,44 @@ namespace Zotlabs\Update; -class _1243 { +class _1243 +{ - function run() { - - q("START TRANSACTION"); + public function run() + { - if(ACTIVE_DBTYPE == DBTYPE_POSTGRES) { - $r1 = q("ALTER TABLE profile ADD pronouns text NOT NULL DEFAULT '' "); - $r2 = q("ALTER TABLE xprof ADD xprof_pronouns text NOT NULL DEFAULT '' "); + q("START TRANSACTION"); - $r = ($r1 && $r2); - } - else { - $r1 = q("ALTER TABLE `profile` ADD `pronouns` char(191) NOT NULL DEFAULT '' "); - $r2 = q("ALTER TABLE `xprof` ADD `xprof_pronouns` char(191) NOT NULL DEFAULT '' "); - $r = ($r1 && $r2); - } + if (ACTIVE_DBTYPE == DBTYPE_POSTGRES) { + $r1 = q("ALTER TABLE profile ADD pronouns text NOT NULL DEFAULT '' "); + $r2 = q("ALTER TABLE xprof ADD xprof_pronouns text NOT NULL DEFAULT '' "); - if($r) { - q("COMMIT"); - return UPDATE_SUCCESS; - } + $r = ($r1 && $r2); + } else { + $r1 = q("ALTER TABLE `profile` ADD `pronouns` char(191) NOT NULL DEFAULT '' "); + $r2 = q("ALTER TABLE `xprof` ADD `xprof_pronouns` char(191) NOT NULL DEFAULT '' "); + $r = ($r1 && $r2); + } - q("ROLLBACK"); - return UPDATE_FAILED; + if ($r) { + q("COMMIT"); + return UPDATE_SUCCESS; + } - } - - function verify() { - - $columns = db_columns('profile'); - $columns2 = db_columns('xprof'); - - if(in_array('pronouns',$columns) && in_array('xprof_pronouns',$columns2)) { - return true; - } - - return false; - } + q("ROLLBACK"); + return UPDATE_FAILED; + } + public function verify() + { + $columns = db_columns('profile'); + $columns2 = db_columns('xprof'); + if (in_array('pronouns', $columns) && in_array('xprof_pronouns', $columns2)) { + return true; + } + return false; + } } diff --git a/Zotlabs/Update/_1244.php b/Zotlabs/Update/_1244.php index d14f7a121..1711bb5c8 100644 --- a/Zotlabs/Update/_1244.php +++ b/Zotlabs/Update/_1244.php @@ -2,42 +2,42 @@ namespace Zotlabs\Update; -class _1244 { +class _1244 +{ - function run() { - - q("START TRANSACTION"); + public function run() + { - if(ACTIVE_DBTYPE == DBTYPE_POSTGRES) { - $r1 = q("ALTER TABLE xchan ADD xchan_created timestamp NOT NULL DEFAULT '0001-01-01 00:00:00' "); - $r2 = q("create index \"xchan_created_idx\" on xchan (\"xchan_created\")"); + q("START TRANSACTION"); - $r = ($r1 && $r2); - } - else { - $r = q("ALTER TABLE `xchan` ADD `xchan_created` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' , + if (ACTIVE_DBTYPE == DBTYPE_POSTGRES) { + $r1 = q("ALTER TABLE xchan ADD xchan_created timestamp NOT NULL DEFAULT '0001-01-01 00:00:00' "); + $r2 = q("create index \"xchan_created_idx\" on xchan (\"xchan_created\")"); + + $r = ($r1 && $r2); + } else { + $r = q("ALTER TABLE `xchan` ADD `xchan_created` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' , ADD INDEX `xchan_created` (`xchan_created`)"); - } + } - if($r) { - q("COMMIT"); - return UPDATE_SUCCESS; - } + if ($r) { + q("COMMIT"); + return UPDATE_SUCCESS; + } - q("ROLLBACK"); - return UPDATE_FAILED; + q("ROLLBACK"); + return UPDATE_FAILED; + } - } + public function verify() + { - function verify() { + $columns = db_columns('xchan'); - $columns = db_columns('xchan'); - - if(in_array('xchan_created',$columns)) { - return true; - } - - return false; - } + if (in_array('xchan_created', $columns)) { + return true; + } + return false; + } } diff --git a/Zotlabs/Update/_1245.php b/Zotlabs/Update/_1245.php index ba9a77791..d54c66ead 100644 --- a/Zotlabs/Update/_1245.php +++ b/Zotlabs/Update/_1245.php @@ -2,17 +2,18 @@ namespace Zotlabs\Update; -class _1245 { +class _1245 +{ - function run() { + public function run() + { - q("delete from app where app_url like '%%/nocomment'"); - return UPDATE_SUCCESS; - - } - - function verify() { - return true; - } + q("delete from app where app_url like '%%/nocomment'"); + return UPDATE_SUCCESS; + } + public function verify() + { + return true; + } } diff --git a/Zotlabs/Update/_1246.php b/Zotlabs/Update/_1246.php index 5526f6106..572da2ab0 100644 --- a/Zotlabs/Update/_1246.php +++ b/Zotlabs/Update/_1246.php @@ -2,22 +2,23 @@ namespace Zotlabs\Update; -class _1246 { +class _1246 +{ - function run() { - // remove deprecated apps from system list - - $network = 'bee400a93e3b95225374f02dcc74bfa37445f628bb10d05e3904aa8e3dd12c3b337a0caed2455a32ea85d8aaebc310f5b8571e7471f10a19bcfd77a7a997681f'; - $affinity = '76eecd5b73a0df8cddfcda430f0970ce82fefddb2b6440397b8318ccf4ae8011953fe249b0e450a1fe8829d5d3b5a62588818fd859087bbddc65a99c856d8b7d'; - + public function run() + { + // remove deprecated apps from system list - q("delete from app where (app_id = '$network' OR app_id = '$affinity')"); - return UPDATE_SUCCESS; + $network = 'bee400a93e3b95225374f02dcc74bfa37445f628bb10d05e3904aa8e3dd12c3b337a0caed2455a32ea85d8aaebc310f5b8571e7471f10a19bcfd77a7a997681f'; + $affinity = '76eecd5b73a0df8cddfcda430f0970ce82fefddb2b6440397b8318ccf4ae8011953fe249b0e450a1fe8829d5d3b5a62588818fd859087bbddc65a99c856d8b7d'; - } - function verify() { - return true; - } + q("delete from app where (app_id = '$network' OR app_id = '$affinity')"); + return UPDATE_SUCCESS; + } + public function verify() + { + return true; + } } diff --git a/Zotlabs/Update/_1247.php b/Zotlabs/Update/_1247.php index e922105be..ecced522f 100644 --- a/Zotlabs/Update/_1247.php +++ b/Zotlabs/Update/_1247.php @@ -2,19 +2,20 @@ namespace Zotlabs\Update; -class _1247 { +class _1247 +{ - function run() { - // remove deprecated apps from system list - $access = 'a2e9cee1a71e8b82f662d131a7cda1606b84b9be283715c967544e19cb34dd0821b65580c942ca38d7620638a44f26034536597a2c3a5c969e2dbaedfcd1d282'; + public function run() + { + // remove deprecated apps from system list + $access = 'a2e9cee1a71e8b82f662d131a7cda1606b84b9be283715c967544e19cb34dd0821b65580c942ca38d7620638a44f26034536597a2c3a5c969e2dbaedfcd1d282'; - q("delete from app where app_id = '$access' "); - return UPDATE_SUCCESS; - - } - - function verify() { - return true; - } + q("delete from app where app_id = '$access' "); + return UPDATE_SUCCESS; + } + public function verify() + { + return true; + } } diff --git a/Zotlabs/Update/_1248.php b/Zotlabs/Update/_1248.php index cb82d2f5b..96b01cee6 100644 --- a/Zotlabs/Update/_1248.php +++ b/Zotlabs/Update/_1248.php @@ -2,42 +2,42 @@ namespace Zotlabs\Update; -class _1248 { +class _1248 +{ - function run() { - - q("START TRANSACTION"); + public function run() + { - if(ACTIVE_DBTYPE == DBTYPE_POSTGRES) { - $r1 = q("ALTER TABLE atoken ADD atoken_guid NOT NULL DEFAULT '' "); - $r2 = q("create index \"atoken_guid\" on xchan (\"atoken_guid\")"); + q("START TRANSACTION"); - $r = ($r1 && $r2); - } - else { - $r = q("ALTER TABLE `atoken` ADD `atoken_guid` char(191) NOT NULL DEFAULT '' , + if (ACTIVE_DBTYPE == DBTYPE_POSTGRES) { + $r1 = q("ALTER TABLE atoken ADD atoken_guid NOT NULL DEFAULT '' "); + $r2 = q("create index \"atoken_guid\" on xchan (\"atoken_guid\")"); + + $r = ($r1 && $r2); + } else { + $r = q("ALTER TABLE `atoken` ADD `atoken_guid` char(191) NOT NULL DEFAULT '' , ADD INDEX `atoken_guid` (`atoken_guid`)"); - } + } - if($r) { - q("COMMIT"); - return UPDATE_SUCCESS; - } + if ($r) { + q("COMMIT"); + return UPDATE_SUCCESS; + } - q("ROLLBACK"); - return UPDATE_FAILED; + q("ROLLBACK"); + return UPDATE_FAILED; + } - } + public function verify() + { - function verify() { + $columns = db_columns('atoken'); - $columns = db_columns('atoken'); - - if(in_array('atoken_guid',$columns)) { - return true; - } - - return false; - } + if (in_array('atoken_guid', $columns)) { + return true; + } + return false; + } } diff --git a/Zotlabs/Update/_1249.php b/Zotlabs/Update/_1249.php index a38137b50..e23fe02cd 100644 --- a/Zotlabs/Update/_1249.php +++ b/Zotlabs/Update/_1249.php @@ -4,36 +4,38 @@ namespace Zotlabs\Update; use Zotlabs\Access\Permissions; -class _1249 { +class _1249 +{ - function run() { + public function run() + { - $channels = []; + $channels = []; - $r = q("select * from abook where abook_self = 0"); - if ($r) { - foreach ($r as $rv) { - if (! in_array(intval($rv['abook_channel']),$channels)) { - set_pconfig($rv['abook_channel'], 'perm_limits', 'post_mail', PERMS_SPECIFIC); - $channels[] = intval($rv['abook_channel']); - } + $r = q("select * from abook where abook_self = 0"); + if ($r) { + foreach ($r as $rv) { + if (!in_array(intval($rv['abook_channel']), $channels)) { + set_pconfig($rv['abook_channel'], 'perm_limits', 'post_mail', PERMS_SPECIFIC); + $channels[] = intval($rv['abook_channel']); + } - $x = get_abconfig($rv['abook_channel'],$rv['abook_xchan'],'system','my_perms'); - if ($x) { - $y = explode(',',$x); - if ($y && in_array('post_comments',$y) && ! in_array('post_mail',$y)) { - $y[] = 'post_mail'; - set_abconfig($rv['abook_channel'],$rv['abook_xchan'],'system','my_perms', implode(',',$y)); - } - } - } - } + $x = get_abconfig($rv['abook_channel'], $rv['abook_xchan'], 'system', 'my_perms'); + if ($x) { + $y = explode(',', $x); + if ($y && in_array('post_comments', $y) && !in_array('post_mail', $y)) { + $y[] = 'post_mail'; + set_abconfig($rv['abook_channel'], $rv['abook_xchan'], 'system', 'my_perms', implode(',', $y)); + } + } + } + } - return UPDATE_SUCCESS; - } - - function verify() { - return true; - } + return UPDATE_SUCCESS; + } + public function verify() + { + return true; + } } diff --git a/Zotlabs/Update/_1250.php b/Zotlabs/Update/_1250.php index 61f31d471..82e0261e3 100644 --- a/Zotlabs/Update/_1250.php +++ b/Zotlabs/Update/_1250.php @@ -2,19 +2,20 @@ namespace Zotlabs\Update; -class _1250 { +class _1250 +{ - function run() { - // remove deprecated apps from system list - $access = '067b70e92e35cc1b729c8c386bf8289cbec2618911a87c460a9b4705f2c151f8535402d468d469eeb630fad2c9cdd9aced80fb2b7cb29e47ae8f9c22c83ee7f2'; + public function run() + { + // remove deprecated apps from system list + $access = '067b70e92e35cc1b729c8c386bf8289cbec2618911a87c460a9b4705f2c151f8535402d468d469eeb630fad2c9cdd9aced80fb2b7cb29e47ae8f9c22c83ee7f2'; - q("delete from app where app_id = '$access' "); - return UPDATE_SUCCESS; - - } - - function verify() { - return true; - } + q("delete from app where app_id = '$access' "); + return UPDATE_SUCCESS; + } + public function verify() + { + return true; + } } diff --git a/Zotlabs/Update/_1251.php b/Zotlabs/Update/_1251.php index a2b33f3b8..2d9a5d81d 100644 --- a/Zotlabs/Update/_1251.php +++ b/Zotlabs/Update/_1251.php @@ -2,34 +2,35 @@ namespace Zotlabs\Update; -class _1251 { +class _1251 +{ - function run() { + public function run() + { - $default = ((ACTIVE_DBTYPE == DBTYPE_POSTGRES) ? " default ''" : ''); + $default = ((ACTIVE_DBTYPE == DBTYPE_POSTGRES) ? " default ''" : ''); - q("START TRANSACTION"); - $r = q("ALTER TABLE dreport ADD dreport_log text NOT NULL $default"); + q("START TRANSACTION"); + $r = q("ALTER TABLE dreport ADD dreport_log text NOT NULL $default"); - if ($r) { - q("COMMIT"); - return UPDATE_SUCCESS; - } + if ($r) { + q("COMMIT"); + return UPDATE_SUCCESS; + } - q("ROLLBACK"); - return UPDATE_FAILED; + q("ROLLBACK"); + return UPDATE_FAILED; + } - } + public function verify() + { - function verify() { + $columns = db_columns('dreport'); - $columns = db_columns('dreport'); - - if (in_array('dreport_log',$columns)) { - return true; - } - - return false; - } + if (in_array('dreport_log', $columns)) { + return true; + } + return false; + } } diff --git a/Zotlabs/Update/_1252.php b/Zotlabs/Update/_1252.php index f1505c533..f5e951a4d 100644 --- a/Zotlabs/Update/_1252.php +++ b/Zotlabs/Update/_1252.php @@ -2,41 +2,47 @@ namespace Zotlabs\Update; -class _1252 { +class _1252 +{ - function run() { - $sys = get_sys_channel(); - if ($sys) { - $sitename = get_config('system','sitename'); - $siteinfo = get_config('system','siteinfo'); + public function run() + { + $sys = get_sys_channel(); + if ($sys) { + $sitename = get_config('system', 'sitename'); + $siteinfo = get_config('system', 'siteinfo'); - if ($sitename) { - q("update channel set channel_name = '%s' where channel_id = %d", - dbesc($sitename), - intval($sys['channel_id']) - ); - q("update profile set fullname = '%s' where uid = %d and is_default = 1", - dbesc($sitename), - intval($sys['channel_id']) - ); - q("update xchan set xchan_name = '%s', xchan_name_date = '%s' where xchan_hash = '%s'", - dbesc($sitename), - dbesc(datetime_convert()), - dbesc($sys['channel_hash']) - ); - } - if ($siteinfo) { - q("update profile set about = '%s' where uid = %d and is_default = 1", - dbesc($siteinfo), - intval($sys['channel_id']) - ); - } - } - return UPDATE_SUCCESS; - } - - function verify() { - return true; - } + if ($sitename) { + q( + "update channel set channel_name = '%s' where channel_id = %d", + dbesc($sitename), + intval($sys['channel_id']) + ); + q( + "update profile set fullname = '%s' where uid = %d and is_default = 1", + dbesc($sitename), + intval($sys['channel_id']) + ); + q( + "update xchan set xchan_name = '%s', xchan_name_date = '%s' where xchan_hash = '%s'", + dbesc($sitename), + dbesc(datetime_convert()), + dbesc($sys['channel_hash']) + ); + } + if ($siteinfo) { + q( + "update profile set about = '%s' where uid = %d and is_default = 1", + dbesc($siteinfo), + intval($sys['channel_id']) + ); + } + } + return UPDATE_SUCCESS; + } + public function verify() + { + return true; + } } diff --git a/Zotlabs/Update/_1253.php b/Zotlabs/Update/_1253.php index 4874ff51d..12fdc88e9 100644 --- a/Zotlabs/Update/_1253.php +++ b/Zotlabs/Update/_1253.php @@ -4,25 +4,27 @@ namespace Zotlabs\Update; use Zotlabs\Lib\Config; -class _1253 { +class _1253 +{ - function run() { - $mode = PUBLIC_STREAM_NONE; - if (Config::Get('system','site_firehose')) { - $mode = PUBLIC_STREAM_SITE; - } - if (intval(Config::Get('system','disable_discover_tab',1)) === 0) { - $mode = PUBLIC_STREAM_FULL; - } - Config::Set('system','public_stream_mode', $mode); - Config::Delete('system','disable_discover_tab'); - Config::Delete('system','site_firehose'); - - return UPDATE_SUCCESS; - } + public function run() + { + $mode = PUBLIC_STREAM_NONE; + if (Config::Get('system', 'site_firehose')) { + $mode = PUBLIC_STREAM_SITE; + } + if (intval(Config::Get('system', 'disable_discover_tab', 1)) === 0) { + $mode = PUBLIC_STREAM_FULL; + } + Config::Set('system', 'public_stream_mode', $mode); + Config::Delete('system', 'disable_discover_tab'); + Config::Delete('system', 'site_firehose'); - function verify() { - return true; - } + return UPDATE_SUCCESS; + } + public function verify() + { + return true; + } } diff --git a/Zotlabs/Update/_1254.php b/Zotlabs/Update/_1254.php index 460642861..eba615b56 100644 --- a/Zotlabs/Update/_1254.php +++ b/Zotlabs/Update/_1254.php @@ -4,17 +4,20 @@ namespace Zotlabs\Update; use Zotlabs\Lib\Config; -class _1254 { +class _1254 +{ - function run() { - q("UPDATE channel SET channel_notifyflags = channel_notifyflags + %d WHERE true", - intval(NOTIFY_RESHARE) - ); - return UPDATE_SUCCESS; - } - - function verify() { - return true; - } + public function run() + { + q( + "UPDATE channel SET channel_notifyflags = channel_notifyflags + %d WHERE true", + intval(NOTIFY_RESHARE) + ); + return UPDATE_SUCCESS; + } + public function verify() + { + return true; + } } diff --git a/Zotlabs/Web/Controller.php b/Zotlabs/Web/Controller.php index a31737c1e..a9949ff0f 100644 --- a/Zotlabs/Web/Controller.php +++ b/Zotlabs/Web/Controller.php @@ -7,11 +7,18 @@ namespace Zotlabs\Web; * */ -class Controller { +class Controller +{ - function init() {} - function post() {} - function get() {} + public function init() + { + } + public function post() + { + } + + public function get() + { + } } - diff --git a/Zotlabs/Web/HTTPHeaders.php b/Zotlabs/Web/HTTPHeaders.php index 832e82dfa..019cb1934 100644 --- a/Zotlabs/Web/HTTPHeaders.php +++ b/Zotlabs/Web/HTTPHeaders.php @@ -2,60 +2,57 @@ namespace Zotlabs\Web; -class HTTPHeaders { +class HTTPHeaders +{ - private $in_progress = []; - private $parsed = []; + private $in_progress = []; + private $parsed = []; - function __construct($headers) { + public function __construct($headers) + { - $lines = explode("\n",str_replace("\r",'',$headers)); - if ($lines) { - foreach ($lines as $line) { - if (preg_match('/^\s+/',$line,$matches) && trim($line)) { - if (isset($this->in_progress['k'])) { - $this->in_progress['v'] .= ' ' . ltrim($line); - continue; - } - } - else { - if (isset($this->in_progress['k'])) { - $this->parsed[] = [ $this->in_progress['k'] => $this->in_progress['v'] ]; - $this->in_progress = []; - } - $key = strtolower(substr($line,0,strpos($line,':'))); - if ($key) { - $this->in_progress['k'] = $key; - $this->in_progress['v'] = ltrim(substr($line,strpos($line,':') + 1)); - } - } - - } - if (isset($this->in_progress['k'])) { - $this->parsed[] = [ $this->in_progress['k'] => $this->in_progress['v'] ]; - $this->in_progress = []; - } - } - } - - function fetch() { - return $this->parsed; - } - - function fetcharr() { - $ret = []; - if ($this->parsed) { - foreach ($this->parsed as $x) { - foreach ($x as $y => $z) { - $ret[$y] = $z; - } - } - } - return $ret; - } + $lines = explode("\n", str_replace("\r", '', $headers)); + if ($lines) { + foreach ($lines as $line) { + if (preg_match('/^\s+/', $line, $matches) && trim($line)) { + if (isset($this->in_progress['k'])) { + $this->in_progress['v'] .= ' ' . ltrim($line); + continue; + } + } else { + if (isset($this->in_progress['k'])) { + $this->parsed[] = [$this->in_progress['k'] => $this->in_progress['v']]; + $this->in_progress = []; + } + $key = strtolower(substr($line, 0, strpos($line, ':'))); + if ($key) { + $this->in_progress['k'] = $key; + $this->in_progress['v'] = ltrim(substr($line, strpos($line, ':') + 1)); + } + } + } + if (isset($this->in_progress['k'])) { + $this->parsed[] = [$this->in_progress['k'] => $this->in_progress['v']]; + $this->in_progress = []; + } + } + } + public function fetch() + { + return $this->parsed; + } + public function fetcharr() + { + $ret = []; + if ($this->parsed) { + foreach ($this->parsed as $x) { + foreach ($x as $y => $z) { + $ret[$y] = $z; + } + } + } + return $ret; + } } - - - diff --git a/Zotlabs/Web/HTTPSig.php b/Zotlabs/Web/HTTPSig.php index fcc4f4b55..e5009274c 100644 --- a/Zotlabs/Web/HTTPSig.php +++ b/Zotlabs/Web/HTTPSig.php @@ -2,6 +2,8 @@ namespace Zotlabs\Web; +use DateTime; +use DateTimeZone; use Zotlabs\Lib\ActivityStreams; use Zotlabs\Lib\Activity; use Zotlabs\Lib\Webfinger; @@ -15,677 +17,682 @@ use Zotlabs\Lib\Keyutils; * * @see https://tools.ietf.org/html/draft-cavage-http-signatures-10 */ - -class HTTPSig { - - /** - * @brief RFC5843 - * - * @see https://tools.ietf.org/html/rfc5843 - * - * @param string $body The value to create the digest for - * @param string $alg hash algorithm (one of 'sha256','sha512') - * @return string The generated digest header string for $body - */ - - static function generate_digest_header($body,$alg = 'sha256') { - - $digest = base64_encode(hash($alg, $body, true)); - switch ($alg) { - case 'sha512': - return 'SHA-512=' . $digest; - case 'sha256': - default: - return 'SHA-256=' . $digest; - break; - } - } - - - - static function find_headers($data,&$body) { - - // decide if $data arrived via controller submission or curl - // changes $body for the caller - - if (is_array($data) && array_key_exists('header',$data)) { - if (! $data['success']) { - $body = EMPTY_STR; - return []; - } - - if (! $data['header']) { - $body = EMPTY_STR; - return []; - } - - $h = new HTTPHeaders($data['header']); - $headers = $h->fetcharr(); - $body = $data['body']; - $headers['(request-target)'] = $data['request_target']; - } - - else { - $headers = []; - $headers['(request-target)'] = strtolower($_SERVER['REQUEST_METHOD']) . ' ' . $_SERVER['REQUEST_URI']; - $headers['content-type'] = $_SERVER['CONTENT_TYPE']; - $headers['content-length'] = $_SERVER['CONTENT_LENGTH']; - - foreach ($_SERVER as $k => $v) { - if (strpos($k,'HTTP_') === 0) { - $field = str_replace('_','-',strtolower(substr($k,5))); - $headers[$field] = $v; - } - } - } - - //logger('SERVER: ' . print_r($_SERVER,true), LOGGER_ALL); - //logger('headers: ' . print_r($headers,true), LOGGER_ALL); - - return $headers; - } - - - // See draft-cavage-http-signatures-10 - - static function verify($data,$key = '', $keytype = '') { - - $body = $data; - $headers = null; - - $result = [ - 'signer' => '', - 'portable_id' => '', - 'header_signed' => false, - 'header_valid' => false, - 'content_signed' => false, - 'content_valid' => false - ]; - - - $headers = self::find_headers($data,$body); - - if (! $headers) { - return $result; - } - - if (is_array($body)) { - btlogger('body is array!' . print_r($body,true)); - } - - - $sig_block = null; - - if (array_key_exists('signature',$headers)) { - $sig_block = self::parse_sigheader($headers['signature']); - } - elseif (array_key_exists('authorization',$headers)) { - $sig_block = self::parse_sigheader($headers['authorization']); - } - - if (! $sig_block) { - logger('no signature provided.', LOGGER_DEBUG); - return $result; - } - - // Warning: This log statement includes binary data - // logger('sig_block: ' . print_r($sig_block,true), LOGGER_DATA); - - $result['header_signed'] = true; - - $signed_headers = $sig_block['headers']; - if (! $signed_headers) { - $signed_headers = [ 'date' ]; - } - $signed_data = ''; - foreach ($signed_headers as $h) { - if (array_key_exists($h,$headers)) { - $signed_data .= $h . ': ' . $headers[$h] . "\n"; - } - if ($h === '(created)') { - if ($sig_block['algorithm'] && (strpos($sig_block['algorithm'],'rsa') !== false || strpos($sig_block['algorithm'],'hmac') !== false || strpos($sig_block['algorithm'],'ecdsa') !== false)) { - logger('created not allowed here'); - return $result; - } - if ((! isset($sig_block['(created)'])) || (! intval($sig_block['(created)'])) || intval($sig_block['(created)']) > time()) { - logger('created in future'); - return $result; - } - $signed_data .= '(created): ' . $sig_block['(created)'] . "\n"; - } - if ($h === '(expires)') { - if ($sig_block['algorithm'] && (strpos($sig_block['algorithm'],'rsa') !== false || strpos($sig_block['algorithm'],'hmac') !== false || strpos($sig_block['algorithm'],'ecdsa') !== false)) { - logger('expires not allowed here'); - return $result; - } - if ((! isset($sig_block['(expires)'])) || (! intval($sig_block['(expires)'])) || intval($sig_block['(expires)']) < time()) { - logger('signature expired'); - return $result; - } - $signed_data .= '(expires): ' . $sig_block['(expires)'] . "\n"; - } - if ($h === 'date') { - $d = new \DateTime($headers[$h]); - $d->setTimeZone(new \DateTimeZone('UTC')); - $dplus = datetime_convert('UTC','UTC','now + 1 day'); - $dminus = datetime_convert('UTC','UTC','now - 1 day'); - $c = $d->format('Y-m-d H:i:s'); - if ($c > $dplus || $c < $dminus) { - logger('bad time: ' . $c); - return $result; - } - } - } - $signed_data = rtrim($signed_data,"\n"); - - $algorithm = null; - - if ($sig_block['algorithm'] === 'rsa-sha256') { - $algorithm = 'sha256'; - } - if ($sig_block['algorithm'] === 'rsa-sha512') { - $algorithm = 'sha512'; - } - - if (! array_key_exists('keyId',$sig_block)) { - return $result; - } - - $result['signer'] = $sig_block['keyId']; - - $fkey = self::get_key($key,$keytype,$result['signer']); - - if ($sig_block['algorithm'] === 'hs2019') { - if (isset($fkey['algorithm'])) { - if (strpos($fkey['algorithm'],'rsa-sha256') !== false) { - $algorithm = 'sha256'; - } - if (strpos($fkey['algorithm'],'rsa-sha512') !== false) { - $algorithm = 'sha512'; - } - } - } - - - if (! ($fkey && $fkey['public_key'])) { - return $result; - } - - $x = Crypto::verify($signed_data,$sig_block['signature'],$fkey['public_key'],$algorithm); - - logger('verified: ' . $x, LOGGER_DEBUG); - - if (! $x) { - - // try again, ignoring the local actor (xchan) cache and refetching the key - // from its source - - $fkey = self::get_key($key,$keytype,$result['signer'],true); - - if ($fkey && $fkey['public_key']) { - $y = Crypto::verify($signed_data,$sig_block['signature'],$fkey['public_key'],$algorithm); - logger('verified: (cache reload) ' . $x, LOGGER_DEBUG); - } - - if (! $y) { - logger('verify failed for ' . $result['signer'] . ' alg=' . $algorithm . (($fkey['public_key']) ? '' : ' no key')); - $sig_block['signature'] = base64_encode($sig_block['signature']); - logger('affected sigblock: ' . print_r($sig_block,true)); - logger('headers: ' . print_r($headers,true)); - logger('server: ' . print_r($_SERVER,true)); - return $result; - } - } - - $result['portable_id'] = $fkey['portable_id']; - $result['header_valid'] = true; - - if (in_array('digest',$signed_headers)) { - $result['content_signed'] = true; - $digest = explode('=', $headers['digest'], 2); - if ($digest[0] === 'SHA-256') { - $hashalg = 'sha256'; - } - if ($digest[0] === 'SHA-512') { - $hashalg = 'sha512'; - } - - if (base64_encode(hash($hashalg,$body,true)) === $digest[1]) { - $result['content_valid'] = true; - } - - logger('Content_Valid: ' . (($result['content_valid']) ? 'true' : 'false')); - if (! $result['content_valid']) { - logger('invalid content signature: data ' . print_r($data,true)); - logger('invalid content signature: headers ' . print_r($headers,true)); - logger('invalid content signature: body ' . print_r($body,true)); - } - } - - return $result; - } - - static function get_key($key,$keytype,$id,$force = false) { - - if ($key) { - if (function_exists($key)) { - return $key($id); - } - return [ 'public_key' => $key ]; - } - - if ($keytype === 'zot6') { - $key = self::get_zotfinger_key($id,$force); - if ($key) { - return $key; - } - } - - - if (strpos($id,'#') === false) { - $key = self::get_webfinger_key($id,$force); - if ($key) { - return $key; - } - } - - $key = self::get_activitystreams_key($id,$force); - return $key; - - } - - - static function convertKey($key) { - - if (strstr($key,'RSA ')) { - return Keyutils::rsatopem($key); - } - elseif (substr($key,0,5) === 'data:') { - return Keyutils::convertSalmonKey($key); - } - else { - return $key; - } - - } - - - /** - * @brief - * - * @param string $id - * @return boolean|string - * false if no pub key found, otherwise return the pub key - */ - - static function get_activitystreams_key($id,$force = false) { - - // Check the local cache first, but remove any fragments like #main-key since these won't be present in our cached data - - $cache_url = ((strpos($id,'#')) ? substr($id,0,strpos($id,'#')) : $id); - - // $force is used to ignore the local cache and only use the remote data; for instance the cached key might be stale - - if (! $force) { - $x = q("select * from xchan left join hubloc on xchan_hash = hubloc_hash where ( hubloc_addr = '%s' or hubloc_id_url = '%s' or hubloc_hash = '%s') order by hubloc_id desc", - dbesc(str_replace('acct:','',$cache_url)), - dbesc($cache_url), - dbesc($cache_url) - ); - - if ($x) { - $best = Libzot::zot_record_preferred($x); - } - - if ($best && $best['xchan_pubkey']) { - return [ 'portable_id' => $best['xchan_hash'], 'public_key' => $best['xchan_pubkey'] , 'algorithm' => get_xconfig($best['xchan_hash'],'system','signing_algorithm'), 'hubloc' => $best ]; - } - } - - // The record wasn't in cache. Fetch it now. - - $r = Activity::fetch($id); - $signatureAlgorithm = EMPTY_STR; - - if ($r) { - if (array_key_exists('publicKey',$r) && array_key_exists('publicKeyPem',$r['publicKey']) && array_key_exists('id',$r['publicKey'])) { - if ($r['publicKey']['id'] === $id || $r['id'] === $id) { - $portable_id = ((array_key_exists('owner',$r['publicKey'])) ? $r['publicKey']['owner'] : EMPTY_STR); - - // the w3c sec context has conflicting names and no defined values for this property except - // "http://www.w3.org/2000/09/xmldsig#rsa-sha1" - - // Since the names conflict, it could mess up LD-signatures but we will accept both, and at this - // time we will only look for the substrings 'rsa-sha256' and 'rsa-sha512' within those properties. - // We will also accept a toplevel 'sigAlgorithm' regardless of namespace with the same constraints. - // Default to rsa-sha256 if we can't figure out. If they're sending 'hs2019' we have to - // look for something. - - if (isset($r['publicKey']['signingAlgorithm'])) { - $signatureAlgorithm = $r['publicKey']['signingAlgorithm']; - set_xconfig($portable_id,'system','signing_algorithm',$signatureAlgorithm); - } - if (isset($r['publicKey']['signatureAlgorithm'])) { - $signatureAlgorithm = $r['publicKey']['signatureAlgorithm']; - set_xconfig($portable_id,'system','signing_algorithm',$signatureAlgorithm); - } - - if (isset($r['sigAlgorithm'])) { - $signatureAlgorithm = $r['sigAlgorithm']; - set_xconfig($portable_id,'system','signing_algorithm',$signatureAlgorithm); - } - - return [ 'public_key' => self::convertKey($r['publicKey']['publicKeyPem']), 'portable_id' => $portable_id, 'algorithm' => (($signatureAlgorithm) ? $signatureAlgorithm : 'rsa-sha256'), 'hubloc' => [] ]; - } - } - } - - // No key was found - - return false; - } - - - static function get_webfinger_key($id,$force = false) { - - if (! $force) { - $x = q("select * from xchan left join hubloc on xchan_hash = hubloc_hash where ( hubloc_addr = '%s' or hubloc_id_url = '%s' or hubloc_hash = '%s') order by hubloc_id desc", - dbesc(str_replace('acct:','',$id)), - dbesc($id), - dbesc($id) - ); - - if ($x) { - $best = Libzot::zot_record_preferred($x); - } - - if ($best && $best['xchan_pubkey']) { - return [ 'portable_id' => $best['xchan_hash'], 'public_key' => $best['xchan_pubkey'] , 'algorithm' => get_xconfig($best['xchan_hash'],'system','signing_algorithm'), 'hubloc' => $best ]; - } - } - - $wf = Webfinger::exec($id); - $key = [ 'portable_id' => '', 'public_key' => '', 'algorithm' => '', 'hubloc' => [] ]; - - if ($wf) { - if (array_key_exists('properties',$wf) && array_key_exists('https://w3id.org/security/v1#publicKeyPem',$wf['properties'])) { - $key['public_key'] = self::convertKey($wf['properties']['https://w3id.org/security/v1#publicKeyPem']); - } - if (array_key_exists('links', $wf) && is_array($wf['links'])) { - foreach ($wf['links'] as $l) { - if (! (is_array($l) && array_key_exists('rel',$l))) { - continue; - } - if ($l['rel'] === 'magic-public-key' && array_key_exists('href',$l) && $key['public_key'] === EMPTY_STR) { - $key['public_key'] = self::convertKey($l['href']); - } - } - } - } - - return (($key['public_key']) ? $key : false); - } - - - static function get_zotfinger_key($id,$force = false) { - - if (! $force) { - $x = q("select * from xchan left join hubloc on xchan_hash = hubloc_hash where ( hubloc_addr = '%s' or hubloc_id_url = '%s' ) and hubloc_network in ('nomad','zot6') order by hubloc_id desc", - dbesc(str_replace('acct:','',$id)), - dbesc($id) - ); - - if ($x) { - $best = Libzot::zot_record_preferred($x); - } - - if ($best && $best['xchan_pubkey']) { - return [ 'portable_id' => $best['xchan_hash'], 'public_key' => $best['xchan_pubkey'] , 'algorithm' => get_xconfig($best['xchan_hash'],'system','signing_algorithm'), 'hubloc' => $best ]; - } - } - - $wf = Webfinger::exec($id); - $key = [ 'portable_id' => '', 'public_key' => '', 'algorithm' => '', 'hubloc' => [] ]; - - if ($wf) { - if (array_key_exists('properties',$wf) && array_key_exists('https://w3id.org/security/v1#publicKeyPem',$wf['properties'])) { - $key['public_key'] = self::convertKey($wf['properties']['https://w3id.org/security/v1#publicKeyPem']); - } - if (array_key_exists('links', $wf) && is_array($wf['links'])) { - foreach ($wf['links'] as $l) { - if (! (is_array($l) && array_key_exists('rel',$l))) { - continue; - } - if (in_array($l['rel'], ['http://purl.org/nomad', 'http://purl.org/zot/protocol/6.0']) && array_key_exists('href',$l) && $l['href'] !== EMPTY_STR) { - - // The third argument to Zotfinger::exec() tells it not to verify signatures - // Since we're inside a function that is fetching keys with which to verify signatures, - // this is necessary to prevent infinite loops. - - $z = Zotfinger::exec($l['href'], null, false); - if ($z) { - $i = Libzot::import_xchan($z['data']); - if ($i['success']) { - $key['portable_id'] = $i['hash']; - - $x = q("select * from xchan left join hubloc on xchan_hash = hubloc_hash where hubloc_id_url = '%s' order by hubloc_id desc limit 1", - dbesc($l['href']) - ); - if ($x) { - $key['hubloc'] = $x[0]; - } - $key['algorithm'] = get_xconfig($i['hash'],'system','signing_algorithm'); - } - } - } - if ($l['rel'] === 'magic-public-key' && array_key_exists('href',$l) && $key['public_key'] === EMPTY_STR) { - $key['public_key'] = self::convertKey($l['href']); - } - } - } - } - - return (($key['public_key']) ? $key : false); - } - - - /** - * @brief - * - * @param array $head - * @param string $prvkey - * @param string $keyid (optional, default '') - * @param boolean $auth (optional, default false) - * @param string $alg (optional, default 'sha256') - * @param array $encryption [ 'key', 'algorithm' ] or false - * @return array - */ - static function create_sig($head, $prvkey, $keyid = EMPTY_STR, $auth = false, $alg = 'sha256', $encryption = false ) { - - $return_headers = []; - - if ($alg === 'sha256') { - $algorithm = 'rsa-sha256'; - } - if ($alg === 'sha512') { - $algorithm = 'rsa-sha512'; - } - - $x = self::sign($head,$prvkey,$alg); - - $headerval = 'keyId="' . $keyid . '",algorithm="hs2019",headers="' . $x['headers'] . '",signature="' . $x['signature'] . '"'; - - if ($encryption) { - $x = Crypto::encapsulate($headerval,$encryption['key'],$encryption['algorithm']); - if (is_array($x)) { - $headerval = 'iv="' . $x['iv'] . '",key="' . $x['key'] . '",alg="' . $x['alg'] . '",data="' . $x['data'] . '"'; - } - } - - if ($auth) { - $sighead = 'Authorization: Signature ' . $headerval; - } - else { - $sighead = 'Signature: ' . $headerval; - } - - if ($head) { - foreach ($head as $k => $v) { - // strip the request-target virtual header from the output headers - if ($k === '(request-target)') { - continue; - } - $return_headers[] = $k . ': ' . $v; - } - } - $return_headers[] = $sighead; - - return $return_headers; - } - - /** - * @brief set headers - * - * @param array $headers - * @return void - */ - - static function set_headers($headers) { - if ($headers && is_array($headers)) { - foreach ($headers as $h) { - header($h); - } - } - } - - - /** - * @brief - * - * @param array $head - * @param string $prvkey - * @param string $alg (optional) default 'sha256' - * @return array - */ - - static function sign($head, $prvkey, $alg = 'sha256') { - - $ret = []; - - $headers = ''; - $fields = ''; - - logger('signing: ' . print_r($head,true), LOGGER_DATA); - - if ($head) { - foreach ($head as $k => $v) { - $headers .= strtolower($k) . ': ' . trim($v) . "\n"; - if ($fields) { - $fields .= ' '; - } - $fields .= strtolower($k); - } - // strip the trailing linefeed - $headers = rtrim($headers,"\n"); - } - - $sig = base64_encode(Crypto::sign($headers,$prvkey,$alg)); - - $ret['headers'] = $fields; - $ret['signature'] = $sig; - - return $ret; - } - - /** - * @brief - * - * @param string $header - * @return array associate array with - * - \e string \b keyID - * - \e string \b algorithm - * - \e array \b headers - * - \e string \b signature - */ - - static function parse_sigheader($header) { - - $ret = []; - $matches = []; - - // if the header is encrypted, decrypt with (default) site private key and continue - - if (preg_match('/iv="(.*?)"/ism',$header,$matches)) { - $header = self::decrypt_sigheader($header); - } - - if (preg_match('/keyId="(.*?)"/ism',$header,$matches)) { - $ret['keyId'] = $matches[1]; - } - if (preg_match('/created=([0-9]*)/ism',$header,$matches)) { - $ret['(created)'] = $matches[1]; - } - if (preg_match('/expires=([0-9]*)/ism',$header,$matches)) { - $ret['(expires)'] = $matches[1]; - } - if (preg_match('/algorithm="(.*?)"/ism',$header,$matches)) { - $ret['algorithm'] = $matches[1]; - } - if (preg_match('/headers="(.*?)"/ism',$header,$matches)) { - $ret['headers'] = explode(' ', $matches[1]); - } - if (preg_match('/signature="(.*?)"/ism',$header,$matches)) { - $ret['signature'] = base64_decode(preg_replace('/\s+/','',$matches[1])); - } - - if (($ret['signature']) && ($ret['algorithm']) && (! $ret['headers'])) { - $ret['headers'] = [ 'date' ]; - } - - return $ret; - } - - - /** - * @brief - * - * @param string $header - * @param string $prvkey (optional), if not set use site private key - * @return array|string associative array, empty string if failue - * - \e string \b iv - * - \e string \b key - * - \e string \b alg - * - \e string \b data - */ - - static function decrypt_sigheader($header, $prvkey = null) { - - $iv = $key = $alg = $data = null; - - if (! $prvkey) { - $prvkey = get_config('system', 'prvkey'); - } - - $matches = []; - - if (preg_match('/iv="(.*?)"/ism',$header,$matches)) { - $iv = $matches[1]; - } - if (preg_match('/key="(.*?)"/ism',$header,$matches)) { - $key = $matches[1]; - } - if (preg_match('/alg="(.*?)"/ism',$header,$matches)) { - $alg = $matches[1]; - } - if (preg_match('/data="(.*?)"/ism',$header,$matches)) { - $data = $matches[1]; - } - - if ($iv && $key && $alg && $data) { - return Crypto::unencapsulate([ 'encrypted' => true, 'iv' => $iv, 'key' => $key, 'alg' => $alg, 'data' => $data ] , $prvkey); - } - - return ''; - } - +class HTTPSig +{ + + /** + * @brief RFC5843 + * + * @see https://tools.ietf.org/html/rfc5843 + * + * @param string $body The value to create the digest for + * @param string $alg hash algorithm (one of 'sha256','sha512') + * @return string The generated digest header string for $body + */ + + public static function generate_digest_header($body, $alg = 'sha256') + { + + $digest = base64_encode(hash($alg, $body, true)); + switch ($alg) { + case 'sha512': + return 'SHA-512=' . $digest; + case 'sha256': + default: + return 'SHA-256=' . $digest; + break; + } + } + + + public static function find_headers($data, &$body) + { + + // decide if $data arrived via controller submission or curl + // changes $body for the caller + + if (is_array($data) && array_key_exists('header', $data)) { + if (!$data['success']) { + $body = EMPTY_STR; + return []; + } + + if (!$data['header']) { + $body = EMPTY_STR; + return []; + } + + $h = new HTTPHeaders($data['header']); + $headers = $h->fetcharr(); + $body = $data['body']; + $headers['(request-target)'] = $data['request_target']; + } else { + $headers = []; + $headers['(request-target)'] = strtolower($_SERVER['REQUEST_METHOD']) . ' ' . $_SERVER['REQUEST_URI']; + $headers['content-type'] = $_SERVER['CONTENT_TYPE']; + $headers['content-length'] = $_SERVER['CONTENT_LENGTH']; + + foreach ($_SERVER as $k => $v) { + if (strpos($k, 'HTTP_') === 0) { + $field = str_replace('_', '-', strtolower(substr($k, 5))); + $headers[$field] = $v; + } + } + } + + //logger('SERVER: ' . print_r($_SERVER,true), LOGGER_ALL); + //logger('headers: ' . print_r($headers,true), LOGGER_ALL); + + return $headers; + } + + + // See draft-cavage-http-signatures-10 + + public static function verify($data, $key = '', $keytype = '') + { + + $body = $data; + $headers = null; + + $result = [ + 'signer' => '', + 'portable_id' => '', + 'header_signed' => false, + 'header_valid' => false, + 'content_signed' => false, + 'content_valid' => false + ]; + + + $headers = self::find_headers($data, $body); + + if (!$headers) { + return $result; + } + + if (is_array($body)) { + btlogger('body is array!' . print_r($body, true)); + } + + + $sig_block = null; + + if (array_key_exists('signature', $headers)) { + $sig_block = self::parse_sigheader($headers['signature']); + } elseif (array_key_exists('authorization', $headers)) { + $sig_block = self::parse_sigheader($headers['authorization']); + } + + if (!$sig_block) { + logger('no signature provided.', LOGGER_DEBUG); + return $result; + } + + // Warning: This log statement includes binary data + // logger('sig_block: ' . print_r($sig_block,true), LOGGER_DATA); + + $result['header_signed'] = true; + + $signed_headers = $sig_block['headers']; + if (!$signed_headers) { + $signed_headers = ['date']; + } + $signed_data = ''; + foreach ($signed_headers as $h) { + if (array_key_exists($h, $headers)) { + $signed_data .= $h . ': ' . $headers[$h] . "\n"; + } + if ($h === '(created)') { + if ($sig_block['algorithm'] && (strpos($sig_block['algorithm'], 'rsa') !== false || strpos($sig_block['algorithm'], 'hmac') !== false || strpos($sig_block['algorithm'], 'ecdsa') !== false)) { + logger('created not allowed here'); + return $result; + } + if ((!isset($sig_block['(created)'])) || (!intval($sig_block['(created)'])) || intval($sig_block['(created)']) > time()) { + logger('created in future'); + return $result; + } + $signed_data .= '(created): ' . $sig_block['(created)'] . "\n"; + } + if ($h === '(expires)') { + if ($sig_block['algorithm'] && (strpos($sig_block['algorithm'], 'rsa') !== false || strpos($sig_block['algorithm'], 'hmac') !== false || strpos($sig_block['algorithm'], 'ecdsa') !== false)) { + logger('expires not allowed here'); + return $result; + } + if ((!isset($sig_block['(expires)'])) || (!intval($sig_block['(expires)'])) || intval($sig_block['(expires)']) < time()) { + logger('signature expired'); + return $result; + } + $signed_data .= '(expires): ' . $sig_block['(expires)'] . "\n"; + } + if ($h === 'date') { + $d = new DateTime($headers[$h]); + $d->setTimeZone(new DateTimeZone('UTC')); + $dplus = datetime_convert('UTC', 'UTC', 'now + 1 day'); + $dminus = datetime_convert('UTC', 'UTC', 'now - 1 day'); + $c = $d->format('Y-m-d H:i:s'); + if ($c > $dplus || $c < $dminus) { + logger('bad time: ' . $c); + return $result; + } + } + } + $signed_data = rtrim($signed_data, "\n"); + + $algorithm = null; + + if ($sig_block['algorithm'] === 'rsa-sha256') { + $algorithm = 'sha256'; + } + if ($sig_block['algorithm'] === 'rsa-sha512') { + $algorithm = 'sha512'; + } + + if (!array_key_exists('keyId', $sig_block)) { + return $result; + } + + $result['signer'] = $sig_block['keyId']; + + $fkey = self::get_key($key, $keytype, $result['signer']); + + if ($sig_block['algorithm'] === 'hs2019') { + if (isset($fkey['algorithm'])) { + if (strpos($fkey['algorithm'], 'rsa-sha256') !== false) { + $algorithm = 'sha256'; + } + if (strpos($fkey['algorithm'], 'rsa-sha512') !== false) { + $algorithm = 'sha512'; + } + } + } + + + if (!($fkey && $fkey['public_key'])) { + return $result; + } + + $x = Crypto::verify($signed_data, $sig_block['signature'], $fkey['public_key'], $algorithm); + + logger('verified: ' . $x, LOGGER_DEBUG); + + if (!$x) { + // try again, ignoring the local actor (xchan) cache and refetching the key + // from its source + + $fkey = self::get_key($key, $keytype, $result['signer'], true); + + if ($fkey && $fkey['public_key']) { + $y = Crypto::verify($signed_data, $sig_block['signature'], $fkey['public_key'], $algorithm); + logger('verified: (cache reload) ' . $x, LOGGER_DEBUG); + } + + if (!$y) { + logger('verify failed for ' . $result['signer'] . ' alg=' . $algorithm . (($fkey['public_key']) ? '' : ' no key')); + $sig_block['signature'] = base64_encode($sig_block['signature']); + logger('affected sigblock: ' . print_r($sig_block, true)); + logger('headers: ' . print_r($headers, true)); + logger('server: ' . print_r($_SERVER, true)); + return $result; + } + } + + $result['portable_id'] = $fkey['portable_id']; + $result['header_valid'] = true; + + if (in_array('digest', $signed_headers)) { + $result['content_signed'] = true; + $digest = explode('=', $headers['digest'], 2); + if ($digest[0] === 'SHA-256') { + $hashalg = 'sha256'; + } + if ($digest[0] === 'SHA-512') { + $hashalg = 'sha512'; + } + + if (base64_encode(hash($hashalg, $body, true)) === $digest[1]) { + $result['content_valid'] = true; + } + + logger('Content_Valid: ' . (($result['content_valid']) ? 'true' : 'false')); + if (!$result['content_valid']) { + logger('invalid content signature: data ' . print_r($data, true)); + logger('invalid content signature: headers ' . print_r($headers, true)); + logger('invalid content signature: body ' . print_r($body, true)); + } + } + + return $result; + } + + public static function get_key($key, $keytype, $id, $force = false) + { + + if ($key) { + if (function_exists($key)) { + return $key($id); + } + return ['public_key' => $key]; + } + + if ($keytype === 'zot6') { + $key = self::get_zotfinger_key($id, $force); + if ($key) { + return $key; + } + } + + + if (strpos($id, '#') === false) { + $key = self::get_webfinger_key($id, $force); + if ($key) { + return $key; + } + } + + $key = self::get_activitystreams_key($id, $force); + return $key; + } + + + public static function convertKey($key) + { + + if (strstr($key, 'RSA ')) { + return Keyutils::rsatopem($key); + } elseif (substr($key, 0, 5) === 'data:') { + return Keyutils::convertSalmonKey($key); + } else { + return $key; + } + } + + + /** + * @brief + * + * @param string $id + * @return bool|string + * false if no pub key found, otherwise return the pub key + */ + + public static function get_activitystreams_key($id, $force = false) + { + + // Check the local cache first, but remove any fragments like #main-key since these won't be present in our cached data + + $cache_url = ((strpos($id, '#')) ? substr($id, 0, strpos($id, '#')) : $id); + + // $force is used to ignore the local cache and only use the remote data; for instance the cached key might be stale + + if (!$force) { + $x = q( + "select * from xchan left join hubloc on xchan_hash = hubloc_hash where ( hubloc_addr = '%s' or hubloc_id_url = '%s' or hubloc_hash = '%s') order by hubloc_id desc", + dbesc(str_replace('acct:', '', $cache_url)), + dbesc($cache_url), + dbesc($cache_url) + ); + + if ($x) { + $best = Libzot::zot_record_preferred($x); + } + + if ($best && $best['xchan_pubkey']) { + return ['portable_id' => $best['xchan_hash'], 'public_key' => $best['xchan_pubkey'], 'algorithm' => get_xconfig($best['xchan_hash'], 'system', 'signing_algorithm'), 'hubloc' => $best]; + } + } + + // The record wasn't in cache. Fetch it now. + + $r = Activity::fetch($id); + $signatureAlgorithm = EMPTY_STR; + + if ($r) { + if (array_key_exists('publicKey', $r) && array_key_exists('publicKeyPem', $r['publicKey']) && array_key_exists('id', $r['publicKey'])) { + if ($r['publicKey']['id'] === $id || $r['id'] === $id) { + $portable_id = ((array_key_exists('owner', $r['publicKey'])) ? $r['publicKey']['owner'] : EMPTY_STR); + + // the w3c sec context has conflicting names and no defined values for this property except + // "http://www.w3.org/2000/09/xmldsig#rsa-sha1" + + // Since the names conflict, it could mess up LD-signatures but we will accept both, and at this + // time we will only look for the substrings 'rsa-sha256' and 'rsa-sha512' within those properties. + // We will also accept a toplevel 'sigAlgorithm' regardless of namespace with the same constraints. + // Default to rsa-sha256 if we can't figure out. If they're sending 'hs2019' we have to + // look for something. + + if (isset($r['publicKey']['signingAlgorithm'])) { + $signatureAlgorithm = $r['publicKey']['signingAlgorithm']; + set_xconfig($portable_id, 'system', 'signing_algorithm', $signatureAlgorithm); + } + if (isset($r['publicKey']['signatureAlgorithm'])) { + $signatureAlgorithm = $r['publicKey']['signatureAlgorithm']; + set_xconfig($portable_id, 'system', 'signing_algorithm', $signatureAlgorithm); + } + + if (isset($r['sigAlgorithm'])) { + $signatureAlgorithm = $r['sigAlgorithm']; + set_xconfig($portable_id, 'system', 'signing_algorithm', $signatureAlgorithm); + } + + return ['public_key' => self::convertKey($r['publicKey']['publicKeyPem']), 'portable_id' => $portable_id, 'algorithm' => (($signatureAlgorithm) ? $signatureAlgorithm : 'rsa-sha256'), 'hubloc' => []]; + } + } + } + + // No key was found + + return false; + } + + + public static function get_webfinger_key($id, $force = false) + { + + if (!$force) { + $x = q( + "select * from xchan left join hubloc on xchan_hash = hubloc_hash where ( hubloc_addr = '%s' or hubloc_id_url = '%s' or hubloc_hash = '%s') order by hubloc_id desc", + dbesc(str_replace('acct:', '', $id)), + dbesc($id), + dbesc($id) + ); + + if ($x) { + $best = Libzot::zot_record_preferred($x); + } + + if ($best && $best['xchan_pubkey']) { + return ['portable_id' => $best['xchan_hash'], 'public_key' => $best['xchan_pubkey'], 'algorithm' => get_xconfig($best['xchan_hash'], 'system', 'signing_algorithm'), 'hubloc' => $best]; + } + } + + $wf = Webfinger::exec($id); + $key = ['portable_id' => '', 'public_key' => '', 'algorithm' => '', 'hubloc' => []]; + + if ($wf) { + if (array_key_exists('properties', $wf) && array_key_exists('https://w3id.org/security/v1#publicKeyPem', $wf['properties'])) { + $key['public_key'] = self::convertKey($wf['properties']['https://w3id.org/security/v1#publicKeyPem']); + } + if (array_key_exists('links', $wf) && is_array($wf['links'])) { + foreach ($wf['links'] as $l) { + if (!(is_array($l) && array_key_exists('rel', $l))) { + continue; + } + if ($l['rel'] === 'magic-public-key' && array_key_exists('href', $l) && $key['public_key'] === EMPTY_STR) { + $key['public_key'] = self::convertKey($l['href']); + } + } + } + } + + return (($key['public_key']) ? $key : false); + } + + + public static function get_zotfinger_key($id, $force = false) + { + + if (!$force) { + $x = q( + "select * from xchan left join hubloc on xchan_hash = hubloc_hash where ( hubloc_addr = '%s' or hubloc_id_url = '%s' ) and and hubloc_network in ('nomad','zot6') order by hubloc_id desc", + dbesc(str_replace('acct:', '', $id)), + dbesc($id) + ); + + if ($x) { + $best = Libzot::zot_record_preferred($x); + } + + if ($best && $best['xchan_pubkey']) { + return ['portable_id' => $best['xchan_hash'], 'public_key' => $best['xchan_pubkey'], 'algorithm' => get_xconfig($best['xchan_hash'], 'system', 'signing_algorithm'), 'hubloc' => $best]; + } + } + + $wf = Webfinger::exec($id); + $key = ['portable_id' => '', 'public_key' => '', 'algorithm' => '', 'hubloc' => []]; + + if ($wf) { + if (array_key_exists('properties', $wf) && array_key_exists('https://w3id.org/security/v1#publicKeyPem', $wf['properties'])) { + $key['public_key'] = self::convertKey($wf['properties']['https://w3id.org/security/v1#publicKeyPem']); + } + if (array_key_exists('links', $wf) && is_array($wf['links'])) { + foreach ($wf['links'] as $l) { + if (!(is_array($l) && array_key_exists('rel', $l))) { + continue; + } + if (in_array($l['rel'], ['http://purl.org/nomad', 'http://purl.org/zot/protocol/6.0']) && array_key_exists('href',$l) && $l['href'] !== EMPTY_STR) { + // The third argument to Zotfinger::exec() tells it not to verify signatures + // Since we're inside a function that is fetching keys with which to verify signatures, + // this is necessary to prevent infinite loops. + + $z = Zotfinger::exec($l['href'], null, false); + if ($z) { + $i = Libzot::import_xchan($z['data']); + if ($i['success']) { + $key['portable_id'] = $i['hash']; + + $x = q( + "select * from xchan left join hubloc on xchan_hash = hubloc_hash where hubloc_id_url = '%s' order by hubloc_id desc limit 1", + dbesc($l['href']) + ); + if ($x) { + $key['hubloc'] = $x[0]; + } + $key['algorithm'] = get_xconfig($i['hash'], 'system', 'signing_algorithm'); + } + } + } + if ($l['rel'] === 'magic-public-key' && array_key_exists('href', $l) && $key['public_key'] === EMPTY_STR) { + $key['public_key'] = self::convertKey($l['href']); + } + } + } + } + + return (($key['public_key']) ? $key : false); + } + + + /** + * @brief + * + * @param array $head + * @param string $prvkey + * @param string $keyid (optional, default '') + * @param bool $auth (optional, default false) + * @param string $alg (optional, default 'sha256') + * @param array $encryption [ 'key', 'algorithm' ] or false + * @return array + */ + public static function create_sig($head, $prvkey, $keyid = EMPTY_STR, $auth = false, $alg = 'sha256', $encryption = false) + { + + $return_headers = []; + + if ($alg === 'sha256') { + $algorithm = 'rsa-sha256'; + } + if ($alg === 'sha512') { + $algorithm = 'rsa-sha512'; + } + + $x = self::sign($head, $prvkey, $alg); + + $headerval = 'keyId="' . $keyid . '",algorithm="' . $algorithm . '",headers="' . $x['headers'] . '",signature="' . $x['signature'] . '"'; + + if ($encryption) { + $x = Crypto::encapsulate($headerval, $encryption['key'], $encryption['algorithm']); + if (is_array($x)) { + $headerval = 'iv="' . $x['iv'] . '",key="' . $x['key'] . '",alg="' . $x['alg'] . '",data="' . $x['data'] . '"'; + } + } + + if ($auth) { + $sighead = 'Authorization: Signature ' . $headerval; + } else { + $sighead = 'Signature: ' . $headerval; + } + + if ($head) { + foreach ($head as $k => $v) { + // strip the request-target virtual header from the output headers + if ($k === '(request-target)') { + continue; + } + $return_headers[] = $k . ': ' . $v; + } + } + $return_headers[] = $sighead; + + return $return_headers; + } + + /** + * @brief set headers + * + * @param array $headers + * @return void + */ + + public static function set_headers($headers) + { + if ($headers && is_array($headers)) { + foreach ($headers as $h) { + header($h); + } + } + } + + + /** + * @brief + * + * @param array $head + * @param string $prvkey + * @param string $alg (optional) default 'sha256' + * @return array + */ + + public static function sign($head, $prvkey, $alg = 'sha256') + { + + $ret = []; + + $headers = ''; + $fields = ''; + + logger('signing: ' . print_r($head, true), LOGGER_DATA); + + if ($head) { + foreach ($head as $k => $v) { + $headers .= strtolower($k) . ': ' . trim($v) . "\n"; + if ($fields) { + $fields .= ' '; + } + $fields .= strtolower($k); + } + // strip the trailing linefeed + $headers = rtrim($headers, "\n"); + } + + $sig = base64_encode(Crypto::sign($headers, $prvkey, $alg)); + + $ret['headers'] = $fields; + $ret['signature'] = $sig; + + return $ret; + } + + /** + * @brief + * + * @param string $header + * @return array associate array with + * - \e string \b keyID + * - \e string \b algorithm + * - \e array \b headers + * - \e string \b signature + */ + + public static function parse_sigheader($header) + { + + $ret = []; + $matches = []; + + // if the header is encrypted, decrypt with (default) site private key and continue + + if (preg_match('/iv="(.*?)"/ism', $header, $matches)) { + $header = self::decrypt_sigheader($header); + } + + if (preg_match('/keyId="(.*?)"/ism', $header, $matches)) { + $ret['keyId'] = $matches[1]; + } + if (preg_match('/created=([0-9]*)/ism', $header, $matches)) { + $ret['(created)'] = $matches[1]; + } + if (preg_match('/expires=([0-9]*)/ism', $header, $matches)) { + $ret['(expires)'] = $matches[1]; + } + if (preg_match('/algorithm="(.*?)"/ism', $header, $matches)) { + $ret['algorithm'] = $matches[1]; + } + if (preg_match('/headers="(.*?)"/ism', $header, $matches)) { + $ret['headers'] = explode(' ', $matches[1]); + } + if (preg_match('/signature="(.*?)"/ism', $header, $matches)) { + $ret['signature'] = base64_decode(preg_replace('/\s+/', '', $matches[1])); + } + + if (($ret['signature']) && ($ret['algorithm']) && (!$ret['headers'])) { + $ret['headers'] = ['date']; + } + + return $ret; + } + + + /** + * @brief + * + * @param string $header + * @param string $prvkey (optional), if not set use site private key + * @return array|string associative array, empty string if failue + * - \e string \b iv + * - \e string \b key + * - \e string \b alg + * - \e string \b data + */ + + public static function decrypt_sigheader($header, $prvkey = null) + { + + $iv = $key = $alg = $data = null; + + if (!$prvkey) { + $prvkey = get_config('system', 'prvkey'); + } + + $matches = []; + + if (preg_match('/iv="(.*?)"/ism', $header, $matches)) { + $iv = $matches[1]; + } + if (preg_match('/key="(.*?)"/ism', $header, $matches)) { + $key = $matches[1]; + } + if (preg_match('/alg="(.*?)"/ism', $header, $matches)) { + $alg = $matches[1]; + } + if (preg_match('/data="(.*?)"/ism', $header, $matches)) { + $data = $matches[1]; + } + + if ($iv && $key && $alg && $data) { + return Crypto::unencapsulate(['encrypted' => true, 'iv' => $iv, 'key' => $key, 'alg' => $alg, 'data' => $data], $prvkey); + } + + return ''; + } } diff --git a/Zotlabs/Web/HttpMeta.php b/Zotlabs/Web/HttpMeta.php index 4afd5b81f..23e8cc13e 100644 --- a/Zotlabs/Web/HttpMeta.php +++ b/Zotlabs/Web/HttpMeta.php @@ -2,113 +2,112 @@ namespace Zotlabs\Web; +class HttpMeta +{ -class HttpMeta { + private $vars = null; + private $og = null; - private $vars = null; - private $og = null; + public function __construct() + { - function __construct() { + $this->vars = []; + $this->og = []; + $this->ogproperties = []; + } - $this->vars = []; - $this->og = []; - $this->ogproperties = []; + //Set Meta Value + // Mode: + // 0 = Default - set if no value currently exists + // 1 = Overwrite - replace existing value(s) + // 2 = Multi - append to the array of values + public function set($property, $value, $mode = 0) + { + $ogallowsmulti = ['image', 'audio', 'video']; + if (strpos($property, 'og:') === 0) { + $count = 0; + foreach ($this->og as $ogk => $ogdata) { + if (strpos($ogdata['property'], $property) === 0) { + if ($mode == 1) { + unset($this->og[$ogk]); + unset($this->ogproperties[$property]); + } elseif ($mode == 0) { + return; + } elseif ($value == $ogdata['value']) { + return; + } else { + $count++; + } + } + } - } + if ($value !== null) { + //mode = 1 with value === null will delete the property entirely. + $components = explode(':', $property); + $ogp = $components[1]; - //Set Meta Value - // Mode: - // 0 = Default - set if no value currently exists - // 1 = Overwrite - replace existing value(s) - // 2 = Multi - append to the array of values - function set($property,$value,$mode=0) { - $ogallowsmulti = ['image','audio','video']; - if (strpos($property,'og:') === 0) { - $count = 0; - foreach ($this->og as $ogk => $ogdata) { - if (strpos($ogdata['property'],$property) === 0) { - if ($mode == 1) { - unset($this->og[$ogk]); - unset($this->ogproperties[$property]); - } - elseif ($mode == 0) { - return; - } - elseif ($value == $ogdata['value']) { - return; - } - else { - $count++; - } - } - } + if (!$count || in_array($ogp, $ogallowsmulti)) { + $this->og[] = ['property' => $property, 'value' => $value]; + $this->ogproperties[$property] = $property; + } + } + } else { + $this->vars[$property] = $value; + } + } - if ($value !== null) { - //mode = 1 with value === null will delete the property entirely. - $components = explode(':',$property); - $ogp=$components[1]; + public function check_required() + { + if ( + in_array('og:title', $this->ogproperties) + && in_array('og:type', $this->ogproperties) + && (in_array('og:image', $this->ogproperties) + || in_array('og:image:url', $this->ogproperties)) + && (array_key_exists('og:url', $this->ogproperties) + || array_key_exists('og:url:secure_url', $this->ogproperties)) + && array_key_exists('og:description', $this->ogproperties) + ) { + return true; + } + return false; + } - if (!$count || in_array($ogp,$ogallowsmulti)) { - $this->og[]=['property'=>$property,'value'=>$value]; - $this->ogproperties[$property] = $property; - } - } - } else { - $this->vars[$property] = $value; - } - } + public function get_field($field) + { + if (strpos($field, 'og:') === 0) { + foreach ($this->og as $ogdata) { + if (strpos($ogdata['property'], $field) === 0) { + $arr[$field][] = $ogdata['value']; + } + } + } else { + $arr = $this->vars; + } - function check_required() { - if ( - in_array('og:title',$this->ogproperties) - && in_array('og:type', $this->ogproperties) - && (in_array('og:image',$this->ogproperties) - || in_array('og:image:url',$this->ogproperties)) - && (array_key_exists('og:url', $this->ogproperties) - || array_key_exists('og:url:secure_url', $this->ogproperties)) - && array_key_exists('og:description', $this->ogproperties) - ) { - return true; - } - return false; - } - - function get_field($field) { - if (strpos($field,'og:') === 0) { - foreach ($this->og as $ogdata) { - if (strpos($ogdata['property'],$field) === 0) { - $arr[$field][] = $ogdata['value']; - } - } - } - else { - $arr = $this->vars; - } - - if (isset($arr) && is_array($arr) && array_key_exists($field,$arr) && $arr[$field]) { - return $arr[$field]; - } - return false; - } + if (isset($arr) && is_array($arr) && array_key_exists($field, $arr) && $arr[$field]) { + return $arr[$field]; + } + return false; + } - function get() { - // use 'name' for most meta fields, and 'property' for opengraph properties - $o = ''; - if ($this->vars) { - foreach ($this->vars as $k => $v) { - $o .= '' . "\r\n" ; - } - } - if ($this->check_required()) { - foreach ($this->og as $ogdata) { - $o .= '' . "\r\n" ; - } - } - if ($o) { - return "\r\n" . $o; - } - return $o; - } - + public function get() + { + // use 'name' for most meta fields, and 'property' for opengraph properties + $o = ''; + if ($this->vars) { + foreach ($this->vars as $k => $v) { + $o .= '' . "\r\n"; + } + } + if ($this->check_required()) { + foreach ($this->og as $ogdata) { + $o .= '' . "\r\n"; + } + } + if ($o) { + return "\r\n" . $o; + } + return $o; + } } diff --git a/Zotlabs/Web/Router.php b/Zotlabs/Web/Router.php index bfd1ad8e8..a2e01136e 100644 --- a/Zotlabs/Web/Router.php +++ b/Zotlabs/Web/Router.php @@ -33,237 +33,233 @@ use Exception; * so within the module init and/or post functions and then invoke killme() to terminate * further processing. */ -class Router { +class Router +{ - private $modname = ''; - private $controller = null; + private $modname = ''; + private $controller = null; - /** - * @brief Router constructor. - * - * @throws Exception module not found - */ - function __construct() { + /** + * @brief Router constructor. + * + * @throws Exception module not found + */ + public function __construct() + { - $module = App::$module; - $modname = "Zotlabs\\Module\\" . ucfirst($module); + $module = App::$module; + $modname = "Zotlabs\\Module\\" . ucfirst($module); - if (strlen($module)) { + if (strlen($module)) { + /* + * We will always have a module name. + * First see if we have a plugin handling this route + */ - /* - * We will always have a module name. - * First see if we have a plugin handling this route - */ - - $routes = Route::get(); - if ($routes) { - foreach ($routes as $route) { - if (is_array($route) && strtolower($route[1]) === $module) { - if (file_exists($route[0])) { - include_once($route[0]); - } - if (class_exists($modname)) { - $this->controller = new $modname; - App::$module_loaded = true; - } - } - } - } + $routes = Route::get(); + if ($routes) { + foreach ($routes as $route) { + if (is_array($route) && strtolower($route[1]) === $module) { + if (file_exists($route[0])) { + include_once($route[0]); + } + if (class_exists($modname)) { + $this->controller = new $modname(); + App::$module_loaded = true; + } + } + } + } - /* - * If the site has a custom module to over-ride the standard module, use it. - * Otherwise, look for the standard program module - */ + /* + * If the site has a custom module to over-ride the standard module, use it. + * Otherwise, look for the standard program module + */ - if(! (App::$module_loaded)) { - try { - $filename = 'Zotlabs/SiteModule/'. ucfirst($module). '.php'; - if (file_exists($filename)) { - // This won't be picked up by the autoloader, so load it explicitly - require_once($filename); - $this->controller = new $modname; - App::$module_loaded = true; - } - else { - $filename = 'Zotlabs/Module/'. ucfirst($module). '.php'; - if (file_exists($filename)) { - $this->controller = new $modname; - App::$module_loaded = true; - } - } - if (! App::$module_loaded) { - throw new Exception('Module not found'); - } - } - catch(Exception $e) { + if (!(App::$module_loaded)) { + try { + $filename = 'Zotlabs/SiteModule/' . ucfirst($module) . '.php'; + if (file_exists($filename)) { + // This won't be picked up by the autoloader, so load it explicitly + require_once($filename); + $this->controller = new $modname(); + App::$module_loaded = true; + } else { + $filename = 'Zotlabs/Module/' . ucfirst($module) . '.php'; + if (file_exists($filename)) { + $this->controller = new $modname(); + App::$module_loaded = true; + } + } + if (!App::$module_loaded) { + throw new Exception('Module not found'); + } + } catch (Exception $e) { + } + } - } - } + $x = [ + 'module' => $module, + 'installed' => App::$module_loaded, + 'controller' => $this->controller + ]; - $x = [ - 'module' => $module, - 'installed' => App::$module_loaded, - 'controller' => $this->controller - ]; + /** + * @hooks module_loaded + * Called when a module has been successfully locate to server a URL request. + * This provides a place for plugins to register module handlers which don't otherwise exist + * on the system, or to completely over-ride an existing module. + * If the plugin sets 'installed' to true we won't throw a 404 error for the specified module even if + * there is no specific module file or matching plugin name. + * The plugin should catch at least one of the module hooks for this URL. + * * \e string \b module + * * \e boolean \b installed + * * \e mixed \b controller - The initialized module object + */ + call_hooks('module_loaded', $x); + if ($x['installed']) { + App::$module_loaded = true; + $this->controller = $x['controller']; + } - /** - * @hooks module_loaded - * Called when a module has been successfully locate to server a URL request. - * This provides a place for plugins to register module handlers which don't otherwise exist - * on the system, or to completely over-ride an existing module. - * If the plugin sets 'installed' to true we won't throw a 404 error for the specified module even if - * there is no specific module file or matching plugin name. - * The plugin should catch at least one of the module hooks for this URL. - * * \e string \b module - * * \e boolean \b installed - * * \e mixed \b controller - The initialized module object - */ - call_hooks('module_loaded', $x); - if ($x['installed']) { - App::$module_loaded = true; - $this->controller = $x['controller']; - } + /* + * The URL provided does not resolve to a valid module. + */ - /* - * The URL provided does not resolve to a valid module. - */ + if (!(App::$module_loaded)) { + // undo the setting of a letsencrypt acme-challenge rewrite rule + // which blocks access to our .well-known routes. + // Also provide a config setting for sites that have a legitimate need + // for a custom .htaccess in the .well-known directory; but they should + // make the file read-only so letsencrypt doesn't modify it - if (! (App::$module_loaded)) { + if (strpos($_SERVER['REQUEST_URI'], '/.well-known/') === 0) { + if (file_exists('.well-known/.htaccess') && get_config('system', 'fix_apache_acme', true)) { + rename('.well-known/.htaccess', '.well-known/.htaccess.old'); + } + } - // undo the setting of a letsencrypt acme-challenge rewrite rule - // which blocks access to our .well-known routes. - // Also provide a config setting for sites that have a legitimate need - // for a custom .htaccess in the .well-known directory; but they should - // make the file read-only so letsencrypt doesn't modify it + $x = [ + 'module' => $module, + 'installed' => App::$module_loaded, + 'controller' => $this->controller + ]; + call_hooks('page_not_found', $x); - if (strpos($_SERVER['REQUEST_URI'],'/.well-known/') === 0) { - if (file_exists('.well-known/.htaccess') && get_config('system','fix_apache_acme',true)) { - rename('.well-known/.htaccess','.well-known/.htaccess.old'); - } - } + // Stupid browser tried to pre-fetch our Javascript img template. + // Don't log the event or return anything - just quietly exit. - $x = [ - 'module' => $module, - 'installed' => App::$module_loaded, - 'controller' => $this->controller - ]; - call_hooks('page_not_found',$x); + if ((x($_SERVER, 'QUERY_STRING')) && preg_match('/{[0-9]}/', $_SERVER['QUERY_STRING']) !== 0) { + killme(); + } - // Stupid browser tried to pre-fetch our Javascript img template. - // Don't log the event or return anything - just quietly exit. + 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); + } - if ((x($_SERVER, 'QUERY_STRING')) && preg_match('/{[0-9]}/', $_SERVER['QUERY_STRING']) !== 0) { - killme(); - } + header($_SERVER['SERVER_PROTOCOL'] . ' 404 Not Found'); + $tpl = get_markup_template('404.tpl'); + App::$page['content'] = replace_macros(get_markup_template('404.tpl'), ['$message' => t('Page 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); - } + // pretend this is a module so it will initialise the theme + App::$module = '404'; + App::$module_loaded = true; + App::$error = true; + } + } + } - header($_SERVER['SERVER_PROTOCOL'] . ' 404 Not Found'); - $tpl = get_markup_template('404.tpl'); - App::$page['content'] = replace_macros(get_markup_template('404.tpl'), [ '$message' => t('Page not found.') ]); + /** + * @brief + * + */ + public function Dispatch() + { - // pretend this is a module so it will initialise the theme - App::$module = '404'; - App::$module_loaded = true; - App::$error = true; - } - } - } + /** + * Call module functions + */ - /** - * @brief - * - */ - function Dispatch() { + if (App::$module_loaded) { + App::$page['page_title'] = App::$module; + $placeholder = ''; - /** - * Call module functions - */ + /* + * No theme has been specified when calling the module_init functions + * For this reason, please restrict the use of templates to those which + * do not provide any presentation details - as themes will not be able + * to over-ride them. + */ - if (App::$module_loaded) { + $arr = ['init' => true, 'replace' => false]; + call_hooks(App::$module . '_mod_init', $arr); + if (!$arr['replace']) { + if ($this->controller && method_exists($this->controller, 'init')) { + $this->controller->init(); + } + } - App::$page['page_title'] = App::$module; - $placeholder = ''; - - /* - * No theme has been specified when calling the module_init functions - * For this reason, please restrict the use of templates to those which - * do not provide any presentation details - as themes will not be able - * to over-ride them. - */ - - $arr = [ 'init' => true, 'replace' => false ]; - call_hooks(App::$module . '_mod_init', $arr); - if (! $arr['replace']) { - if ($this->controller && method_exists($this->controller,'init')) { - $this->controller->init(); - } - } - - /* - * Do all theme initialisation here before calling any additional module functions. - * The module_init function may have changed the theme. - * Additionally any page with a Comanche template may alter the theme. - * So we'll check for those now. - */ + /* + * Do all theme initialisation here before calling any additional module functions. + * The module_init function may have changed the theme. + * Additionally any page with a Comanche template may alter the theme. + * So we'll check for those now. + */ - /* - * In case a page has overloaded a module, see if we already have a layout defined - * otherwise, if a PDL file exists for this module, use it - * The member may have also created a customised PDL that's stored in the config - */ + /* + * In case a page has overloaded a module, see if we already have a layout defined + * otherwise, if a PDL file exists for this module, use it + * The member may have also created a customised PDL that's stored in the config + */ - load_pdl(); + load_pdl(); - /* - * load current theme info - */ + /* + * load current theme info + */ - $current_theme = Theme::current(); + $current_theme = Theme::current(); - $theme_info_file = 'view/theme/' . $current_theme[0] . '/php/theme.php'; - if (file_exists($theme_info_file)) { - require_once($theme_info_file); - } + $theme_info_file = 'view/theme/' . $current_theme[0] . '/php/theme.php'; + if (file_exists($theme_info_file)) { + require_once($theme_info_file); + } - if (function_exists(str_replace('-', '_', $current_theme[0]) . '_init')) { - $func = str_replace('-', '_', $current_theme[0]) . '_init'; - $func($a); - } - elseif (x(App::$theme_info, 'extends') && file_exists('view/theme/' . App::$theme_info['extends'] . '/php/theme.php')) { - require_once('view/theme/' . App::$theme_info['extends'] . '/php/theme.php'); - if (function_exists(str_replace('-', '_', App::$theme_info['extends']) . '_init')) { - $func = str_replace('-', '_', App::$theme_info['extends']) . '_init'; - $func($a); - } - } + if (function_exists(str_replace('-', '_', $current_theme[0]) . '_init')) { + $func = str_replace('-', '_', $current_theme[0]) . '_init'; + $func($a); + } elseif (x(App::$theme_info, 'extends') && file_exists('view/theme/' . App::$theme_info['extends'] . '/php/theme.php')) { + require_once('view/theme/' . App::$theme_info['extends'] . '/php/theme.php'); + if (function_exists(str_replace('-', '_', App::$theme_info['extends']) . '_init')) { + $func = str_replace('-', '_', App::$theme_info['extends']) . '_init'; + $func($a); + } + } - if (($_SERVER['REQUEST_METHOD'] === 'POST') && (! App::$error) && (! x($_POST, 'auth-params'))) { - call_hooks(App::$module . '_mod_post', $_POST); - if ($this->controller && method_exists($this->controller,'post')) { - $this->controller->post(); - } - } + if (($_SERVER['REQUEST_METHOD'] === 'POST') && (!App::$error) && (!x($_POST, 'auth-params'))) { + call_hooks(App::$module . '_mod_post', $_POST); + if ($this->controller && method_exists($this->controller, 'post')) { + $this->controller->post(); + } + } - if (! App::$error) { - $arr = [ 'content' => \App::$page['content'], 'replace' => false ]; - call_hooks(App::$module . '_mod_content', $arr); + if (!App::$error) { + $arr = ['content' => App::$page['content'], 'replace' => false]; + call_hooks(App::$module . '_mod_content', $arr); - if (! $arr['replace']) { - if ($this->controller && method_exists($this->controller,'get')) { - $arr = [ 'content' => $this->controller->get(), 'replace' => false ]; - } - } - call_hooks(App::$module . '_mod_aftercontent', $arr); - App::$page['content'] = (($arr['replace']) ? $arr['content'] : App::$page['content'] . $arr['content']); - } - } - } + if (!$arr['replace']) { + if ($this->controller && method_exists($this->controller, 'get')) { + $arr = ['content' => $this->controller->get(), 'replace' => false]; + } + } + call_hooks(App::$module . '_mod_aftercontent', $arr); + App::$page['content'] = (($arr['replace']) ? $arr['content'] : App::$page['content'] . $arr['content']); + } + } + } } diff --git a/Zotlabs/Web/Session.php b/Zotlabs/Web/Session.php index 9dbcbdd27..6fa9ed2e8 100644 --- a/Zotlabs/Web/Session.php +++ b/Zotlabs/Web/Session.php @@ -4,6 +4,7 @@ namespace Zotlabs\Web; use Zotlabs\Web\SessionHandler ; + /** * * @brief This file includes session related functions. @@ -13,187 +14,187 @@ use Zotlabs\Web\SessionHandler */ -class Session { +class Session +{ - private $handler = null; - private $session_started = false; - private $custom_handler = false; + private $handler = null; + private $session_started = false; + private $custom_handler = false; - public function init() { + public function init() + { - $gc_probability = 50; + $gc_probability = 50; - ini_set('session.gc_probability', $gc_probability); - ini_set('session.use_only_cookies', 1); - ini_set('session.cookie_httponly', 1); + ini_set('session.gc_probability', $gc_probability); + ini_set('session.use_only_cookies', 1); + ini_set('session.cookie_httponly', 1); - $this->custom_handler = boolval(get_config('system', 'session_custom', false)); - - /* - * Set our session storage functions. - */ - - if ($this->custom_handler) { - /* Custom handler (files, memached, redis..) */ + $this->custom_handler = boolval(get_config('system', 'session_custom', false)); - $session_save_handler = strval(get_config('system', 'session_save_handler', Null)); - $session_save_path = strval(get_config('system', 'session_save_path', Null)); - if ($session_save_handler && $session_save_path) { - ini_set('session.save_handler', $session_save_handler); - ini_set('session.save_path', $session_save_path); - } - else { - logger('Session save handler or path not set.',LOGGER_NORMAL,LOG_ERR); - } - } - else { - $handler = new SessionHandler(); + /* + * Set our session storage functions. + */ - $this->handler = $handler; + if ($this->custom_handler) { + /* Custom handler (files, memached, redis..) */ - $x = session_set_save_handler($handler,false); - if (! $x) { - logger('Session save handler initialisation failed.',LOGGER_NORMAL,LOG_ERR); - } - }; - - // Force cookies to be secure (https only) if this site is SSL enabled. - // Must be done before session_start(). + $session_save_handler = strval(get_config('system', 'session_save_handler', null)); + $session_save_path = strval(get_config('system', 'session_save_path', null)); + if ($session_save_handler && $session_save_path) { + ini_set('session.save_handler', $session_save_handler); + ini_set('session.save_path', $session_save_path); + } else { + logger('Session save handler or path not set.', LOGGER_NORMAL, LOG_ERR); + } + } else { + $handler = new SessionHandler(); - $arr = session_get_cookie_params(); - - // Note when setting cookies: set the domain to false which creates a single domain - // cookie. If you use a hostname it will create a .domain.com wildcard which will - // have some nasty side effects if you have any other subdomains running hubzilla. + $this->handler = $handler; - session_set_cookie_params( - ((isset($arr['lifetime'])) ? $arr['lifetime'] : 0), - ((isset($arr['path'])) ? $arr['path'] : '/'), - (($arr['domain']) ? $arr['domain'] : false), - ((isset($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) == 'on') ? true : false), - ((isset($arr['httponly'])) ? $arr['httponly'] : true) - ); + $x = session_set_save_handler($handler, false); + if (! $x) { + logger('Session save handler initialisation failed.', LOGGER_NORMAL, LOG_ERR); + } + } - register_shutdown_function('session_write_close'); + // Force cookies to be secure (https only) if this site is SSL enabled. + // Must be done before session_start(). - } + $arr = session_get_cookie_params(); - public function start() { - session_start(); - $this->session_started = true; - } + // Note when setting cookies: set the domain to false which creates a single domain + // cookie. If you use a hostname it will create a .domain.com wildcard which will + // have some nasty side effects if you have any other subdomains running hubzilla. - /** - * @brief Resets the current session. - * - * @return void - */ + session_set_cookie_params( + ((isset($arr['lifetime'])) ? $arr['lifetime'] : 0), + ((isset($arr['path'])) ? $arr['path'] : '/'), + (($arr['domain']) ? $arr['domain'] : false), + ((isset($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) == 'on') ? true : false), + ((isset($arr['httponly'])) ? $arr['httponly'] : true) + ); - public function nuke() { - $this->new_cookie(0); // 0 means delete on browser exit - if ($_SESSION && count($_SESSION)) { - foreach ($_SESSION as $k => $v) { - unset($_SESSION[$k]); - } - } - } + register_shutdown_function('session_write_close'); + } - public function new_cookie($xtime) { + public function start() + { + session_start(); + $this->session_started = true; + } - $newxtime = (($xtime > 0) ? (time() + $xtime) : 0); + /** + * @brief Resets the current session. + * + * @return void + */ - $old_sid = session_id(); + public function nuke() + { + $this->new_cookie(0); // 0 means delete on browser exit + if ($_SESSION && count($_SESSION)) { + foreach ($_SESSION as $k => $v) { + unset($_SESSION[$k]); + } + } + } - $arr = session_get_cookie_params(); + public function new_cookie($xtime) + { - if (($this->handler || $this->custom_handler) && $this->session_started) { + $newxtime = (($xtime > 0) ? (time() + $xtime) : 0); - session_regenerate_id(true); + $old_sid = session_id(); - // force SessionHandler record creation with the new session_id - // which occurs as a side effect of read() - if (! $this->custom_handler) { - $this->handler->read(session_id()); - } - } - else { - logger('no session handler'); - } - - if (x($_COOKIE, 'jsdisabled')) { - setcookie('jsdisabled', $_COOKIE['jsdisabled'], $newxtime, '/', false,((isset($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) == 'on') ? true : false),((isset($arr['httponly'])) ? $arr['httponly'] : true)); - } - setcookie(session_name(),session_id(),$newxtime, '/', false,((isset($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) == 'on') ? true : false),((isset($arr['httponly'])) ? $arr['httponly'] : true)); + $arr = session_get_cookie_params(); - $arr = array('expire' => $xtime); - call_hooks('new_cookie', $arr); + if (($this->handler || $this->custom_handler) && $this->session_started) { + session_regenerate_id(true); - } + // force SessionHandler record creation with the new session_id + // which occurs as a side effect of read() + if (! $this->custom_handler) { + $this->handler->read(session_id()); + } + } else { + logger('no session handler'); + } - public function extend_cookie() { + if (x($_COOKIE, 'jsdisabled')) { + setcookie('jsdisabled', $_COOKIE['jsdisabled'], $newxtime, '/', false, ((isset($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) == 'on') ? true : false), ((isset($arr['httponly'])) ? $arr['httponly'] : true)); + } + setcookie(session_name(), session_id(), $newxtime, '/', false, ((isset($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) == 'on') ? true : false), ((isset($arr['httponly'])) ? $arr['httponly'] : true)); - $arr = session_get_cookie_params(); + $arr = array('expire' => $xtime); + call_hooks('new_cookie', $arr); + } - // if there's a long-term cookie, extend it + public function extend_cookie() + { - $xtime = (($_SESSION['remember_me']) ? (60 * 60 * 24 * 365) : 0 ); + $arr = session_get_cookie_params(); - if ($xtime) { - setcookie(session_name(),session_id(),(time() + $xtime), '/', false,((isset($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) == 'on') ? true : false),((isset($arr['httponly'])) ? $arr['httponly'] : true)); - } - $arr = array('expire' => $xtime); - call_hooks('extend_cookie', $arr); + // if there's a long-term cookie, extend it - } + $xtime = (($_SESSION['remember_me']) ? (60 * 60 * 24 * 365) : 0 ); + + if ($xtime) { + setcookie(session_name(), session_id(), (time() + $xtime), '/', false, ((isset($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) == 'on') ? true : false), ((isset($arr['httponly'])) ? $arr['httponly'] : true)); + } + $arr = array('expire' => $xtime); + call_hooks('extend_cookie', $arr); + } - public function return_check() { + public function return_check() + { - // check a returning visitor against IP changes. - // If the change results in being blocked from re-entry with the current cookie - // nuke the session and logout. - // Returning at all indicates the session is still valid. + // check a returning visitor against IP changes. + // If the change results in being blocked from re-entry with the current cookie + // nuke the session and logout. + // Returning at all indicates the session is still valid. - // first check if we're enforcing that sessions can't change IP address - // @todo what to do with IPv6 addresses + // first check if we're enforcing that sessions can't change IP address + // @todo what to do with IPv6 addresses - if ($_SESSION['addr'] && $_SESSION['addr'] != $_SERVER['REMOTE_ADDR']) { - logger('SECURITY: Session IP address changed: ' . $_SESSION['addr'] . ' != ' . $_SERVER['REMOTE_ADDR']); + if ($_SESSION['addr'] && $_SESSION['addr'] != $_SERVER['REMOTE_ADDR']) { + logger('SECURITY: Session IP address changed: ' . $_SESSION['addr'] . ' != ' . $_SERVER['REMOTE_ADDR']); - $partial1 = substr($_SESSION['addr'], 0, strrpos($_SESSION['addr'], '.')); - $partial2 = substr($_SERVER['REMOTE_ADDR'], 0, strrpos($_SERVER['REMOTE_ADDR'], '.')); + $partial1 = substr($_SESSION['addr'], 0, strrpos($_SESSION['addr'], '.')); + $partial2 = substr($_SERVER['REMOTE_ADDR'], 0, strrpos($_SERVER['REMOTE_ADDR'], '.')); - $paranoia = intval(get_pconfig($_SESSION['uid'], 'system', 'paranoia')); + $paranoia = intval(get_pconfig($_SESSION['uid'], 'system', 'paranoia')); - if (! $paranoia) { - $paranoia = intval(get_config('system', 'paranoia')); - } - - switch ($paranoia) { - case 0: - // no IP checking - break; - case 2: - // check 2 octets - $partial1 = substr($partial1, 0, strrpos($partial1, '.')); - $partial2 = substr($partial2, 0, strrpos($partial2, '.')); - if ($partial1 == $partial2) { - break; - } - case 1: - // check 3 octets - if ($partial1 == $partial2) { - break; - } - case 3: - default: - // check any difference at all - logger('Session address changed. Paranoid setting in effect, blocking session. ' . $_SESSION['addr'] . ' != ' . $_SERVER['REMOTE_ADDR']); - $this->nuke(); - goaway(z_root()); - break; - } - } - return true; - } + if (! $paranoia) { + $paranoia = intval(get_config('system', 'paranoia')); + } + + switch ($paranoia) { + case 0: + // no IP checking + break; + case 2: + // check 2 octets + $partial1 = substr($partial1, 0, strrpos($partial1, '.')); + $partial2 = substr($partial2, 0, strrpos($partial2, '.')); + if ($partial1 == $partial2) { + break; + } + case 1: + // check 3 octets + if ($partial1 == $partial2) { + break; + } + case 3: + default: + // check any difference at all + logger('Session address changed. Paranoid setting in effect, blocking session. ' . $_SESSION['addr'] . ' != ' . $_SERVER['REMOTE_ADDR']); + $this->nuke(); + goaway(z_root()); + break; + } + } + return true; + } } diff --git a/Zotlabs/Web/SessionHandler.php b/Zotlabs/Web/SessionHandler.php index 92753a166..aa25d713e 100644 --- a/Zotlabs/Web/SessionHandler.php +++ b/Zotlabs/Web/SessionHandler.php @@ -4,90 +4,98 @@ namespace Zotlabs\Web; use SessionHandlerInterface; -class SessionHandler implements SessionHandlerInterface { +class SessionHandler implements SessionHandlerInterface +{ - function open ($s, $n) { - return true; - } + public function open($s, $n) + { + return true; + } - // IMPORTANT: if we read the session and it doesn't exist, create an empty record. - // We rely on this due to differing PHP implementation of session_regenerate_id() - // some which call read explicitly and some that do not. So we call it explicitly - // just after sid regeneration to force a record to exist. + // IMPORTANT: if we read the session and it doesn't exist, create an empty record. + // We rely on this due to differing PHP implementation of session_regenerate_id() + // some which call read explicitly and some that do not. So we call it explicitly + // just after sid regeneration to force a record to exist. - function read ($id) { + public function read($id) + { - if ($id) { - $r = q("SELECT sess_data FROM session WHERE sid= '%s'", dbesc($id)); + if ($id) { + $r = q("SELECT sess_data FROM session WHERE sid= '%s'", dbesc($id)); - if ($r) { - return $r[0]['sess_data']; - } - else { - q("INSERT INTO session (sess_data, sid, expire) values ('%s', '%s', '%s')", - dbesc(''), - dbesc($id), - dbesc(time() + 1800) - ); - } - } + if ($r) { + return $r[0]['sess_data']; + } else { + q( + "INSERT INTO session (sess_data, sid, expire) values ('%s', '%s', '%s')", + dbesc(''), + dbesc($id), + dbesc(time() + 1800) + ); + } + } - return ''; - } + return ''; + } - function write ($id, $data) { + public function write($id, $data) + { - // Pretend everything is hunky-dory, even though it isn't. There probably isn't anything - // we can do about it in any event. - - if (! $id) { - return true; - } + // Pretend everything is hunky-dory, even though it isn't. There probably isn't anything + // we can do about it in any event. - // Unless we authenticate somehow, only keep a session for 30 minutes - // The viewer can extend this by performing any web action using the - // original cookie, but this allows us to cleanup the hundreds or - // thousands of empty sessions left around from web crawlers which are - // assigned cookies on each page that they never use. + if (!$id) { + return true; + } - $expire = time() + 1800; + // Unless we authenticate somehow, only keep a session for 30 minutes + // The viewer can extend this by performing any web action using the + // original cookie, but this allows us to cleanup the hundreds or + // thousands of empty sessions left around from web crawlers which are + // assigned cookies on each page that they never use. - if ($_SESSION) { - if (array_key_exists('remember_me',$_SESSION) && intval($_SESSION['remember_me'])) - $expire = time() + (60 * 60 * 24 * 365); - elseif (local_channel()) - $expire = time() + (60 * 60 * 24 * 3); - elseif (remote_channel()) - $expire = time() + (60 * 60 * 24 * 1); - } + $expire = time() + 1800; - q("UPDATE session + if ($_SESSION) { + if (array_key_exists('remember_me', $_SESSION) && intval($_SESSION['remember_me'])) { + $expire = time() + (60 * 60 * 24 * 365); + } elseif (local_channel()) { + $expire = time() + (60 * 60 * 24 * 3); + } elseif (remote_channel()) { + $expire = time() + (60 * 60 * 24 * 1); + } + } + + q( + "UPDATE session SET sess_data = '%s', expire = '%s' WHERE sid = '%s'", - dbesc($data), - dbesc($expire), - dbesc($id) - ); + dbesc($data), + dbesc($expire), + dbesc($id) + ); - return true; - } - - - function close() { - return true; - } + return true; + } - function destroy ($id) { - q("DELETE FROM session WHERE sid = '%s'", dbesc($id)); - return true; - } + public function close() + { + return true; + } - function gc($expire) { - q("DELETE FROM session WHERE expire < %d", dbesc(time())); - return true; - } + public function destroy($id) + { + q("DELETE FROM session WHERE sid = '%s'", dbesc($id)); + return true; + } + + public function gc($expire) + { + q("DELETE FROM session WHERE expire < %d", dbesc(time())); + return true; + } } diff --git a/Zotlabs/Web/SubModule.php b/Zotlabs/Web/SubModule.php index af828b364..a4a4a8ba9 100644 --- a/Zotlabs/Web/SubModule.php +++ b/Zotlabs/Web/SubModule.php @@ -8,68 +8,71 @@ use Zotlabs\Extend\Route; * @brief * */ -class SubModule { - private $controller = false; +class SubModule +{ - /** - * @brief Submodule constructor. - * - * Initiate sub-modules. By default the submodule name is in argv(1), though this is configurable. - * Example: Given a URL path such as /admin/plugins, and the Admin module initiates sub-modules. - * This means we'll look for a class Plugins in Zotlabs/Module/Admin/Plugins.php - * The specific methods and calling parameters are up to the top level module controller logic. - * - * **If** you were to provide sub-module support on the photos module, you would probably use - * $whicharg = 2, as photos are typically called with a URL path of /photos/channel_address/submodule_name - * where submodule_name might be something like album or image. - * - * @param int $whicharg - */ - function __construct($whicharg = 1) { + private $controller = false; - if (argc() < ($whicharg + 1)) { - return; - } + /** + * @brief Submodule constructor. + * + * Initiate sub-modules. By default the submodule name is in argv(1), though this is configurable. + * Example: Given a URL path such as /admin/plugins, and the Admin module initiates sub-modules. + * This means we'll look for a class Plugins in Zotlabs/Module/Admin/Plugins.php + * The specific methods and calling parameters are up to the top level module controller logic. + * + * **If** you were to provide sub-module support on the photos module, you would probably use + * $whicharg = 2, as photos are typically called with a URL path of /photos/channel_address/submodule_name + * where submodule_name might be something like album or image. + * + * @param int $whicharg + */ + public function __construct($whicharg = 1) + { - $filename = 'Zotlabs/Module/' . ucfirst(argv(0)) . '/'. ucfirst(argv($whicharg)) . '.php'; - $modname = '\\Zotlabs\\Module\\' . ucfirst(argv(0)) . '\\' . ucfirst(argv($whicharg)); + if (argc() < ($whicharg + 1)) { + return; + } - if (file_exists($filename)) { - $this->controller = new $modname(); - } + $filename = 'Zotlabs/Module/' . ucfirst(argv(0)) . '/' . ucfirst(argv($whicharg)) . '.php'; + $modname = '\\Zotlabs\\Module\\' . ucfirst(argv(0)) . '\\' . ucfirst(argv($whicharg)); - $routes = Route::get(); + if (file_exists($filename)) { + $this->controller = new $modname(); + } - if ($routes) { - foreach ($routes as $route) { - if (is_array($route) && strtolower($route[1]) === strtolower(argv(0)) . '/' . strtolower(argv($whicharg))) { - include_once($route[0]); - if (class_exists($modname)) { - $this->controller = new $modname; - } - } - } - } - } + $routes = Route::get(); - /** - * @brief - * - * @param string $method - * @return boolean|mixed - */ - function call($method) { + if ($routes) { + foreach ($routes as $route) { + if (is_array($route) && strtolower($route[1]) === strtolower(argv(0)) . '/' . strtolower(argv($whicharg))) { + include_once($route[0]); + if (class_exists($modname)) { + $this->controller = new $modname(); + } + } + } + } + } - if (! $this->controller) { - return false; - } + /** + * @brief + * + * @param string $method + * @return bool|mixed + */ + public function call($method) + { - if (method_exists($this->controller, $method)) { - return $this->controller->$method(); - } + if (!$this->controller) { + return false; + } - return false; - } + if (method_exists($this->controller, $method)) { + return $this->controller->$method(); + } + return false; + } } diff --git a/Zotlabs/Web/WebServer.php b/Zotlabs/Web/WebServer.php index a5bbc4240..84cfa0309 100644 --- a/Zotlabs/Web/WebServer.php +++ b/Zotlabs/Web/WebServer.php @@ -4,198 +4,199 @@ namespace Zotlabs\Web; use App; -class WebServer { +class WebServer +{ - public function run() { + public function run() + { - /* - * Bootstrap the application, load configuration, load modules, load theme, etc. - */ + /* + * Bootstrap the application, load configuration, load modules, load theme, etc. + */ - require_once('boot.php'); + require_once('boot.php'); - if (file_exists('maintenance_lock') || file_exists('cache/maintenance_lock')) { - http_status_exit(503,'System unavailable'); - } - - sys_boot(); + if (file_exists('maintenance_lock') || file_exists('cache/maintenance_lock')) { + http_status_exit(503, 'System unavailable'); + } - $this->start_session(); + sys_boot(); - $this->set_language(); + $this->start_session(); - $this->set_identities(); - - $this->initialise_notifications(); - - if (App::$install) { - - /* - * During installation, only permit the view module and setup module. - * The view module is required to expand/replace variables in style.css - */ - - if (App::$module !== 'view') { - App::$module = 'setup'; - } - } - else { + $this->set_language(); - /* - * check_config() is responsible for running update scripts. These automatically - * update the DB schema whenever we push a new one out. It also checks to see if - * any plugins have been added or removed and reacts accordingly. - */ + $this->set_identities(); - check_config(); - } + $this->initialise_notifications(); - $this->create_channel_links(); + if (App::$install) { + /* + * During installation, only permit the view module and setup module. + * The view module is required to expand/replace variables in style.css + */ - $this->initialise_content(); + if (App::$module !== 'view') { + App::$module = 'setup'; + } + } else { + /* + * check_config() is responsible for running update scripts. These automatically + * update the DB schema whenever we push a new one out. It also checks to see if + * any plugins have been added or removed and reacts accordingly. + */ - $Router = new Router(); - $Router->Dispatch(); + check_config(); + } - // if the observer is a visitor, add some javascript to the page to let - // the application take them home. - - $this->set_homebase(); + $this->create_channel_links(); - // now that we've been through the module content, see if the page reported - // a permission problem via session based notifications and if so, a 403 - // response would seem to be in order. + $this->initialise_content(); - if (is_array($_SESSION['sysmsg']) && stristr(implode("", $_SESSION['sysmsg']), t('Permission denied'))) { - header($_SERVER['SERVER_PROTOCOL'] . ' 403 ' . t('Permission denied.')); - } + $Router = new Router(); + $Router->Dispatch(); - construct_page(); + // if the observer is a visitor, add some javascript to the page to let + // the application take them home. - killme(); - } + $this->set_homebase(); - private function start_session() { + // now that we've been through the module content, see if the page reported + // a permission problem via session based notifications and if so, a 403 + // response would seem to be in order. - if (App::$session) { - App::$session->start(); - } - else { - session_start(); - register_shutdown_function('session_write_close'); - } + if (is_array($_SESSION['sysmsg']) && stristr(implode("", $_SESSION['sysmsg']), t('Permission denied'))) { + header($_SERVER['SERVER_PROTOCOL'] . ' 403 ' . t('Permission denied.')); + } - } + construct_page(); - private function set_language() { + killme(); + } - /* - * Determine the language of the interface - */ + private function start_session() + { - // First use the browser preference, if available. This will fall back to 'en' - // if there is no built-in language support for the preferred languagge - - - App::$language = get_best_language(); - load_translation_table(App::$language, App::$install); + if (App::$session) { + App::$session->start(); + } else { + session_start(); + register_shutdown_function('session_write_close'); + } + } - // See if there's a request to over-ride the language - // store it in the session. - - if (array_key_exists('system_language',$_REQUEST)) { - if (strlen($_REQUEST['system_language'])) { - $_SESSION['language'] = $_REQUEST['system_language']; - } - else { - // reset to default if it's an empty string - unset($_SESSION['language']); - } - } - - // If we've over-ridden the language, set it now. - - if ((x($_SESSION, 'language')) && ($_SESSION['language'] !== App::$language)) { - App::$language = $_SESSION['language']; - load_translation_table(App::$language); - } + private function set_language() + { - } + /* + * Determine the language of the interface + */ - private function set_identities() { + // First use the browser preference, if available. This will fall back to 'en' + // if there is no built-in language support for the preferred languagge - if ((x($_GET,'zid')) && (! App::$install)) { - App::$query_string = strip_zids(App::$query_string); - if (! local_channel()) { - if ($_SESSION['my_address'] !== $_GET['zid']) { - $_SESSION['my_address'] = $_GET['zid']; - $_SESSION['authenticated'] = 0; - } - zid_init(); - } - } - if ((x($_GET,'zat')) && (! App::$install)) { - App::$query_string = strip_zats(App::$query_string); - if (! local_channel()) { - zat_init(); - } - } + App::$language = get_best_language(); + load_translation_table(App::$language, App::$install); - if ((x($_REQUEST,'owt')) && (! App::$install)) { - $token = $_REQUEST['owt']; - App::$query_string = strip_query_param(App::$query_string,'owt'); - owt_init($token); - } + // See if there's a request to over-ride the language + // store it in the session. - if ((x($_SESSION, 'authenticated')) || (x($_POST, 'auth-params')) || (App::$module === 'login')) { - require('include/auth.php'); - } - } + if (array_key_exists('system_language', $_REQUEST)) { + if (strlen($_REQUEST['system_language'])) { + $_SESSION['language'] = $_REQUEST['system_language']; + } else { + // reset to default if it's an empty string + unset($_SESSION['language']); + } + } - private function initialise_notifications() { - if (! x($_SESSION, 'sysmsg')) { - $_SESSION['sysmsg'] = []; - } + // If we've over-ridden the language, set it now. - if (! x($_SESSION, 'sysmsg_info')) { - $_SESSION['sysmsg_info'] = []; - } - } + if ((x($_SESSION, 'language')) && ($_SESSION['language'] !== App::$language)) { + App::$language = $_SESSION['language']; + load_translation_table(App::$language); + } + } - private function initialise_content() { + private function set_identities() + { - /* initialise content region */ + if ((x($_GET, 'zid')) && (! App::$install)) { + App::$query_string = strip_zids(App::$query_string); + if (! local_channel()) { + if ($_SESSION['my_address'] !== $_GET['zid']) { + $_SESSION['my_address'] = $_GET['zid']; + $_SESSION['authenticated'] = 0; + } + zid_init(); + } + } - if (! x(App::$page, 'content')) { - App::$page['content'] = EMPTY_STR; - } + if ((x($_GET, 'zat')) && (! App::$install)) { + App::$query_string = strip_zats(App::$query_string); + if (! local_channel()) { + zat_init(); + } + } - call_hooks('page_content_top', App::$page['content']); - } + if ((x($_REQUEST, 'owt')) && (! App::$install)) { + $token = $_REQUEST['owt']; + App::$query_string = strip_query_param(App::$query_string, 'owt'); + owt_init($token); + } - private function create_channel_links() { + if ((x($_SESSION, 'authenticated')) || (x($_POST, 'auth-params')) || (App::$module === 'login')) { + require('include/auth.php'); + } + } - /* Initialise the Link: response header if this is a channel page. - * This cannot be done inside the channel module because some protocol - * addons over-ride the module functions and these links are common - * to all protocol drivers; thus doing it here avoids duplication. - */ + private function initialise_notifications() + { + if (! x($_SESSION, 'sysmsg')) { + $_SESSION['sysmsg'] = []; + } - if (( App::$module === 'channel' ) && argc() > 1) { - App::$channel_links = [ - [ - 'rel' => 'jrd', - 'type' => 'application/jrd+json', - 'href' => z_root() . '/.well-known/webfinger?f=&resource=acct%3A' . argv(1) . '%40' . App::get_hostname() - ], + if (! x($_SESSION, 'sysmsg_info')) { + $_SESSION['sysmsg_info'] = []; + } + } - [ - 'rel' => 'alternate', - 'type' => 'application/x-zot+json', - 'href' => z_root() . '/channel/' . argv(1) - ], + private function initialise_content() + { + + /* initialise content region */ + + if (! x(App::$page, 'content')) { + App::$page['content'] = EMPTY_STR; + } + + call_hooks('page_content_top', App::$page['content']); + } + + private function create_channel_links() + { + + /* Initialise the Link: response header if this is a channel page. + * This cannot be done inside the channel module because some protocol + * addons over-ride the module functions and these links are common + * to all protocol drivers; thus doing it here avoids duplication. + */ + + if (( App::$module === 'channel' ) && argc() > 1) { + App::$channel_links = [ + [ + 'rel' => 'jrd', + 'type' => 'application/jrd+json', + 'href' => z_root() . '/.well-known/webfinger?f=&resource=acct%3A' . argv(1) . '%40' . App::get_hostname() + ], + + [ + 'rel' => 'alternate', + 'type' => 'application/x-zot+json', + 'href' => z_root() . '/channel/' . argv(1) + ], [ 'rel' => 'alternate', @@ -203,39 +204,39 @@ class WebServer { 'href' => z_root() . '/channel/' . argv(1) ], - [ - 'rel' => 'self', - 'type' => 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"', - 'href' => z_root() . '/channel/' . argv(1) - ], + [ + 'rel' => 'self', + 'type' => 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"', + 'href' => z_root() . '/channel/' . argv(1) + ], - [ - 'rel' => 'self', - 'type' => 'application/activity+json', - 'href' => z_root() . '/channel/' . argv(1) - ] - ]; + [ + 'rel' => 'self', + 'type' => 'application/activity+json', + 'href' => z_root() . '/channel/' . argv(1) + ] + ]; - $x = [ 'channel_address' => argv(1), 'channel_links' => App::$channel_links ]; - call_hooks('channel_links', $x ); - App::$channel_links = $x['channel_links']; - header('Link: ' . App::get_channel_links()); - } - } + $x = [ 'channel_address' => argv(1), 'channel_links' => App::$channel_links ]; + call_hooks('channel_links', $x); + App::$channel_links = $x['channel_links']; + header('Link: ' . App::get_channel_links()); + } + } - private function set_homebase() { + private function set_homebase() + { - // If you're just visiting, let javascript take you home + // If you're just visiting, let javascript take you home - if (x($_SESSION, 'visitor_home')) { - $homebase = $_SESSION['visitor_home']; - } - elseif (local_channel()) { - $homebase = z_root() . '/channel/' . App::$channel['channel_address']; - } + if (x($_SESSION, 'visitor_home')) { + $homebase = $_SESSION['visitor_home']; + } elseif (local_channel()) { + $homebase = z_root() . '/channel/' . App::$channel['channel_address']; + } - if (isset($homebase)) { - App::$page['content'] .= ''; - } - } + if (isset($homebase)) { + App::$page['content'] .= ''; + } + } } diff --git a/Zotlabs/Widget/Activity.php b/Zotlabs/Widget/Activity.php index b5421e2a7..78e5cd777 100644 --- a/Zotlabs/Widget/Activity.php +++ b/Zotlabs/Widget/Activity.php @@ -4,66 +4,64 @@ namespace Zotlabs\Widget; use Zotlabs\Lib\LibBlock; +class Activity +{ -class Activity { + public function widget($arr) + { - function widget($arr) { + if (!local_channel()) { + return ''; + } - if (! local_channel()) { - return ''; - } + $o = EMPTY_STR; - $o = EMPTY_STR; + if (is_array($arr) && array_key_exists('limit', $arr)) { + $limit = " limit " . intval($limit) . " "; + } else { + $limit = EMPTY_STR; + } - if (is_array($arr) && array_key_exists('limit',$arr)) { - $limit = " limit " . intval($limit) . " "; - } - else { - $limit = EMPTY_STR; - } + $perms_sql = item_permissions_sql(local_channel()) . item_normal(); - $perms_sql = item_permissions_sql(local_channel()) . item_normal(); + $r = q( + "select author_xchan from item where item_unseen = 1 and uid = %d $perms_sql", + intval(local_channel()) + ); - $r = q("select author_xchan from item where item_unseen = 1 and uid = %d $perms_sql", - intval(local_channel()) - ); + $contributors = []; + $arr = []; - $contributors = []; - $arr = []; + if ($r) { + foreach ($r as $rv) { + if (array_key_exists($rv['author_xchan'], $contributors)) { + $contributors[$rv['author_xchan']]++; + } else { + $contributors[$rv['author_xchan']] = 1; + } + } + foreach ($contributors as $k => $v) { + if (!LibBlock::fetch_by_entity(local_channel(), $k)) { + $arr[] = ['author_xchan' => $k, 'total' => $v]; + } + } + usort($arr, 'total_sort'); + xchan_query($arr); + } - if ($r) { - foreach ($r as $rv) { - if (array_key_exists($rv['author_xchan'],$contributors)) { - $contributors[$rv['author_xchan']] ++; - } - else { - $contributors[$rv['author_xchan']] = 1; - } - } - foreach($contributors as $k => $v) { - if (! LibBlock::fetch_by_entity(local_channel(), $k)) { - $arr[] = [ 'author_xchan' => $k, 'total' => $v ]; - } - } - usort($arr,'total_sort'); - xchan_query($arr); - } + $x = ['entries' => $arr]; + call_hooks('activity_widget', $x); + $arr = $x['entries']; - $x = [ 'entries' => $arr ]; - call_hooks('activity_widget',$x); - $arr = $x['entries']; - - if($arr) { - $o .= '
      '; - $o .= '

      ' . t('Activity','widget') . '

      '; - } - return $o; - } - -} + if ($arr) { + $o .= '
      '; + $o .= '

      ' . t('Activity', 'widget') . '

      '; + } + return $o; + } +} diff --git a/Zotlabs/Widget/Activity_filter.php b/Zotlabs/Widget/Activity_filter.php index 229f2a624..194059c92 100644 --- a/Zotlabs/Widget/Activity_filter.php +++ b/Zotlabs/Widget/Activity_filter.php @@ -5,330 +5,329 @@ namespace Zotlabs\Widget; use App; use Zotlabs\Lib\Apps; -class Activity_filter { +class Activity_filter +{ - function widget($arr) { + public function widget($arr) + { - if (! local_channel()) { - return EMPTY_STR; - } + if (!local_channel()) { + return EMPTY_STR; + } - $cmd = App::$cmd; + $cmd = App::$cmd; - $filter_active = false; - $events_active = false; - $video_active = false; - $polls_active = false; - $group_active = false; - $drafts_active = false; - $forum_active = false; - - $tabs = []; + $filter_active = false; + $events_active = false; + $video_active = false; + $polls_active = false; + $group_active = false; + $drafts_active = false; + $forum_active = false; - $dm_active = ((isset($_GET['dm']) && intval($_GET['dm'])) ? 'active' : ''); - if ($dm_active) { - $filter_active = 'dm'; - } + $tabs = []; - $tabs[] = [ - 'label' => t('Direct Messages'), - 'icon' => 'envelope-o', - 'url' => z_root() . '/' . $cmd . '/?dm=1', - 'sel' => $dm_active, - 'title' => t('Show direct (private) messages') - ]; + $dm_active = ((isset($_GET['dm']) && intval($_GET['dm'])) ? 'active' : ''); + if ($dm_active) { + $filter_active = 'dm'; + } + + $tabs[] = [ + 'label' => t('Direct Messages'), + 'icon' => 'envelope-o', + 'url' => z_root() . '/' . $cmd . '/?dm=1', + 'sel' => $dm_active, + 'title' => t('Show direct (private) messages') + ]; - $conv_active = ((isset($_GET['conv']) && intval($_GET['conv'])) ? 'active' : ''); - if ($conv_active) { - $filter_active = 'personal'; - } + $conv_active = ((isset($_GET['conv']) && intval($_GET['conv'])) ? 'active' : ''); + if ($conv_active) { + $filter_active = 'personal'; + } - $tabs[] = [ - 'label' => t('Personal Posts'), - 'icon' => 'user-circle', - 'url' => z_root() . '/' . $cmd . '/?conv=1', - 'sel' => $conv_active, - 'title' => t('Show posts that mention or involve me') - ]; + $tabs[] = [ + 'label' => t('Personal Posts'), + 'icon' => 'user-circle', + 'url' => z_root() . '/' . $cmd . '/?conv=1', + 'sel' => $conv_active, + 'title' => t('Show posts that mention or involve me') + ]; - $starred_active = ((isset($_GET['star']) && intval($_GET['star'])) ? 'active' : ''); - if ($starred_active) { - $filter_active = 'star'; - } + $starred_active = ((isset($_GET['star']) && intval($_GET['star'])) ? 'active' : ''); + if ($starred_active) { + $filter_active = 'star'; + } - $tabs[] = [ - 'label' => t('Saved Posts'), - 'icon' => 'star', - 'url' => z_root() . '/' . $cmd . '/?star=1', - 'sel' => $starred_active, - 'title' => t('Show posts that I have saved') - ]; + $tabs[] = [ + 'label' => t('Saved Posts'), + 'icon' => 'star', + 'url' => z_root() . '/' . $cmd . '/?star=1', + 'sel' => $starred_active, + 'title' => t('Show posts that I have saved') + ]; - if (local_channel() && Apps::system_app_installed(local_channel(),'Drafts')) { - $drafts_active = ((isset($_GET['draft']) && intval($_GET['draft'])) ? 'active' : ''); - if ($drafts_active) { - $filter_active = 'drafts'; - } + if (local_channel() && Apps::system_app_installed(local_channel(), 'Drafts')) { + $drafts_active = ((isset($_GET['draft']) && intval($_GET['draft'])) ? 'active' : ''); + if ($drafts_active) { + $filter_active = 'drafts'; + } - $tabs[] = [ - 'label' => t('Drafts'), - 'icon' => 'floppy-o', - 'url' => z_root() . '/' . $cmd . '/?draft=1', - 'sel' => $drafts_active, - 'title' => t('Show drafts that I have saved') - ]; - } + $tabs[] = [ + 'label' => t('Drafts'), + 'icon' => 'floppy-o', + 'url' => z_root() . '/' . $cmd . '/?draft=1', + 'sel' => $drafts_active, + 'title' => t('Show drafts that I have saved') + ]; + } - if(x($_GET,'search')) { - $video_active = (($_GET['search'] == 'video]') ? 'active' : ''); - $filter_active = (($events_active) ? 'videos' : 'search'); - } + if (x($_GET, 'search')) { + $video_active = (($_GET['search'] == 'video]') ? 'active' : ''); + $filter_active = (($events_active) ? 'videos' : 'search'); + } - $tabs[] = [ - 'label' => t('Videos'), - 'icon' => 'video', - 'url' => z_root() . '/' . $cmd . '/?search=video%5D', - 'sel' => $video_active, - 'title' => t('Show posts that include videos') - ]; + $tabs[] = [ + 'label' => t('Videos'), + 'icon' => 'video', + 'url' => z_root() . '/' . $cmd . '/?search=video%5D', + 'sel' => $video_active, + 'title' => t('Show posts that include videos') + ]; - if(x($_GET,'verb')) { - $events_active = (($_GET['verb'] == '.Event') ? 'active' : ''); - $polls_active = (($_GET['verb'] == '.Question') ? 'active' : ''); - $filter_active = (($events_active) ? 'events' : 'polls'); - } + if (x($_GET, 'verb')) { + $events_active = (($_GET['verb'] == '.Event') ? 'active' : ''); + $polls_active = (($_GET['verb'] == '.Question') ? 'active' : ''); + $filter_active = (($events_active) ? 'events' : 'polls'); + } - $tabs[] = [ - 'label' => t('Events'), - 'icon' => 'calendar', - 'url' => z_root() . '/' . $cmd . '/?verb=%2EEvent', - 'sel' => $events_active, - 'title' => t('Show posts that include events') - ]; + $tabs[] = [ + 'label' => t('Events'), + 'icon' => 'calendar', + 'url' => z_root() . '/' . $cmd . '/?verb=%2EEvent', + 'sel' => $events_active, + 'title' => t('Show posts that include events') + ]; - $tabs[] = [ - 'label' => t('Polls'), - 'icon' => 'bar-chart', - 'url' => z_root() . '/' . $cmd . '/?verb=%2EQuestion', - 'sel' => $polls_active, - 'title' => t('Show posts that include polls') - ]; + $tabs[] = [ + 'label' => t('Polls'), + 'icon' => 'bar-chart', + 'url' => z_root() . '/' . $cmd . '/?verb=%2EQuestion', + 'sel' => $polls_active, + 'title' => t('Show posts that include polls') + ]; + $groups = q( + "SELECT * FROM pgrp WHERE deleted = 0 AND uid = %d ORDER BY gname ASC", + intval(local_channel()) + ); - $groups = q("SELECT * FROM pgrp WHERE deleted = 0 AND uid = %d ORDER BY gname ASC", - intval(local_channel()) - ); + if ($groups || Apps::system_app_installed(local_channel(), 'Virtual Lists')) { + if ($groups) { + foreach ($groups as $g) { + if (x($_GET, 'gid')) { + $group_active = (($_GET['gid'] == $g['id']) ? 'active' : ''); + $filter_active = 'group'; + } + $gsub[] = [ + 'label' => $g['gname'], + 'icon' => '', + 'url' => z_root() . '/' . $cmd . '/?f=&gid=' . $g['id'], + 'sel' => $group_active, + 'title' => sprintf(t('Show posts related to the %s access list'), $g['gname']) + ]; + } + } + if (Apps::system_app_installed(local_channel(), 'Virtual Lists')) { + foreach ([':1', ':2', ':3'] as $l) { + switch ($l) { + case ':1': + $gname = t('Connections'); + break; + case ':2': + $gname = t('Nomad'); + break; + case ':3': + $gname = t('ActivityPub'); + break; + default: + break; + } - if($groups || Apps::system_app_installed(local_channel(),'Virtual Lists')) { - if ($groups) { - foreach($groups as $g) { - if(x($_GET,'gid')) { - $group_active = (($_GET['gid'] == $g['id']) ? 'active' : ''); - $filter_active = 'group'; - } - $gsub[] = [ - 'label' => $g['gname'], - 'icon' => '', - 'url' => z_root() . '/' . $cmd . '/?f=&gid=' . $g['id'], - 'sel' => $group_active, - 'title' => sprintf(t('Show posts related to the %s access list'), $g['gname']) - ]; - } - } - if (Apps::system_app_installed(local_channel(),'Virtual Lists')) { - foreach ([ ':1', ':2', ':3' ] as $l) { - switch ($l) { - case ':1': - $gname = t('Connections'); - break; - case ':2': - $gname = t('Nomad'); - break; - case ':3': - $gname = t('ActivityPub'); - break; - default: - break; - } + if (x($_GET, 'gid')) { + $group_active = (($_GET['gid'] == $l) ? 'active' : ''); + $filter_active = 'group'; + } - if(x($_GET,'gid')) { - $group_active = (($_GET['gid'] == $l) ? 'active' : ''); - $filter_active = 'group'; - } - - $gsub[] = [ - 'label' => $gname, - 'icon' => '', - 'url' => z_root() . '/' . $cmd . '/?f=&gid=' . $l, - 'sel' => $group_active, - 'title' => sprintf(t('Show posts related to the %s access list'), $gname) - ]; - } - } - $tabs[] = [ - 'id' => 'privacy_groups', - 'label' => t('Lists'), - 'icon' => 'users', - 'url' => '#', - 'sel' => (($filter_active == 'group') ? true : false), - 'title' => t('Show my access lists'), - 'sub' => $gsub - ]; - } + $gsub[] = [ + 'label' => $gname, + 'icon' => '', + 'url' => z_root() . '/' . $cmd . '/?f=&gid=' . $l, + 'sel' => $group_active, + 'title' => sprintf(t('Show posts related to the %s access list'), $gname) + ]; + } + } + $tabs[] = [ + 'id' => 'privacy_groups', + 'label' => t('Lists'), + 'icon' => 'users', + 'url' => '#', + 'sel' => (($filter_active == 'group') ? true : false), + 'title' => t('Show my access lists'), + 'sub' => $gsub + ]; + } - $forums = get_forum_channels(local_channel(),1); + $forums = get_forum_channels(local_channel(), 1); - if($forums) { - foreach($forums as $f) { - if(x($_GET,'pf') && x($_GET,'cid')) { - $forum_active = ((x($_GET,'pf') && $_GET['cid'] == $f['abook_id']) ? 'active' : ''); - $filter_active = 'forums'; - } - $fsub[] = [ - 'label' => $f['xchan_name'], - 'img' => $f['xchan_photo_s'], - 'url' => z_root() . '/' . $cmd . '/?f=&pf=1&cid=' . $f['abook_id'], - 'sel' => $forum_active, - 'title' => t('Show posts to this group'), - 'lock' => ((isset($f['private_forum']) && $f['private_forum']) ? 'lock' : ''), - 'edit' => t('New post'), - 'edit_url' => $f['xchan_url'] - ]; - } + if ($forums) { + foreach ($forums as $f) { + if (x($_GET, 'pf') && x($_GET, 'cid')) { + $forum_active = ((x($_GET, 'pf') && $_GET['cid'] == $f['abook_id']) ? 'active' : ''); + $filter_active = 'forums'; + } + $fsub[] = [ + 'label' => $f['xchan_name'], + 'img' => $f['xchan_photo_s'], + 'url' => z_root() . '/' . $cmd . '/?f=&pf=1&cid=' . $f['abook_id'], + 'sel' => $forum_active, + 'title' => t('Show posts to this group'), + 'lock' => ((isset($f['private_forum']) && $f['private_forum']) ? 'lock' : ''), + 'edit' => t('New post'), + 'edit_url' => $f['xchan_url'] + ]; + } - $tabs[] = [ - 'id' => 'forums', - 'label' => t('Groups'), - 'icon' => 'comments-o', - 'url' => '#', - 'sel' => (($filter_active == 'forums') ? true : false), - 'title' => t('Show groups'), - 'sub' => $fsub - ]; - } + $tabs[] = [ + 'id' => 'forums', + 'label' => t('Groups'), + 'icon' => 'comments-o', + 'url' => '#', + 'sel' => (($filter_active == 'forums') ? true : false), + 'title' => t('Show groups'), + 'sub' => $fsub + ]; + } - if(feature_enabled(local_channel(),'filing')) { - $terms = q("select distinct term from term where uid = %d and ttype = %d order by term asc", - intval(local_channel()), - intval(TERM_FILE) - ); + if (feature_enabled(local_channel(), 'filing')) { + $terms = q( + "select distinct term from term where uid = %d and ttype = %d order by term asc", + intval(local_channel()), + intval(TERM_FILE) + ); - if($terms) { - foreach($terms as $t) { - if(x($_GET,'file')) { - $file_active = (($_GET['file'] == $t['term']) ? 'active' : ''); - $filter_active = 'file'; - } - $tsub[] = [ - 'label' => $t['term'], - 'icon' => '', - 'url' => z_root() . '/' . $cmd . '/?f=&file=' . $t['term'], - 'sel' => $file_active, - 'title' => sprintf(t('Show posts that I have filed to %s'), $t['term']), - ]; - } + if ($terms) { + foreach ($terms as $t) { + if (x($_GET, 'file')) { + $file_active = (($_GET['file'] == $t['term']) ? 'active' : ''); + $filter_active = 'file'; + } + $tsub[] = [ + 'label' => $t['term'], + 'icon' => '', + 'url' => z_root() . '/' . $cmd . '/?f=&file=' . $t['term'], + 'sel' => $file_active, + 'title' => sprintf(t('Show posts that I have filed to %s'), $t['term']), + ]; + } - $tabs[] = [ - 'id' => 'saved_folders', - 'label' => t('Saved Folders'), - 'icon' => 'folder', - 'url' => '#', - 'sel' => (($filter_active == 'file') ? true : false), - 'title' => t('Show filed post categories'), - 'sub' => $tsub + $tabs[] = [ + 'id' => 'saved_folders', + 'label' => t('Saved Folders'), + 'icon' => 'folder', + 'url' => '#', + 'sel' => (($filter_active == 'file') ? true : false), + 'title' => t('Show filed post categories'), + 'sub' => $tsub - ]; - } - } + ]; + } + } - $ft = get_pconfig(local_channel(),'system','followed_tags', EMPTY_STR); - if (is_array($ft) && $ft) { - foreach($ft as $t) { - $tag_active = ((isset($_GET['netsearch']) && $_GET['netsearch'] === '#' . $t) ? 'active' : ''); - if ($tag_active) { - $filter_active = 'tags'; - } - - $tsub[] = [ - 'label' => '#' . $t, - 'icon' => '', - 'url' => z_root() . '/' . $cmd . '/?search=' . '%23' . $t, - 'sel' => $tag_active, - 'title' => sprintf(t('Show posts with hashtag %s'), '#' . $t), - ]; - } + $ft = get_pconfig(local_channel(), 'system', 'followed_tags', EMPTY_STR); + if (is_array($ft) && $ft) { + foreach ($ft as $t) { + $tag_active = ((isset($_GET['netsearch']) && $_GET['netsearch'] === '#' . $t) ? 'active' : ''); + if ($tag_active) { + $filter_active = 'tags'; + } - $tabs[] = [ - 'id' => 'followed_tags', - 'label' => t('Followed Hashtags'), - 'icon' => 'bookmark', - 'url' => '#', - 'sel' => (($filter_active == 'tags') ? true : false), - 'title' => t('Show followed hashtags'), - 'sub' => $tsub - ]; + $tsub[] = [ + 'label' => '#' . $t, + 'icon' => '', + 'url' => z_root() . '/' . $cmd . '/?search=' . '%23' . $t, + 'sel' => $tag_active, + 'title' => sprintf(t('Show posts with hashtag %s'), '#' . $t), + ]; + } - } + $tabs[] = [ + 'id' => 'followed_tags', + 'label' => t('Followed Hashtags'), + 'icon' => 'bookmark', + 'url' => '#', + 'sel' => (($filter_active == 'tags') ? true : false), + 'title' => t('Show followed hashtags'), + 'sub' => $tsub + ]; + } +// if(x($_GET,'search')) { +// $filter_active = 'search'; +// $tabs[] = [ +// 'label' => t('Search'), +// 'icon' => 'search', +// 'url' => z_root() . '/' . $cmd . '/?search=' . $_GET['search'], +// 'sel' => 'active disabled', +// 'title' => t('Panel search') +// ]; +// } -// if(x($_GET,'search')) { -// $filter_active = 'search'; -// $tabs[] = [ -// 'label' => t('Search'), -// 'icon' => 'search', -// 'url' => z_root() . '/' . $cmd . '/?search=' . $_GET['search'], -// 'sel' => 'active disabled', -// 'title' => t('Panel search') -// ]; -// } + $name = []; + if (isset($_GET['name']) && $_GET['name']) { + $filter_active = 'name'; + } + $name = [ + 'label' => x($_GET, 'name') ? $_GET['name'] : t('Name'), + 'icon' => 'filter', + 'url' => z_root() . '/' . $cmd . '/', + 'sel' => $filter_active == 'name' ? 'is-valid' : '', + 'title' => '' + ]; - $name = []; - if(isset($_GET['name']) && $_GET['name']) { - $filter_active = 'name'; - } - $name = [ - 'label' => x($_GET,'name') ? $_GET['name'] : t('Name'), - 'icon' => 'filter', - 'url'=> z_root() . '/' . $cmd . '/', - 'sel'=> $filter_active == 'name' ? 'is-valid' : '', - 'title' => '' - ]; + $reset = []; + if ($filter_active) { + $reset = [ + 'label' => '', + 'icon' => 'remove', + 'url' => z_root() . '/' . $cmd, + 'sel' => '', + 'title' => t('Remove active filter') + ]; + } - $reset = []; - if ($filter_active) { - $reset = [ - 'label' => '', - 'icon' => 'remove', - 'url'=> z_root() . '/' . $cmd, - 'sel'=> '', - 'title' => t('Remove active filter') - ]; - } + $arr = ['tabs' => $tabs]; - $arr = ['tabs' => $tabs]; + call_hooks('activity_filter', $arr); - call_hooks('activity_filter', $arr); + $o = ''; - $o = ''; + if ($arr['tabs']) { + $content = replace_macros(get_markup_template('common_pills.tpl'), [ + '$pills' => $arr['tabs'] + ]); - if($arr['tabs']) { - $content = replace_macros(get_markup_template('common_pills.tpl'), [ - '$pills' => $arr['tabs'] - ]); - - $o .= replace_macros(get_markup_template('activity_filter_widget.tpl'), [ - '$title' => t('Stream Filters'), - '$reset' => $reset, - '$content' => $content, - '$name' => $name - ]); - } - - return $o; - - } + $o .= replace_macros(get_markup_template('activity_filter_widget.tpl'), [ + '$title' => t('Stream Filters'), + '$reset' => $reset, + '$content' => $content, + '$name' => $name + ]); + } + return $o; + } } diff --git a/Zotlabs/Widget/Admin.php b/Zotlabs/Widget/Admin.php index b4eec9940..2cb617ef2 100644 --- a/Zotlabs/Widget/Admin.php +++ b/Zotlabs/Widget/Admin.php @@ -2,69 +2,71 @@ namespace Zotlabs\Widget; -class Admin { +use App; - function widget($arr) { +class Admin +{ - /* - * Side bar links - */ + public function widget($arr) + { - if(! is_site_admin()) { - return ''; - } + /* + * Side bar links + */ - $o = ''; + if (!is_site_admin()) { + return ''; + } - // array( url, name, extra css classes ) + $o = ''; - $aside = [ - 'site' => array(z_root() . '/admin/site/', t('Site'), 'site'), -// 'profile_photo' => array(z_root() . '/admin/profile_photo', t('Site icon/logo'), 'profile_photo'), -// 'cover_photo' => array(z_root() . '/admin/cover_photo', t('Site photo'), 'cover_photo'), - 'accounts' => array(z_root() . '/admin/accounts/', t('Accounts'), 'accounts', 'pending-update', t('Member registrations waiting for confirmation')), - 'channels' => array(z_root() . '/admin/channels/', t('Channels'), 'channels'), - 'security' => array(z_root() . '/admin/security/', t('Security'), 'security'), -// 'features' => array(z_root() . '/admin/features/', t('Features'), 'features'), - 'addons' => array(z_root() . '/admin/addons/', t('Addons'), 'addons'), - 'themes' => array(z_root() . '/admin/themes/', t('Themes'), 'themes'), - 'queue' => array(z_root() . '/admin/queue', t('Inspect queue'), 'queue'), -// 'profs' => array(z_root() . '/admin/profs', t('Profile Fields'), 'profs'), - 'dbsync' => array(z_root() . '/admin/dbsync/', t('DB updates'), 'dbsync') - ]; + // array( url, name, extra css classes ) - /* get plugins admin page */ + $aside = [ + 'site' => array(z_root() . '/admin/site/', t('Site'), 'site'), +// 'profile_photo' => array(z_root() . '/admin/profile_photo', t('Site icon/logo'), 'profile_photo'), +// 'cover_photo' => array(z_root() . '/admin/cover_photo', t('Site photo'), 'cover_photo'), + 'accounts' => array(z_root() . '/admin/accounts/', t('Accounts'), 'accounts', 'pending-update', t('Member registrations waiting for confirmation')), + 'channels' => array(z_root() . '/admin/channels/', t('Channels'), 'channels'), + 'security' => array(z_root() . '/admin/security/', t('Security'), 'security'), +// 'features' => array(z_root() . '/admin/features/', t('Features'), 'features'), + 'addons' => array(z_root() . '/admin/addons/', t('Addons'), 'addons'), + 'themes' => array(z_root() . '/admin/themes/', t('Themes'), 'themes'), + 'queue' => array(z_root() . '/admin/queue', t('Inspect queue'), 'queue'), +// 'profs' => array(z_root() . '/admin/profs', t('Profile Fields'), 'profs'), + 'dbsync' => array(z_root() . '/admin/dbsync/', t('DB updates'), 'dbsync') + ]; - $r = q("SELECT * FROM addon WHERE plugin_admin = 1"); + /* get plugins admin page */ - $plugins = []; - if($r) { - foreach ($r as $h){ - $plugin = $h['aname']; - $plugins[] = array(z_root() . '/admin/addons/' . $plugin, $plugin, 'plugin'); - // temp plugins with admin - \App::$plugins_admin[] = $plugin; - } - } + $r = q("SELECT * FROM addon WHERE plugin_admin = 1"); - $logs = array(z_root() . '/admin/logs/', t('Logs'), 'logs'); + $plugins = []; + if ($r) { + foreach ($r as $h) { + $plugin = $h['aname']; + $plugins[] = array(z_root() . '/admin/addons/' . $plugin, $plugin, 'plugin'); + // temp plugins with admin + App::$plugins_admin[] = $plugin; + } + } - $arr = array('links' => $aside,'plugins' => $plugins,'logs' => $logs); - call_hooks('admin_aside',$arr); + $logs = array(z_root() . '/admin/logs/', t('Logs'), 'logs'); - $o .= replace_macros(get_markup_template('admin_aside.tpl'), array( - '$admin' => $aside, - '$admtxt' => t('Admin'), - '$plugadmtxt' => t('Addon Features'), - '$plugins' => $plugins, - '$logtxt' => t('Logs'), - '$logs' => $logs, - '$h_pending' => t('Member registrations waiting for confirmation'), - '$admurl'=> z_root() . '/admin/' - )); + $arr = array('links' => $aside, 'plugins' => $plugins, 'logs' => $logs); + call_hooks('admin_aside', $arr); - return $o; + $o .= replace_macros(get_markup_template('admin_aside.tpl'), array( + '$admin' => $aside, + '$admtxt' => t('Admin'), + '$plugadmtxt' => t('Addon Features'), + '$plugins' => $plugins, + '$logtxt' => t('Logs'), + '$logs' => $logs, + '$h_pending' => t('Member registrations waiting for confirmation'), + '$admurl' => z_root() . '/admin/' + )); - } + return $o; + } } - diff --git a/Zotlabs/Widget/Affinity.php b/Zotlabs/Widget/Affinity.php index bcd54f9cf..ecca26414 100644 --- a/Zotlabs/Widget/Affinity.php +++ b/Zotlabs/Widget/Affinity.php @@ -4,46 +4,47 @@ namespace Zotlabs\Widget; use Zotlabs\Lib\Apps; -class Affinity { +class Affinity +{ - function widget($arr) { + public function widget($arr) + { - if(! local_channel()) - return ''; - - $default_cmin = ((Apps::system_app_installed(local_channel(),'Friend Zoom')) ? get_pconfig(local_channel(),'affinity','cmin',0) : 0); - $default_cmax = ((Apps::system_app_installed(local_channel(),'Friend Zoom')) ? get_pconfig(local_channel(),'affinity','cmax',99) : 99); + if (!local_channel()) { + return ''; + } - $cmin = ((x($_REQUEST,'cmin')) ? intval($_REQUEST['cmin']) : $default_cmin); - $cmax = ((x($_REQUEST,'cmax')) ? intval($_REQUEST['cmax']) : $default_cmax); + $default_cmin = ((Apps::system_app_installed(local_channel(), 'Friend Zoom')) ? get_pconfig(local_channel(), 'affinity', 'cmin', 0) : 0); + $default_cmax = ((Apps::system_app_installed(local_channel(), 'Friend Zoom')) ? get_pconfig(local_channel(), 'affinity', 'cmax', 99) : 99); + + $cmin = ((x($_REQUEST, 'cmin')) ? intval($_REQUEST['cmin']) : $default_cmin); + $cmax = ((x($_REQUEST, 'cmax')) ? intval($_REQUEST['cmax']) : $default_cmax); - if(Apps::system_app_installed(local_channel(),'Friend Zoom')) { + if (Apps::system_app_installed(local_channel(), 'Friend Zoom')) { + $labels = array( + 0 => t('Me'), + 20 => t('Family'), + 40 => t('Friends'), + 60 => t('Peers'), + 80 => t('Connections'), + 99 => t('All') + ); + call_hooks('affinity_labels', $labels); - $labels = array( - 0 => t('Me'), - 20 => t('Family'), - 40 => t('Friends'), - 60 => t('Peers'), - 80 => t('Connections'), - 99 => t('All') - ); - call_hooks('affinity_labels',$labels); + $tpl = get_markup_template('main_slider.tpl'); + $x = replace_macros($tpl, [ + '$cmin' => $cmin, + '$cmax' => $cmax, + '$lbl' => t('Friend zoom in/out'), + '$refresh' => t('Refresh'), + '$labels' => $labels, + ]); - $tpl = get_markup_template('main_slider.tpl'); - $x = replace_macros($tpl, [ - '$cmin' => $cmin, - '$cmax' => $cmax, - '$lbl' => t('Friend zoom in/out'), - '$refresh' => t('Refresh'), - '$labels' => $labels, - ]); - - $arr = array('html' => $x); - call_hooks('main_slider',$arr); - return $arr['html']; - } - return ''; - } + $arr = array('html' => $x); + call_hooks('main_slider', $arr); + return $arr['html']; + } + return ''; + } } - \ No newline at end of file diff --git a/Zotlabs/Widget/Album.php b/Zotlabs/Widget/Album.php index 45d941d75..500cf601e 100644 --- a/Zotlabs/Widget/Album.php +++ b/Zotlabs/Widget/Album.php @@ -3,108 +3,113 @@ namespace Zotlabs\Widget; use App; + require_once('include/attach.php'); -class Album { +class Album +{ - function widget($args) { + public function widget($args) + { - $owner_uid = App::$profile_uid; - $sql_extra = permissions_sql($owner_uid); + $owner_uid = App::$profile_uid; + $sql_extra = permissions_sql($owner_uid); - if (! perm_is_allowed($owner_uid,get_observer_hash(),'view_storage')) { - return ''; - } + if (!perm_is_allowed($owner_uid, get_observer_hash(), 'view_storage')) { + return ''; + } - if ($args['album']) { - $album = $args['album']; - } - if ($args['title']) { - $title = $args['title']; - } - - /** - * This may return incorrect permissions if you have multiple directories of the same name. - * It is a limitation of the photo table using a name for a photo album instead of a folder hash - */ + if ($args['album']) { + $album = $args['album']; + } + if ($args['title']) { + $title = $args['title']; + } - if ($album) { - $x = q("select hash from attach where filename = '%s' and uid = %d limit 1", - dbesc($album), - intval($owner_uid) - ); - if($x) { - $y = attach_can_view_folder($owner_uid,get_observer_hash(),$x[0]['hash']); - if(! $y) - return ''; - } - } + /** + * This may return incorrect permissions if you have multiple directories of the same name. + * It is a limitation of the photo table using a name for a photo album instead of a folder hash + */ - $order = 'DESC'; + if ($album) { + $x = q( + "select hash from attach where filename = '%s' and uid = %d limit 1", + dbesc($album), + intval($owner_uid) + ); + if ($x) { + $y = attach_can_view_folder($owner_uid, get_observer_hash(), $x[0]['hash']); + if (!$y) { + return ''; + } + } + } - $r = q("SELECT p.resource_id, p.id, p.filename, p.mimetype, p.imgscale, p.description, p.created FROM photo p INNER JOIN + $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 ) $sql_extra GROUP BY resource_id) ph ON (p.resource_id = ph.resource_id AND p.imgscale = ph.imgscale) ORDER BY created $order ", - intval($owner_uid), - dbesc($album), - intval(PHOTO_NORMAL), - intval(PHOTO_PROFILE) - ); + intval($owner_uid), + dbesc($album), + intval(PHOTO_NORMAL), + intval(PHOTO_PROFILE) + ); - //edit album name - $album_edit = null; + //edit album name + $album_edit = null; - $photos = []; - if($r) { - $twist = 'rotright'; - foreach($r as $rr) { + $photos = []; + if ($r) { + $twist = 'rotright'; + foreach ($r as $rr) { + if ($twist == 'rotright') { + $twist = 'rotleft'; + } else { + $twist = 'rotright'; + } - if($twist == 'rotright') - $twist = 'rotleft'; - else - $twist = 'rotright'; + $ext = $phototypes[$rr['mimetype']]; - $ext = $phototypes[$rr['mimetype']]; + $imgalt_e = $rr['filename']; + $desc_e = $rr['description']; - $imgalt_e = $rr['filename']; - $desc_e = $rr['description']; - - $imagelink = (z_root() . '/photos/' . \App::$profile['channel_address'] . '/image/' . $rr['resource_id']); + $imagelink = (z_root() . '/photos/' . App::$profile['channel_address'] . '/image/' . $rr['resource_id']); - $photos[] = array( - 'id' => $rr['id'], - 'twist' => ' ' . $twist . rand(2,4), - 'link' => $imagelink, - 'title' => t('View Photo'), - 'src' => z_root() . '/photo/' . $rr['resource_id'] . '-' . $rr['imgscale'] . '.' .$ext, - 'alt' => $imgalt_e, - 'desc'=> $desc_e, - 'ext' => $ext, - 'hash'=> $rr['resource_id'], - 'unknown' => t('Unknown') - ); - } - } + $photos[] = array( + 'id' => $rr['id'], + 'twist' => ' ' . $twist . rand(2, 4), + 'link' => $imagelink, + 'title' => t('View Photo'), + 'src' => z_root() . '/photo/' . $rr['resource_id'] . '-' . $rr['imgscale'] . '.' . $ext, + 'alt' => $imgalt_e, + 'desc' => $desc_e, + 'ext' => $ext, + 'hash' => $rr['resource_id'], + 'unknown' => t('Unknown') + ); + } + } - $tpl = get_markup_template('photo_album.tpl'); - $o .= replace_macros($tpl, array( - '$photos' => $photos, - '$album' => (($title) ? $title : $album), - '$album_id' => rand(), - '$album_edit' => array(t('Edit Album'), $album_edit), - '$can_post' => false, - '$upload' => array(t('Upload'), z_root() . '/photos/' . App::$profile['channel_address'] . '/upload/' . bin2hex($album)), - '$order' => false, - '$upload_form' => $upload_form, - '$usage' => $usage_message - )); - - return $o; - } + $tpl = get_markup_template('photo_album.tpl'); + $o .= replace_macros($tpl, array( + '$photos' => $photos, + '$album' => (($title) ? $title : $album), + '$album_id' => rand(), + '$album_edit' => array(t('Edit Album'), $album_edit), + '$can_post' => false, + '$upload' => array(t('Upload'), z_root() . '/photos/' . App::$profile['channel_address'] . '/upload/' . bin2hex($album)), + '$order' => false, + '$upload_form' => $upload_form, + '$usage' => $usage_message + )); + + return $o; + } } - diff --git a/Zotlabs/Widget/Appcategories.php b/Zotlabs/Widget/Appcategories.php index c698be069..2544ffd7f 100644 --- a/Zotlabs/Widget/Appcategories.php +++ b/Zotlabs/Widget/Appcategories.php @@ -2,28 +2,33 @@ namespace Zotlabs\Widget; -class Appcategories { +class Appcategories +{ - function widget($arr) { + public function widget($arr) + { - if(! local_channel()) - return ''; + if (!local_channel()) { + return ''; + } - $selected = ((x($_REQUEST,'cat')) ? htmlspecialchars($_REQUEST['cat'],ENT_COMPAT,'UTF-8') : ''); + $selected = ((x($_REQUEST, 'cat')) ? htmlspecialchars($_REQUEST['cat'], ENT_COMPAT, 'UTF-8') : ''); - // @FIXME ??? $srchurl undefined here - commented out until is reviewed - //$srchurl = rtrim(preg_replace('/cat\=[^\&].*?(\&|$)/is','',$srchurl),'&'); - //$srchurl = str_replace(array('?f=','&f='),array('',''),$srchurl); + // @FIXME ??? $srchurl undefined here - commented out until is reviewed + //$srchurl = rtrim(preg_replace('/cat\=[^\&].*?(\&|$)/is','',$srchurl),'&'); + //$srchurl = str_replace(array('?f=','&f='),array('',''),$srchurl); - // Leaving this line which negates the effect of the two invalid lines prior - $srchurl = z_root() . '/apps'; - if(argc() > 1 && argv(1) === 'available') - $srchurl .= '/available'; + // Leaving this line which negates the effect of the two invalid lines prior + $srchurl = z_root() . '/apps'; + if (argc() > 1 && argv(1) === 'available') { + $srchurl .= '/available'; + } - $terms = []; + $terms = []; - $r = q("select distinct(term.term) + $r = q( + "select distinct(term.term) from term join app on term.oid = app.id where app_channel = %d and term.uid = app_channel @@ -31,23 +36,24 @@ class Appcategories { and term.term != 'nav_featured_app' and term.term != 'nav_pinned_app' order by term.term asc", - intval(local_channel()), - intval(TERM_OBJ_APP) - ); + intval(local_channel()), + intval(TERM_OBJ_APP) + ); - if($r) { - foreach($r as $rr) - $terms[] = array('name' => $rr['term'], 'selected' => (($selected == $rr['term']) ? 'selected' : '')); + if ($r) { + foreach ($r as $rr) { + $terms[] = array('name' => $rr['term'], 'selected' => (($selected == $rr['term']) ? 'selected' : '')); + } - return replace_macros(get_markup_template('categories_widget.tpl'),array( - '$title' => t('Categories'), - '$desc' => '', - '$sel_all' => (($selected == '') ? 'selected' : ''), - '$all' => t('Everything'), - '$terms' => $terms, - '$base' => $srchurl, + return replace_macros(get_markup_template('categories_widget.tpl'), array( + '$title' => t('Categories'), + '$desc' => '', + '$sel_all' => (($selected == '') ? 'selected' : ''), + '$all' => t('Everything'), + '$terms' => $terms, + '$base' => $srchurl, - )); - } - } + )); + } + } } diff --git a/Zotlabs/Widget/Appcloud.php b/Zotlabs/Widget/Appcloud.php index 2a4671eee..f3cbe74c2 100644 --- a/Zotlabs/Widget/Appcloud.php +++ b/Zotlabs/Widget/Appcloud.php @@ -2,12 +2,14 @@ namespace Zotlabs\Widget; -class Appcloud { +class Appcloud +{ - function widget($arr) { - if(! local_channel()) - return ''; - return app_tagblock(z_root() . '/apps'); - } + public function widget($arr) + { + if (!local_channel()) { + return ''; + } + return app_tagblock(z_root() . '/apps'); + } } - diff --git a/Zotlabs/Widget/Appstore.php b/Zotlabs/Widget/Appstore.php index 0f5d29ee7..18cdca009 100644 --- a/Zotlabs/Widget/Appstore.php +++ b/Zotlabs/Widget/Appstore.php @@ -2,17 +2,18 @@ namespace Zotlabs\Widget; +class Appstore +{ -class Appstore { - - function widget($arr) { - $store = ((argc() > 1 && argv(1) === 'available') ? 1 : 0); - return replace_macros(get_markup_template('appstore.tpl'), [ - '$title' => t('App Collections'), - '$options' => [ - [ z_root() . '/apps', t('Installed Apps'), 1 - $store ], - [ z_root() . '/apps/available', t('Available Apps'), $store ] - ] - ]); - } + public function widget($arr) + { + $store = ((argc() > 1 && argv(1) === 'available') ? 1 : 0); + return replace_macros(get_markup_template('appstore.tpl'), [ + '$title' => t('App Collections'), + '$options' => [ + [z_root() . '/apps', t('Installed Apps'), 1 - $store], + [z_root() . '/apps/available', t('Available Apps'), $store] + ] + ]); + } } diff --git a/Zotlabs/Widget/Archive.php b/Zotlabs/Widget/Archive.php index 9adaac38f..a12cf1205 100644 --- a/Zotlabs/Widget/Archive.php +++ b/Zotlabs/Widget/Archive.php @@ -2,54 +2,59 @@ namespace Zotlabs\Widget; +use App; -class Archive { +class Archive +{ - function widget($arr) { + public function widget($arr) + { - $o = ''; + $o = ''; - if(! \App::$profile_uid) { - return ''; - } + if (!App::$profile_uid) { + return ''; + } - $uid = \App::$profile_uid; + $uid = App::$profile_uid; - if(! feature_enabled($uid,'archives')) - return ''; + if (!feature_enabled($uid, 'archives')) { + return ''; + } - if(! perm_is_allowed($uid,get_observer_hash(),'view_stream')) - return ''; + if (!perm_is_allowed($uid, get_observer_hash(), 'view_stream')) { + return ''; + } - $wall = ((array_key_exists('wall', $arr)) ? intval($arr['wall']) : 0); - $wall = ((array_key_exists('articles', $arr)) ? 2 : $wall); + $wall = ((array_key_exists('wall', $arr)) ? intval($arr['wall']) : 0); + $wall = ((array_key_exists('articles', $arr)) ? 2 : $wall); - $style = ((array_key_exists('style', $arr)) ? $arr['style'] : 'select'); - $showend = ((get_pconfig($uid,'system','archive_show_end_date')) ? true : false); - $mindate = get_pconfig($uid,'system','archive_mindate'); - $visible_years = get_pconfig($uid,'system','archive_visible_years',5); + $style = ((array_key_exists('style', $arr)) ? $arr['style'] : 'select'); + $showend = ((get_pconfig($uid, 'system', 'archive_show_end_date')) ? true : false); + $mindate = get_pconfig($uid, 'system', 'archive_mindate'); + $visible_years = get_pconfig($uid, 'system', 'archive_visible_years', 5); - $url = z_root() . '/' . \App::$cmd; + $url = z_root() . '/' . App::$cmd; - $ret = list_post_dates($uid,$wall,$mindate); + $ret = list_post_dates($uid, $wall, $mindate); - if(! count($ret)) - return ''; + if (!count($ret)) { + return ''; + } - $cutoff_year = intval(datetime_convert('',date_default_timezone_get(),'now','Y')) - $visible_years; - $cutoff = ((array_key_exists($cutoff_year,$ret))? true : false); + $cutoff_year = intval(datetime_convert('', date_default_timezone_get(), 'now', 'Y')) - $visible_years; + $cutoff = ((array_key_exists($cutoff_year, $ret)) ? true : false); - $o = replace_macros(get_markup_template('posted_date_widget.tpl'),array( - '$title' => t('Archives'), - '$size' => $visible_years, - '$cutoff_year' => $cutoff_year, - '$cutoff' => $cutoff, - '$url' => $url, - '$style' => $style, - '$showend' => $showend, - '$dates' => $ret - )); - return $o; - } + $o = replace_macros(get_markup_template('posted_date_widget.tpl'), array( + '$title' => t('Archives'), + '$size' => $visible_years, + '$cutoff_year' => $cutoff_year, + '$cutoff' => $cutoff, + '$url' => $url, + '$style' => $style, + '$showend' => $showend, + '$dates' => $ret + )); + return $o; + } } - diff --git a/Zotlabs/Widget/Bookmarkedchats.php b/Zotlabs/Widget/Bookmarkedchats.php index d64bbdb4b..2584a5e2a 100644 --- a/Zotlabs/Widget/Bookmarkedchats.php +++ b/Zotlabs/Widget/Bookmarkedchats.php @@ -2,27 +2,34 @@ namespace Zotlabs\Widget; -class Bookmarkedchats { +use App; - function widget($arr) { +class Bookmarkedchats +{ - if(! feature_enabled(\App::$profile['profile_uid'],'ajaxchat')) - return ''; + public function widget($arr) + { - $h = get_observer_hash(); - if(! $h) - return; - $r = q("select xchat_url, xchat_desc from xchat where xchat_xchan = '%s' order by xchat_desc", - dbesc($h) - ); - if($r) { - for($x = 0; $x < count($r); $x ++) { - $r[$x]['xchat_url'] = zid($r[$x]['xchat_url']); - } - } - return replace_macros(get_markup_template('bookmarkedchats.tpl'),array( - '$header' => t('Bookmarked Chatrooms'), - '$rooms' => $r - )); - } + if (!feature_enabled(App::$profile['profile_uid'], 'ajaxchat')) { + return ''; + } + + $h = get_observer_hash(); + if (!$h) { + return; + } + $r = q( + "select xchat_url, xchat_desc from xchat where xchat_xchan = '%s' order by xchat_desc", + dbesc($h) + ); + if ($r) { + for ($x = 0; $x < count($r); $x++) { + $r[$x]['xchat_url'] = zid($r[$x]['xchat_url']); + } + } + return replace_macros(get_markup_template('bookmarkedchats.tpl'), array( + '$header' => t('Bookmarked Chatrooms'), + '$rooms' => $r + )); + } } diff --git a/Zotlabs/Widget/Catcloud.php b/Zotlabs/Widget/Catcloud.php index f21fe55fd..4cab3218e 100644 --- a/Zotlabs/Widget/Catcloud.php +++ b/Zotlabs/Widget/Catcloud.php @@ -2,45 +2,47 @@ namespace Zotlabs\Widget; -class Catcloud { +use App; - function widget($arr) { +class Catcloud +{ - if((! \App::$profile['profile_uid']) || (! \App::$profile['channel_hash'])) - return ''; + public function widget($arr) + { - $limit = ((array_key_exists('limit',$arr)) ? intval($arr['limit']) : 50); + if ((!App::$profile['profile_uid']) || (!App::$profile['channel_hash'])) { + return ''; + } - if(array_key_exists('type',$arr)) { - switch($arr['type']) { + $limit = ((array_key_exists('limit', $arr)) ? intval($arr['limit']) : 50); - case 'cards': + if (array_key_exists('type', $arr)) { + switch ($arr['type']) { + case 'cards': + if (!perm_is_allowed(App::$profile['profile_uid'], get_observer_hash(), 'view_pages')) { + return ''; + } - if(! perm_is_allowed(\App::$profile['profile_uid'], get_observer_hash(), 'view_pages')) - return ''; + return card_catblock(App::$profile['profile_uid'], $limit, '', App::$profile['channel_hash']); - return card_catblock(\App::$profile['profile_uid'], $limit, '', \App::$profile['channel_hash']); + case 'articles': + if (!perm_is_allowed(App::$profile['profile_uid'], get_observer_hash(), 'view_articles')) { + return ''; + } - case 'articles': - - if(! perm_is_allowed(\App::$profile['profile_uid'], get_observer_hash(), 'view_articles')) - return ''; - - return article_catblock(\App::$profile['profile_uid'], $limit, '', \App::$profile['channel_hash']); + return article_catblock(App::$profile['profile_uid'], $limit, '', App::$profile['channel_hash']); - default: - break; - } - } + default: + break; + } + } - if(! perm_is_allowed(\App::$profile['profile_uid'], get_observer_hash(), 'view_stream')) - return ''; - - return catblock(\App::$profile['profile_uid'], $limit, '', \App::$profile['channel_hash']); - - - } + if (!perm_is_allowed(App::$profile['profile_uid'], get_observer_hash(), 'view_stream')) { + return ''; + } + return catblock(App::$profile['profile_uid'], $limit, '', App::$profile['channel_hash']); + } } diff --git a/Zotlabs/Widget/Catcloud_wall.php b/Zotlabs/Widget/Catcloud_wall.php index 4e06a8e1c..4a1b9b50d 100644 --- a/Zotlabs/Widget/Catcloud_wall.php +++ b/Zotlabs/Widget/Catcloud_wall.php @@ -4,18 +4,21 @@ namespace Zotlabs\Widget; use App; -class Catcloud_wall { +class Catcloud_wall +{ - function widget($arr) { + public function widget($arr) + { - if((! App::$profile['profile_uid']) || (! App::$profile['channel_hash'])) - return ''; - if(! perm_is_allowed(App::$profile['profile_uid'], get_observer_hash(), 'view_stream')) - return ''; + if ((!App::$profile['profile_uid']) || (!App::$profile['channel_hash'])) { + return ''; + } + if (!perm_is_allowed(App::$profile['profile_uid'], get_observer_hash(), 'view_stream')) { + return ''; + } - $limit = ((array_key_exists('limit',$arr)) ? intval($arr['limit']) : 50); - - return catblock(App::$profile['profile_uid'], $limit, '', App::$profile['channel_hash'], 'wall'); - } + $limit = ((array_key_exists('limit', $arr)) ? intval($arr['limit']) : 50); + return catblock(App::$profile['profile_uid'], $limit, '', App::$profile['channel_hash'], 'wall'); + } } diff --git a/Zotlabs/Widget/Categories.php b/Zotlabs/Widget/Categories.php index 7170a3ded..cd26dba18 100644 --- a/Zotlabs/Widget/Categories.php +++ b/Zotlabs/Widget/Categories.php @@ -5,54 +5,63 @@ namespace Zotlabs\Widget; use App; use Zotlabs\Lib\Apps; -class Categories { +class Categories +{ - function widget($arr) { + public function widget($arr) + { - $cards = ((array_key_exists('cards',$arr) && $arr['cards']) ? true : false); + $cards = ((array_key_exists('cards', $arr) && $arr['cards']) ? true : false); - if(($cards) && (! Apps::system_app_installed(App::$profile['profile_uid'], 'Cards'))) - return ''; + if (($cards) && (!Apps::system_app_installed(App::$profile['profile_uid'], 'Cards'))) { + return ''; + } - $articles = ((array_key_exists('articles',$arr) && $arr['articles']) ? true : false); + $articles = ((array_key_exists('articles', $arr) && $arr['articles']) ? true : false); - if(($articles) && (! Apps::addon_app_installed(App::$profile['profile_uid'],'articles'))) - return ''; + if (($articles) && (!Apps::addon_app_installed(App::$profile['profile_uid'], 'articles'))) { + return ''; + } - if((! App::$profile['profile_uid']) - || (! perm_is_allowed(App::$profile['profile_uid'],get_observer_hash(),(($cards || $articles) ? 'view_pages' : 'view_articles')))) { - return ''; - } + if ( + (!App::$profile['profile_uid']) + || (!perm_is_allowed(App::$profile['profile_uid'], get_observer_hash(), (($cards || $articles) ? 'view_pages' : 'view_articles'))) + ) { + return ''; + } - $cat = ((x($_REQUEST,'cat')) ? htmlspecialchars($_REQUEST['cat'],ENT_COMPAT,'UTF-8') : ''); - $srchurl = (($cards) ? App::$argv[0] . '/' . App::$argv[1] : App::$query_string); - $srchurl = rtrim(preg_replace('/cat\=[^\&].*?(\&|$)/is','',$srchurl),'&'); - $srchurl = str_replace(array('?f=','&f='),array('',''),$srchurl); + $cat = ((x($_REQUEST, 'cat')) ? htmlspecialchars($_REQUEST['cat'], ENT_COMPAT, 'UTF-8') : ''); + $srchurl = (($cards) ? App::$argv[0] . '/' . App::$argv[1] : App::$query_string); + $srchurl = rtrim(preg_replace('/cat\=[^\&].*?(\&|$)/is', '', $srchurl), '&'); + $srchurl = str_replace(array('?f=', '&f='), array('', ''), $srchurl); - if($cards) - return self::cardcategories_widget($srchurl, $cat); - elseif($articles) - return self::articlecategories_widget($srchurl, $cat); - else - return self::categories_widget($srchurl, $cat); - - } + if ($cards) { + return self::cardcategories_widget($srchurl, $cat); + } elseif ($articles) { + return self::articlecategories_widget($srchurl, $cat); + } else { + return self::categories_widget($srchurl, $cat); + } + } - static function articlecategories_widget($baseurl,$selected = '') { - - if(! Apps::system_app_installed(App::$profile['profile_uid'],'Categories')) - return ''; + public static function articlecategories_widget($baseurl, $selected = '') + { - $sql_extra = item_permissions_sql(App::$profile['profile_uid']); + if (!Apps::system_app_installed(App::$profile['profile_uid'], 'Categories')) { + return ''; + } - $item_normal = "and item.item_hidden = 0 and item.item_type = 7 and item.item_deleted = 0 + $sql_extra = item_permissions_sql(App::$profile['profile_uid']); + + $item_normal = "and item.item_hidden = 0 and item.item_type = 7 and item.item_deleted = 0 and item.item_unpublished = 0 and item.item_delayed = 0 and item.item_pending_remove = 0 and item.item_blocked = 0 "; - $terms = []; - $r = q("select distinct(term.term) + $terms = []; + $r = q( + "select distinct(term.term) from term join item on term.oid = item.id where item.uid = %d and term.uid = item.uid @@ -62,41 +71,45 @@ class Categories { $item_normal $sql_extra order by term.term asc", - intval(App::$profile['profile_uid']), - intval(TERM_CATEGORY), - intval(TERM_OBJ_POST), - dbesc(App::$profile['channel_hash']) - ); - if($r && count($r)) { - foreach($r as $rr) - $terms[] = array('name' => $rr['term'], 'selected' => (($selected == $rr['term']) ? 'selected' : '')); + intval(App::$profile['profile_uid']), + intval(TERM_CATEGORY), + intval(TERM_OBJ_POST), + dbesc(App::$profile['channel_hash']) + ); + if ($r && count($r)) { + foreach ($r as $rr) { + $terms[] = array('name' => $rr['term'], 'selected' => (($selected == $rr['term']) ? 'selected' : '')); + } - return replace_macros(get_markup_template('categories_widget.tpl'),array( - '$title' => t('Categories'), - '$desc' => '', - '$sel_all' => (($selected == '') ? 'selected' : ''), - '$all' => t('Everything'), - '$terms' => $terms, - '$base' => $baseurl, + return replace_macros(get_markup_template('categories_widget.tpl'), array( + '$title' => t('Categories'), + '$desc' => '', + '$sel_all' => (($selected == '') ? 'selected' : ''), + '$all' => t('Everything'), + '$terms' => $terms, + '$base' => $baseurl, - )); - } - return ''; - } + )); + } + return ''; + } - static function cardcategories_widget($baseurl,$selected = '') { - - if(! Apps::system_app_installed(App::$profile['profile_uid'],'Categories')) - return ''; + public static function cardcategories_widget($baseurl, $selected = '') + { - $sql_extra = item_permissions_sql(App::$profile['profile_uid']); + if (!Apps::system_app_installed(App::$profile['profile_uid'], 'Categories')) { + return ''; + } - $item_normal = "and item.item_hidden = 0 and item.item_type = 6 and item.item_deleted = 0 + $sql_extra = item_permissions_sql(App::$profile['profile_uid']); + + $item_normal = "and item.item_hidden = 0 and item.item_type = 6 and item.item_deleted = 0 and item.item_unpublished = 0 and item.item_delayed = 0 and item.item_pending_remove = 0 and item.item_blocked = 0 "; - $terms = []; - $r = q("select distinct(term.term) + $terms = []; + $r = q( + "select distinct(term.term) from term join item on term.oid = item.id where item.uid = %d and term.uid = item.uid @@ -106,42 +119,46 @@ class Categories { $item_normal $sql_extra order by term.term asc", - intval(App::$profile['profile_uid']), - intval(TERM_CATEGORY), - intval(TERM_OBJ_POST), - dbesc(App::$profile['channel_hash']) - ); - if($r && count($r)) { - foreach($r as $rr) - $terms[] = array('name' => $rr['term'], 'selected' => (($selected == $rr['term']) ? 'selected' : '')); + intval(App::$profile['profile_uid']), + intval(TERM_CATEGORY), + intval(TERM_OBJ_POST), + dbesc(App::$profile['channel_hash']) + ); + if ($r && count($r)) { + foreach ($r as $rr) { + $terms[] = array('name' => $rr['term'], 'selected' => (($selected == $rr['term']) ? 'selected' : '')); + } - return replace_macros(get_markup_template('categories_widget.tpl'),array( - '$title' => t('Categories'), - '$desc' => '', - '$sel_all' => (($selected == '') ? 'selected' : ''), - '$all' => t('Everything'), - '$terms' => $terms, - '$base' => $baseurl, + return replace_macros(get_markup_template('categories_widget.tpl'), array( + '$title' => t('Categories'), + '$desc' => '', + '$sel_all' => (($selected == '') ? 'selected' : ''), + '$all' => t('Everything'), + '$terms' => $terms, + '$base' => $baseurl, - )); - } - return ''; - } + )); + } + return ''; + } - static function categories_widget($baseurl,$selected = '') { - - if(! Apps::system_app_installed(App::$profile['profile_uid'],'Categories')) - return ''; + public static function categories_widget($baseurl, $selected = '') + { - require_once('include/security.php'); + if (!Apps::system_app_installed(App::$profile['profile_uid'], 'Categories')) { + return ''; + } - $sql_extra = item_permissions_sql(App::$profile['profile_uid']); - - $item_normal = item_normal(); + require_once('include/security.php'); - $terms = []; - $r = q("select distinct(term.term) from term join item on term.oid = item.id + $sql_extra = item_permissions_sql(App::$profile['profile_uid']); + + $item_normal = item_normal(); + + $terms = []; + $r = q( + "select distinct(term.term) from term join item on term.oid = item.id where item.uid = %d and term.uid = item.uid and term.ttype = %d @@ -152,27 +169,27 @@ class Categories { $item_normal $sql_extra order by term.term asc", - intval(App::$profile['profile_uid']), - intval(TERM_CATEGORY), - intval(TERM_OBJ_POST), - dbesc(App::$profile['channel_hash']), - dbesc(ACTIVITY_UPDATE) - ); - if($r && count($r)) { - foreach($r as $rr) - $terms[] = array('name' => $rr['term'], 'selected' => (($selected == $rr['term']) ? 'selected' : '')); + intval(App::$profile['profile_uid']), + intval(TERM_CATEGORY), + intval(TERM_OBJ_POST), + dbesc(App::$profile['channel_hash']), + dbesc(ACTIVITY_UPDATE) + ); + if ($r && count($r)) { + foreach ($r as $rr) { + $terms[] = array('name' => $rr['term'], 'selected' => (($selected == $rr['term']) ? 'selected' : '')); + } - return replace_macros(get_markup_template('categories_widget.tpl'),array( - '$title' => t('Categories'), - '$desc' => '', - '$sel_all' => (($selected == '') ? 'selected' : ''), - '$all' => t('Everything'), - '$terms' => $terms, - '$base' => $baseurl, - - )); - } - return ''; - } + return replace_macros(get_markup_template('categories_widget.tpl'), array( + '$title' => t('Categories'), + '$desc' => '', + '$sel_all' => (($selected == '') ? 'selected' : ''), + '$all' => t('Everything'), + '$terms' => $terms, + '$base' => $baseurl, + )); + } + return ''; + } } diff --git a/Zotlabs/Widget/Cdav.php b/Zotlabs/Widget/Cdav.php index 2a2e0855f..3be6e49ba 100644 --- a/Zotlabs/Widget/Cdav.php +++ b/Zotlabs/Widget/Cdav.php @@ -2,189 +2,193 @@ namespace Zotlabs\Widget; +use App; +use DBA; +use Sabre\CardDAV\Backend\PDO; +class Cdav +{ -class Cdav { + public function widget() + { + if (!local_channel()) { + return; + } - function widget() { - if(!local_channel()) - return; + $channel = App::get_channel(); + $principalUri = 'principals/' . $channel['channel_address']; - $channel = \App::get_channel(); - $principalUri = 'principals/' . $channel['channel_address']; + if (!cdav_principal($principalUri)) { + return; + } - if(!cdav_principal($principalUri)) - return; + $pdo = DBA::$dba->db; - $pdo = \DBA::$dba->db; + require_once 'vendor/autoload.php'; - require_once 'vendor/autoload.php'; + $o = ''; - $o = ''; + if (argc() <= 3 && argv(1) === 'calendar') { + $caldavBackend = new \Sabre\CalDAV\Backend\PDO($pdo); - if(argc() <= 3 && argv(1) === 'calendar') { + $sabrecals = $caldavBackend->getCalendarsForUser($principalUri); - $caldavBackend = new \Sabre\CalDAV\Backend\PDO($pdo); + //TODO: we should probably also check for permission to send stream here + $local_channels = q( + "SELECT * FROM channel LEFT JOIN abook ON abook_xchan = channel_hash WHERE channel_system = 0 AND channel_removed = 0 AND channel_hash != '%s' AND abook_channel = %d", + dbesc($channel['channel_hash']), + intval($channel['channel_id']) + ); - $sabrecals = $caldavBackend->getCalendarsForUser($principalUri); + $sharee_options .= '' . "\r\n"; + foreach ($local_channels as $local_channel) { + $sharee_options .= '' . "\r\n"; + } - //TODO: we should probably also check for permission to send stream here - $local_channels = q("SELECT * FROM channel LEFT JOIN abook ON abook_xchan = channel_hash WHERE channel_system = 0 AND channel_removed = 0 AND channel_hash != '%s' AND abook_channel = %d", - dbesc($channel['channel_hash']), - intval($channel['channel_id']) - ); + $access_options = '' . "\r\n"; + $access_options .= '' . "\r\n"; - $sharee_options .= '' . "\r\n"; - foreach($local_channels as $local_channel) { - $sharee_options .= '' . "\r\n"; - } + //list calendars + foreach ($sabrecals as $sabrecal) { + if ($sabrecal['share-access'] == 1) { + $access = ''; + } + if ($sabrecal['share-access'] == 2) { + $access = 'read'; + } + if ($sabrecal['share-access'] == 3) { + $access = 'read-write'; + } - $access_options = '' . "\r\n"; - $access_options .= '' . "\r\n"; + $invites = $caldavBackend->getInvites($sabrecal['id']); - //list calendars - foreach($sabrecals as $sabrecal) { - if($sabrecal['share-access'] == 1) - $access = ''; - if($sabrecal['share-access'] == 2) - $access = 'read'; - if($sabrecal['share-access'] == 3) - $access = 'read-write'; + $json_source = '/cdav/calendar/json/' . $sabrecal['id'][0] . '/' . $sabrecal['id'][1]; - $invites = $caldavBackend->getInvites($sabrecal['id']); + $switch = get_pconfig(local_channel(), 'cdav_calendar', $sabrecal['id'][0]); - $json_source = '/cdav/calendar/json/' . $sabrecal['id'][0] . '/' . $sabrecal['id'][1]; + $color = (($sabrecal['{http://apple.com/ns/ical/}calendar-color']) ? $sabrecal['{http://apple.com/ns/ical/}calendar-color'] : '#6cad39'); - $switch = get_pconfig(local_channel(), 'cdav_calendar', $sabrecal['id'][0]); + $editable = (($sabrecal['share-access'] == 2) ? 'false' : 'true'); // false/true must be string since we're passing it to javascript - $color = (($sabrecal['{http://apple.com/ns/ical/}calendar-color']) ? $sabrecal['{http://apple.com/ns/ical/}calendar-color'] : '#6cad39'); + $sharees = []; + $share_displayname = []; - $editable = (($sabrecal['share-access'] == 2) ? 'false' : 'true'); // false/true must be string since we're passing it to javascript + foreach ($invites as $invite) { + if (strpos($invite->href, 'mailto:') !== false) { + $sharee = channelx_by_nick(substr($invite->principal, 11)); + $sharees[] = [ + 'name' => $sharee['channel_name'], + 'access' => (($invite->access == 3) ? ' (RW)' : ' (R)'), + 'hash' => $sharee['channel_hash'] + ]; + } + } - $sharees = []; - $share_displayname = []; + if (!$access) { + $my_calendars[] = [ + 'ownernick' => $channel['channel_address'], + 'uri' => $sabrecal['uri'], + 'displayname' => $sabrecal['{DAV:}displayname'], + 'calendarid' => $sabrecal['id'][0], + 'instanceid' => $sabrecal['id'][1], + 'json_source' => $json_source, + 'color' => $color, + 'editable' => $editable, + 'switch' => $switch, + 'sharees' => $sharees + ]; + } else { + $shared_calendars[] = [ + 'ownernick' => $channel['channel_address'], + 'uri' => $sabrecal['uri'], + 'displayname' => $sabrecal['{DAV:}displayname'], + 'calendarid' => $sabrecal['id'][0], + 'instanceid' => $sabrecal['id'][1], + 'json_source' => $json_source, + 'color' => $color, + 'editable' => $editable, + 'switch' => $switch, + 'sharer' => $sabrecal['{urn:ietf:params:xml:ns:caldav}calendar-description'], + 'access' => $access + ]; + } - foreach($invites as $invite) { - if(strpos($invite->href, 'mailto:') !== false) { - $sharee = channelx_by_nick(substr($invite->principal, 11)); - $sharees[] = [ - 'name' => $sharee['channel_name'], - 'access' => (($invite->access == 3) ? ' (RW)' : ' (R)'), - 'hash' => $sharee['channel_hash'] - ]; - } - } + if (!$access || $access === 'read-write') { + $writable_calendars[] = [ + 'displayname' => ((!$access) ? $sabrecal['{DAV:}displayname'] : $share_displayname[0]), + 'id' => $sabrecal['id'] + ]; + } + } - if(!$access) { - $my_calendars[] = [ - 'ownernick' => $channel['channel_address'], - 'uri' => $sabrecal['uri'], - 'displayname' => $sabrecal['{DAV:}displayname'], - 'calendarid' => $sabrecal['id'][0], - 'instanceid' => $sabrecal['id'][1], - 'json_source' => $json_source, - 'color' => $color, - 'editable' => $editable, - 'switch' => $switch, - 'sharees' => $sharees - ]; - } - else { - $shared_calendars[] = [ - 'ownernick' => $channel['channel_address'], - 'uri' => $sabrecal['uri'], - 'displayname' => $sabrecal['{DAV:}displayname'], - 'calendarid' => $sabrecal['id'][0], - 'instanceid' => $sabrecal['id'][1], - 'json_source' => $json_source, - 'color' => $color, - 'editable' => $editable, - 'switch' => $switch, - 'sharer' => $sabrecal['{urn:ietf:params:xml:ns:caldav}calendar-description'], - 'access' => $access - ]; - } + $calendars[] = [ + 'ownernick' => $channel['channel_address'], + 'displayname' => $channel['channel_name'], + 'calendarid' => 'calendar', + 'json_source' => '/calendar/json', + 'color' => '#3a87ad', + 'editable' => true, + 'switch' => get_pconfig(local_channel(), 'cdav_calendar', 'calendar') + ]; - if(!$access || $access === 'read-write') { - $writable_calendars[] = [ - 'displayname' => ((!$access) ? $sabrecal['{DAV:}displayname'] : $share_displayname[0]), - 'id' => $sabrecal['id'] - ]; - } - } + $o .= replace_macros(get_markup_template('cdav_widget_calendar.tpl'), [ + '$calendars_label' => t('Channel Calendar'), + '$calendars' => $calendars, + '$my_calendars_label' => t('CalDAV Calendars'), + '$my_calendars' => $my_calendars, + '$shared_calendars_label' => t('Shared CalDAV Calendars'), + '$shared_calendars' => $shared_calendars, + '$sharee_options' => $sharee_options, + '$access_options' => $access_options, + '$share_label' => t('Share this calendar'), + '$share' => t('Share'), + '$edit_label' => t('Calendar name and color'), + '$edit' => t('Edit'), + '$create_label' => t('Create new CalDAV calendar'), + '$create' => t('Create'), + '$create_placeholder' => t('Calendar Name'), + '$tools_label' => t('Calendar Tools'), + '$tools_options_label' => [t('Channel Calendars'), t('CalDAV Calendars')], + '$import_label' => t('Import calendar'), + '$import_placeholder' => t('Select a calendar to import to'), + '$upload' => t('Upload'), + '$writable_calendars' => $writable_calendars + ]); - $calendars[] = [ - 'ownernick' => $channel['channel_address'], - 'displayname' => $channel['channel_name'], - 'calendarid' => 'calendar', - 'json_source' => '/calendar/json', - 'color' => '#3a87ad', - 'editable' => true, - 'switch' => get_pconfig(local_channel(), 'cdav_calendar', 'calendar') - ]; + return $o; + } - $o .= replace_macros(get_markup_template('cdav_widget_calendar.tpl'), [ - '$calendars_label' => t('Channel Calendar'), - '$calendars' => $calendars, - '$my_calendars_label' => t('CalDAV Calendars'), - '$my_calendars' => $my_calendars, - '$shared_calendars_label' => t('Shared CalDAV Calendars'), - '$shared_calendars' => $shared_calendars, - '$sharee_options' => $sharee_options, - '$access_options' => $access_options, - '$share_label' => t('Share this calendar'), - '$share' => t('Share'), - '$edit_label' => t('Calendar name and color'), - '$edit' => t('Edit'), - '$create_label' => t('Create new CalDAV calendar'), - '$create' => t('Create'), - '$create_placeholder' => t('Calendar Name'), - '$tools_label' => t('Calendar Tools'), - '$tools_options_label' => [t('Channel Calendars'), t('CalDAV Calendars')], - '$import_label' => t('Import calendar'), - '$import_placeholder' => t('Select a calendar to import to'), - '$upload' => t('Upload'), - '$writable_calendars' => $writable_calendars - ]); + if (argc() >= 2 && argv(1) === 'addressbook') { + $carddavBackend = new PDO($pdo); - return $o; + $sabreabooks = $carddavBackend->getAddressBooksForUser($principalUri); - } + //list addressbooks + foreach ($sabreabooks as $sabreabook) { + $addressbooks[] = [ + 'ownernick' => $channel['channel_address'], + 'uri' => $sabreabook['uri'], + 'displayname' => $sabreabook['{DAV:}displayname'], + 'id' => $sabreabook['id'] - if(argc() >= 2 && argv(1) === 'addressbook') { + ]; + } - $carddavBackend = new \Sabre\CardDAV\Backend\PDO($pdo); + $o .= replace_macros(get_markup_template('cdav_widget_addressbook.tpl'), [ + '$addressbooks_label' => t('Addressbooks'), + '$addressbooks' => $addressbooks, + '$edit_label' => t('Addressbook name'), + '$edit' => t('Edit'), + '$create_label' => t('Create new addressbook'), + '$create_placeholder' => t('Addressbook Name'), + '$create' => t('Create'), + '$tools_label' => t('Addressbook Tools'), + '$import_label' => t('Import addressbook'), + '$import_placeholder' => t('Select an addressbook to import to'), + '$upload' => t('Upload') + ]); - $sabreabooks = $carddavBackend->getAddressBooksForUser($principalUri); - - //list addressbooks - foreach($sabreabooks as $sabreabook) { - $addressbooks[] = [ - 'ownernick' => $channel['channel_address'], - 'uri' => $sabreabook['uri'], - 'displayname' => $sabreabook['{DAV:}displayname'], - 'id' => $sabreabook['id'] - - ]; - } - - $o .= replace_macros(get_markup_template('cdav_widget_addressbook.tpl'), [ - '$addressbooks_label' => t('Addressbooks'), - '$addressbooks' => $addressbooks, - '$edit_label' => t('Addressbook name'), - '$edit' => t('Edit'), - '$create_label' => t('Create new addressbook'), - '$create_placeholder' => t('Addressbook Name'), - '$create' => t('Create'), - '$tools_label' => t('Addressbook Tools'), - '$import_label' => t('Import addressbook'), - '$import_placeholder' => t('Select an addressbook to import to'), - '$upload' => t('Upload') - ]); - - return $o; - - } - - } + return $o; + } + } } diff --git a/Zotlabs/Widget/Chatroom_list.php b/Zotlabs/Widget/Chatroom_list.php index e2aad0e05..425dc9488 100644 --- a/Zotlabs/Widget/Chatroom_list.php +++ b/Zotlabs/Widget/Chatroom_list.php @@ -2,23 +2,29 @@ namespace Zotlabs\Widget; -class Chatroom_list { +use App; +use Zotlabs\Lib\Chatroom; - function widget($arr) { +class Chatroom_list +{ - if(! \App::$profile) - return ''; + public function widget($arr) + { - $r = \Zotlabs\Lib\Chatroom::roomlist(\App::$profile['profile_uid']); + if (!App::$profile) { + return ''; + } - if($r) { - return replace_macros(get_markup_template('chatroomlist.tpl'), array( - '$header' => t('Chatrooms'), - '$baseurl' => z_root(), - '$nickname' => \App::$profile['channel_address'], - '$items' => $r, - '$overview' => t('Overview') - )); - } - } + $r = Chatroom::roomlist(App::$profile['profile_uid']); + + if ($r) { + return replace_macros(get_markup_template('chatroomlist.tpl'), array( + '$header' => t('Chatrooms'), + '$baseurl' => z_root(), + '$nickname' => App::$profile['channel_address'], + '$items' => $r, + '$overview' => t('Overview') + )); + } + } } diff --git a/Zotlabs/Widget/Chatroom_members.php b/Zotlabs/Widget/Chatroom_members.php index 8ed77fb3c..c9385831f 100644 --- a/Zotlabs/Widget/Chatroom_members.php +++ b/Zotlabs/Widget/Chatroom_members.php @@ -2,14 +2,15 @@ namespace Zotlabs\Widget; -class Chatroom_members { +class Chatroom_members +{ - // The actual contents are filled in via AJAX - - function widget() { - return replace_macros(get_markup_template('chatroom_members.tpl'), array( - '$header' => t('Chat Members') - )); - } + // The actual contents are filled in via AJAX + public function widget() + { + return replace_macros(get_markup_template('chatroom_members.tpl'), array( + '$header' => t('Chat Members') + )); + } } diff --git a/Zotlabs/Widget/Clock.php b/Zotlabs/Widget/Clock.php index 8dae200c0..fd37c383f 100644 --- a/Zotlabs/Widget/Clock.php +++ b/Zotlabs/Widget/Clock.php @@ -2,13 +2,15 @@ namespace Zotlabs\Widget; -class Clock { +class Clock +{ - function widget($arr) { + public function widget($arr) + { - $miltime = ((isset($arr['military']) && $arr['military']) ? intval($arr['military']) : false); + $miltime = ((isset($arr['military']) && $arr['military']) ? intval($arr['military']) : false); - $o = <<< EOT + $o = <<< EOT

      '; - $o .= ''; + $o .= ''; - $o .= '
      ' . '

      ' . t('Tasks') . '

      '; - $o .= '
      '; - $o .= '
      '; - return $o; - - } + $o .= '
      ' . '

      ' . t('Tasks') . '

      '; + $o .= '
      '; + $o .= '
      '; + return $o; + } } - diff --git a/Zotlabs/Widget/Vcard.php b/Zotlabs/Widget/Vcard.php index 268a466b6..1b80c8356 100644 --- a/Zotlabs/Widget/Vcard.php +++ b/Zotlabs/Widget/Vcard.php @@ -4,11 +4,10 @@ namespace Zotlabs\Widget; use App; -class Vcard { - - function widget($arr) { - return vcard_from_xchan('', App::get_observer()); - } - +class Vcard +{ + public function widget($arr) + { + return vcard_from_xchan('', App::get_observer()); + } } - diff --git a/Zotlabs/Widget/Website_portation_tools.php b/Zotlabs/Widget/Website_portation_tools.php index 1cf3bb78a..7c11a1f08 100644 --- a/Zotlabs/Widget/Website_portation_tools.php +++ b/Zotlabs/Widget/Website_portation_tools.php @@ -2,21 +2,27 @@ namespace Zotlabs\Widget; +use App; -class Website_portation_tools { +class Website_portation_tools +{ - function widget($arr) { + public function widget($arr) + { - // mod menu doesn't load a profile. For any modules which load a profile, check it. - // otherwise local_channel() is sufficient for permissions. + // mod menu doesn't load a profile. For any modules which load a profile, check it. + // otherwise local_channel() is sufficient for permissions. - if(\App::$profile['profile_uid']) - if((\App::$profile['profile_uid'] != local_channel()) && (! \App::$is_sys)) - return ''; + if (App::$profile['profile_uid']) { + if ((App::$profile['profile_uid'] != local_channel()) && (!App::$is_sys)) { + return ''; + } + } - if(! local_channel()) - return ''; + if (!local_channel()) { + return ''; + } - return website_portation_tools(); - } + return website_portation_tools(); + } } diff --git a/Zotlabs/Widget/Zcard.php b/Zotlabs/Widget/Zcard.php index 12e53eaab..22fed3bf6 100644 --- a/Zotlabs/Widget/Zcard.php +++ b/Zotlabs/Widget/Zcard.php @@ -2,10 +2,14 @@ namespace Zotlabs\Widget; -class Zcard { +use App; - function widget($args) { - $channel = channelx_by_n(\App::$profile_uid); - return get_zcard($channel,get_observer_hash(),array('width' => 875)); - } +class Zcard +{ + + public function widget($args) + { + $channel = channelx_by_n(App::$profile_uid); + return get_zcard($channel, get_observer_hash(), array('width' => 875)); + } } diff --git a/Zotlabs/Zot6/IHandler.php b/Zotlabs/Zot6/IHandler.php index 0813b6ce0..6bbeae55b 100644 --- a/Zotlabs/Zot6/IHandler.php +++ b/Zotlabs/Zot6/IHandler.php @@ -2,15 +2,14 @@ namespace Zotlabs\Zot6; -interface IHandler { +interface IHandler +{ - function Notify($data,$hub); + public function Notify($data, $hub); - function Rekey($sender,$data,$hub); + public function Rekey($sender, $data, $hub); - function Refresh($sender,$recipients,$hub,$force); - - function Purge($sender,$recipients,$hub); + public function Refresh($sender, $recipients, $hub, $force); + public function Purge($sender, $recipients, $hub); } - diff --git a/Zotlabs/Zot6/Receiver.php b/Zotlabs/Zot6/Receiver.php index f70f926d2..d1c2b5d97 100644 --- a/Zotlabs/Zot6/Receiver.php +++ b/Zotlabs/Zot6/Receiver.php @@ -7,226 +7,223 @@ use Zotlabs\Lib\Libzot; use Zotlabs\Lib\Crypto; use Zotlabs\Web\HTTPSig; -class Receiver { +class Receiver +{ - protected $data; - protected $encrypted; - protected $error; - protected $messagetype; - protected $sender; - protected $site_id; - protected $validated; - protected $recipients; - protected $response; - protected $handler; - protected $prvkey; - protected $rawdata; - protected $sigdata; + protected $data; + protected $encrypted; + protected $error; + protected $messagetype; + protected $sender; + protected $site_id; + protected $validated; + protected $recipients; + protected $response; + protected $handler; + protected $prvkey; + protected $rawdata; + protected $sigdata; - function __construct($handler, $localdata = null) { + public function __construct($handler, $localdata = null) + { - $this->error = false; - $this->validated = false; - $this->messagetype = ''; - $this->response = [ 'success' => false ]; - $this->handler = $handler; - $this->data = null; - $this->rawdata = null; - $this->site_id = null; - $this->prvkey = Config::get('system','prvkey'); + $this->error = false; + $this->validated = false; + $this->messagetype = ''; + $this->response = ['success' => false]; + $this->handler = $handler; + $this->data = null; + $this->rawdata = null; + $this->site_id = null; + $this->prvkey = Config::get('system', 'prvkey'); - if($localdata) { - $this->rawdata = $localdata; - } - else { - $this->rawdata = file_get_contents('php://input'); + if ($localdata) { + $this->rawdata = $localdata; + } else { + $this->rawdata = file_get_contents('php://input'); - // All access to the zot endpoint must use http signatures + // All access to the zot endpoint must use http signatures - if (! $this->Valid_Httpsig()) { - logger('signature failed'); - $this->error = true; - $this->response['message'] = 'signature invalid'; - return; - } - } + if (!$this->Valid_Httpsig()) { + logger('signature failed'); + $this->error = true; + $this->response['message'] = 'signature invalid'; + return; + } + } - logger('received raw: ' . print_r($this->rawdata,true), LOGGER_DATA); + logger('received raw: ' . print_r($this->rawdata, true), LOGGER_DATA); - if ($this->rawdata) { - $this->data = json_decode($this->rawdata,true); - if(($this->data) && (! is_array($this->data)) && (substr($this->data,0,1) === "{")) { + if ($this->rawdata) { + $this->data = json_decode($this->rawdata, true); + if (($this->data) && (!is_array($this->data)) && (substr($this->data, 0, 1) === "{")) { + // Multiple json encoding has been seen in the wild and needs to be fixed on the sending side. + // Proceed anyway and log the event with a backtrace. - // Multiple json encoding has been seen in the wild and needs to be fixed on the sending side. - // Proceed anyway and log the event with a backtrace. + btlogger('multiple encoding detected'); + $this->data = json_decode($this->data, true); + } + } else { + $this->error = true; + $this->response['message'] = 'no data'; + } - btlogger('multiple encoding detected'); - $this->data = json_decode($this->data,true); - } - } - else { - $this->error = true; - $this->response['message'] = 'no data'; - } + logger('received_json: ' . json_encode($this->data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES), LOGGER_DATA); - logger('received_json: ' . json_encode($this->data,JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES), LOGGER_DATA); + logger('received: ' . print_r($this->data, true), LOGGER_DATA); - logger('received: ' . print_r($this->data,true), LOGGER_DATA); + if ($this->data && is_array($this->data)) { + $this->encrypted = ((array_key_exists('encrypted', $this->data) && intval($this->data['encrypted'])) ? true : false); - if ($this->data && is_array($this->data)) { - $this->encrypted = ((array_key_exists('encrypted',$this->data) && intval($this->data['encrypted'])) ? true : false); - - if ($this->encrypted && $this->prvkey) { - $uncrypted = Crypto::unencapsulate($this->data,$this->prvkey); - if ($uncrypted) { - $this->data = json_decode($uncrypted,true); - } - else { - $this->error = true; - $this->response['message'] = 'no data'; - } - } - } - } - - - function run() { - - if ($this->error) { - // make timing attacks on the decryption engine a bit more difficult - usleep(mt_rand(10000,100000)); - return($this->response); - } - - if ($this->data) { - if (array_key_exists('type',$this->data)) { - $this->messagetype = $this->data['type']; - } - - if (! $this->messagetype) { - $this->error = true; - $this->response['message'] = 'no datatype'; - return $this->response; - } - - $this->sender = ((array_key_exists('sender',$this->data)) ? $this->data['sender'] : null); - $this->recipients = ((array_key_exists('recipients',$this->data)) ? $this->data['recipients'] : null); - $this->site_id = ((array_key_exists('site_id',$this->data)) ? $this->data['site_id'] : null); - } - - if ($this->sender) { - $result = $this->ValidateSender(); - if (! $result) { - $this->error = true; - return $this->response; - } - } - - return $this->Dispatch(); - } - - function ValidateSender() { - - $hub = Libzot::valid_hub($this->sender,$this->site_id); - - if (! $hub) { - $x = Libzot::register_hub($this->sigdata['signer']); - if($x['success']) { - $hub = Libzot::valid_hub($this->sender,$this->site_id); - } - if(! $hub) { - $this->response['message'] = 'sender unknown'; - return false; - } - } - - if (! check_siteallowed($hub['hubloc_url'])) { - $this->response['message'] = 'forbidden'; - return false; - } - - if (! check_channelallowed($this->sender)) { - $this->response['message'] = 'forbidden'; - return false; - } - - Libzot::update_hub_connected($hub,$this->site_id); - - $this->validated = true; - $this->hub = $hub; - return true; + if ($this->encrypted && $this->prvkey) { + $uncrypted = Crypto::unencapsulate($this->data, $this->prvkey); + if ($uncrypted) { + $this->data = json_decode($uncrypted, true); + } else { + $this->error = true; + $this->response['message'] = 'no data'; + } + } + } } - function Valid_Httpsig() { + public function run() + { - $result = false; + if ($this->error) { + // make timing attacks on the decryption engine a bit more difficult + usleep(mt_rand(10000, 100000)); + return ($this->response); + } - $this->sigdata = HTTPSig::verify($this->rawdata, EMPTY_STR, 'zot6'); + if ($this->data) { + if (array_key_exists('type', $this->data)) { + $this->messagetype = $this->data['type']; + } - if ($this->sigdata && $this->sigdata['header_signed'] && $this->sigdata['header_valid']) { - $result = true; + if (!$this->messagetype) { + $this->error = true; + $this->response['message'] = 'no datatype'; + return $this->response; + } - // It is OK to not have signed content - not all messages provide content. - // But if it is signed, it has to be valid + $this->sender = ((array_key_exists('sender', $this->data)) ? $this->data['sender'] : null); + $this->recipients = ((array_key_exists('recipients', $this->data)) ? $this->data['recipients'] : null); + $this->site_id = ((array_key_exists('site_id', $this->data)) ? $this->data['site_id'] : null); + } - if (($this->sigdata['content_signed']) && (! $this->sigdata['content_valid'])) { - $result = false; - } - } - return $result; - } - - function Dispatch() { + if ($this->sender) { + $result = $this->ValidateSender(); + if (!$result) { + $this->error = true; + return $this->response; + } + } - switch ($this->messagetype) { + return $this->Dispatch(); + } - case 'purge': - $this->response = $this->handler->Purge($this->sender,$this->recipients,$this->hub); - break; + public function ValidateSender() + { - case 'refresh': - $this->response = $this->handler->Refresh($this->sender,$this->recipients,$this->hub,false); - break; - - case 'force_refresh': - $this->response = $this->handler->Refresh($this->sender,$this->recipients,$this->hub,true); - break; + $hub = Libzot::valid_hub($this->sender, $this->site_id); - case 'rekey': - $this->response = $this->handler->Rekey($this->sender, $this->data,$this->hub); - break; + if (!$hub) { + $x = Libzot::register_hub($this->sigdata['signer']); + if ($x['success']) { + $hub = Libzot::valid_hub($this->sender, $this->site_id); + } + if (!$hub) { + $this->response['message'] = 'sender unknown'; + return false; + } + } - case 'activity': - case 'response': // upstream message - case 'sync': - default: - // Only accept these message types with a valid sender - if ($this->sender) { - $this->response = $this->handler->Notify($this->data,$this->hub); - } - break; + if (!check_siteallowed($hub['hubloc_url'])) { + $this->response['message'] = 'forbidden'; + return false; + } - } + if (!check_channelallowed($this->sender)) { + $this->response['message'] = 'forbidden'; + return false; + } - logger('response_to_return: ' . print_r($this->response,true),LOGGER_DATA); + Libzot::update_hub_connected($hub, $this->site_id); - if ($this->encrypted) { - $this->EncryptResponse(); - } + $this->validated = true; + $this->hub = $hub; + return true; + } - return($this->response); - } - function EncryptResponse() { - $algorithm = Libzot::best_algorithm($this->hub['site_crypto']); - if ($algorithm) { - $this->response = Crypto::encapsulate(json_encode($this->response),$this->hub['hubloc_sitekey'], $algorithm); - } - } + public function Valid_Httpsig() + { + $result = false; + + $this->sigdata = HTTPSig::verify($this->rawdata, EMPTY_STR, 'zot6'); + + if ($this->sigdata && $this->sigdata['header_signed'] && $this->sigdata['header_valid']) { + $result = true; + + // It is OK to not have signed content - not all messages provide content. + // But if it is signed, it has to be valid + + if (($this->sigdata['content_signed']) && (!$this->sigdata['content_valid'])) { + $result = false; + } + } + return $result; + } + + public function Dispatch() + { + + switch ($this->messagetype) { + case 'purge': + $this->response = $this->handler->Purge($this->sender, $this->recipients, $this->hub); + break; + + case 'refresh': + $this->response = $this->handler->Refresh($this->sender, $this->recipients, $this->hub, false); + break; + + case 'force_refresh': + $this->response = $this->handler->Refresh($this->sender, $this->recipients, $this->hub, true); + break; + + case 'rekey': + $this->response = $this->handler->Rekey($this->sender, $this->data, $this->hub); + break; + + case 'activity': + case 'response': // upstream message + case 'sync': + default: + // Only accept these message types with a valid sender + if ($this->sender) { + $this->response = $this->handler->Notify($this->data, $this->hub); + } + break; + } + + logger('response_to_return: ' . print_r($this->response, true), LOGGER_DATA); + + if ($this->encrypted) { + $this->EncryptResponse(); + } + + return ($this->response); + } + + public function EncryptResponse() + { + $algorithm = Libzot::best_algorithm($this->hub['site_crypto']); + if ($algorithm) { + $this->response = Crypto::encapsulate(json_encode($this->response), $this->hub['hubloc_sitekey'], $algorithm); + } + } } - - - diff --git a/Zotlabs/Zot6/Zot6Handler.php b/Zotlabs/Zot6/Zot6Handler.php index 07e85cb12..98dabcbba 100644 --- a/Zotlabs/Zot6/Zot6Handler.php +++ b/Zotlabs/Zot6/Zot6Handler.php @@ -5,188 +5,196 @@ namespace Zotlabs\Zot6; use Zotlabs\Lib\Libzot; use Zotlabs\Lib\Queue; -class Zot6Handler implements IHandler { +class Zot6Handler implements IHandler +{ - function Notify($data,$hub) { - return self::reply_notify($data,$hub); - } + public function Notify($data, $hub) + { + return self::reply_notify($data, $hub); + } - function Rekey($sender,$data,$hub) { - return self::reply_rekey_request($sender,$data,$hub); - } + public function Rekey($sender, $data, $hub) + { + return self::reply_rekey_request($sender, $data, $hub); + } - function Refresh($sender,$recipients,$hub,$force) { - return self::reply_refresh($sender,$recipients,$hub,$force); - } + public function Refresh($sender, $recipients, $hub, $force) + { + return self::reply_refresh($sender, $recipients, $hub, $force); + } - function Purge($sender,$recipients,$hub) { - return self::reply_purge($sender,$recipients,$hub); - } + public function Purge($sender, $recipients, $hub) + { + return self::reply_purge($sender, $recipients, $hub); + } - // Implementation of specific methods follows; - // These generally do a small amout of validation and call Libzot - // to do any heavy lifting + // Implementation of specific methods follows; + // These generally do a small amout of validation and call Libzot + // to do any heavy lifting - static function reply_notify($data,$hub) { + public static function reply_notify($data, $hub) + { - $ret = [ 'success' => false ]; + $ret = ['success' => false]; - logger('notify received from ' . $hub['hubloc_url']); + logger('notify received from ' . $hub['hubloc_url']); - $x = Libzot::fetch($data,$hub); - $ret['delivery_report'] = $x; - - - $ret['success'] = true; - return $ret; - } + $x = Libzot::fetch($data, $hub); + $ret['delivery_report'] = $x; + $ret['success'] = true; + return $ret; + } - /** - * @brief Remote channel info (such as permissions or photo or something) - * has been updated. Grab a fresh copy and sync it. - * - * The difference between refresh and force_refresh is that force_refresh - * unconditionally creates a directory update record, even if no changes were - * detected upon processing. - * - * @param array $sender - * @param array $recipients - * - * @return json_return_and_die() - */ - static function reply_refresh($sender, $recipients,$hub,$force) { - $ret = array('success' => false); + /** + * @brief Remote channel info (such as permissions or photo or something) + * has been updated. Grab a fresh copy and sync it. + * + * The difference between refresh and force_refresh is that force_refresh + * unconditionally creates a directory update record, even if no changes were + * detected upon processing. + * + * @param array $sender + * @param array $recipients + * + * @return json_return_and_die() + */ - if($recipients) { + public static function reply_refresh($sender, $recipients, $hub, $force) + { + $ret = array('success' => false); - // This would be a permissions update, typically for one connection + if ($recipients) { + // This would be a permissions update, typically for one connection - foreach ($recipients as $recip) { - $r = q("select channel.*,xchan.* from channel + foreach ($recipients as $recip) { + $r = q( + "select channel.*,xchan.* from channel left join xchan on channel_hash = xchan_hash where channel_hash ='%s' limit 1", - dbesc($recip) - ); + dbesc($recip) + ); - $x = Libzot::refresh( [ 'hubloc_id_url' => $hub['hubloc_id_url'] ], $r[0], $force ); - } - } - else { + $x = Libzot::refresh(['hubloc_id_url' => $hub['hubloc_id_url']], $r[0], $force); + } + } else { + // system wide refresh + $x = Libzot::refresh(['hubloc_id_url' => $hub['hubloc_id_url']], null, $force); + } - // system wide refresh - $x = Libzot::refresh( [ 'hubloc_id_url' => $hub['hubloc_id_url'] ], null, $force ); - } - - $ret['success'] = true; - return $ret; - } + $ret['success'] = true; + return $ret; + } - static function rekey_request($sender,$data,$hub) { + public static function rekey_request($sender, $data, $hub) + { - $ret = array('success' => false); + $ret = array('success' => false); - // newsig is newkey signed with oldkey + // newsig is newkey signed with oldkey - // The original xchan will remain. In Zot/Receiver we will have imported the new xchan and hubloc to verify - // the packet authenticity. What we will do now is verify that the keychange operation was signed by the - // oldkey, and if so change all the abook, abconfig, group, and permission elements which reference the - // old xchan_hash. + // The original xchan will remain. In Zot/Receiver we will have imported the new xchan and hubloc to verify + // the packet authenticity. What we will do now is verify that the keychange operation was signed by the + // oldkey, and if so change all the abook, abconfig, group, and permission elements which reference the + // old xchan_hash. - if((! $data['old_key']) && (! $data['new_key']) && (! $data['new_sig'])) - return $ret; + if ((!$data['old_key']) && (!$data['new_key']) && (!$data['new_sig'])) { + return $ret; + } - $old = null; + $old = null; - if(Libzot::verify($data['old_guid'],$data['old_guid_sig'],$data['old_key'])) { - $oldhash = make_xchan_hash($data['old_guid'],$data['old_key']); - $old = q("select * from xchan where xchan_hash = '%s' limit 1", - dbesc($oldhash) - ); - } - else - return $ret; + if (Libzot::verify($data['old_guid'], $data['old_guid_sig'], $data['old_key'])) { + $oldhash = make_xchan_hash($data['old_guid'], $data['old_key']); + $old = q( + "select * from xchan where xchan_hash = '%s' limit 1", + dbesc($oldhash) + ); + } else { + return $ret; + } - if(! $old) { - return $ret; - } + if (!$old) { + return $ret; + } - $xchan = $old[0]; + $xchan = $old[0]; - if(! Libzot::verify($data['new_key'],$data['new_sig'],$xchan['xchan_pubkey'])) { - return $ret; - } + if (!Libzot::verify($data['new_key'], $data['new_sig'], $xchan['xchan_pubkey'])) { + return $ret; + } - $r = q("select * from xchan where xchan_hash = '%s' limit 1", - dbesc($sender) - ); + $r = q( + "select * from xchan where xchan_hash = '%s' limit 1", + dbesc($sender) + ); - $newxchan = $r[0]; + $newxchan = $r[0]; - // @todo - // if ! $update create a linked identity + // @todo + // if ! $update create a linked identity - xchan_change_key($xchan,$newxchan,$data); + xchan_change_key($xchan, $newxchan, $data); - $ret['success'] = true; - return $ret; - } + $ret['success'] = true; + return $ret; + } - /** - * @brief - * - * @param array $sender - * @param array $recipients - * - * return json_return_and_die() - */ + /** + * @brief + * + * @param array $sender + * @param array $recipients + * + * return json_return_and_die() + */ - static function reply_purge($sender, $recipients, $hub) { + public static function reply_purge($sender, $recipients, $hub) + { - $ret = array('success' => false); + $ret = array('success' => false); - if (! $sender) { - return $ret; - } + if (!$sender) { + return $ret; + } - if ($recipients) { - // basically this means "unfriend" - foreach ($recipients as $recip) { - $channel = q("select channel.*,xchan.* from channel + if ($recipients) { + // basically this means "unfriend" + foreach ($recipients as $recip) { + $channel = q( + "select channel.*,xchan.* from channel left join xchan on channel_hash = xchan_hash where channel_hash = '%s' limit 1", - dbesc($recip) - ); - if ($channel) { - $abook = q("select abook_id from abook where abook_channel = %d and abook_xchan = '%s' limit 1", - intval($channel[0]['channel_id']), - dbesc($sender) - ); - if ($abook) { - contact_remove($channel[0]['channel_id'],$abook[0]['abook_id']); - } - } - } - $ret['success'] = true; - } - else { + dbesc($recip) + ); + if ($channel) { + $abook = q( + "select abook_id from abook where abook_channel = %d and abook_xchan = '%s' limit 1", + intval($channel[0]['channel_id']), + dbesc($sender) + ); + if ($abook) { + contact_remove($channel[0]['channel_id'], $abook[0]['abook_id']); + } + } + } + $ret['success'] = true; + } else { + // Unfriend everybody - basically this means the channel has committed suicide - // Unfriend everybody - basically this means the channel has committed suicide + remove_all_xchan_resources($sender); - remove_all_xchan_resources($sender); - - $ret['success'] = true; - } - - return $ret; - } + $ret['success'] = true; + } + return $ret; + } } diff --git a/boot.php b/boot.php index 23850a9b1..d9e971311 100755 --- a/boot.php +++ b/boot.php @@ -126,7 +126,7 @@ define ( 'PNG_QUALITY', 8 ); /** * Language detection parameters */ -define ( 'LANGUAGE_DETECT_MIN_LENGTH', 128 ); +define ( 'LANGUAGE_DETECT_MIN_LENGTH', 64 ); define ( 'LANGUAGE_DETECT_MIN_CONFIDENCE', 0.01 ); @@ -654,6 +654,9 @@ function sys_boot() { App::$install = ((file_exists('.htconfig.php') && filesize('.htconfig.php')) ? false : true); + $db_host = $db_user = $db_pass = $db_data = EMPTY_STR; + $db_port = $db_type = 0; + @include('.htconfig.php'); // allow somebody to set some initial settings @@ -662,7 +665,7 @@ function sys_boot() { @include('.htpreconfig.php'); } - if (array_key_exists('default_timezone',get_defined_vars())) { + if (isset($default_timezone)) { App::$config['system']['timezone'] = $default_timezone; } @@ -890,11 +893,11 @@ class App { // normally self::$hostname (also scheme and port) will be filled in during startup. // Set it manually from $_SERVER variables only if it wasn't. - + if (! self::$hostname) { self::$hostname = punify(get_host()); self::$scheme = 'http'; - + if (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS']) { self::$scheme = 'https'; } @@ -919,7 +922,7 @@ class App { // Rewrite rules on the server will convert incoming paths to a request parameter. // Strip this path information from our stored copy of the query_string, in case // we need to re-use the rest of the original query. - + if (isset($_SERVER['QUERY_STRING']) && substr($_SERVER['QUERY_STRING'], 0, 4) === "req=") { self::$query_string = str_replace(['<','>'],['<','>'],substr($_SERVER['QUERY_STRING'], 4)); // removing leading '/' - maybe a nginx problem @@ -933,7 +936,7 @@ class App { // Here is where start breaking out the URL path information to both route the // web request based on the leading path component, and also to use remaining // path components as C-style arguments to our individual controller modules. - + if (isset($_GET['req'])) { self::$cmd = escape_tags(trim($_GET['req'],'/\\')); } @@ -1006,7 +1009,7 @@ class App { * register template engines (probably just smarty, but this can be extended) */ - self::register_template_engine(get_class(new SmartyTemplate)); + self::register_template_engine(get_class(new SmartyTemplate())); } @@ -1254,7 +1257,7 @@ class App { } } if (! $name) { - echo "template engine $class cannot be registered without a name.\n"; + echo "template engine $class cannot be registered without a name.\n"; killme(); } self::$template_engines[$name] = $class; @@ -1267,7 +1270,7 @@ class App { * * @param string $name Template engine name * - * @return object Template Engine instance + * @return void Template Engine instance */ public static function template_engine($name = '') { if ($name !== '') { @@ -1286,7 +1289,7 @@ class App { } else { $class = self::$template_engines[$template_engine]; - $obj = new $class; + $obj = new $class(); self::$template_engine_instance[$template_engine] = $obj; return $obj; } @@ -1294,7 +1297,7 @@ class App { // If we fell through to this step, it is considered fatal. - echo "template engine $template_engine is not registered!\n"; + echo "template engine $template_engine is not registered!\n"; killme(); } @@ -1415,8 +1418,9 @@ function z_root() { * @return string */ function absurl($path) { - if (strpos($path, '/') === 0) + if (strpos($path, '/') === 0) { return z_path() . $path; + } return $path; } @@ -1433,7 +1437,7 @@ function os_mkdir($path, $mode = 0777, $recursive = false) { * @brief Recursively delete a directory. * * @param string $path - * @return boolean + * @return bool */ function rrmdir($path) { if (is_dir($path) === true) { @@ -1454,7 +1458,7 @@ function rrmdir($path) { /** * @brief Function to check if request was an AJAX (xmlhttprequest) request. * - * @return boolean + * @return bool */ function is_ajax() { return (!empty($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest'); @@ -1658,10 +1662,10 @@ function fix_system_urls($oldurl, $newurl) { * on the value of App::$config['system']['register_policy']. * Returns the complete html for inserting into the page * - * @param boolean $register (optional) default false + * @param bool $register (optional) default false * @param string $form_id (optional) default \e main-login - * @param boolean $hiddens (optional) default false - * @param boolean $login_page (optional) default true + * @param bool $hiddens (optional) default false + * @param bool $login_page (optional) default true * @return string Parsed HTML code. */ function login($register = false, $form_id = 'main-login', $hiddens = false, $login_page = true) { @@ -1998,7 +2002,6 @@ function check_php_cli() { logger('PHP command line interpreter not found.'); throw new Exception('interpreter not found.'); - return false; } @@ -2069,7 +2072,7 @@ function load_contact_links($uid) { // logger('load_contact_links'); - $r = q("SELECT abook_id, abook_flags, abook_self, abook_incl, abook_excl, abook_my_perms, abook_their_perms, xchan_hash, xchan_photo_m, xchan_name, xchan_url, xchan_network from abook left join xchan on abook_xchan = xchan_hash where abook_channel = %d ", + $r = q("SELECT abook_id, abook_flags, abook_self, abook_incl, abook_excl, abook_my_perms, abook_their_perms, xchan_hash, xchan_photo_m, xchan_name, xchan_url, xchan_addr, xchan_network from abook left join xchan on abook_xchan = xchan_hash where abook_channel = %d ", intval($uid) ); if($r) { @@ -2185,6 +2188,7 @@ function get_custom_nav($navname) { if (! $navname) return App::$page['nav']; // load custom nav menu by name here + return EMPTY_STR; } /** @@ -2505,7 +2509,7 @@ function z_get_temp_dir() { if(! $temp_dir) $temp_dir = sys_get_temp_dir(); - return $upload_dir; + return $temp_dir; } @@ -2567,19 +2571,19 @@ function check_cron_broken() { if(! $t) { // never checked before. Start the timer. set_config('system','lastcroncheck',datetime_convert()); - return; + return true; } if($t > datetime_convert('UTC','UTC','now - 3 days')) { // Wait for 3 days before we do anything so as not to swamp the admin with messages - return; + return true; } set_config('system','lastcroncheck',datetime_convert()); if(($d) && ($d > datetime_convert('UTC','UTC','now - 3 days'))) { // Scheduled tasks have run successfully in the last 3 days. - return; + return true; } return z_mail( @@ -2602,8 +2606,8 @@ function check_cron_broken() { /** * @brief * - * @param boolean $allow_account (optional) default false - * @return boolean + * @param bool $allow_account (optional) default false + * @return bool */ function observer_prohibited($allow_account = false) { if($allow_account) { diff --git a/composer.json b/composer.json index 66d8e3c30..79dc91610 100644 --- a/composer.json +++ b/composer.json @@ -44,8 +44,9 @@ "masterminds/html5": "^2.6", "forkawesome/fork-awesome": "^1.1", "p3k/emoji-detector": "^0.2.1", - "phpseclib/phpseclib": "^2.0" - }, + "phpseclib/phpseclib": "^2.0", + "ext-json": "*" + }, "require-dev" : { "phpunit/phpunit" : "@stable", "behat/behat" : "@stable", diff --git a/doc/develop/en/Creating_Addons.mc b/doc/develop/en/Creating_Addons.mc index c6be35930..0e7a9f22e 100644 --- a/doc/develop/en/Creating_Addons.mc +++ b/doc/develop/en/Creating_Addons.mc @@ -123,7 +123,7 @@ Let's go ahead and add some code to implement our post_local hook handler. $cities = []; $zones = timezone_identifiers_list(); foreach ($zones as $zone) { - if ((strpos($zone,'/')) && (! stristr($zone,'US/')) && (! stristr($zone,'Etc/'))) { + if ((strpos($zone,'/')) && (stristr($zone,'US/') === false) && (stristr($zone,'Etc/') === false)) { $cities[] = str_replace('_', ' ',substr($zone,strrpos($zone,'/') + 1)); } } diff --git a/include/account.php b/include/account.php index 8f279a7b8..a26e21de1 100644 --- a/include/account.php +++ b/include/account.php @@ -8,490 +8,508 @@ use Zotlabs\Lib\Crypto; use Zotlabs\Lib\System; -function get_account_by_id($account_id) { - $r = q("select * from account where account_id = %d", - intval($account_id) - ); - return (($r) ? array_shift($r) : false); +function get_account_by_id($account_id) +{ + $r = q( + "select * from account where account_id = %d", + intval($account_id) + ); + return (($r) ? array_shift($r) : false); } -function check_account_email($email) { +function check_account_email($email) +{ - $email = punify($email); - $result = [ 'error' => false, 'message' => '' ]; + $email = punify($email); + $result = [ 'error' => false, 'message' => '' ]; - // Caution: empty email isn't counted as an error in this function. - // Check for empty value separately. + // Caution: empty email isn't counted as an error in this function. + // Check for empty value separately. - if (! strlen($email)) { - return $result; - } + if (! strlen($email)) { + return $result; + } - if (! validate_email($email)) { - $result['message'] .= t('Not a valid email address') . EOL; - } - elseif (! allowed_email($email)) { - $result['message'] = t('Your email domain is not among those allowed on this site'); - } - else { - $r = q("select account_email from account where account_email = '%s' limit 1", - dbesc($email) - ); - if ($r) { - $result['message'] .= t('Your email address is already registered at this site.'); - } - } - if ($result['message']) { - $result['error'] = true; - } + if (! validate_email($email)) { + $result['message'] .= t('Not a valid email address') . EOL; + } elseif (! allowed_email($email)) { + $result['message'] = t('Your email domain is not among those allowed on this site'); + } else { + $r = q( + "select account_email from account where account_email = '%s' limit 1", + dbesc($email) + ); + if ($r) { + $result['message'] .= t('Your email address is already registered at this site.'); + } + } + if ($result['message']) { + $result['error'] = true; + } - $arr = array('email' => $email, 'result' => $result); - call_hooks('check_account_email', $arr); + $arr = array('email' => $email, 'result' => $result); + call_hooks('check_account_email', $arr); - return $arr['result']; + return $arr['result']; } -function check_account_password($password) { - $result = [ 'error' => false, 'message' => '' ]; +function check_account_password($password) +{ + $result = [ 'error' => false, 'message' => '' ]; - // The only validation we perform by default is pure Javascript to - // check minimum length and that both entered passwords match. - // Use hooked functions to perform complexity requirement checks. + // The only validation we perform by default is pure Javascript to + // check minimum length and that both entered passwords match. + // Use hooked functions to perform complexity requirement checks. - $arr = [ 'password' => $password, 'result' => $result ]; - call_hooks('check_account_password', $arr); + $arr = [ 'password' => $password, 'result' => $result ]; + call_hooks('check_account_password', $arr); - return $arr['result']; + return $arr['result']; } -function check_account_invite($invite_code) { - $result = [ 'error' => false, 'message' => '' ]; +function check_account_invite($invite_code) +{ + $result = [ 'error' => false, 'message' => '' ]; - $using_invites = get_config('system','invitation_only'); + $using_invites = get_config('system', 'invitation_only'); - if ($using_invites && defined('INVITE_WORKING')) { - if (! $invite_code) { - $result['message'] .= t('An invitation is required.') . EOL; - } - $r = q("select * from register where hash = '%s' limit 1", dbesc($invite_code)); - if (! $r) { - $result['message'] .= t('Invitation could not be verified.') . EOL; - } - } - if (strlen($result['message'])) { - $result['error'] = true; - } + if ($using_invites && defined('INVITE_WORKING')) { + if (! $invite_code) { + $result['message'] .= t('An invitation is required.') . EOL; + } + $r = q("select * from register where hash = '%s' limit 1", dbesc($invite_code)); + if (! $r) { + $result['message'] .= t('Invitation could not be verified.') . EOL; + } + } + if (strlen($result['message'])) { + $result['error'] = true; + } - $arr = [ 'invite_code' => $invite_code, 'result' => $result ]; - call_hooks('check_account_invite', $arr); + $arr = [ 'invite_code' => $invite_code, 'result' => $result ]; + call_hooks('check_account_invite', $arr); - return $arr['result']; + return $arr['result']; } -function check_account_admin($arr) { - if (is_site_admin()) { - return true; - } - $admin_email = trim(get_config('system','admin_email','')); - if (strlen($admin_email) && $admin_email === trim($arr['email'])) { - return true; - } - return false; +function check_account_admin($arr) +{ + if (is_site_admin()) { + return true; + } + $admin_email = trim(get_config('system', 'admin_email', '')); + if (strlen($admin_email) && $admin_email === trim($arr['email'])) { + return true; + } + return false; } -function account_total() { - $r = q("select account_id from account where true"); - // Distinguish between an empty array and an error - if (is_array($r)) { - return count($r); - } - return false; +function account_total() +{ + $r = q("select account_id from account where true"); + // Distinguish between an empty array and an error + if (is_array($r)) { + return count($r); + } + return false; } -function account_store_lowlevel($arr) { +function account_store_lowlevel($arr) +{ $store = [ - 'account_parent' => ((array_key_exists('account_parent',$arr)) ? $arr['account_parent'] : '0'), - 'account_default_channel' => ((array_key_exists('account_default_channel',$arr)) ? $arr['account_default_channel'] : '0'), - 'account_salt' => ((array_key_exists('account_salt',$arr)) ? $arr['account_salt'] : ''), - 'account_password' => ((array_key_exists('account_password',$arr)) ? $arr['account_password'] : ''), - 'account_email' => ((array_key_exists('account_email',$arr)) ? $arr['account_email'] : ''), - 'account_external' => ((array_key_exists('account_external',$arr)) ? $arr['account_external'] : ''), - 'account_language' => ((array_key_exists('account_language',$arr)) ? $arr['account_language'] : 'en'), - 'account_created' => ((array_key_exists('account_created',$arr)) ? $arr['account_created'] : '0001-01-01 00:00:00'), - 'account_lastlog' => ((array_key_exists('account_lastlog',$arr)) ? $arr['account_lastlog'] : '0001-01-01 00:00:00'), - 'account_flags' => ((array_key_exists('account_flags',$arr)) ? $arr['account_flags'] : '0'), - 'account_roles' => ((array_key_exists('account_roles',$arr)) ? $arr['account_roles'] : '0'), - 'account_reset' => ((array_key_exists('account_reset',$arr)) ? $arr['account_reset'] : ''), - 'account_expires' => ((array_key_exists('account_expires',$arr)) ? $arr['account_expires'] : '0001-01-01 00:00:00'), - 'account_expire_notified' => ((array_key_exists('account_expire_notified',$arr)) ? $arr['account_expire_notified'] : '0001-01-01 00:00:00'), - 'account_service_class' => ((array_key_exists('account_service_class',$arr)) ? $arr['account_service_class'] : ''), - 'account_level' => ((array_key_exists('account_level',$arr)) ? $arr['account_level'] : '0'), - 'account_password_changed' => ((array_key_exists('account_password_changed',$arr)) ? $arr['account_password_changed'] : '0001-01-01 00:00:00') - ]; - - return create_table_from_array('account',$store); + 'account_parent' => ((array_key_exists('account_parent', $arr)) ? $arr['account_parent'] : '0'), + 'account_default_channel' => ((array_key_exists('account_default_channel', $arr)) ? $arr['account_default_channel'] : '0'), + 'account_salt' => ((array_key_exists('account_salt', $arr)) ? $arr['account_salt'] : ''), + 'account_password' => ((array_key_exists('account_password', $arr)) ? $arr['account_password'] : ''), + 'account_email' => ((array_key_exists('account_email', $arr)) ? $arr['account_email'] : ''), + 'account_external' => ((array_key_exists('account_external', $arr)) ? $arr['account_external'] : ''), + 'account_language' => ((array_key_exists('account_language', $arr)) ? $arr['account_language'] : 'en'), + 'account_created' => ((array_key_exists('account_created', $arr)) ? $arr['account_created'] : '0001-01-01 00:00:00'), + 'account_lastlog' => ((array_key_exists('account_lastlog', $arr)) ? $arr['account_lastlog'] : '0001-01-01 00:00:00'), + 'account_flags' => ((array_key_exists('account_flags', $arr)) ? $arr['account_flags'] : '0'), + 'account_roles' => ((array_key_exists('account_roles', $arr)) ? $arr['account_roles'] : '0'), + 'account_reset' => ((array_key_exists('account_reset', $arr)) ? $arr['account_reset'] : ''), + 'account_expires' => ((array_key_exists('account_expires', $arr)) ? $arr['account_expires'] : '0001-01-01 00:00:00'), + 'account_expire_notified' => ((array_key_exists('account_expire_notified', $arr)) ? $arr['account_expire_notified'] : '0001-01-01 00:00:00'), + 'account_service_class' => ((array_key_exists('account_service_class', $arr)) ? $arr['account_service_class'] : ''), + 'account_level' => ((array_key_exists('account_level', $arr)) ? $arr['account_level'] : '0'), + 'account_password_changed' => ((array_key_exists('account_password_changed', $arr)) ? $arr['account_password_changed'] : '0001-01-01 00:00:00') + ]; + return create_table_from_array('account', $store); } -function create_account($arr) { +function create_account($arr) +{ - // Required: { email, password } + // Required: { email, password } - $result = [ 'success' => false, 'email' => '', 'password' => '', 'message' => '' ]; + $result = [ 'success' => false, 'email' => '', 'password' => '', 'message' => '' ]; - $invite_code = ((isset($arr['invite_code'])) ? notags(trim($arr['invite_code'])) : ''); - $email = ((isset($arr['email'])) ? notags(punify(trim($arr['email']))) : ''); - $password = ((isset($arr['password'])) ? trim($arr['password']) : ''); - $password2 = ((isset($arr['password2'])) ? trim($arr['password2']) : ''); - $parent = ((isset($arr['parent'])) ? intval($arr['parent']) : 0 ); - $flags = ((isset($arr['account_flags'])) ? intval($arr['account_flags']) : ACCOUNT_OK); - $roles = ((isset($arr['account_roles'])) ? intval($arr['account_roles']) : 0 ); - $expires = ((isset($arr['expires'])) ? intval($arr['expires']) : NULL_DATE); + $invite_code = ((isset($arr['invite_code'])) ? notags(trim($arr['invite_code'])) : ''); + $email = ((isset($arr['email'])) ? notags(punify(trim($arr['email']))) : ''); + $password = ((isset($arr['password'])) ? trim($arr['password']) : ''); + $password2 = ((isset($arr['password2'])) ? trim($arr['password2']) : ''); + $parent = ((isset($arr['parent'])) ? intval($arr['parent']) : 0 ); + $flags = ((isset($arr['account_flags'])) ? intval($arr['account_flags']) : ACCOUNT_OK); + $roles = ((isset($arr['account_roles'])) ? intval($arr['account_roles']) : 0 ); + $expires = ((isset($arr['expires'])) ? intval($arr['expires']) : NULL_DATE); - $default_service_class = get_config('system','default_service_class', EMPTY_STR); + $default_service_class = get_config('system', 'default_service_class', EMPTY_STR); - if (! ($email && $password)) { - $result['message'] = t('Please enter the required information.'); - return $result; - } + if (! ($email && $password)) { + $result['message'] = t('Please enter the required information.'); + return $result; + } - // prevent form hackery + // prevent form hackery - if (($roles & ACCOUNT_ROLE_ADMIN) && (! check_account_admin($arr))) { - $roles = $roles - ACCOUNT_ROLE_ADMIN; - } + if (($roles & ACCOUNT_ROLE_ADMIN) && (! check_account_admin($arr))) { + $roles = $roles - ACCOUNT_ROLE_ADMIN; + } - // allow the admin_email account to be admin, but only if it's the first account. + // allow the admin_email account to be admin, but only if it's the first account. - $c = account_total(); - if (($c === 0) && (check_account_admin($arr))) { - $roles |= ACCOUNT_ROLE_ADMIN; - } + $c = account_total(); + if (($c === 0) && (check_account_admin($arr))) { + $roles |= ACCOUNT_ROLE_ADMIN; + } - // Ensure that there is a host keypair. + // Ensure that there is a host keypair. - if ((! get_config('system', 'pubkey')) && (! get_config('system', 'prvkey'))) { - $hostkey = Crypto::new_keypair(4096); - set_config('system', 'pubkey', $hostkey['pubkey']); - set_config('system', 'prvkey', $hostkey['prvkey']); - } + if ((! get_config('system', 'pubkey')) && (! get_config('system', 'prvkey'))) { + $hostkey = Crypto::new_keypair(4096); + set_config('system', 'pubkey', $hostkey['pubkey']); + set_config('system', 'prvkey', $hostkey['prvkey']); + } - $invite_result = check_account_invite($invite_code); - if ($invite_result['error']) { - $result['message'] = $invite_result['message']; - return $result; - } + $invite_result = check_account_invite($invite_code); + if ($invite_result['error']) { + $result['message'] = $invite_result['message']; + return $result; + } - $email_result = check_account_email($email); + $email_result = check_account_email($email); - if ($email_result['error']) { - $result['message'] = $email_result['message']; - return $result; - } + if ($email_result['error']) { + $result['message'] = $email_result['message']; + return $result; + } - $password_result = check_account_password($password); + $password_result = check_account_password($password); - if ($password_result['error']) { - $result['message'] = $password_result['message']; - return $result; - } + if ($password_result['error']) { + $result['message'] = $password_result['message']; + return $result; + } - $salt = random_string(32); - $password_encoded = hash('whirlpool', $salt . $password); + $salt = random_string(32); + $password_encoded = hash('whirlpool', $salt . $password); - $r = account_store_lowlevel( - [ - 'account_parent' => intval($parent), - 'account_salt' => $salt, - 'account_password' => $password_encoded, - 'account_email' => $email, - 'account_language' => get_best_language(), - 'account_created' => datetime_convert(), - 'account_flags' => intval($flags), - 'account_roles' => intval($roles), - 'account_expires' => $expires, - 'account_service_class' => $default_service_class - ] - ); - if (! $r) { - logger('create_account: DB INSERT failed.'); - $result['message'] = t('Failed to store account information.'); - return($result); - } + $r = account_store_lowlevel( + [ + 'account_parent' => intval($parent), + 'account_salt' => $salt, + 'account_password' => $password_encoded, + 'account_email' => $email, + 'account_language' => get_best_language(), + 'account_created' => datetime_convert(), + 'account_flags' => intval($flags), + 'account_roles' => intval($roles), + 'account_expires' => $expires, + 'account_service_class' => $default_service_class + ] + ); + if (! $r) { + logger('create_account: DB INSERT failed.'); + $result['message'] = t('Failed to store account information.'); + return($result); + } - $r = q("select * from account where account_email = '%s' and account_password = '%s' limit 1", - dbesc($email), - dbesc($password_encoded) - ); - if ($r && is_array($r) && count($r)) { - $result['account'] = $r[0]; - } - else { - logger('create_account: could not retrieve newly created account'); - } + $r = q( + "select * from account where account_email = '%s' and account_password = '%s' limit 1", + dbesc($email), + dbesc($password_encoded) + ); + if ($r && is_array($r) && count($r)) { + $result['account'] = $r[0]; + } else { + logger('create_account: could not retrieve newly created account'); + } - // Set the parent record to the current record_id if no parent was provided + // Set the parent record to the current record_id if no parent was provided - if (! $parent) { - $r = q("update account set account_parent = %d where account_id = %d", - intval($result['account']['account_id']), - intval($result['account']['account_id']) - ); - if (! $r) { - logger('create_account: failed to set parent'); - } - $result['account']['parent'] = $result['account']['account_id']; - } + if (! $parent) { + $r = q( + "update account set account_parent = %d where account_id = %d", + intval($result['account']['account_id']), + intval($result['account']['account_id']) + ); + if (! $r) { + logger('create_account: failed to set parent'); + } + $result['account']['parent'] = $result['account']['account_id']; + } - $result['success'] = true; - $result['email'] = $email; - $result['password'] = $password; + $result['success'] = true; + $result['email'] = $email; + $result['password'] = $password; - call_hooks('register_account',$result); + call_hooks('register_account', $result); - return $result; + return $result; } -function verify_email_address($arr) { +function verify_email_address($arr) +{ - if (array_key_exists('resend',$arr)) { - $email = $arr['email']; - $a = q("select * from account where account_email = '%s' limit 1", - dbesc($arr['email']) - ); - if (! ($a && ($a[0]['account_flags'] & ACCOUNT_UNVERIFIED))) { - return false; - } - $account = array_shift($a); - $v = q("select * from register where uid = %d and password = 'verify' limit 1", - intval($account['account_id']) - ); - if ($v) { - $hash = $v[0]['hash']; - } - else { - return false; - } - } - else { - $hash = random_string(24); + if (array_key_exists('resend', $arr)) { + $email = $arr['email']; + $a = q( + "select * from account where account_email = '%s' limit 1", + dbesc($arr['email']) + ); + if (! ($a && ($a[0]['account_flags'] & ACCOUNT_UNVERIFIED))) { + return false; + } + $account = array_shift($a); + $v = q( + "select * from register where uid = %d and password = 'verify' limit 1", + intval($account['account_id']) + ); + if ($v) { + $hash = $v[0]['hash']; + } else { + return false; + } + } else { + $hash = random_string(24); - $r = q("INSERT INTO register ( hash, created, uid, password, lang ) VALUES ( '%s', '%s', %d, '%s', '%s' ) ", - dbesc($hash), - dbesc(datetime_convert()), - intval($arr['account']['account_id']), - dbesc('verify'), - dbesc($arr['account']['account_language']) - ); - $account = $arr['account']; - } + $r = q( + "INSERT INTO register ( hash, created, uid, password, lang ) VALUES ( '%s', '%s', %d, '%s', '%s' ) ", + dbesc($hash), + dbesc(datetime_convert()), + intval($arr['account']['account_id']), + dbesc('verify'), + dbesc($arr['account']['account_language']) + ); + $account = $arr['account']; + } - push_lang(($account['account_language']) ? $account['account_language'] : 'en'); + push_lang(($account['account_language']) ? $account['account_language'] : 'en'); - $email_msg = replace_macros(get_intltext_template('register_verify_member.tpl'), - [ - '$sitename' => System::get_site_name(), - '$siteurl' => z_root(), - '$email' => $arr['email'], - '$uid' => $account['account_id'], - '$hash' => $hash, - '$details' => $details - ] - ); + $email_msg = replace_macros( + get_intltext_template('register_verify_member.tpl'), + [ + '$sitename' => System::get_site_name(), + '$siteurl' => z_root(), + '$email' => $arr['email'], + '$uid' => $account['account_id'], + '$hash' => $hash, + '$details' => $details + ] + ); - $res = z_mail( - [ - 'toEmail' => $arr['email'], - 'messageSubject' => sprintf( t('Registration confirmation for %s'), System::get_site_name()), - 'textVersion' => $email_msg, - ] - ); + $res = z_mail( + [ + 'toEmail' => $arr['email'], + 'messageSubject' => sprintf(t('Registration confirmation for %s'), System::get_site_name()), + 'textVersion' => $email_msg, + ] + ); - pop_lang(); + pop_lang(); - if ($res) { - $delivered ++; - } - else { - logger('send_reg_approval_email: failed to account_id: ' . $arr['account']['account_id']); - } - return $res; + if ($res) { + $delivered ++; + } else { + logger('send_reg_approval_email: failed to account_id: ' . $arr['account']['account_id']); + } + return $res; } -function send_reg_approval_email($arr) { +function send_reg_approval_email($arr) +{ - $r = q("select * from account where (account_roles & %d) >= 4096", - intval(ACCOUNT_ROLE_ADMIN) - ); - if (! ($r && is_array($r) && count($r))) { - return false; - } + $r = q( + "select * from account where (account_roles & %d) >= 4096", + intval(ACCOUNT_ROLE_ADMIN) + ); + if (! ($r && is_array($r) && count($r))) { + return false; + } - $admins = []; + $admins = []; - foreach ($r as $rr) { - if (strlen($rr['account_email'])) { - $admins[] = [ 'email' => $rr['account_email'], 'lang' => $rr['account_lang'] ]; - } - } + foreach ($r as $rr) { + if (strlen($rr['account_email'])) { + $admins[] = [ 'email' => $rr['account_email'], 'lang' => $rr['account_lang'] ]; + } + } - if (! count($admins)) { - return false; - } + if (! count($admins)) { + return false; + } - $hash = random_string(); + $hash = random_string(); - $r = q("INSERT INTO register ( hash, created, uid, password, lang ) VALUES ( '%s', '%s', %d, '%s', '%s' ) ", - dbesc($hash), - dbesc(datetime_convert()), - intval($arr['account']['account_id']), - dbesc(''), - dbesc($arr['account']['account_language']) - ); + $r = q( + "INSERT INTO register ( hash, created, uid, password, lang ) VALUES ( '%s', '%s', %d, '%s', '%s' ) ", + dbesc($hash), + dbesc(datetime_convert()), + intval($arr['account']['account_id']), + dbesc(''), + dbesc($arr['account']['account_language']) + ); - $ip = ((isset($_SERVER['REMOTE_ADDR'])) ? $_SERVER['REMOTE_ADDR'] : EMPTY_STR); + $ip = ((isset($_SERVER['REMOTE_ADDR'])) ? $_SERVER['REMOTE_ADDR'] : EMPTY_STR); - $details = (($ip) ? $ip . ' [' . gethostbyaddr($ip) . ']' : '[unknown or stealth IP]'); + $details = (($ip) ? $ip . ' [' . gethostbyaddr($ip) . ']' : '[unknown or stealth IP]'); - $delivered = 0; + $delivered = 0; - foreach ($admins as $admin) { - if (strlen($admin['lang'])) { - push_lang($admin['lang']); - } - else { - push_lang('en'); - } + foreach ($admins as $admin) { + if (strlen($admin['lang'])) { + push_lang($admin['lang']); + } else { + push_lang('en'); + } - $email_msg = replace_macros(get_intltext_template('register_verify_eml.tpl'), [ - '$sitename' => get_config('system','sitename'), - '$siteurl' => z_root(), - '$email' => $arr['email'], - '$uid' => $arr['account']['account_id'], - '$hash' => $hash, - '$details' => $details - ]); + $email_msg = replace_macros(get_intltext_template('register_verify_eml.tpl'), [ + '$sitename' => get_config('system', 'sitename'), + '$siteurl' => z_root(), + '$email' => $arr['email'], + '$uid' => $arr['account']['account_id'], + '$hash' => $hash, + '$details' => $details + ]); - $res = z_mail( - [ - 'toEmail' => $admin['email'], - 'messageSubject' => sprintf( t('Registration request at %s'), get_config('system','sitename')), - 'textVersion' => $email_msg, - ] - ); + $res = z_mail( + [ + 'toEmail' => $admin['email'], + 'messageSubject' => sprintf(t('Registration request at %s'), get_config('system', 'sitename')), + 'textVersion' => $email_msg, + ] + ); - if ($res) { - $delivered ++; - } - else { - logger('send_reg_approval_email: failed to ' . $admin['email'] . 'account_id: ' . $arr['account']['account_id']); - } - - pop_lang(); - } + if ($res) { + $delivered ++; + } else { + logger('send_reg_approval_email: failed to ' . $admin['email'] . 'account_id: ' . $arr['account']['account_id']); + } + + pop_lang(); + } - return ($delivered ? true : false); + return ($delivered ? true : false); } -function send_register_success_email($email,$password) { +function send_register_success_email($email, $password) +{ - $email_msg = replace_macros(get_intltext_template('register_open_eml.tpl'), [ - '$sitename' => System::get_site_name(), - '$siteurl' => z_root(), - '$email' => $email, - '$password' => t('your registration password'), - ]); + $email_msg = replace_macros(get_intltext_template('register_open_eml.tpl'), [ + '$sitename' => System::get_site_name(), + '$siteurl' => z_root(), + '$email' => $email, + '$password' => t('your registration password'), + ]); - $res = z_mail( - [ - 'toEmail' => $email, - 'messageSubject' => sprintf( t('Registration details for %s'), System::get_site_name()), - 'textVersion' => $email_msg, - ] - ); + $res = z_mail( + [ + 'toEmail' => $email, + 'messageSubject' => sprintf(t('Registration details for %s'), System::get_site_name()), + 'textVersion' => $email_msg, + ] + ); - return ($res ? true : false); + return ($res ? true : false); } /** * @brief Allows a user registration. * * @param string $hash - * @return array|boolean + * @return array|bool */ -function account_allow($hash) { +function account_allow($hash) +{ - $ret = array('success' => false); + $ret = array('success' => false); - $register = q("SELECT * FROM register WHERE hash = '%s' LIMIT 1", - dbesc($hash) - ); + $register = q( + "SELECT * FROM register WHERE hash = '%s' LIMIT 1", + dbesc($hash) + ); - if (! $register) { - return $ret; - } + if (! $register) { + return $ret; + } - $account = q("SELECT * FROM account WHERE account_id = %d LIMIT 1", - intval($register[0]['uid']) - ); + $account = q( + "SELECT * FROM account WHERE account_id = %d LIMIT 1", + intval($register[0]['uid']) + ); - if (! $account) { - return $ret; - } + if (! $account) { + return $ret; + } - $r = q("DELETE FROM register WHERE hash = '%s'", - dbesc($register[0]['hash']) - ); + $r = q( + "DELETE FROM register WHERE hash = '%s'", + dbesc($register[0]['hash']) + ); - $r = q("update account set account_flags = (account_flags & ~%d) where (account_flags & %d) > 0 and account_id = %d", - intval(ACCOUNT_BLOCKED), - intval(ACCOUNT_BLOCKED), - intval($register[0]['uid']) - ); - $r = q("update account set account_flags = (account_flags & ~%d) where (account_flags & %d) > 0 and account_id = %d", - intval(ACCOUNT_PENDING), - intval(ACCOUNT_PENDING), - intval($register[0]['uid']) - ); + $r = q( + "update account set account_flags = (account_flags & ~%d) where (account_flags & %d) > 0 and account_id = %d", + intval(ACCOUNT_BLOCKED), + intval(ACCOUNT_BLOCKED), + intval($register[0]['uid']) + ); + $r = q( + "update account set account_flags = (account_flags & ~%d) where (account_flags & %d) > 0 and account_id = %d", + intval(ACCOUNT_PENDING), + intval(ACCOUNT_PENDING), + intval($register[0]['uid']) + ); - push_lang($register[0]['lang']); + push_lang($register[0]['lang']); - $email_tpl = get_intltext_template("register_open_eml.tpl"); - $email_msg = replace_macros($email_tpl, [ - '$sitename' => System::get_site_name(), - '$siteurl' => z_root(), - '$username' => $account[0]['account_email'], - '$email' => $account[0]['account_email'], - '$password' => '', - '$uid' => $account[0]['account_id'] - ]); + $email_tpl = get_intltext_template("register_open_eml.tpl"); + $email_msg = replace_macros($email_tpl, [ + '$sitename' => System::get_site_name(), + '$siteurl' => z_root(), + '$username' => $account[0]['account_email'], + '$email' => $account[0]['account_email'], + '$password' => '', + '$uid' => $account[0]['account_id'] + ]); - $res = z_mail( - [ - 'toEmail' => $account[0]['account_email'], - 'messageSubject' => sprintf( t('Registration details for %s'), System::get_site_name()), - 'textVersion' => $email_msg, - ] - ); + $res = z_mail( + [ + 'toEmail' => $account[0]['account_email'], + 'messageSubject' => sprintf(t('Registration details for %s'), System::get_site_name()), + 'textVersion' => $email_msg, + ] + ); - pop_lang(); + pop_lang(); - if (get_config('system','auto_channel_create')) { - auto_channel_create($register[0]['uid']); - } + if (get_config('system', 'auto_channel_create')) { + auto_channel_create($register[0]['uid']); + } - if ($res) { - info( t('Account approved.') . EOL ); - return true; - } + if ($res) { + info(t('Account approved.') . EOL); + return true; + } } @@ -503,156 +521,170 @@ function account_allow($hash) { * allowed to have friends on this system * * @param string $hash - * @return boolean + * @return bool */ -function account_deny($hash) { +function account_deny($hash) +{ - $register = q("SELECT * FROM register WHERE hash = '%s' LIMIT 1", - dbesc($hash) - ); + $register = q( + "SELECT * FROM register WHERE hash = '%s' LIMIT 1", + dbesc($hash) + ); - if(! $register) { - return false; - } + if (! $register) { + return false; + } - $account = q("SELECT account_id, account_email FROM account WHERE account_id = %d LIMIT 1", - intval($register[0]['uid']) - ); + $account = q( + "SELECT account_id, account_email FROM account WHERE account_id = %d LIMIT 1", + intval($register[0]['uid']) + ); - if (! $account) { - return false; - } + if (! $account) { + return false; + } - $r = q("DELETE FROM account WHERE account_id = %d", - intval($register[0]['uid']) - ); + $r = q( + "DELETE FROM account WHERE account_id = %d", + intval($register[0]['uid']) + ); - $r = q("DELETE FROM register WHERE id = %d", - intval($register[0]['id']) - ); - notice( sprintf(t('Registration revoked for %s'), $account[0]['account_email']) . EOL); - - return true; + $r = q( + "DELETE FROM register WHERE id = %d", + intval($register[0]['id']) + ); + notice(sprintf(t('Registration revoked for %s'), $account[0]['account_email']) . EOL); + return true; } // called from regver to activate an account from the email verification link -function account_approve($hash) { +function account_approve($hash) +{ - $ret = false; + $ret = false; - // Note: when the password in the register table is 'verify', the uid actually contains the account_id + // Note: when the password in the register table is 'verify', the uid actually contains the account_id - $register = q("SELECT * FROM register WHERE hash = '%s' and password = 'verify' LIMIT 1", - dbesc($hash) - ); + $register = q( + "SELECT * FROM register WHERE hash = '%s' and password = 'verify' LIMIT 1", + dbesc($hash) + ); - if (! $register) { - return $ret; - } + if (! $register) { + return $ret; + } - $account = q("SELECT * FROM account WHERE account_id = %d LIMIT 1", - intval($register[0]['uid']) - ); + $account = q( + "SELECT * FROM account WHERE account_id = %d LIMIT 1", + intval($register[0]['uid']) + ); - if (! $account) { - return $ret; - } + if (! $account) { + return $ret; + } - $r = q("DELETE FROM register WHERE hash = '%s' and password = 'verify'", - dbesc($register[0]['hash']) - ); + $r = q( + "DELETE FROM register WHERE hash = '%s' and password = 'verify'", + dbesc($register[0]['hash']) + ); - $r = q("update account set account_flags = (account_flags & ~%d) where (account_flags & %d)>0 and account_id = %d", - intval(ACCOUNT_BLOCKED), - intval(ACCOUNT_BLOCKED), - intval($register[0]['uid']) - ); - $r = q("update account set account_flags = (account_flags & ~%d) where (account_flags & %d)>0 and account_id = %d", - intval(ACCOUNT_PENDING), - intval(ACCOUNT_PENDING), - intval($register[0]['uid']) - ); - $r = q("update account set account_flags = (account_flags & ~%d) where (account_flags & %d)>0 and account_id = %d", - intval(ACCOUNT_UNVERIFIED), - intval(ACCOUNT_UNVERIFIED), - intval($register[0]['uid']) - ); + $r = q( + "update account set account_flags = (account_flags & ~%d) where (account_flags & %d)>0 and account_id = %d", + intval(ACCOUNT_BLOCKED), + intval(ACCOUNT_BLOCKED), + intval($register[0]['uid']) + ); + $r = q( + "update account set account_flags = (account_flags & ~%d) where (account_flags & %d)>0 and account_id = %d", + intval(ACCOUNT_PENDING), + intval(ACCOUNT_PENDING), + intval($register[0]['uid']) + ); + $r = q( + "update account set account_flags = (account_flags & ~%d) where (account_flags & %d)>0 and account_id = %d", + intval(ACCOUNT_UNVERIFIED), + intval(ACCOUNT_UNVERIFIED), + intval($register[0]['uid']) + ); - // get a fresh copy after we've modified it. + // get a fresh copy after we've modified it. - $account = q("SELECT * FROM account WHERE account_id = %d LIMIT 1", - intval($register[0]['uid']) - ); + $account = q( + "SELECT * FROM account WHERE account_id = %d LIMIT 1", + intval($register[0]['uid']) + ); - if (! $account) { - return $ret; - } + if (! $account) { + return $ret; + } - if (get_config('system','auto_channel_create')) { - auto_channel_create($register[0]['uid']); - } - else { - $_SESSION['login_return_url'] = 'new_channel'; - authenticate_success($account[0],null,true,true,false,true); - } + if (get_config('system', 'auto_channel_create')) { + auto_channel_create($register[0]['uid']); + } else { + $_SESSION['login_return_url'] = 'new_channel'; + authenticate_success($account[0], null, true, true, false, true); + } - return true; + return true; } /** * @brief Checks for accounts that have past their expiration date. * - * If the account has a service class which is not the site default, + * If the account has a service class which is not the site default, * the service class is reset to the site default and expiration reset to never. * If the account has no service class it is expired and subsequently disabled. * called from include/poller.php as a scheduled task. * * Reclaiming resources which are no longer within the service class limits is - * not the job of this function, but this can be implemented by plugin if desired. - * Default behaviour is to stop allowing additional resources to be consumed. + * not the job of this function, but this can be implemented by plugin if desired. + * Default behaviour is to stop allowing additional resources to be consumed. */ -function downgrade_accounts() { +function downgrade_accounts() +{ - $r = q("select * from account where not ( account_flags & %d ) > 0 + $r = q( + "select * from account where not ( account_flags & %d ) > 0 and account_expires > '%s' and account_expires < %s ", - intval(ACCOUNT_EXPIRED), - dbesc(NULL_DATE), - db_getfunc('UTC_TIMESTAMP') - ); + intval(ACCOUNT_EXPIRED), + dbesc(NULL_DATE), + db_getfunc('UTC_TIMESTAMP') + ); - if (! $r) { - return; - } + if (! $r) { + return; + } - $basic = get_config('system','default_service_class'); + $basic = get_config('system', 'default_service_class'); - foreach ($r as $rr) { - if (($basic) && ($rr['account_service_class']) && ($rr['account_service_class'] != $basic)) { - $x = q("UPDATE account set account_service_class = '%s', account_expires = '%s' + foreach ($r as $rr) { + if (($basic) && ($rr['account_service_class']) && ($rr['account_service_class'] != $basic)) { + $x = q( + "UPDATE account set account_service_class = '%s', account_expires = '%s' where account_id = %d", - dbesc($basic), - dbesc(NULL_DATE), - intval($rr['account_id']) - ); - $ret = [ 'account' => $rr ]; - call_hooks('account_downgrade', $ret ); - logger('downgrade_accounts: Account id ' . $rr['account_id'] . ' downgraded.'); - } - else { - $x = q("UPDATE account SET account_flags = (account_flags | %d) where account_id = %d", - intval(ACCOUNT_EXPIRED), - intval($rr['account_id']) - ); - $ret = [ 'account' => $rr ]; - call_hooks('account_downgrade', $ret); - logger('downgrade_accounts: Account id ' . $rr['account_id'] . ' expired.'); - } - } + dbesc($basic), + dbesc(NULL_DATE), + intval($rr['account_id']) + ); + $ret = [ 'account' => $rr ]; + call_hooks('account_downgrade', $ret); + logger('downgrade_accounts: Account id ' . $rr['account_id'] . ' downgraded.'); + } else { + $x = q( + "UPDATE account SET account_flags = (account_flags | %d) where account_id = %d", + intval(ACCOUNT_EXPIRED), + intval($rr['account_id']) + ); + $ret = [ 'account' => $rr ]; + call_hooks('account_downgrade', $ret); + logger('downgrade_accounts: Account id ' . $rr['account_id'] . ' expired.'); + } + } } @@ -677,23 +709,24 @@ function downgrade_accounts() { * * @param int $uid The channel_id to check * @param string $property The service class property to check for - * @param string|boolean $usage (optional) The value to check against - * @return boolean + * @param string|bool $usage (optional) The value to check against + * @return bool */ -function service_class_allows($uid, $property, $usage = false) { - $limit = service_class_fetch($uid, $property); +function service_class_allows($uid, $property, $usage = false) +{ + $limit = service_class_fetch($uid, $property); - if ($limit === false) { - return true; // No service class set => everything is allowed - } - - $limit = engr_units_to_bytes($limit); - if ($usage === false) { - // We use negative values for not allowed properties in a subscriber plan - return (($limit) ? (bool) $limit : true); - } else { - return (((intval($usage)) < intval($limit)) ? true : false); - } + if ($limit === false) { + return true; // No service class set => everything is allowed + } + + $limit = engr_units_to_bytes($limit); + if ($usage === false) { + // We use negative values for not allowed properties in a subscriber plan + return (($limit) ? (bool) $limit : true); + } else { + return (((intval($usage)) < intval($limit)) ? true : false); + } } /** @@ -714,25 +747,26 @@ function service_class_allows($uid, $property, $usage = false) { * * @param int $aid The account_id to check * @param string $property The service class property to check for - * @param int|boolean $usage (optional) The value to check against - * @return boolean + * @param int|bool $usage (optional) The value to check against + * @return bool */ -function account_service_class_allows($aid, $property, $usage = false) { +function account_service_class_allows($aid, $property, $usage = false) +{ - $limit = account_service_class_fetch($aid, $property); + $limit = account_service_class_fetch($aid, $property); - if ($limit === false) { - return true; // No service class is set => everything is allowed - } - - $limit = engr_units_to_bytes($limit); + if ($limit === false) { + return true; // No service class is set => everything is allowed + } + + $limit = engr_units_to_bytes($limit); - if ($usage === false) { - // We use negative values for not allowed properties in a subscriber plan - return (($limit) ? (bool) $limit : true); - } else { - return (((intval($usage)) < intval($limit)) ? true : false); - } + if ($usage === false) { + // We use negative values for not allowed properties in a subscriber plan + return (($limit) ? (bool) $limit : true); + } else { + return (((intval($usage)) < intval($limit)) ? true : false); + } } /** @@ -748,36 +782,37 @@ function account_service_class_allows($aid, $property, $usage = false) { * * @param int $uid The channel_id to query * @param string $property The service property name to check for - * @return boolean|int + * @return bool|int * * @todo Should we merge this with account_service_class_fetch()? */ -function service_class_fetch($uid, $property) { +function service_class_fetch($uid, $property) +{ - if ($uid == local_channel()) { - $service_class = App::$account['account_service_class']; - } - else { - $r = q("select account_service_class + if ($uid == local_channel()) { + $service_class = App::$account['account_service_class']; + } else { + $r = q( + "select account_service_class from channel c, account a where c.channel_account_id = a.account_id and c.channel_id = %d limit 1", - intval($uid) - ); - if ($r) { - $service_class = $r[0]['account_service_class']; - } - } - if (! $service_class) { - return false; // everything is allowed - } - $arr = get_config('service_class', $service_class); + intval($uid) + ); + if ($r) { + $service_class = $r[0]['account_service_class']; + } + } + if (! $service_class) { + return false; // everything is allowed + } + $arr = get_config('service_class', $service_class); - if (! is_array($arr) || (! count($arr))) { - return false; - } + if (! is_array($arr) || (! count($arr))) { + return false; + } - return((array_key_exists($property, $arr)) ? $arr[$property] : false); + return((array_key_exists($property, $arr)) ? $arr[$property] : false); } /** @@ -790,48 +825,55 @@ function service_class_fetch($uid, $property) { * * @param int $aid The account_id to query * @param string $property The service property name to check for - * @return boolean|int + * @return bool|int */ -function account_service_class_fetch($aid, $property) { +function account_service_class_fetch($aid, $property) +{ - $r = q("select account_service_class as service_class from account where account_id = %d limit 1", - intval($aid) - ); - if($r !== false && count($r)) { - $service_class = $r[0]['service_class']; - } + $r = q( + "select account_service_class as service_class from account where account_id = %d limit 1", + intval($aid) + ); + if ($r !== false && count($r)) { + $service_class = $r[0]['service_class']; + } - if(! x($service_class)) - return false; // everything is allowed + if (! x($service_class)) { + return false; // everything is allowed + } - $arr = get_config('service_class', $service_class); + $arr = get_config('service_class', $service_class); - if(! is_array($arr) || (! count($arr))) - return false; + if (! is_array($arr) || (! count($arr))) { + return false; + } - return((array_key_exists($property, $arr)) ? $arr[$property] : false); + return((array_key_exists($property, $arr)) ? $arr[$property] : false); } -function upgrade_link($bbcode = false) { - $l = get_config('service_class', 'upgrade_link'); - if(! $l) - return ''; - if($bbcode) - $t = sprintf('[zrl=%s]' . t('Click here to upgrade.') . '[/zrl]', $l); - else - $t = sprintf('' . t('Click here to upgrade.') . '
      ', $l); - return $t; +function upgrade_link($bbcode = false) +{ + $l = get_config('service_class', 'upgrade_link'); + if (! $l) { + return ''; + } + if ($bbcode) { + $t = sprintf('[zrl=%s]' . t('Click here to upgrade.') . '[/zrl]', $l); + } else { + $t = sprintf('' . t('Click here to upgrade.') . '
      ', $l); + } + return $t; } -function upgrade_message($bbcode = false) { - $x = upgrade_link($bbcode); - return t('This action exceeds the limits set by your subscription plan.') . (($x) ? ' ' . $x : '') ; +function upgrade_message($bbcode = false) +{ + $x = upgrade_link($bbcode); + return t('This action exceeds the limits set by your subscription plan.') . (($x) ? ' ' . $x : '') ; } -function upgrade_bool_message($bbcode = false) { - $x = upgrade_link($bbcode); - return t('This action is not available under your subscription plan.') . (($x) ? ' ' . $x : '') ; +function upgrade_bool_message($bbcode = false) +{ + $x = upgrade_link($bbcode); + return t('This action is not available under your subscription plan.') . (($x) ? ' ' . $x : '') ; } - - diff --git a/include/acl_selectors.php b/include/acl_selectors.php index f084247f9..5d036244d 100644 --- a/include/acl_selectors.php +++ b/include/acl_selectors.php @@ -6,156 +6,156 @@ */ use Zotlabs\Lib\Apps; +use Zotlabs\Lib\PermissionDescription; -function fixacl(&$item) { - $item = str_replace( [ '<', '>' ], [ '', '' ], $item); +function fixacl(&$item) +{ + $item = str_replace([ '<', '>' ], [ '', '' ], $item); } /** * Builds a modal dialog for editing permissions, using acl_selector.tpl as the template. * * @param array $defaults Optional access control list for the initial state of the dialog. -* @param boolean $show_jotnets Whether plugins for federated networks should be included in the permissions dialog -* @param \Zotlabs\Lib\PermissionDescription $emptyACL_description - An optional description for the permission implied by selecting an empty ACL. Preferably an instance of PermissionDescription. +* @param bool $show_jotnets Whether plugins for federated networks should be included in the permissions dialog +* @param PermissionDescription $emptyACL_description - An optional description for the permission implied by selecting an empty ACL. Preferably an instance of PermissionDescription. * @param string $dialog_description Optional message to include at the top of the dialog. E.g. "Warning: Post permissions cannot be changed once sent". * @param string $context_help Allows the dialog to present a help icon. E.g. "acl_dialog_post" -* @param boolean $readonly Not implemented yet. When implemented, the dialog will use acl_readonly.tpl instead, so that permissions may be viewed for posts that can no longer have their permissions changed. +* @param bool $readonly Not implemented yet. When implemented, the dialog will use acl_readonly.tpl instead, so that permissions may be viewed for posts that can no longer have their permissions changed. * * @return string html modal dialog built from acl_selector.tpl */ -function populate_acl($defaults = null,$show_jotnets = true, $emptyACL_description = '', $dialog_description = '', $context_help = '', $readonly = false) { +function populate_acl($defaults = null, $show_jotnets = true, $emptyACL_description = '', $dialog_description = '', $context_help = '', $readonly = false) +{ - $allow_cid = $allow_gid = $deny_cid = $deny_gid = false; - $showall_origin = ''; - $showall_icon = 'fa-globe'; - $role = get_pconfig(local_channel(), 'system', 'permissions_role'); + $allow_cid = $allow_gid = $deny_cid = $deny_gid = false; + $showall_origin = ''; + $showall_icon = 'fa-globe'; + $role = get_pconfig(local_channel(), 'system', 'permissions_role'); - if (! $emptyACL_description) { - $showall_caption = t('Visible to your default audience'); - - } - elseif (is_a($emptyACL_description, '\\Zotlabs\\Lib\\PermissionDescription')) { - $showall_caption = $emptyACL_description->get_permission_description(); - $showall_origin = (($role === 'custom') ? $emptyACL_description->get_permission_origin_description() : ''); - $showall_icon = $emptyACL_description->get_permission_icon(); - } - else { - // For backwards compatibility we still accept a string... for now! - $showall_caption = $emptyACL_description; - } + if (! $emptyACL_description) { + $showall_caption = t('Visible to your default audience'); + } elseif (is_a($emptyACL_description, '\\Zotlabs\\Lib\\PermissionDescription')) { + $showall_caption = $emptyACL_description->get_permission_description(); + $showall_origin = (($role === 'custom') ? $emptyACL_description->get_permission_origin_description() : ''); + $showall_icon = $emptyACL_description->get_permission_icon(); + } else { + // For backwards compatibility we still accept a string... for now! + $showall_caption = $emptyACL_description; + } - if (is_array($defaults)) { - $allow_cid = ((strlen($defaults['allow_cid'])) - ? explode('><', $defaults['allow_cid']) : [] ); - $allow_gid = ((strlen($defaults['allow_gid'])) - ? explode('><', $defaults['allow_gid']) : [] ); - $deny_cid = ((strlen($defaults['deny_cid'])) - ? explode('><', $defaults['deny_cid']) : [] ); - $deny_gid = ((strlen($defaults['deny_gid'])) - ? explode('><', $defaults['deny_gid']) : [] ); - array_walk($allow_cid,'fixacl'); - array_walk($allow_gid,'fixacl'); - array_walk($deny_cid,'fixacl'); - array_walk($deny_gid,'fixacl'); - } + if (is_array($defaults)) { + $allow_cid = ((strlen($defaults['allow_cid'])) + ? explode('><', $defaults['allow_cid']) : [] ); + $allow_gid = ((strlen($defaults['allow_gid'])) + ? explode('><', $defaults['allow_gid']) : [] ); + $deny_cid = ((strlen($defaults['deny_cid'])) + ? explode('><', $defaults['deny_cid']) : [] ); + $deny_gid = ((strlen($defaults['deny_gid'])) + ? explode('><', $defaults['deny_gid']) : [] ); + array_walk($allow_cid, 'fixacl'); + array_walk($allow_gid, 'fixacl'); + array_walk($deny_cid, 'fixacl'); + array_walk($deny_gid, 'fixacl'); + } - $channel = ((local_channel()) ? App::get_channel() : ''); - $has_acl = false; - $single_group = false; - $just_me = false; - $custom = false; + $channel = ((local_channel()) ? App::get_channel() : ''); + $has_acl = false; + $single_group = false; + $just_me = false; + $custom = false; - if ($allow_cid || $allow_gid || $deny_gid || $deny_cid) { - $has_acl = true; - $custom = true; - } + if ($allow_cid || $allow_gid || $deny_gid || $deny_cid) { + $has_acl = true; + $custom = true; + } - if (count($allow_gid) === 1 && (! $allow_cid) && (! $deny_gid) && (! $deny_cid)) { - $single_group = true; - $custom = false; - } + if (count($allow_gid) === 1 && (! $allow_cid) && (! $deny_gid) && (! $deny_cid)) { + $single_group = true; + $custom = false; + } - if (count($allow_cid) === 1 && $channel && $allow_cid[0] === $channel['channel_hash'] && (! $allow_gid) && (! $deny_gid) && (! $deny_cid)) { - $just_me = true; - $custom = false; - } + if (count($allow_cid) === 1 && $channel && $allow_cid[0] === $channel['channel_hash'] && (! $allow_gid) && (! $deny_gid) && (! $deny_cid)) { + $just_me = true; + $custom = false; + } - $groups = EMPTY_STR; + $groups = EMPTY_STR; - $r = q("SELECT id, hash, gname FROM pgrp WHERE deleted = 0 AND uid = %d ORDER BY gname ASC", - intval(local_channel()) - ); + $r = q( + "SELECT id, hash, gname FROM pgrp WHERE deleted = 0 AND uid = %d ORDER BY gname ASC", + intval(local_channel()) + ); - if ($r) { - foreach ($r as $rr) { - $selected = (($single_group && $rr['hash'] === $allow_gid[0]) ? ' selected = "selected" ' : ''); - $groups .= '' . "\r\n"; - } - } + if ($r) { + foreach ($r as $rr) { + $selected = (($single_group && $rr['hash'] === $allow_gid[0]) ? ' selected = "selected" ' : ''); + $groups .= '' . "\r\n"; + } + } - if ($channel && Apps::system_app_installed($channel['channel_id'],'Virtual Lists')) { - $selected = (($single_group && 'connections:' . $channel['channel_hash'] === $allow_gid[0]) ? ' selected = "selected" ' : ''); - $groups .= '' . "\r\n"; - if (get_pconfig($channel['channel_id'],'system','activitypub',get_config('system','activitypub', ACTIVITYPUB_ENABLED))) { - $selected = (($single_group && 'activitypub:' . $channel['channel_hash'] === $allow_gid[0]) ? ' selected = "selected" ' : ''); - $groups .= '' . "\r\n"; - } - $selected = (($single_group && 'zot:' . $channel['channel_hash'] === $allow_gid[0]) ? ' selected = "selected" ' : ''); - $groups .= '' . "\r\n"; - - } + if ($channel && Apps::system_app_installed($channel['channel_id'], 'Virtual Lists')) { + $selected = (($single_group && 'connections:' . $channel['channel_hash'] === $allow_gid[0]) ? ' selected = "selected" ' : ''); + $groups .= '' . "\r\n"; + if (get_pconfig($channel['channel_id'], 'system', 'activitypub', get_config('system', 'activitypub', ACTIVITYPUB_ENABLED))) { + $selected = (($single_group && 'activitypub:' . $channel['channel_hash'] === $allow_gid[0]) ? ' selected = "selected" ' : ''); + $groups .= '' . "\r\n"; + } + $selected = (($single_group && 'zot:' . $channel['channel_hash'] === $allow_gid[0]) ? ' selected = "selected" ' : ''); + $groups .= '' . "\r\n"; + } - $forums = get_forum_channels(local_channel(),1); - $selected = false; - if ($forums) { - foreach ($forums as $f) { + $forums = get_forum_channels(local_channel(), 1); + $selected = false; + if ($forums) { + foreach ($forums as $f) { + $selected = (($single_group && $f['hash'] === $allow_cid[0]) ? ' selected = "selected" ' : ''); + $groups .= '' . "\r\n"; + } + } - $selected = (($single_group && $f['hash'] === $allow_cid[0]) ? ' selected = "selected" ' : ''); - $groups .= '' . "\r\n"; - } - } + // preset acl with DM to a single xchan (not a group) + if ($selected === false && count($allow_cid) === 1 && $channel && $allow_cid[0] !== $channel['channel_hash'] && (! $allow_gid) && (! $deny_gid) && (! $deny_cid)) { + $f = q( + "select * from xchan where xchan_hash = '%s'", + dbesc($allow_cid[0]) + ); + if ($f) { + $custom = false; + $selected = ' selected="selected" '; + $groups .= '' . "\r\n"; + } + } - // preset acl with DM to a single xchan (not a group) - if ($selected === false && count($allow_cid) === 1 && $channel && $allow_cid[0] !== $channel['channel_hash'] && (! $allow_gid) && (! $deny_gid) && (! $deny_cid)) { - $f = q("select * from xchan where xchan_hash = '%s'", - dbesc($allow_cid[0]) - ); - if ($f) { - $custom = false; - $selected = ' selected="selected" '; - $groups .= '' . "\r\n"; - } - } + $tpl = get_markup_template("acl_selector.tpl"); + $o = replace_macros($tpl, array( + '$showall' => $showall_caption, + '$onlyme' => t('Only me'), + '$groups' => $groups, + '$public_selected' => (($has_acl) ? false : ' selected="selected" '), + '$justme_selected' => (($just_me) ? ' selected="selected" ' : ''), + '$custom_selected' => (($custom) ? ' selected="selected" ' : ''), + '$showallOrigin' => $showall_origin, + '$showallIcon' => $showall_icon, + '$select_label' => t('Who can see this?'), + '$custom' => t('Custom selection'), + '$showlimitedDesc' => t('Select "Show" to allow viewing. "Don\'t show" lets you override and limit the scope of "Show".'), + '$show' => t('Show'), + '$hide' => t("Don't show"), + '$search' => t('Search'), + '$allowcid' => json_encode($allow_cid), + '$allowgid' => json_encode($allow_gid), + '$denycid' => json_encode($deny_cid), + '$denygid' => json_encode($deny_gid), + '$aclModalTitle' => t('Permissions'), + '$aclModalDesc' => $dialog_description, + '$aclModalDismiss' => t('Close'), +// '$helpUrl' => (($context_help == '') ? '' : (z_root() . '/help/' . $context_help)) + )); - $tpl = get_markup_template("acl_selector.tpl"); - $o = replace_macros($tpl, array( - '$showall' => $showall_caption, - '$onlyme' => t('Only me'), - '$groups' => $groups, - '$public_selected' => (($has_acl) ? false : ' selected="selected" '), - '$justme_selected' => (($just_me) ? ' selected="selected" ' : ''), - '$custom_selected' => (($custom) ? ' selected="selected" ' : ''), - '$showallOrigin' => $showall_origin, - '$showallIcon' => $showall_icon, - '$select_label' => t('Who can see this?'), - '$custom' => t('Custom selection'), - '$showlimitedDesc' => t('Select "Show" to allow viewing. "Don\'t show" lets you override and limit the scope of "Show".'), - '$show' => t('Show'), - '$hide' => t("Don't show"), - '$search' => t('Search'), - '$allowcid' => json_encode($allow_cid), - '$allowgid' => json_encode($allow_gid), - '$denycid' => json_encode($deny_cid), - '$denygid' => json_encode($deny_gid), - '$aclModalTitle' => t('Permissions'), - '$aclModalDesc' => $dialog_description, - '$aclModalDismiss' => t('Close'), -// '$helpUrl' => (($context_help == '') ? '' : (z_root() . '/help/' . $context_help)) - )); - - return $o; + return $o; } /** @@ -169,18 +169,19 @@ function populate_acl($defaults = null,$show_jotnets = true, $emptyACL_descripti * * @return string Description to present to user in modal permissions dialog */ -function get_post_aclDialogDescription() { +function get_post_aclDialogDescription() +{ - // I'm trying to make two points in this description text - warn about finality of wall - // post permissions, and try to clear up confusion that these permissions set who is - // *shown* the post, istead of who is able to see the post, i.e. make it clear that clicking - // the "Show" button on a group does not post it to the feed of people in that group, it - // mearly allows those people to view the post if they are viewing/following this channel. - $description = t('Post permissions cannot be changed after a post is shared.
      These permissions set who is allowed to view the post.'); + // I'm trying to make two points in this description text - warn about finality of wall + // post permissions, and try to clear up confusion that these permissions set who is + // *shown* the post, istead of who is able to see the post, i.e. make it clear that clicking + // the "Show" button on a group does not post it to the feed of people in that group, it + // mearly allows those people to view the post if they are viewing/following this channel. + $description = t('Post permissions cannot be changed after a post is shared.
      These permissions set who is allowed to view the post.'); - // Lets keep the emphasis styling seperate from the translation. It may change. - //$emphasisOpen = '
      '; - //$emphasisClose = ''; + // Lets keep the emphasis styling seperate from the translation. It may change. + //$emphasisOpen = ''; + //$emphasisClose = ''; - return $description; + return $description; } diff --git a/include/addon.php b/include/addon.php index 189dc3e68..49058ca0f 100644 --- a/include/addon.php +++ b/include/addon.php @@ -13,18 +13,19 @@ * @param string $error_text text of error * @param bool $uninstall uninstall plugin */ -function handleerrors_plugin($plugin,$notice,$log,$uninstall=false){ - logger("Addons: [" . $plugin . "] Error: ".$log, LOGGER_ERROR); - if ($notice != '') { - notice("[" . $plugin . "] Error: ".$notice, LOGGER_ERROR); - } +function handleerrors_plugin($plugin, $notice, $log, $uninstall = false) +{ + logger("Addons: [" . $plugin . "] Error: ".$log, LOGGER_ERROR); + if ($notice != '') { + notice("[" . $plugin . "] Error: ".$notice, LOGGER_ERROR); + } - if ($uninstall) { - $idx = array_search($plugin, App::$plugins); - unset(App::$plugins[$idx]); - uninstall_plugin($plugin); - set_config("system","addon", implode(", ",App::$plugins)); - } + if ($uninstall) { + $idx = array_search($plugin, App::$plugins); + unset(App::$plugins[$idx]); + uninstall_plugin($plugin); + set_config("system", "addon", implode(", ", App::$plugins)); + } } /** @@ -32,53 +33,56 @@ function handleerrors_plugin($plugin,$notice,$log,$uninstall=false){ * * @param string $plugin name of the addon */ -function unload_plugin($plugin){ - logger("Addons: unloading " . $plugin, LOGGER_DEBUG); +function unload_plugin($plugin) +{ + logger("Addons: unloading " . $plugin, LOGGER_DEBUG); - @include_once('addon/' . $plugin . '/' . $plugin . '.php'); - if (function_exists($plugin . '_unload')) { - $func = $plugin . '_unload'; - try { - $func(); - } catch (Exception $e) { - handleerrors_plugin($plugin,"Unable to unload.",$e->getMessage()); - } - } + @include_once('addon/' . $plugin . '/' . $plugin . '.php'); + if (function_exists($plugin . '_unload')) { + $func = $plugin . '_unload'; + try { + $func(); + } catch (Exception $e) { + handleerrors_plugin($plugin, "Unable to unload.", $e->getMessage()); + } + } } /** * @brief Uninstalls an addon. * * @param string $plugin name of the addon - * @return boolean + * @return bool */ -function uninstall_plugin($plugin) { +function uninstall_plugin($plugin) +{ - unload_plugin($plugin); + unload_plugin($plugin); - if(! file_exists('addon/' . $plugin . '/' . $plugin . '.php')) { - q("DELETE FROM addon WHERE aname = '%s' ", - dbesc($plugin) - ); - return false; - } + if (! file_exists('addon/' . $plugin . '/' . $plugin . '.php')) { + q( + "DELETE FROM addon WHERE aname = '%s' ", + dbesc($plugin) + ); + return false; + } - logger("Addons: uninstalling " . $plugin); - //$t = @filemtime('addon/' . $plugin . '/' . $plugin . '.php'); - @include_once('addon/' . $plugin . '/' . $plugin . '.php'); - if(function_exists($plugin . '_uninstall')) { - $func = $plugin . '_uninstall'; - try { - $func(); - } catch (Exception $e) { - handleerrors_plugin($plugin,"Unable to uninstall.","Unable to run _uninstall : ".$e->getMessage()); - } - } - - q("DELETE FROM addon WHERE aname = '%s' ", - dbesc($plugin) - ); + logger("Addons: uninstalling " . $plugin); + //$t = @filemtime('addon/' . $plugin . '/' . $plugin . '.php'); + @include_once('addon/' . $plugin . '/' . $plugin . '.php'); + if (function_exists($plugin . '_uninstall')) { + $func = $plugin . '_uninstall'; + try { + $func(); + } catch (Exception $e) { + handleerrors_plugin($plugin, "Unable to uninstall.", "Unable to run _uninstall : ".$e->getMessage()); + } + } + q( + "DELETE FROM addon WHERE aname = '%s' ", + dbesc($plugin) + ); } /** @@ -90,37 +94,41 @@ function uninstall_plugin($plugin) { * @param string $plugin name of the addon * @return bool */ -function install_plugin($plugin) { - if(! file_exists('addon/' . $plugin . '/' . $plugin . '.php')) - return false; +function install_plugin($plugin) +{ + if (! file_exists('addon/' . $plugin . '/' . $plugin . '.php')) { + return false; + } - logger("Addons: installing " . $plugin); - $t = @filemtime('addon/' . $plugin . '/' . $plugin . '.php'); - @include_once('addon/' . $plugin . '/' . $plugin . '.php'); - if(function_exists($plugin . '_install')) { - $func = $plugin . '_install'; - try { - $func(); - } catch (Exception $e) { - handleerrors_plugin($plugin,"Install failed.","Install failed : ".$e->getMessage()); - return; - } - } + logger("Addons: installing " . $plugin); + $t = @filemtime('addon/' . $plugin . '/' . $plugin . '.php'); + @include_once('addon/' . $plugin . '/' . $plugin . '.php'); + if (function_exists($plugin . '_install')) { + $func = $plugin . '_install'; + try { + $func(); + } catch (Exception $e) { + handleerrors_plugin($plugin, "Install failed.", "Install failed : ".$e->getMessage()); + return; + } + } - $plugin_admin = (function_exists($plugin . '_plugin_admin') ? 1 : 0); + $plugin_admin = (function_exists($plugin . '_plugin_admin') ? 1 : 0); - $d = q("select * from addon where aname = '%s' limit 1", - dbesc($plugin) - ); - if(! $d) { - q("INSERT INTO addon (aname, installed, tstamp, plugin_admin) VALUES ( '%s', 1, %d , %d ) ", - dbesc($plugin), - intval($t), - $plugin_admin - ); - } + $d = q( + "select * from addon where aname = '%s' limit 1", + dbesc($plugin) + ); + if (! $d) { + q( + "INSERT INTO addon (aname, installed, tstamp, plugin_admin) VALUES ( '%s', 1, %d , %d ) ", + dbesc($plugin), + intval($t), + $plugin_admin + ); + } - load_plugin($plugin); + load_plugin($plugin); } /** @@ -129,38 +137,40 @@ function install_plugin($plugin) { * @param string $plugin name of the addon * @return bool */ -function load_plugin($plugin) { - // silently fail if plugin was removed - if(! file_exists('addon/' . $plugin . '/' . $plugin . '.php')) - return false; +function load_plugin($plugin) +{ + // silently fail if plugin was removed + if (! file_exists('addon/' . $plugin . '/' . $plugin . '.php')) { + return false; + } - logger("Addons: loading " . $plugin, LOGGER_DEBUG); - //$t = @filemtime('addon/' . $plugin . '/' . $plugin . '.php'); - @include_once('addon/' . $plugin . '/' . $plugin . '.php'); - if(function_exists($plugin . '_load')) { - $func = $plugin . '_load'; - try { - $func(); - } catch (Exception $e) { - handleerrors_plugin($plugin,"Unable to load.","FAILED loading : ".$e->getMessage(),true); - return; - } + logger("Addons: loading " . $plugin, LOGGER_DEBUG); + //$t = @filemtime('addon/' . $plugin . '/' . $plugin . '.php'); + @include_once('addon/' . $plugin . '/' . $plugin . '.php'); + if (function_exists($plugin . '_load')) { + $func = $plugin . '_load'; + try { + $func(); + } catch (Exception $e) { + handleerrors_plugin($plugin, "Unable to load.", "FAILED loading : ".$e->getMessage(), true); + return; + } - // we can add the following with the previous SQL - // once most site tables have been updated. - // This way the system won't fall over dead during the update. + // we can add the following with the previous SQL + // once most site tables have been updated. + // This way the system won't fall over dead during the update. - if(file_exists('addon/' . $plugin . '/.hidden')) { - q("update addon set hidden = 1 where name = '%s'", - dbesc($plugin) - ); - } - return true; - } - else { - logger("Addons: FAILED loading " . $plugin . " (missing _load function)"); - return false; - } + if (file_exists('addon/' . $plugin . '/.hidden')) { + q( + "update addon set hidden = 1 where name = '%s'", + dbesc($plugin) + ); + } + return true; + } else { + logger("Addons: FAILED loading " . $plugin . " (missing _load function)"); + return false; + } } @@ -168,151 +178,161 @@ function load_plugin($plugin) { * @brief Check if addon is installed (deprecated). * * @param string $name - * @return boolean + * @return bool */ -function plugin_is_installed($name) { - $r = q("select aname from addon where aname = '%s' and installed = 1 limit 1", - dbesc($name) - ); - if($r) - return true; +function plugin_is_installed($name) +{ + $r = q( + "select aname from addon where aname = '%s' and installed = 1 limit 1", + dbesc($name) + ); + if ($r) { + return true; + } - return false; + return false; } /** * @brief Check if addon is installed (use this one). * * @param string $name - * @return boolean + * @return bool */ -function addon_is_installed($name) { - $r = q("select aname from addon where aname = '%s' and installed = 1 limit 1", - dbesc($name) - ); - if($r) - return true; +function addon_is_installed($name) +{ + $r = q( + "select aname from addon where aname = '%s' and installed = 1 limit 1", + dbesc($name) + ); + if ($r) { + return true; + } - return false; + return false; } /** * @brief Reload all updated plugins. */ -function reload_plugins() { - $plugins = get_config('system', 'addon'); - if(strlen($plugins)) { - $r = q("SELECT * FROM addon WHERE installed = 1"); - if(count($r)) - $installed = $r; - else - $installed = []; +function reload_plugins() +{ + $plugins = get_config('system', 'addon'); + if (strlen($plugins)) { + $r = q("SELECT * FROM addon WHERE installed = 1"); + if (count($r)) { + $installed = $r; + } else { + $installed = []; + } - $parr = explode(',', $plugins); + $parr = explode(',', $plugins); - if(count($parr)) { - foreach($parr as $pl) { - $pl = trim($pl); + if (count($parr)) { + foreach ($parr as $pl) { + $pl = trim($pl); - $fname = 'addon/' . $pl . '/' . $pl . '.php'; + $fname = 'addon/' . $pl . '/' . $pl . '.php'; - if(file_exists($fname)) { - $t = @filemtime($fname); - foreach($installed as $i) { - if(($i['aname'] == $pl) && ($i['tstamp'] != $t)) { - logger('Reloading plugin: ' . $i['aname']); - @include_once($fname); + if (file_exists($fname)) { + $t = @filemtime($fname); + foreach ($installed as $i) { + if (($i['aname'] == $pl) && ($i['tstamp'] != $t)) { + logger('Reloading plugin: ' . $i['aname']); + @include_once($fname); - if(function_exists($pl . '_unload')) { - $func = $pl . '_unload'; - try { - $func(); - } catch (Exception $e) { - handleerrors_plugin($plugin,"","UNLOAD FAILED (uninstalling) : ".$e->getMessage(),true); - continue; - } - } - if(function_exists($pl . '_load')) { - $func = $pl . '_load'; - try { - $func(); - } catch (Exception $e) { - handleerrors_plugin($plugin,"","LOAD FAILED (uninstalling): ".$e->getMessage(),true); - continue; - } - } - q("UPDATE addon SET tstamp = %d WHERE id = %d", - intval($t), - intval($i['id']) - ); - } - } - } - } - } - } + if (function_exists($pl . '_unload')) { + $func = $pl . '_unload'; + try { + $func(); + } catch (Exception $e) { + handleerrors_plugin($plugin, "", "UNLOAD FAILED (uninstalling) : ".$e->getMessage(), true); + continue; + } + } + if (function_exists($pl . '_load')) { + $func = $pl . '_load'; + try { + $func(); + } catch (Exception $e) { + handleerrors_plugin($plugin, "", "LOAD FAILED (uninstalling): ".$e->getMessage(), true); + continue; + } + } + q( + "UPDATE addon SET tstamp = %d WHERE id = %d", + intval($t), + intval($i['id']) + ); + } + } + } + } + } + } } -function plugins_installed_list() { +function plugins_installed_list() +{ - $r = q("select * from addon where installed = 1 order by aname asc"); - return(($r) ? ids_to_array($r,'aname') : []); + $r = q("select * from addon where installed = 1 order by aname asc"); + return(($r) ? ids_to_array($r, 'aname') : []); } -function plugins_sync() { +function plugins_sync() +{ - /** - * - * Synchronise plugins: - * - * App::$config['system']['addon'] contains a comma-separated list of names - * of plugins/addons which are used on this system. - * Go through the database list of already installed addons, and if we have - * an entry, but it isn't in the config list, call the unload procedure - * and mark it uninstalled in the database (for now we'll remove it). - * Then go through the config list and if we have a plugin that isn't installed, - * call the install procedure and add it to the database. - * - */ + /** + * + * Synchronise plugins: + * + * App::$config['system']['addon'] contains a comma-separated list of names + * of plugins/addons which are used on this system. + * Go through the database list of already installed addons, and if we have + * an entry, but it isn't in the config list, call the unload procedure + * and mark it uninstalled in the database (for now we'll remove it). + * Then go through the config list and if we have a plugin that isn't installed, + * call the install procedure and add it to the database. + * + */ - $installed = plugins_installed_list(); + $installed = plugins_installed_list(); - $plugins = get_config('system', 'addon', ''); + $plugins = get_config('system', 'addon', ''); - $plugins_arr = explode(',', $plugins); + $plugins_arr = explode(',', $plugins); - // array_trim is in include/text.php + // array_trim is in include/text.php - if(! array_walk($plugins_arr,'array_trim')) - return; + if (! array_walk($plugins_arr, 'array_trim')) { + return; + } - App::$plugins = $plugins_arr; + App::$plugins = $plugins_arr; - $installed_arr = []; + $installed_arr = []; - if(count($installed)) { - foreach($installed as $i) { - if(! in_array($i, $plugins_arr)) { - unload_plugin($i); - } - else { - $installed_arr[] = $i; - } - } - } - - if(count($plugins_arr)) { - foreach($plugins_arr as $p) { - if(! in_array($p, $installed_arr)) { - load_plugin($p); - } - } - } + if (count($installed)) { + foreach ($installed as $i) { + if (! in_array($i, $plugins_arr)) { + unload_plugin($i); + } else { + $installed_arr[] = $i; + } + } + } + if (count($plugins_arr)) { + foreach ($plugins_arr as $p) { + if (! in_array($p, $installed_arr)) { + load_plugin($p); + } + } + } } @@ -321,19 +341,20 @@ function plugins_sync() { * * @return array */ -function visible_plugin_list() { - - $r = q("select * from addon where hidden = 0 order by aname asc"); - $x = (($r) ? ids_to_array($r,'aname') : []); - $y = []; - if($x) { - foreach($x as $xv) { - if(is_dir('addon/' . $xv)) { - $y[] = $xv; - } - } - } - return $y; +function visible_plugin_list() +{ + + $r = q("select * from addon where hidden = 0 order by aname asc"); + $x = (($r) ? ids_to_array($r, 'aname') : []); + $y = []; + if ($x) { + foreach ($x as $xv) { + if (is_dir('addon/' . $xv)) { + $y[] = $xv; + } + } + } + return $y; } @@ -348,23 +369,27 @@ function visible_plugin_list() { * @param int $priority A priority (defaults to 0) * @return mixed|bool */ -function register_hook($hook, $file, $function, $priority = 0) { - $r = q("SELECT * FROM hook WHERE hook = '%s' AND file = '%s' AND fn = '%s' LIMIT 1", - dbesc($hook), - dbesc($file), - dbesc($function) - ); - if($r) - return true; +function register_hook($hook, $file, $function, $priority = 0) +{ + $r = q( + "SELECT * FROM hook WHERE hook = '%s' AND file = '%s' AND fn = '%s' LIMIT 1", + dbesc($hook), + dbesc($file), + dbesc($function) + ); + if ($r) { + return true; + } - $r = q("INSERT INTO hook (hook, file, fn, priority) VALUES ( '%s', '%s', '%s', '%s' )", - dbesc($hook), - dbesc($file), - dbesc($function), - dbesc($priority) - ); + $r = q( + "INSERT INTO hook (hook, file, fn, priority) VALUES ( '%s', '%s', '%s', '%s' )", + dbesc($hook), + dbesc($file), + dbesc($function), + dbesc($priority) + ); - return $r; + return $r; } @@ -378,14 +403,16 @@ function register_hook($hook, $file, $function, $priority = 0) { * @param string $function the name of the function that the hook called * @return array */ -function unregister_hook($hook, $file, $function) { - $r = q("DELETE FROM hook WHERE hook = '%s' AND file = '%s' AND fn = '%s'", - dbesc($hook), - dbesc($file), - dbesc($function) - ); +function unregister_hook($hook, $file, $function) +{ + $r = q( + "DELETE FROM hook WHERE hook = '%s' AND file = '%s' AND fn = '%s'", + dbesc($hook), + dbesc($file), + dbesc($function) + ); - return $r; + return $r; } /** @@ -400,35 +427,35 @@ function unregister_hook($hook, $file, $function) { */ -function load_hooks() { +function load_hooks() +{ - App::$hooks = []; + App::$hooks = []; - $r = q("SELECT * FROM hook WHERE true ORDER BY priority DESC"); - if($r) { - - foreach($r as $rv) { - $duplicated = false; - if(! array_key_exists($rv['hook'],App::$hooks)) { - App::$hooks[$rv['hook']] = []; - } - else { - foreach(App::$hooks[$rv['hook']] as $h) { - if($h[0] === $rv['file'] && $h[1] === $rv['fn']) { - $duplicated = true; - q("delete from hook where id = %d", - intval($rv['id']) - ); - logger('duplicate hook ' . $h[1] . ' removed'); - } - } - } - if(! $duplicated) { - App::$hooks[$rv['hook']][] = [ $rv['file'], $rv['fn'], $rv['priority'], $rv['hook_version']]; - } - } - } - // logger('hooks: ' . print_r(App::$hooks,true)); + $r = q("SELECT * FROM hook WHERE true ORDER BY priority DESC"); + if ($r) { + foreach ($r as $rv) { + $duplicated = false; + if (! array_key_exists($rv['hook'], App::$hooks)) { + App::$hooks[$rv['hook']] = []; + } else { + foreach (App::$hooks[$rv['hook']] as $h) { + if ($h[0] === $rv['file'] && $h[1] === $rv['fn']) { + $duplicated = true; + q( + "delete from hook where id = %d", + intval($rv['id']) + ); + logger('duplicate hook ' . $h[1] . ' removed'); + } + } + } + if (! $duplicated) { + App::$hooks[$rv['hook']][] = [ $rv['file'], $rv['fn'], $rv['priority'], $rv['hook_version']]; + } + } + } + // logger('hooks: ' . print_r(App::$hooks,true)); } /** @@ -450,15 +477,18 @@ function load_hooks() { * @param int $version (optional) default 0 * @param int $priority (optional) default 0 */ -function insert_hook($hook, $fn, $version = 0, $priority = 0) { +function insert_hook($hook, $fn, $version = 0, $priority = 0) +{ - if(! is_array(App::$hooks)) - App::$hooks = []; + if (! is_array(App::$hooks)) { + App::$hooks = []; + } - if(! array_key_exists($hook, App::$hooks)) - App::$hooks[$hook] = []; + if (! array_key_exists($hook, App::$hooks)) { + App::$hooks[$hook] = []; + } - App::$hooks[$hook][] = array('', $fn, $priority, $version); + App::$hooks[$hook][] = array('', $fn, $priority, $version); } /** @@ -470,46 +500,47 @@ function insert_hook($hook, $fn, $version = 0, $priority = 0) { * @param string $name of the hook to call * @param[in,out] string|array &$data to transmit to the callback handler */ -function call_hooks($name, &$data = null) { - $a = 0; +function call_hooks($name, &$data = null) +{ + $a = 0; - if (isset(App::$hooks[$name])) { - foreach(App::$hooks[$name] as $hook) { - $origfn = $hook[1]; - if($hook[0]) - @include_once($hook[0]); - if(preg_match('|^a:[0-9]+:{.*}$|s', $hook[1])) { - $hook[1] = unserialize($hook[1]); - } - elseif(strpos($hook[1],'::')) { - // We shouldn't need to do this, but it appears that PHP - // isn't able to directly execute a string variable with a class - // method in the manner we are attempting it, so we'll - // turn it into an array. - $hook[1] = explode('::',$hook[1]); - } + if (isset(App::$hooks[$name])) { + foreach (App::$hooks[$name] as $hook) { + $origfn = $hook[1]; + if ($hook[0]) { + @include_once($hook[0]); + } + if (preg_match('|^a:[0-9]+:{.*}$|s', $hook[1])) { + $hook[1] = unserialize($hook[1]); + } elseif (strpos($hook[1], '::')) { + // We shouldn't need to do this, but it appears that PHP + // isn't able to directly execute a string variable with a class + // method in the manner we are attempting it, so we'll + // turn it into an array. + $hook[1] = explode('::', $hook[1]); + } - if(is_callable($hook[1])) { - $func = $hook[1]; - if($hook[3]) - $func($data); - else - $func($a, $data); - } - else { - - // Don't do any DB write calls if we're currently logging a possibly failed DB call. - if(! DBA::$logging) { - // The hook should be removed so we don't process it. - q("DELETE FROM hook WHERE hook = '%s' AND file = '%s' AND fn = '%s'", - dbesc($name), - dbesc($hook[0]), - dbesc($origfn) - ); - } - } - } - } + if (is_callable($hook[1])) { + $func = $hook[1]; + if ($hook[3]) { + $func($data); + } else { + $func($a, $data); + } + } else { + // Don't do any DB write calls if we're currently logging a possibly failed DB call. + if (! DBA::$logging) { + // The hook should be removed so we don't process it. + q( + "DELETE FROM hook WHERE hook = '%s' AND file = '%s' AND fn = '%s'", + dbesc($name), + dbesc($hook[0]), + dbesc($origfn) + ); + } + } + } + } } @@ -528,47 +559,48 @@ function call_hooks($name, &$data = null) { * @param string $plugin the name of the plugin * @return array with the plugin information */ -function get_plugin_info($plugin){ - $m = []; - $info = array( - 'name' => $plugin, - 'description' => '', - 'author' => [], - 'maintainer' => [], - 'version' => '', - 'requires' => '' - ); +function get_plugin_info($plugin) +{ + $m = []; + $info = array( + 'name' => $plugin, + 'description' => '', + 'author' => [], + 'maintainer' => [], + 'version' => '', + 'requires' => '' + ); - if (!is_file("addon/$plugin/$plugin.php")) - return $info; + if (!is_file("addon/$plugin/$plugin.php")) { + return $info; + } - $f = file_get_contents("addon/$plugin/$plugin.php"); - $f = escape_tags($f); - $r = preg_match("|/\*.*\*/|msU", $f, $m); + $f = file_get_contents("addon/$plugin/$plugin.php"); + $f = escape_tags($f); + $r = preg_match("|/\*.*\*/|msU", $f, $m); - if ($r){ - $ll = explode("\n", $m[0]); - foreach( $ll as $l ) { - $l = trim($l, "\t\n\r */"); - if ($l != ""){ - list($k, $v) = array_map("trim", explode(":", $l, 2)); - $k = strtolower($k); - if ($k == 'author' || $k == 'maintainer'){ - $r = preg_match("|([^<]+)<([^>]+)>|", $v, $m); - if ($r) { - $info[$k][] = array('name' => $m[1], 'link' => $m[2]); - } else { - $info[$k][] = array('name' => $v); - } - } - else { - $info[$k] = $v; - } - } - } - } + if ($r) { + $ll = explode("\n", $m[0]); + foreach ($ll as $l) { + $l = trim($l, "\t\n\r */"); + if ($l != "") { + list($k, $v) = array_map("trim", explode(":", $l, 2)); + $k = strtolower($k); + if ($k == 'author' || $k == 'maintainer') { + $r = preg_match("|([^<]+)<([^>]+)>|", $v, $m); + if ($r) { + $info[$k][] = array('name' => $m[1], 'link' => $m[2]); + } else { + $info[$k][] = array('name' => $v); + } + } else { + $info[$k] = $v; + } + } + } + } - return $info; + return $info; } /** @@ -586,116 +618,123 @@ function get_plugin_info($plugin){ * @param string $widget the name of the widget * @return array with the information */ -function get_widget_info($widget){ - $m = []; - $info = array( - 'name' => $widget, - 'description' => '', - 'author' => [], - 'maintainer' => [], - 'version' => '', - 'requires' => '' - ); +function get_widget_info($widget) +{ + $m = []; + $info = array( + 'name' => $widget, + 'description' => '', + 'author' => [], + 'maintainer' => [], + 'version' => '', + 'requires' => '' + ); - $ucwidget = ucfirst($widget); + $ucwidget = ucfirst($widget); - $checkpaths = [ - "Zotlabs/SiteWidget/$ucwidget.php", - "Zotlabs/Widget/$ucwidget.php", - "addon/$ucwidget/$ucwidget.php", - "addon/$widget.php" - ]; + $checkpaths = [ + "Zotlabs/SiteWidget/$ucwidget.php", + "Zotlabs/Widget/$ucwidget.php", + "addon/$ucwidget/$ucwidget.php", + "addon/$widget.php" + ]; - $widget_found = false; + $widget_found = false; - foreach ($checkpaths as $path) { - if (is_file($path)) { - $widget_found = true; - $f = file_get_contents($path); - break; - } - } + foreach ($checkpaths as $path) { + if (is_file($path)) { + $widget_found = true; + $f = file_get_contents($path); + break; + } + } - if(! ($widget_found && $f)) - return $info; + if (! ($widget_found && $f)) { + return $info; + } - $f = escape_tags($f); - $r = preg_match("|/\*.*\*/|msU", $f, $m); + $f = escape_tags($f); + $r = preg_match("|/\*.*\*/|msU", $f, $m); - if ($r) { - $ll = explode("\n", $m[0]); - foreach( $ll as $l ) { - $l = trim($l, "\t\n\r */"); - if ($l != ""){ - list($k, $v) = array_map("trim", explode(":", $l, 2)); - $k = strtolower($k); - if ($k == 'author' || $k == 'maintainer'){ - $r = preg_match("|([^<]+)<([^>]+)>|", $v, $m); - if ($r) { - $info[$k][] = array('name' => $m[1], 'link' => $m[2]); - } else { - $info[$k][] = array('name' => $v); - } - } - else { - $info[$k] = $v; - } - } - } - } + if ($r) { + $ll = explode("\n", $m[0]); + foreach ($ll as $l) { + $l = trim($l, "\t\n\r */"); + if ($l != "") { + list($k, $v) = array_map("trim", explode(":", $l, 2)); + $k = strtolower($k); + if ($k == 'author' || $k == 'maintainer') { + $r = preg_match("|([^<]+)<([^>]+)>|", $v, $m); + if ($r) { + $info[$k][] = array('name' => $m[1], 'link' => $m[2]); + } else { + $info[$k][] = array('name' => $v); + } + } else { + $info[$k] = $v; + } + } + } + } - return $info; + return $info; } -function check_plugin_versions($info) { +function check_plugin_versions($info) +{ - if(! is_array($info)) - return true; + if (! is_array($info)) { + return true; + } - if(array_key_exists('minversion',$info) && $info['minversion']) { - if(! version_compare(STD_VERSION,trim($info['minversion']), '>=')) { - logger('minversion limit: ' . $info['name'],LOGGER_NORMAL,LOG_WARNING); - return false; - } - } - if(array_key_exists('maxversion',$info) && $info['maxversion']) { - if(! version_compare(STD_VERSION,trim($info['maxversion']), '<')) { - logger('maxversion limit: ' . $info['name'],LOGGER_NORMAL,LOG_WARNING); - return false; - } - } - if(array_key_exists('minphpversion',$info) && $info['minphpversion']) { - if(! version_compare(PHP_VERSION,trim($info['minphpversion']), '>=')) { - logger('minphpversion limit: ' . $info['name'],LOGGER_NORMAL,LOG_WARNING); - return false; - } - } + if (array_key_exists('minversion', $info) && $info['minversion']) { + if (! version_compare(STD_VERSION, trim($info['minversion']), '>=')) { + logger('minversion limit: ' . $info['name'], LOGGER_NORMAL, LOG_WARNING); + return false; + } + } + if (array_key_exists('maxversion', $info) && $info['maxversion']) { + if (! version_compare(STD_VERSION, trim($info['maxversion']), '<')) { + logger('maxversion limit: ' . $info['name'], LOGGER_NORMAL, LOG_WARNING); + return false; + } + } + if (array_key_exists('minphpversion', $info) && $info['minphpversion']) { + if (! version_compare(PHP_VERSION, trim($info['minphpversion']), '>=')) { + logger('minphpversion limit: ' . $info['name'], LOGGER_NORMAL, LOG_WARNING); + return false; + } + } - if(array_key_exists('requires',$info)) { - $arr = explode(',',$info['requires']); - $found = true; - if($arr) { - foreach($arr as $test) { - $test = trim($test); - if(! $test) - continue; - if(strpos($test,'.')) { - $conf = explode('.',$test); - if(get_config(trim($conf[0]),trim($conf[1]))) - return true; - else - return false; - } - if(! in_array($test,App::$plugins)) - $found = false; - } - } - if(! $found) - return false; - } + if (array_key_exists('requires', $info)) { + $arr = explode(',', $info['requires']); + $found = true; + if ($arr) { + foreach ($arr as $test) { + $test = trim($test); + if (! $test) { + continue; + } + if (strpos($test, '.')) { + $conf = explode('.', $test); + if (get_config(trim($conf[0]), trim($conf[1]))) { + return true; + } else { + return false; + } + } + if (! in_array($test, App::$plugins)) { + $found = false; + } + } + } + if (! $found) { + return false; + } + } - return true; + return true; } @@ -715,66 +754,69 @@ function check_plugin_versions($info) { * @param string $theme the name of the theme * @return array */ -function get_theme_info($theme){ - $m = []; - $info = array( - 'name' => $theme, - 'description' => '', - 'author' => [], - 'version' => '', - 'minversion' => '', - 'maxversion' => '', - 'compat' => '', - 'credits' => '', - 'maintainer' => [], - 'experimental' => false, - 'unsupported' => false - ); +function get_theme_info($theme) +{ + $m = []; + $info = array( + 'name' => $theme, + 'description' => '', + 'author' => [], + 'version' => '', + 'minversion' => '', + 'maxversion' => '', + 'compat' => '', + 'credits' => '', + 'maintainer' => [], + 'experimental' => false, + 'unsupported' => false + ); - if(file_exists("view/theme/$theme/experimental")) - $info['experimental'] = true; + if (file_exists("view/theme/$theme/experimental")) { + $info['experimental'] = true; + } - if(file_exists("view/theme/$theme/unsupported")) - $info['unsupported'] = true; + if (file_exists("view/theme/$theme/unsupported")) { + $info['unsupported'] = true; + } - if (!is_file("view/theme/$theme/php/theme.php")) - return $info; + if (!is_file("view/theme/$theme/php/theme.php")) { + return $info; + } - $f = file_get_contents("view/theme/$theme/php/theme.php"); - $r = preg_match("|/\*.*\*/|msU", $f, $m); + $f = file_get_contents("view/theme/$theme/php/theme.php"); + $r = preg_match("|/\*.*\*/|msU", $f, $m); - if ($r){ - $ll = explode("\n", $m[0]); - foreach( $ll as $l ) { - $l = trim($l, "\t\n\r */"); - if ($l != ""){ - list($k, $v) = array_map("trim", explode(":", $l, 2)); - $k = strtolower($k); - if ($k == 'author'){ - $r = preg_match("|([^<]+)<([^>]+)>|", $v, $m); - if ($r) { - $info['author'][] = array('name' => $m[1], 'link' => $m[2]); - } else { - $info['author'][] = array('name' => $v); - } - } - elseif ($k == 'maintainer'){ - $r = preg_match("|([^<]+)<([^>]+)>|", $v, $m); - if ($r) { - $info['maintainer'][] = array('name' => $m[1], 'link' => $m[2]); - } else { - $info['maintainer'][] = array('name' => $v); - } - } else { - if (array_key_exists($k, $info)){ - $info[$k] = $v; - } - } - } - } - } + if ($r) { + $ll = explode("\n", $m[0]); + foreach ($ll as $l) { + $l = trim($l, "\t\n\r */"); + if ($l != "") { + list($k, $v) = array_map("trim", explode(":", $l, 2)); + $k = strtolower($k); + if ($k == 'author') { + $r = preg_match("|([^<]+)<([^>]+)>|", $v, $m); + if ($r) { + $info['author'][] = array('name' => $m[1], 'link' => $m[2]); + } else { + $info['author'][] = array('name' => $v); + } + } elseif ($k == 'maintainer') { + $r = preg_match("|([^<]+)<([^>]+)>|", $v, $m); + if ($r) { + $info['maintainer'][] = array('name' => $m[1], 'link' => $m[2]); + } else { + $info['maintainer'][] = array('name' => $v); + } + } else { + if (array_key_exists($k, $info)) { + $info[$k] = $v; + } + } + } + } + } - return $info; + return $info; } /** @@ -785,15 +827,17 @@ function get_theme_info($theme){ * @param string $theme The name of the theme * @return string */ -function get_theme_screenshot($theme) { +function get_theme_screenshot($theme) +{ - $exts = array('.png', '.jpg'); - foreach($exts as $ext) { - if(file_exists('view/theme/' . $theme . '/img/screenshot' . $ext)) - return(z_root() . '/view/theme/' . $theme . '/img/screenshot' . $ext); - } + $exts = array('.png', '.jpg'); + foreach ($exts as $ext) { + if (file_exists('view/theme/' . $theme . '/img/screenshot' . $ext)) { + return(z_root() . '/view/theme/' . $theme . '/img/screenshot' . $ext); + } + } - return(z_root() . '/images/blank.png'); + return(z_root() . '/images/blank.png'); } /** @@ -802,77 +846,84 @@ function get_theme_screenshot($theme) { * @param string $src * @param string $media change media attribute (default to 'screen') */ -function head_add_css($src, $media = 'screen') { - App::$css_sources[] = array($src, $media); +function head_add_css($src, $media = 'screen') +{ + App::$css_sources[] = array($src, $media); } -function head_remove_css($src, $media = 'screen') { +function head_remove_css($src, $media = 'screen') +{ - $index = array_search(array($src, $media), App::$css_sources); - if($index !== false) - unset(App::$css_sources[$index]); + $index = array_search(array($src, $media), App::$css_sources); + if ($index !== false) { + unset(App::$css_sources[$index]); + } } -function head_get_css() { - $str = ''; - $sources = App::$css_sources; - if(count($sources)) { - foreach($sources as $source) - $str .= format_css_if_exists($source); - } +function head_get_css() +{ + $str = ''; + $sources = App::$css_sources; + if (count($sources)) { + foreach ($sources as $source) { + $str .= format_css_if_exists($source); + } + } - return $str; + return $str; } -function head_add_link($arr) { - if($arr) { - App::$linkrel[] = $arr; - } +function head_add_link($arr) +{ + if ($arr) { + App::$linkrel[] = $arr; + } } -function head_get_links() { - $str = ''; - $sources = App::$linkrel; - if(count($sources)) { - foreach($sources as $source) { - if(is_array($source) && count($source)) { - $str .= ' $v) { - $str .= ' ' . $k . '="' . $v . '"'; - } - $str .= ' />' . "\r\n"; +function head_get_links() +{ + $str = ''; + $sources = App::$linkrel; + if (count($sources)) { + foreach ($sources as $source) { + if (is_array($source) && count($source)) { + $str .= ' $v) { + $str .= ' ' . $k . '="' . $v . '"'; + } + $str .= ' />' . "\r\n"; + } + } + } - } - } - } - - return $str; + return $str; } -function format_css_if_exists($source) { +function format_css_if_exists($source) +{ - // script_path() returns https://yoursite.tld + // script_path() returns https://yoursite.tld - $path_prefix = script_path(); + $path_prefix = script_path(); - $script = $source[0]; + $script = $source[0]; - if(strpos($script, '/') !== false) { - // The script is a path relative to the server root - $path = $script; - // If the url starts with // then it's an absolute URL - if(substr($script,0,2) === '//') { - $path_prefix = ''; - } - } else { - // It's a file from the theme - $path = '/' . theme_include($script); - } + if (strpos($script, '/') !== false) { + // The script is a path relative to the server root + $path = $script; + // If the url starts with // then it's an absolute URL + if (substr($script, 0, 2) === '//') { + $path_prefix = ''; + } + } else { + // It's a file from the theme + $path = '/' . theme_include($script); + } - if($path) { - $qstring = ((parse_url($path, PHP_URL_QUERY)) ? '&' : '?') . 'v=' . STD_VERSION; - return '' . "\r\n"; - } + if ($path) { + $qstring = ((parse_url($path, PHP_URL_QUERY)) ? '&' : '?') . 'v=' . STD_VERSION; + return '' . "\r\n"; + } } /** @@ -885,46 +936,51 @@ function format_css_if_exists($source) { * * @return string */ -function script_path() { - if(x($_SERVER,'HTTPS') && $_SERVER['HTTPS']) - $scheme = 'https'; - elseif(x($_SERVER,'SERVER_PORT') && (intval($_SERVER['SERVER_PORT']) == 443)) - $scheme = 'https'; - elseif (!empty($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https' || !empty($_SERVER['HTTP_X_FORWARDED_SSL']) && $_SERVER['HTTP_X_FORWARDED_SSL'] == 'on') - $scheme = 'https'; - else - $scheme = 'http'; +function script_path() +{ + if (x($_SERVER, 'HTTPS') && $_SERVER['HTTPS']) { + $scheme = 'https'; + } elseif (x($_SERVER, 'SERVER_PORT') && (intval($_SERVER['SERVER_PORT']) == 443)) { + $scheme = 'https'; + } elseif (!empty($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https' || !empty($_SERVER['HTTP_X_FORWARDED_SSL']) && $_SERVER['HTTP_X_FORWARDED_SSL'] == 'on') { + $scheme = 'https'; + } else { + $scheme = 'http'; + } - // Some proxy setups may require using http_host + // Some proxy setups may require using http_host - if(isset(App::$config['system']['script_path_use_http_host']) && intval(App::$config['system']['script_path_use_http_host'])) - $server_var = 'HTTP_HOST'; - else - $server_var = 'SERVER_NAME'; + if (isset(App::$config['system']['script_path_use_http_host']) && intval(App::$config['system']['script_path_use_http_host'])) { + $server_var = 'HTTP_HOST'; + } else { + $server_var = 'SERVER_NAME'; + } - if(x($_SERVER,$server_var)) { - $hostname = $_SERVER[$server_var]; - } - else { - return z_root(); - } + if (x($_SERVER, $server_var)) { + $hostname = $_SERVER[$server_var]; + } else { + return z_root(); + } - return $scheme . '://' . $hostname; + return $scheme . '://' . $hostname; } -function head_add_js($src, $priority = 0) { - if(! (isset(App::$js_sources[$priority]) && is_array(App::$js_sources[$priority]))) { - App::$js_sources[$priority] = []; - } - App::$js_sources[$priority][] = $src; +function head_add_js($src, $priority = 0) +{ + if (! (isset(App::$js_sources[$priority]) && is_array(App::$js_sources[$priority]))) { + App::$js_sources[$priority] = []; + } + App::$js_sources[$priority][] = $src; } -function head_remove_js($src, $priority = 0) { +function head_remove_js($src, $priority = 0) +{ - $index = array_search($src, App::$js_sources[$priority]); - if($index !== false) - unset(App::$js_sources[$priority][$index]); + $index = array_search($src, App::$js_sources[$priority]); + if ($index !== false) { + unset(App::$js_sources[$priority][$index]); + } } /** @@ -934,149 +990,162 @@ function head_remove_js($src, $priority = 0) { * * @return string */ -function head_get_js() { +function head_get_js() +{ - $str = ''; - if(App::$js_sources) { - ksort(App::$js_sources,SORT_NUMERIC); - foreach(App::$js_sources as $sources) { - if(count($sources)) { - foreach($sources as $source) { - if($source === 'main.js') - continue; - $str .= format_js_if_exists($source); - } - } - } - } + $str = ''; + if (App::$js_sources) { + ksort(App::$js_sources, SORT_NUMERIC); + foreach (App::$js_sources as $sources) { + if (count($sources)) { + foreach ($sources as $source) { + if ($source === 'main.js') { + continue; + } + $str .= format_js_if_exists($source); + } + } + } + } - return $str; + return $str; } -function head_get_main_js() { - $str = ''; - $sources = array('main.js'); - if(count($sources)) - foreach($sources as $source) - $str .= format_js_if_exists($source,true); +function head_get_main_js() +{ + $str = ''; + $sources = array('main.js'); + if (count($sources)) { + foreach ($sources as $source) { + $str .= format_js_if_exists($source, true); + } + } - return $str; + return $str; } -function format_js_if_exists($source) { - $path_prefix = script_path(); +function format_js_if_exists($source) +{ + $path_prefix = script_path(); - if(strpos($source,'/') !== false) { - // The source is a known path on the system - $path = $source; - // If the url starts with // then it's an absolute URL - if(substr($source,0,2) === '//') { - $path_prefix = ''; - } - } - else { - // It's a file from the theme - $path = '/' . theme_include($source); - } - if($path) { - $qstring = ((parse_url($path, PHP_URL_QUERY)) ? '&' : '?') . 'v=' . STD_VERSION; - return '' . "\r\n" ; - } + if (strpos($source, '/') !== false) { + // The source is a known path on the system + $path = $source; + // If the url starts with // then it's an absolute URL + if (substr($source, 0, 2) === '//') { + $path_prefix = ''; + } + } else { + // It's a file from the theme + $path = '/' . theme_include($source); + } + if ($path) { + $qstring = ((parse_url($path, PHP_URL_QUERY)) ? '&' : '?') . 'v=' . STD_VERSION; + return '' . "\r\n" ; + } } -function theme_include($file, $root = '') { +function theme_include($file, $root = '') +{ - // Make sure $root ends with a slash / if it's not blank - if($root !== '' && substr($root,-1) !== '/') - $root = $root . '/'; - $theme_info = App::$theme_info; + // Make sure $root ends with a slash / if it's not blank + if ($root !== '' && substr($root, -1) !== '/') { + $root = $root . '/'; + } + $theme_info = App::$theme_info; - if(array_key_exists('extends',$theme_info)) - $parent = $theme_info['extends']; - else - $parent = 'NOPATH'; + if (array_key_exists('extends', $theme_info)) { + $parent = $theme_info['extends']; + } else { + $parent = 'NOPATH'; + } - $theme = Zotlabs\Render\Theme::current(); - $thname = $theme[0]; + $theme = Zotlabs\Render\Theme::current(); + $thname = $theme[0]; - $ext = substr($file,strrpos($file,'.')+1); + $ext = substr($file, strrpos($file, '.')+1); - $paths = array( - "{$root}view/theme/$thname/$ext/$file", - "{$root}view/theme/$parent/$ext/$file", - "{$root}view/site/$ext/$file", - "{$root}view/$ext/$file", - ); + $paths = array( + "{$root}view/theme/$thname/$ext/$file", + "{$root}view/theme/$parent/$ext/$file", + "{$root}view/site/$ext/$file", + "{$root}view/$ext/$file", + ); - foreach($paths as $p) { - // strpos() is faster than strstr when checking if one string is in another (http://php.net/manual/en/function.strstr.php) - if(strpos($p,'NOPATH') !== false) - continue; - if(file_exists($p)) - return $p; - } + foreach ($paths as $p) { + // strpos() is faster than strstr when checking if one string is in another (http://php.net/manual/en/function.strstr.php) + if (strpos($p, 'NOPATH') !== false) { + continue; + } + if (file_exists($p)) { + return $p; + } + } - return ''; + return ''; } -function get_intltext_template($s, $root = '') { +function get_intltext_template($s, $root = '') +{ $testroot = ($root=='') ? $testroot = "ROOT" : $root; $t = App::template_engine(); - if (isset(App::$override_intltext_templates[$testroot][$s]["content"])) { - return App::$override_intltext_templates[$testroot][$s]["content"]; - } else { - if (isset(App::$override_intltext_templates[$testroot][$s]["root"]) && - isset(App::$override_intltext_templates[$testroot][$s]["file"])) { - $s = App::$override_intltext_templates[$testroot][$s]["file"]; - $root = App::$override_intltext_templates[$testroot][$s]["root"]; - } elseif (App::$override_templateroot) { - $newroot = App::$override_templateroot.$root; - if ($newroot != '' && substr($newroot,-1) != '/' ) { + if (isset(App::$override_intltext_templates[$testroot][$s]["content"])) { + return App::$override_intltext_templates[$testroot][$s]["content"]; + } else { + if (isset(App::$override_intltext_templates[$testroot][$s]["root"]) && + isset(App::$override_intltext_templates[$testroot][$s]["file"])) { + $s = App::$override_intltext_templates[$testroot][$s]["file"]; + $root = App::$override_intltext_templates[$testroot][$s]["root"]; + } elseif (App::$override_templateroot) { + $newroot = App::$override_templateroot.$root; + if ($newroot != '' && substr($newroot, -1) != '/') { $newroot .= '/'; - } - $template = $t->get_intltext_template($s, $newroot); - } - $template = $t->get_intltext_template($s, $root); - return $template; + } + $template = $t->get_intltext_template($s, $newroot); } + $template = $t->get_intltext_template($s, $root); + return $template; + } } -function get_markup_template($s, $root = '') { +function get_markup_template($s, $root = '') +{ $testroot = ($root=='') ? $testroot = "ROOT" : $root; $t = App::template_engine(); - if (isset(App::$override_markup_templates[$testroot][$s]["content"])) { - return App::$override_markup_templates[$testroot][$s]["content"]; - } else { - if (isset(App::$override_markup_templates[$testroot][$s]["root"]) && - isset(App::$override_markup_templates[$testroot][$s]["file"])) { - $s = App::$override_markup_templates[$testroot][$s]["file"]; - $root = App::$override_markup_templates[$testroot][$s]["root"]; - } elseif (App::$override_templateroot) { - $newroot = App::$override_templateroot.$root; - if ($newroot != '' && substr($newroot,-1) != '/' ) { + if (isset(App::$override_markup_templates[$testroot][$s]["content"])) { + return App::$override_markup_templates[$testroot][$s]["content"]; + } else { + if (isset(App::$override_markup_templates[$testroot][$s]["root"]) && + isset(App::$override_markup_templates[$testroot][$s]["file"])) { + $s = App::$override_markup_templates[$testroot][$s]["file"]; + $root = App::$override_markup_templates[$testroot][$s]["root"]; + } elseif (App::$override_templateroot) { + $newroot = App::$override_templateroot.$root; + if ($newroot != '' && substr($newroot, -1) != '/') { $newroot .= '/'; - } - $template = $t->get_markup_template($s, $newroot); - } - $template = $t->get_markup_template($s, $root); - return $template; + } + $template = $t->get_markup_template($s, $newroot); } + $template = $t->get_markup_template($s, $root); + return $template; + } } /** * @brief * * @param string $folder - * @return boolean|string + * @return bool|string */ -function folder_exists($folder) { - // Get canonicalized absolute pathname - $path = realpath($folder); +function folder_exists($folder) +{ + // Get canonicalized absolute pathname + $path = realpath($folder); - // If it exist, check if it's a directory - return (($path !== false) && is_dir($path)) ? $path : false; + // If it exist, check if it's a directory + return (($path !== false) && is_dir($path)) ? $path : false; } diff --git a/include/api.php b/include/api.php index ec95967db..35c669f0c 100644 --- a/include/api.php +++ b/include/api.php @@ -11,248 +11,250 @@ require_once('include/attach.php'); require_once('include/api_auth.php'); require_once('include/api_zot.php'); - /* - * - * Zot API. - * - */ + /* + * + * Zot API. + * + */ - $API = []; + $API = []; - $called_api = null; + $called_api = null; - // All commands which require authentication accept a "channel" parameter - // which is the left hand side of the channel address/nickname. - // If provided, the desired channel is selected before carrying out the command. - // If not provided, the default channel associated with the account is used. - // If channel selection fails, the API command requiring login will fail. + // All commands which require authentication accept a "channel" parameter + // which is the left hand side of the channel address/nickname. + // If provided, the desired channel is selected before carrying out the command. + // If not provided, the default channel associated with the account is used. + // If channel selection fails, the API command requiring login will fail. - function api_user() { - $aid = get_account_id(); - $channel = App::get_channel(); - - if ($aid && isset($_REQUEST['channel']) && $_REQUEST['channel']) { +function api_user() +{ + $aid = get_account_id(); + $channel = App::get_channel(); + + if ($aid && isset($_REQUEST['channel']) && $_REQUEST['channel']) { + // Only change channel if it is different than the current channel - // Only change channel if it is different than the current channel - - if ($channel && isset($channel['channel_address']) && $channel['channel_address'] !== $_REQUEST['channel']) { - $c = q("select channel_id from channel where channel_address = '%s' and channel_account_id = %d limit 1", - dbesc($_REQUEST['channel']), - intval($aid) - ); - if ((! $c) || (! change_channel($c[0]['channel_id']))) { - return false; - } - } - } - if (isset($_SESSION['allow_api']) && $_SESSION['allow_api']) { - return local_channel(); - } - return false; - } + if ($channel && isset($channel['channel_address']) && $channel['channel_address'] !== $_REQUEST['channel']) { + $c = q( + "select channel_id from channel where channel_address = '%s' and channel_account_id = %d limit 1", + dbesc($_REQUEST['channel']), + intval($aid) + ); + if ((! $c) || (! change_channel($c[0]['channel_id']))) { + return false; + } + } + } + if (isset($_SESSION['allow_api']) && $_SESSION['allow_api']) { + return local_channel(); + } + return false; +} - function api_date($str) { - // Wed May 23 06:01:13 +0000 2007 - return datetime_convert('UTC', 'UTC', $str, 'D M d H:i:s +0000 Y' ); - } +function api_date($str) +{ + // Wed May 23 06:01:13 +0000 2007 + return datetime_convert('UTC', 'UTC', $str, 'D M d H:i:s +0000 Y'); +} - function api_register_func($path, $func, $auth = false) { - Api_router::register($path,$func,$auth); - } +function api_register_func($path, $func, $auth = false) +{ + Api_router::register($path, $func, $auth); +} - - /************************** - * MAIN API ENTRY POINT * - **************************/ + + /************************** + * MAIN API ENTRY POINT * + **************************/ - function api_call() { +function api_call() +{ - $p = App::$cmd; - $type = null; + $p = App::$cmd; + $type = null; - if (strrpos($p,'.')) { - $type = substr($p,strrpos($p,'.')+1); - if (strpos($type,'/') === false) { - $p = substr($p,0,strrpos($p,'.')); - // recalculate App argc,argv since we just extracted the type from it - App::$argv = explode('/',$p); - App::$argc = count(App::$argv); - } - } + if (strrpos($p, '.')) { + $type = substr($p, strrpos($p, '.')+1); + if (strpos($type, '/') === false) { + $p = substr($p, 0, strrpos($p, '.')); + // recalculate App argc,argv since we just extracted the type from it + App::$argv = explode('/', $p); + App::$argc = count(App::$argv); + } + } - if ((! $type) || (! in_array($type, [ 'json', 'xml', 'rss', 'as', 'atom' ]))) { - $type = 'json'; - } + if ((! $type) || (! in_array($type, [ 'json', 'xml', 'rss', 'as', 'atom' ]))) { + $type = 'json'; + } - $info = Api_router::find($p); + $info = Api_router::find($p); - if (in_array($type, [ 'rss', 'atom', 'as' ])) { - // These types no longer supported. - $info = false; - } + if (in_array($type, [ 'rss', 'atom', 'as' ])) { + // These types no longer supported. + $info = false; + } - logger('API info: ' . $p . ' type: ' . $type . ' ' . print_r($info,true), LOGGER_DEBUG,LOG_INFO); + logger('API info: ' . $p . ' type: ' . $type . ' ' . print_r($info, true), LOGGER_DEBUG, LOG_INFO); - if ($info) { + if ($info) { + if ($info['auth'] === true && api_user() === false) { + api_login(); + } - if ($info['auth'] === true && api_user() === false) { - api_login(); - } + load_contact_links(api_user()); - load_contact_links(api_user()); + $channel = App::get_channel(); - $channel = App::get_channel(); + logger('API call for ' . ((isset($channel) && is_array($channel)) ? $channel['channel_name'] : '') . ': ' . App::$query_string); + logger('API parameters: ' . print_r($_REQUEST, true)); - logger('API call for ' . ((isset($channel) && is_array($channel)) ? $channel['channel_name'] : '') . ': ' . App::$query_string); - logger('API parameters: ' . print_r($_REQUEST,true)); + $r = call_user_func($info['func'], $type); - $r = call_user_func($info['func'],$type); + if ($r === false) { + return; + } - if ($r === false) { - return; - } - - switch ($type) { - case 'xml': - header ('Content-Type: text/xml'); - return $r; - break; - case 'json': - header ('Content-Type: application/json'); - // Lookup JSONP to understand these lines. They provide cross-domain AJAX ability. - if ($_GET['callback']) { - $r = $_GET['callback'] . '(' . $r . ')' ; - } - return $r; - break; - } - - } + switch ($type) { + case 'xml': + header('Content-Type: text/xml'); + return $r; + break; + case 'json': + header('Content-Type: application/json'); + // Lookup JSONP to understand these lines. They provide cross-domain AJAX ability. + if ($_GET['callback']) { + $r = $_GET['callback'] . '(' . $r . ')' ; + } + return $r; + break; + } + } - $x = [ 'path' => App::$query_string ]; - call_hooks('api_not_found',$x); + $x = [ 'path' => App::$query_string ]; + call_hooks('api_not_found', $x); - header('HTTP/1.1 404 Not Found'); - logger('API call not implemented: ' . App::$query_string . ' - ' . print_r($_REQUEST,true)); - $r = 'not implemented'; - switch ($type){ - case 'xml': - header ('Content-Type: text/xml'); - return '' . "\n" . $r; - break; - case "json": - header ('Content-Type: application/json'); - return json_encode(array('error' => 'not implemented')); - break; - case "rss": - header ('Content-Type: application/rss+xml'); - return '' . "\n" . $r; - break; - case "atom": - header ('Content-Type: application/atom+xml'); - return '' . "\n" . $r; - break; - } - } + header('HTTP/1.1 404 Not Found'); + logger('API call not implemented: ' . App::$query_string . ' - ' . print_r($_REQUEST, true)); + $r = 'not implemented'; + switch ($type) { + case 'xml': + header('Content-Type: text/xml'); + return '' . "\n" . $r; + break; + case "json": + header('Content-Type: application/json'); + return json_encode(array('error' => 'not implemented')); + break; + case "rss": + header('Content-Type: application/rss+xml'); + return '' . "\n" . $r; + break; + case "atom": + header('Content-Type: application/atom+xml'); + return '' . "\n" . $r; + break; + } +} - /** - * load api $templatename for $type and replace $data array - */ + /** + * load api $templatename for $type and replace $data array + */ - function api_apply_template($templatename, $type, $data){ +function api_apply_template($templatename, $type, $data) +{ - switch ($type) { - case 'xml': - if ($data) { - foreach ($data as $k => $v) { - $ret = arrtoxml(str_replace('$','',$k),$v); - } - } - break; - case 'json': - default: - if ($data) { - foreach ($data as $rv) { - $ret = json_encode($rv); - } - } - break; - } + switch ($type) { + case 'xml': + if ($data) { + foreach ($data as $k => $v) { + $ret = arrtoxml(str_replace('$', '', $k), $v); + } + } + break; + case 'json': + default: + if ($data) { + foreach ($data as $rv) { + $ret = json_encode($rv); + } + } + break; + } - return $ret; - } - + return $ret; +} + - function api_client_register($type) { +function api_client_register($type) +{ - logger('api_client_register: ' . print_r($_REQUEST,true)); - - $ret = []; - $key = random_string(16); - $secret = random_string(16); - $name = trim(escape_tags($_REQUEST['client_name'])); - if (! $name) { - json_return_and_die($ret); - } - if (is_array($_REQUEST['redirect_uris'])) { - $redirect = trim($_REQUEST['redirect_uris'][0]); - } - else { - $redirect = trim($_REQUEST['redirect_uris']); - } - $grant_types = trim($_REQUEST['grant_types']); - $scope = trim($_REQUEST['scopes']); - $icon = trim($_REQUEST['logo_uri']); + logger('api_client_register: ' . print_r($_REQUEST, true)); + + $ret = []; + $key = random_string(16); + $secret = random_string(16); + $name = trim(escape_tags($_REQUEST['client_name'])); + if (! $name) { + json_return_and_die($ret); + } + if (is_array($_REQUEST['redirect_uris'])) { + $redirect = trim($_REQUEST['redirect_uris'][0]); + } else { + $redirect = trim($_REQUEST['redirect_uris']); + } + $grant_types = trim($_REQUEST['grant_types']); + $scope = trim($_REQUEST['scopes']); + $icon = trim($_REQUEST['logo_uri']); - $r = q("INSERT INTO oauth_clients (client_id, client_secret, redirect_uri, grant_types, scope, user_id, client_name) + $r = q( + "INSERT INTO oauth_clients (client_id, client_secret, redirect_uri, grant_types, scope, user_id, client_name) VALUES ( '%s', '%s', '%s', '%s', '%s', '%s', '%s' ) ", - dbesc($key), - dbesc($secret), - dbesc($redirect), - dbesc($grant_types), - dbesc($scope), - dbesc((string) api_user()), - dbesc($name) - ); + dbesc($key), + dbesc($secret), + dbesc($redirect), + dbesc($grant_types), + dbesc($scope), + dbesc((string) api_user()), + dbesc($name) + ); - $ret['client_id'] = $key; - $ret['client_secret'] = $secret; - $ret['expires_at'] = 0; - json_return_and_die($ret); - } - - function api_oauth_request_token($type) { - try { - $oauth = new ZotOAuth1(); - $req = OAuth1Request::from_request(); - logger('Req: ' . var_export($req,true),LOGGER_DATA); - $r = $oauth->fetch_request_token($req); - } - catch(Exception $e) { - logger('oauth_exception: ' . print_r($e->getMessage(),true)); - echo 'error=' . OAuth1Util::urlencode_rfc3986($e->getMessage()); - killme(); - } - echo $r; - killme(); - } - - function api_oauth_access_token($type) { - try { - $oauth = new ZotOAuth1(); - $req = OAuth1Request::from_request(); - $r = $oauth->fetch_access_token($req); - } - catch(Exception $e) { - echo 'error=' . OAuth1Util::urlencode_rfc3986($e->getMessage()); - killme(); - } - echo $r; - killme(); - } + $ret['client_id'] = $key; + $ret['client_secret'] = $secret; + $ret['expires_at'] = 0; + json_return_and_die($ret); +} +function api_oauth_request_token($type) +{ + try { + $oauth = new ZotOAuth1(); + $req = OAuth1Request::from_request(); + logger('Req: ' . var_export($req, true), LOGGER_DATA); + $r = $oauth->fetch_request_token($req); + } catch (Exception $e) { + logger('oauth_exception: ' . print_r($e->getMessage(), true)); + echo 'error=' . OAuth1Util::urlencode_rfc3986($e->getMessage()); + killme(); + } + echo $r; + killme(); +} +function api_oauth_access_token($type) +{ + try { + $oauth = new ZotOAuth1(); + $req = OAuth1Request::from_request(); + $r = $oauth->fetch_access_token($req); + } catch (Exception $e) { + echo 'error=' . OAuth1Util::urlencode_rfc3986($e->getMessage()); + killme(); + } + echo $r; + killme(); +} diff --git a/include/api_auth.php b/include/api_auth.php index 08d68f504..4d249d2a1 100644 --- a/include/api_auth.php +++ b/include/api_auth.php @@ -16,172 +16,171 @@ require_once('include/security.php'); * API Login via basic-auth or OAuth */ -function api_login() { +function api_login() +{ - $record = null; - $remote_auth = false; - $sigblock = null; + $record = null; + $remote_auth = false; + $sigblock = null; - if (array_key_exists('REDIRECT_REMOTE_USER',$_SERVER) && (! array_key_exists('HTTP_AUTHORIZATION',$_SERVER))) { - $_SERVER['HTTP_AUTHORIZATION'] = $_SERVER['REDIRECT_REMOTE_USER']; - } + if (array_key_exists('REDIRECT_REMOTE_USER', $_SERVER) && (! array_key_exists('HTTP_AUTHORIZATION', $_SERVER))) { + $_SERVER['HTTP_AUTHORIZATION'] = $_SERVER['REDIRECT_REMOTE_USER']; + } - // login with oauth + // login with oauth - try { - // OAuth 2.0 - $storage = new OAuth2Storage(DBA::$dba->db); - $server = new OAuth2Server($storage); - $request = Request::createFromGlobals(); - if ($server->verifyResourceRequest($request)) { - $token = $server->getAccessTokenData($request); - $uid = $token['user_id']; - $r = q("SELECT * FROM channel WHERE channel_id = %d LIMIT 1", - intval($uid) - ); - if ($r) { - $record = $r[0]; - } - else { - header('HTTP/1.0 401 Unauthorized'); - echo('This api requires login'); - killme(); - } + try { + // OAuth 2.0 + $storage = new OAuth2Storage(DBA::$dba->db); + $server = new OAuth2Server($storage); + $request = Request::createFromGlobals(); + if ($server->verifyResourceRequest($request)) { + $token = $server->getAccessTokenData($request); + $uid = $token['user_id']; + $r = q( + "SELECT * FROM channel WHERE channel_id = %d LIMIT 1", + intval($uid) + ); + if ($r) { + $record = $r[0]; + } else { + header('HTTP/1.0 401 Unauthorized'); + echo('This api requires login'); + killme(); + } - $_SESSION['uid'] = $record['channel_id']; - $_SESSION['addr'] = $_SERVER['REMOTE_ADDR']; + $_SESSION['uid'] = $record['channel_id']; + $_SESSION['addr'] = $_SERVER['REMOTE_ADDR']; - $x = q("select * from account where account_id = %d LIMIT 1", - intval($record['channel_account_id']) - ); - if ($x) { - require_once('include/security.php'); - authenticate_success($x[0], null, true, false, true, true); - $_SESSION['allow_api'] = true; - call_hooks('logged_in', App::$user); - return; - } - } - else { - // OAuth 1.0 - $oauth = new ZotOAuth1(); - $req = OAuth1Request::from_request(); + $x = q( + "select * from account where account_id = %d LIMIT 1", + intval($record['channel_account_id']) + ); + if ($x) { + require_once('include/security.php'); + authenticate_success($x[0], null, true, false, true, true); + $_SESSION['allow_api'] = true; + call_hooks('logged_in', App::$user); + return; + } + } else { + // OAuth 1.0 + $oauth = new ZotOAuth1(); + $req = OAuth1Request::from_request(); - list($consumer, $token) = $oauth->verify_request($req); + list($consumer, $token) = $oauth->verify_request($req); - if (! is_null($token)) { - $oauth->loginUser($token->uid); + if (! is_null($token)) { + $oauth->loginUser($token->uid); - App::set_oauth_key($consumer->key); + App::set_oauth_key($consumer->key); - call_hooks('logged_in', App::$user); - return; - } - - killme(); - } - } - catch (Exception $e) { - logger($e->getMessage()); - } + call_hooks('logged_in', App::$user); + return; + } + + killme(); + } + } catch (Exception $e) { + logger($e->getMessage()); + } - if (array_key_exists('HTTP_AUTHORIZATION',$_SERVER)) { + if (array_key_exists('HTTP_AUTHORIZATION', $_SERVER)) { + /* Basic authentication */ - /* Basic authentication */ + if (substr(trim($_SERVER['HTTP_AUTHORIZATION']), 0, 5) === 'Basic') { + // ignore base64 decoding errors caused by tricksters + $userpass = @base64_decode(substr(trim($_SERVER['HTTP_AUTHORIZATION']), 6)) ; + if (strlen($userpass)) { + list($name, $password) = explode(':', $userpass); + $_SERVER['PHP_AUTH_USER'] = $name; + $_SERVER['PHP_AUTH_PW'] = $password; + } + } - if (substr(trim($_SERVER['HTTP_AUTHORIZATION']),0,5) === 'Basic') { - // ignore base64 decoding errors caused by tricksters - $userpass = @base64_decode(substr(trim($_SERVER['HTTP_AUTHORIZATION']),6)) ; - if (strlen($userpass)) { - list($name, $password) = explode(':', $userpass); - $_SERVER['PHP_AUTH_USER'] = $name; - $_SERVER['PHP_AUTH_PW'] = $password; - } - } + /* OpenWebAuth */ - /* OpenWebAuth */ + if (substr(trim($_SERVER['HTTP_AUTHORIZATION']), 0, 9) === 'Signature') { + $record = null; - if (substr(trim($_SERVER['HTTP_AUTHORIZATION']),0,9) === 'Signature') { + $sigblock = HTTPSig::parse_sigheader($_SERVER['HTTP_AUTHORIZATION']); + if ($sigblock) { + $keyId = str_replace('acct:', '', $sigblock['keyId']); + if ($keyId) { + $r = q( + "select * from hubloc where hubloc_addr = '%s' or hubloc_id_url = '%s'", + dbesc($keyId), + dbesc($keyId) + ); + if (! $r) { + HTTPSig::get_zotfinger_key($keyId); + $r = q( + "select * from hubloc where hubloc_addr = '%s' or hubloc_id_url = '%s'", + dbesc($keyId), + dbesc($keyId) + ); + } - $record = null; + if ($r) { + $r = Libzot::zot_record_preferred($r); + $c = channelx_by_hash($r['hubloc_hash']); + if ($c) { + $a = q( + "select * from account where account_id = %d limit 1", + intval($c['channel_account_id']) + ); + if ($a) { + $record = [ 'channel' => $c, 'account' => $a[0] ]; + $channel_login = $c['channel_id']; + } + } + } - $sigblock = HTTPSig::parse_sigheader($_SERVER['HTTP_AUTHORIZATION']); - if ($sigblock) { - $keyId = str_replace('acct:','',$sigblock['keyId']); - if ($keyId) { - $r = q("select * from hubloc where hubloc_addr = '%s' or hubloc_id_url = '%s'", - dbesc($keyId), - dbesc($keyId) - ); - if (! $r) { - HTTPSig::get_zotfinger_key($keyId); - $r = q("select * from hubloc where hubloc_addr = '%s' or hubloc_id_url = '%s'", - dbesc($keyId), - dbesc($keyId) - ); - } - - if ($r) { - $r = Libzot::zot_record_preferred($r); - $c = channelx_by_hash($r['hubloc_hash']); - if ($c) { - $a = q("select * from account where account_id = %d limit 1", - intval($c['channel_account_id']) - ); - if ($a) { - $record = [ 'channel' => $c, 'account' => $a[0] ]; - $channel_login = $c['channel_id']; - } - } - } - - if ($record) { - $verified = HTTPSig::verify(EMPTY_STR,$record['channel']['channel_pubkey']); - if (! ($verified && $verified['header_signed'] && $verified['header_valid'])) { - $record = null; - } - } - } - } - } - } + if ($record) { + $verified = HTTPSig::verify(EMPTY_STR, $record['channel']['channel_pubkey']); + if (! ($verified && $verified['header_signed'] && $verified['header_valid'])) { + $record = null; + } + } + } + } + } + } - // process normal login request + // process normal login request - if (isset($_SERVER['PHP_AUTH_USER']) && (! $record)) { - $channel_login = 0; - $record = account_verify_password($_SERVER['PHP_AUTH_USER'],$_SERVER['PHP_AUTH_PW']); - if ($record && $record['channel']) { - $channel_login = $record['channel']['channel_id']; - } - } + if (isset($_SERVER['PHP_AUTH_USER']) && (! $record)) { + $channel_login = 0; + $record = account_verify_password($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW']); + if ($record && $record['channel']) { + $channel_login = $record['channel']['channel_id']; + } + } - if (isset($record['account'])) { - authenticate_success($record['account']); + if (isset($record['account'])) { + authenticate_success($record['account']); - if($channel_login) { - change_channel($channel_login); - } - - $_SESSION['allow_api'] = true; - return true; - } - else { - $_SERVER['PHP_AUTH_PW'] = '*****'; - logger('API_login failure: ' . print_r($_SERVER,true), LOGGER_DEBUG); - log_failed_login('API login failure'); - retry_basic_auth(); - } + if ($channel_login) { + change_channel($channel_login); + } + $_SESSION['allow_api'] = true; + return true; + } else { + $_SERVER['PHP_AUTH_PW'] = '*****'; + logger('API_login failure: ' . print_r($_SERVER, true), LOGGER_DEBUG); + log_failed_login('API login failure'); + retry_basic_auth(); + } } -function retry_basic_auth($method = 'Basic') { - header('WWW-Authenticate: ' . $method . ' realm="' . System::get_platform_name() . '"'); - header('HTTP/1.0 401 Unauthorized'); - echo( t('This api method requires authentication')); - killme(); +function retry_basic_auth($method = 'Basic') +{ + header('WWW-Authenticate: ' . $method . ' realm="' . System::get_platform_name() . '"'); + header('HTTP/1.0 401 Unauthorized'); + echo( t('This api method requires authentication')); + killme(); } - diff --git a/include/api_zot.php b/include/api_zot.php index ee29c49a5..361865e05 100644 --- a/include/api_zot.php +++ b/include/api_zot.php @@ -2,582 +2,641 @@ use Zotlabs\Lib\MastAPI; - - - function zot_api_init() { - // mastodon API specific endpoints - api_register_func('api/v1/apps','api_client_register', false); - api_register_func('api/v1/instance','api_mast_instance',false); - - api_register_func('api/z/1.0/verify','api_verify', true); - api_register_func('api/red/version','api_zot_version',false); - api_register_func('api/z/1.0/version','api_zot_version',false); - api_register_func('api/export/basic','api_export_basic', true); - api_register_func('api/red/channel/export/basic','api_export_basic', true); - api_register_func('api/z/1.0/channel/export/basic','api_export_basic', true); - api_register_func('api/red/item/export_page','api_item_export_page', true); - api_register_func('api/z/1.0/item/export_page','api_item_export_page', true); - api_register_func('api/red/channel/list','api_channel_list', true); - api_register_func('api/z/1.0/channel/list','api_channel_list', true); - api_register_func('api/red/channel/stream','api_channel_stream', true); - api_register_func('api/z/1.0/channel/stream','api_channel_stream', true); - api_register_func('api/red/files','api_attach_list', true); - api_register_func('api/z/1.0/files','api_attach_list', true); - api_register_func('api/red/filemeta', 'api_file_meta', true); - api_register_func('api/z/1.0/filemeta', 'api_file_meta', true); - api_register_func('api/red/filedata', 'api_file_data', true); - api_register_func('api/z/1.0/filedata', 'api_file_data', true); - api_register_func('api/red/file/export', 'api_file_export', true); - api_register_func('api/z/1.0/file/export', 'api_file_export', true); - api_register_func('api/red/file', 'api_file_detail', true); - api_register_func('api/z/1.0/file', 'api_file_detail', true); - api_register_func('api/red/albums','api_albums', true); - api_register_func('api/z/1.0/albums','api_albums', true); - api_register_func('api/red/photos','api_photos', true); - api_register_func('api/z/1.0/photos','api_photos', true); - api_register_func('api/red/photo', 'api_photo_detail', true); - api_register_func('api/z/1.0/photo', 'api_photo_detail', true); - api_register_func('api/red/group_members','api_group_members', true); - api_register_func('api/z/1.0/group_members','api_group_members', true); - api_register_func('api/red/group','api_group', true); - api_register_func('api/z/1.0/group','api_group', true); - api_register_func('api/red/xchan','api_red_xchan',true); - api_register_func('api/z/1.0/xchan','api_red_xchan',true); - api_register_func('api/red/item/update','zot_item_update', true); - api_register_func('api/z/1.0/item/update','zot_item_update', true); - api_register_func('api/red/item/full','red_item', true); - api_register_func('api/z/1.0/item/full','red_item', true); - - api_register_func('api/z/1.0/network/stream','api_network_stream', true); - api_register_func('api/z/1.0/abook','api_zot_abook_xchan',true); - api_register_func('api/z/1.0/abconfig','api_zot_abconfig',true); - api_register_func('api/z/1.0/perm_allowed','api_zot_perm_allowed',true); - - return; - } - - - function api_verify($type) { - if (api_user() === false) { - logger('no channel'); - return false; - } - $channel = channelx_by_n(api_user()); - // logger('channel: ' . print_r($channel,true)); - - json_return_and_die($channel); - } - - - function api_zot_version($type) { - - if($type === 'xml') { - header('Content-type: application/xml'); - echo '' . "\r\n" . '' . Zotlabs\Lib\System::get_project_version() . '' . "\r\n"; - killme(); - } - elseif($type === 'json') { - header('Content-type: application/json'); - echo '"' . Zotlabs\Lib\System::get_project_version() . '"'; - killme(); - } - } - - function api_mast_instance($type) { - json_return_and_die(MastAPI::format_site()); - } - - - - /* - * basic channel export - */ - - function api_export_basic($type) { - if(api_user() === false) { - logger('api_export_basic: no user'); - return false; - } - $sections = (($_REQUEST['sections']) ? explode(',',$_REQUEST['sections']) : ''); - if($_REQUEST['posts']) { - $sections = get_default_export_sections(); - $sections[] = 'items'; - } - - json_return_and_die(identity_basic_export(api_user(),$sections)); - } - - function api_item_export_page($type) { - if(api_user() === false) { - logger('api_item_export_page: no user'); - return false; - } - $page = intval($_REQUEST['page']); - $records = intval($_REQUEST['records']); - if(! $records) { - $records = 50; - } - if(! $_REQUEST['since']) - $start = NULL_DATE; - else { - $start = datetime_convert(date_default_timezone_get(),'UTC', $_REQUEST['since']); - } - $finish = datetime_convert(date_default_timezone_get(),'UTC', (($_REQUEST['until']) ? $_REQUEST['until'] : 'now')); - - json_return_and_die(channel_export_items_page(api_user(),$start,$finish,$page,$records)); - } - - - function api_network_stream($type) { - if(api_user() === false) { - logger('api_channel_stream: no user'); - return false; - } - - $channel = channelx_by_n(api_user()); - if(! $channel) - return false; - - - if($_SERVER['REQUEST_METHOD'] == 'POST') { - // json_return_and_die(post_activity_item($_REQUEST)); - } - else { - if(array_key_exists('dbegin',$_REQUEST)) - $_REQUEST['datequery2'] = $_REQUEST['dbegin']; - if(array_key_exists('dend',$_REQUEST)) - $_REQUEST['datequery'] = $_REQUEST['dend']; - - $arr = $_REQUEST; - $ret = []; - $i = items_fetch($arr,App::get_channel(),get_observer_hash()); - if($i) { - foreach($i as $iv) { - $ret[] = encode_item($iv); - } - } - - json_return_and_die($ret); - } - } - - function api_channel_list($type) { - if(api_user() === false) { - logger('api_channel_stream: no user'); - return false; - } - - $channel = channelx_by_n(api_user()); - if(! $channel) - return false; - - $ret = []; - - $r = q("select channel_address from channel where channel_account_id = %d", - intval($channel['channel_account_id']) - ); - - if($r) { - foreach($r as $rv) { - $ret[] = $rv['channel_address']; - } - } - - json_return_and_die($ret); - - } - - - function api_channel_stream($type) { - if(api_user() === false) { - logger('api_channel_stream: no user'); - return false; - } - - $channel = channelx_by_n(api_user()); - if(! $channel) - return false; - - - if($_SERVER['REQUEST_METHOD'] == 'POST') { - json_return_and_die(post_activity_item($_REQUEST)); - } - else { - $mindate = (($_REQUEST['mindate']) ? datetime_convert(date_default_timezone_get(),'UTC',$_REQUEST['mindate']) : ''); - if(! $mindate) - $mindate = datetime_convert(date_default_timezone_get(),'UTC', 'now - 14 days'); - - json_return_and_die(zot_feed($channel['channel_id'],$channel['channel_hash'],[ 'mindate' => $mindate ])); - } - } - - function api_attach_list($type) { - if(api_user() === false) - return false; - - logger('api_user: ' . api_user()); - $hash = ((array_key_exists('filehash',$_REQUEST)) ? $_REQUEST['filehash'] : ''); - $filename = ((array_key_exists('filename',$_REQUEST)) ? $_REQUEST['filename'] : ''); - $filetype = ((array_key_exists('filetype',$_REQUEST)) ? $_REQUEST['filetype'] : ''); - $start = ((array_key_exists('start',$_REQUEST)) ? intval($_REQUEST['start']) : 0); - $records = ((array_key_exists('records',$_REQUEST)) ? intval($_REQUEST['records']) : 0); - - $since = ((array_key_exists('since',$_REQUEST)) ? datetime_convert(date_default_timezone_get(),'UTC',$_REQUEST['since']) : NULL_DATE); - $until = ((array_key_exists('until',$_REQUEST)) ? datetime_convert(date_default_timezone_get(),'UTC',$_REQUEST['until']) : datetime_convert()); - - $x = attach_list_files(api_user(),get_observer_hash(),$hash,$filename,$filetype,'created asc',$start,$records, $since, $until); - - if($start || $records) { - $x['start'] = $start; - $x['records'] = count($x['results']); - } - - json_return_and_die($x); - } - - - function api_file_meta($type) { - if(api_user() === false) - return false; - if(! $_REQUEST['file_id']) return false; - $r = q("select * from attach where uid = %d and hash = '%s' limit 1", - intval(api_user()), - dbesc($_REQUEST['file_id']) - ); - if($r) { - unset($r[0]['content']); - $ret = array('attach' => $r[0]); - json_return_and_die($ret); - } - killme(); - } - - - - function api_file_data($type) { - if(api_user() === false) - return false; - if(! $_REQUEST['file_id']) return false; - $start = (($_REQUEST['start']) ? intval($_REQUEST['start']) : 0); - $length = (($_REQUEST['length']) ? intval($_REQUEST['length']) : 0); - - $r = q("select * from attach where uid = %d and hash like '%s' limit 1", - intval(api_user()), - dbesc($_REQUEST['file_id'] . '%') - ); - if($r) { - $ptr = $r[0]; - if($length === 0) - $length = intval($ptr['filesize']); - - if($ptr['is_dir']) - $ptr['content'] = ''; - elseif(! intval($r[0]['os_storage'])) { - $ptr['start'] = $start; - $x = substr(dbunescbin($ptr['content']),$start,$length); - $ptr['length'] = strlen($x); - $ptr['content'] = base64_encode($x); - } - else { - $fp = fopen(dbunescbin($ptr['content']),'r'); - if($fp) { - $seek = fseek($fp,$start,SEEK_SET); - $x = fread($fp,$length); - $ptr['start'] = $start; - $ptr['length'] = strlen($x); - $ptr['content'] = base64_encode($x); - } - } - - $ret = array('attach' => $ptr); - json_return_and_die($ret); - } - killme(); - } - - - function api_file_export($type) { - if(api_user() === false) - return false; - if(! $_REQUEST['file_id']) - return false; - - $channel = channelx_by_n(api_user()); - - $ret = attach_export_data($channel,$_REQUEST['file_id']); - - if($ret) { - json_return_and_die($ret); - } - killme(); - } - - - function api_file_detail($type) { - if(api_user() === false) - return false; - if(! $_REQUEST['file_id']) return false; - $r = q("select * from attach where uid = %d and hash = '%s' limit 1", - intval(api_user()), - dbesc($_REQUEST['file_id']) - ); - if($r) { - if($r[0]['is_dir']) - $r[0]['content'] = ''; - elseif(intval($r[0]['os_storage'])) - $r[0]['content'] = base64_encode(file_get_contents(dbunescbin($r[0]['content']))); - else - $r[0]['content'] = base64_encode(dbunescbin($r[0]['content'])); - - $ret = array('attach' => $r[0]); - json_return_and_die($ret); - } - killme(); - } - - - - function api_albums($type) { - if(api_user() === false) - return false; - json_return_and_die(photos_albums_list(App::get_channel(),App::get_observer())); - } - - function api_photos($type) { - if(api_user() === false) - return false; - $album = $_REQUEST['album']; - json_return_and_die(photos_list_photos(App::get_channel(),App::get_observer(),$album)); - } - - function api_photo_detail($type) { - if(api_user() === false) - return false; - if(! $_REQUEST['photo_id']) return false; - $scale = ((array_key_exists('scale',$_REQUEST)) ? intval($_REQUEST['scale']) : 0); - $r = q("select * from photo where uid = %d and resource_id = '%s' and imgscale = %d limit 1", - intval(local_channel()), - dbesc($_REQUEST['photo_id']), - intval($scale) - ); - if($r) { - $data = dbunescbin($r[0]['content']); - if(array_key_exists('os_storage',$r[0]) && intval($r[0]['os_storage'])) - $data = file_get_contents($data); - $r[0]['content'] = base64_encode($data); - $ret = array('photo' => $r[0]); - $i = q("select id from item where uid = %d and resource_type = 'photo' and resource_id = '%s' limit 1", - intval(local_channel()), - dbesc($_REQUEST['photo_id']) - ); - if($i) { - $ii = q("select * from item where parent = %d order by id", - intval($i[0]['id']) - ); - if($ii) { - xchan_query($ii,true,0); - $ii = fetch_post_tags($ii,true); - if($ii) { - $ret['item'] = []; - foreach($ii as $iii) - $ret['item'][] = encode_item($iii,true); - } - } - } - - json_return_and_die($ret); - } - killme(); - } - - function api_group_members($type) { - if(api_user() === false) - return false; - - $r = null; - - if($_REQUEST['group_id']) { - $r = q("select * from pgrp where uid = %d and id = %d limit 1", - intval(api_user()), - intval($_REQUEST['group_id']) - ); - } - elseif($_REQUEST['group_name']) { - $r = q("select * from pgrp where uid = %d and gname = '%s' limit 1", - intval(api_user()), - dbesc($_REQUEST['group_name']) - ); - } - - if($r) { - $x = q("select * from pgrp_member left join abook on abook_xchan = xchan and abook_channel = pgrp_member.uid left join xchan on pgrp_member.xchan = xchan.xchan_hash +function zot_api_init() +{ + // mastodon API specific endpoints + api_register_func('api/v1/apps', 'api_client_register', false); + api_register_func('api/v1/instance', 'api_mast_instance', false); + + api_register_func('api/z/1.0/verify', 'api_verify', true); + api_register_func('api/red/version', 'api_zot_version', false); + api_register_func('api/z/1.0/version', 'api_zot_version', false); + api_register_func('api/export/basic', 'api_export_basic', true); + api_register_func('api/red/channel/export/basic', 'api_export_basic', true); + api_register_func('api/z/1.0/channel/export/basic', 'api_export_basic', true); + api_register_func('api/red/item/export_page', 'api_item_export_page', true); + api_register_func('api/z/1.0/item/export_page', 'api_item_export_page', true); + api_register_func('api/red/channel/list', 'api_channel_list', true); + api_register_func('api/z/1.0/channel/list', 'api_channel_list', true); + api_register_func('api/red/channel/stream', 'api_channel_stream', true); + api_register_func('api/z/1.0/channel/stream', 'api_channel_stream', true); + api_register_func('api/red/files', 'api_attach_list', true); + api_register_func('api/z/1.0/files', 'api_attach_list', true); + api_register_func('api/red/filemeta', 'api_file_meta', true); + api_register_func('api/z/1.0/filemeta', 'api_file_meta', true); + api_register_func('api/red/filedata', 'api_file_data', true); + api_register_func('api/z/1.0/filedata', 'api_file_data', true); + api_register_func('api/red/file/export', 'api_file_export', true); + api_register_func('api/z/1.0/file/export', 'api_file_export', true); + api_register_func('api/red/file', 'api_file_detail', true); + api_register_func('api/z/1.0/file', 'api_file_detail', true); + api_register_func('api/red/albums', 'api_albums', true); + api_register_func('api/z/1.0/albums', 'api_albums', true); + api_register_func('api/red/photos', 'api_photos', true); + api_register_func('api/z/1.0/photos', 'api_photos', true); + api_register_func('api/red/photo', 'api_photo_detail', true); + api_register_func('api/z/1.0/photo', 'api_photo_detail', true); + api_register_func('api/red/group_members', 'api_group_members', true); + api_register_func('api/z/1.0/group_members', 'api_group_members', true); + api_register_func('api/red/group', 'api_group', true); + api_register_func('api/z/1.0/group', 'api_group', true); + api_register_func('api/red/xchan', 'api_red_xchan', true); + api_register_func('api/z/1.0/xchan', 'api_red_xchan', true); + api_register_func('api/red/item/update', 'zot_item_update', true); + api_register_func('api/z/1.0/item/update', 'zot_item_update', true); + api_register_func('api/red/item/full', 'red_item', true); + api_register_func('api/z/1.0/item/full', 'red_item', true); + + api_register_func('api/z/1.0/network/stream', 'api_network_stream', true); + api_register_func('api/z/1.0/abook', 'api_zot_abook_xchan', true); + api_register_func('api/z/1.0/abconfig', 'api_zot_abconfig', true); + api_register_func('api/z/1.0/perm_allowed', 'api_zot_perm_allowed', true); + + return; +} + + +function api_verify($type) +{ + if (api_user() === false) { + logger('no channel'); + return false; + } + $channel = channelx_by_n(api_user()); + // logger('channel: ' . print_r($channel,true)); + + json_return_and_die($channel); +} + + +function api_zot_version($type) +{ + + if ($type === 'xml') { + header('Content-type: application/xml'); + echo '' . "\r\n" . '' . Zotlabs\Lib\System::get_project_version() . '' . "\r\n"; + killme(); + } elseif ($type === 'json') { + header('Content-type: application/json'); + echo '"' . Zotlabs\Lib\System::get_project_version() . '"'; + killme(); + } +} + +function api_mast_instance($type) +{ + json_return_and_die(MastAPI::format_site()); +} + + + + /* + * basic channel export + */ + +function api_export_basic($type) +{ + if (api_user() === false) { + logger('api_export_basic: no user'); + return false; + } + $sections = (($_REQUEST['sections']) ? explode(',', $_REQUEST['sections']) : ''); + if ($_REQUEST['posts']) { + $sections = get_default_export_sections(); + $sections[] = 'items'; + } + + json_return_and_die(identity_basic_export(api_user(), $sections)); +} + +function api_item_export_page($type) +{ + if (api_user() === false) { + logger('api_item_export_page: no user'); + return false; + } + $page = intval($_REQUEST['page']); + $records = intval($_REQUEST['records']); + if (! $records) { + $records = 50; + } + if (! $_REQUEST['since']) { + $start = NULL_DATE; + } else { + $start = datetime_convert(date_default_timezone_get(), 'UTC', $_REQUEST['since']); + } + $finish = datetime_convert(date_default_timezone_get(), 'UTC', (($_REQUEST['until']) ? $_REQUEST['until'] : 'now')); + + json_return_and_die(channel_export_items_page(api_user(), $start, $finish, $page, $records)); +} + + +function api_network_stream($type) +{ + if (api_user() === false) { + logger('api_channel_stream: no user'); + return false; + } + + $channel = channelx_by_n(api_user()); + if (! $channel) { + return false; + } + + + if ($_SERVER['REQUEST_METHOD'] == 'POST') { + // json_return_and_die(post_activity_item($_REQUEST)); + } else { + if (array_key_exists('dbegin', $_REQUEST)) { + $_REQUEST['datequery2'] = $_REQUEST['dbegin']; + } + if (array_key_exists('dend', $_REQUEST)) { + $_REQUEST['datequery'] = $_REQUEST['dend']; + } + + $arr = $_REQUEST; + $ret = []; + $i = items_fetch($arr, App::get_channel(), get_observer_hash()); + if ($i) { + foreach ($i as $iv) { + $ret[] = encode_item($iv); + } + } + + json_return_and_die($ret); + } +} + +function api_channel_list($type) +{ + if (api_user() === false) { + logger('api_channel_stream: no user'); + return false; + } + + $channel = channelx_by_n(api_user()); + if (! $channel) { + return false; + } + + $ret = []; + + $r = q( + "select channel_address from channel where channel_account_id = %d", + intval($channel['channel_account_id']) + ); + + if ($r) { + foreach ($r as $rv) { + $ret[] = $rv['channel_address']; + } + } + + json_return_and_die($ret); +} + + +function api_channel_stream($type) +{ + if (api_user() === false) { + logger('api_channel_stream: no user'); + return false; + } + + $channel = channelx_by_n(api_user()); + if (! $channel) { + return false; + } + + + if ($_SERVER['REQUEST_METHOD'] == 'POST') { + json_return_and_die(post_activity_item($_REQUEST)); + } else { + $mindate = (($_REQUEST['mindate']) ? datetime_convert(date_default_timezone_get(), 'UTC', $_REQUEST['mindate']) : ''); + if (! $mindate) { + $mindate = datetime_convert(date_default_timezone_get(), 'UTC', 'now - 14 days'); + } + + json_return_and_die(zot_feed($channel['channel_id'], $channel['channel_hash'], [ 'mindate' => $mindate ])); + } +} + +function api_attach_list($type) +{ + if (api_user() === false) { + return false; + } + + logger('api_user: ' . api_user()); + $hash = ((array_key_exists('filehash', $_REQUEST)) ? $_REQUEST['filehash'] : ''); + $filename = ((array_key_exists('filename', $_REQUEST)) ? $_REQUEST['filename'] : ''); + $filetype = ((array_key_exists('filetype', $_REQUEST)) ? $_REQUEST['filetype'] : ''); + $start = ((array_key_exists('start', $_REQUEST)) ? intval($_REQUEST['start']) : 0); + $records = ((array_key_exists('records', $_REQUEST)) ? intval($_REQUEST['records']) : 0); + + $since = ((array_key_exists('since', $_REQUEST)) ? datetime_convert(date_default_timezone_get(), 'UTC', $_REQUEST['since']) : NULL_DATE); + $until = ((array_key_exists('until', $_REQUEST)) ? datetime_convert(date_default_timezone_get(), 'UTC', $_REQUEST['until']) : datetime_convert()); + + $x = attach_list_files(api_user(), get_observer_hash(), $hash, $filename, $filetype, 'created asc', $start, $records, $since, $until); + + if ($start || $records) { + $x['start'] = $start; + $x['records'] = count($x['results']); + } + + json_return_and_die($x); +} + + +function api_file_meta($type) +{ + if (api_user() === false) { + return false; + } + if (! $_REQUEST['file_id']) { + return false; + } + $r = q( + "select * from attach where uid = %d and hash = '%s' limit 1", + intval(api_user()), + dbesc($_REQUEST['file_id']) + ); + if ($r) { + unset($r[0]['content']); + $ret = array('attach' => $r[0]); + json_return_and_die($ret); + } + killme(); +} + + + +function api_file_data($type) +{ + if (api_user() === false) { + return false; + } + if (! $_REQUEST['file_id']) { + return false; + } + $start = (($_REQUEST['start']) ? intval($_REQUEST['start']) : 0); + $length = (($_REQUEST['length']) ? intval($_REQUEST['length']) : 0); + + $r = q( + "select * from attach where uid = %d and hash like '%s' limit 1", + intval(api_user()), + dbesc($_REQUEST['file_id'] . '%') + ); + if ($r) { + $ptr = $r[0]; + if ($length === 0) { + $length = intval($ptr['filesize']); + } + + if ($ptr['is_dir']) { + $ptr['content'] = ''; + } elseif (! intval($r[0]['os_storage'])) { + $ptr['start'] = $start; + $x = substr(dbunescbin($ptr['content']), $start, $length); + $ptr['length'] = strlen($x); + $ptr['content'] = base64_encode($x); + } else { + $fp = fopen(dbunescbin($ptr['content']), 'r'); + if ($fp) { + $seek = fseek($fp, $start, SEEK_SET); + $x = fread($fp, $length); + $ptr['start'] = $start; + $ptr['length'] = strlen($x); + $ptr['content'] = base64_encode($x); + } + } + + $ret = array('attach' => $ptr); + json_return_and_die($ret); + } + killme(); +} + + +function api_file_export($type) +{ + if (api_user() === false) { + return false; + } + if (! $_REQUEST['file_id']) { + return false; + } + + $channel = channelx_by_n(api_user()); + + $ret = attach_export_data($channel, $_REQUEST['file_id']); + + if ($ret) { + json_return_and_die($ret); + } + killme(); +} + + +function api_file_detail($type) +{ + if (api_user() === false) { + return false; + } + if (! $_REQUEST['file_id']) { + return false; + } + $r = q( + "select * from attach where uid = %d and hash = '%s' limit 1", + intval(api_user()), + dbesc($_REQUEST['file_id']) + ); + if ($r) { + if ($r[0]['is_dir']) { + $r[0]['content'] = ''; + } elseif (intval($r[0]['os_storage'])) { + $r[0]['content'] = base64_encode(file_get_contents(dbunescbin($r[0]['content']))); + } else { + $r[0]['content'] = base64_encode(dbunescbin($r[0]['content'])); + } + + $ret = array('attach' => $r[0]); + json_return_and_die($ret); + } + killme(); +} + + + +function api_albums($type) +{ + if (api_user() === false) { + return false; + } + json_return_and_die(photos_albums_list(App::get_channel(), App::get_observer())); +} + +function api_photos($type) +{ + if (api_user() === false) { + return false; + } + $album = $_REQUEST['album']; + json_return_and_die(photos_list_photos(App::get_channel(), App::get_observer(), $album)); +} + +function api_photo_detail($type) +{ + if (api_user() === false) { + return false; + } + if (! $_REQUEST['photo_id']) { + return false; + } + $scale = ((array_key_exists('scale', $_REQUEST)) ? intval($_REQUEST['scale']) : 0); + $r = q( + "select * from photo where uid = %d and resource_id = '%s' and imgscale = %d limit 1", + intval(local_channel()), + dbesc($_REQUEST['photo_id']), + intval($scale) + ); + if ($r) { + $data = dbunescbin($r[0]['content']); + if (array_key_exists('os_storage', $r[0]) && intval($r[0]['os_storage'])) { + $data = file_get_contents($data); + } + $r[0]['content'] = base64_encode($data); + $ret = array('photo' => $r[0]); + $i = q( + "select id from item where uid = %d and resource_type = 'photo' and resource_id = '%s' limit 1", + intval(local_channel()), + dbesc($_REQUEST['photo_id']) + ); + if ($i) { + $ii = q( + "select * from item where parent = %d order by id", + intval($i[0]['id']) + ); + if ($ii) { + xchan_query($ii, true, 0); + $ii = fetch_post_tags($ii, true); + if ($ii) { + $ret['item'] = []; + foreach ($ii as $iii) { + $ret['item'][] = encode_item($iii, true); + } + } + } + } + + json_return_and_die($ret); + } + killme(); +} + +function api_group_members($type) +{ + if (api_user() === false) { + return false; + } + + $r = null; + + if ($_REQUEST['group_id']) { + $r = q( + "select * from pgrp where uid = %d and id = %d limit 1", + intval(api_user()), + intval($_REQUEST['group_id']) + ); + } elseif ($_REQUEST['group_name']) { + $r = q( + "select * from pgrp where uid = %d and gname = '%s' limit 1", + intval(api_user()), + dbesc($_REQUEST['group_name']) + ); + } + + if ($r) { + $x = q( + "select * from pgrp_member left join abook on abook_xchan = xchan and abook_channel = pgrp_member.uid left join xchan on pgrp_member.xchan = xchan.xchan_hash where gid = %d", - intval($r[0]['id']) - ); - json_return_and_die($x); - } + intval($r[0]['id']) + ); + json_return_and_die($x); + } +} - } +function api_group($type) +{ + if (api_user() === false) { + return false; + } - function api_group($type) { - if(api_user() === false) - return false; - - $r = q("select * from pgrp where uid = %d", - intval(api_user()) - ); - json_return_and_die($r); - } + $r = q( + "select * from pgrp where uid = %d", + intval(api_user()) + ); + json_return_and_die($r); +} - function api_red_xchan($type) { - if(api_user() === false) - return false; - logger('api_xchan'); +function api_red_xchan($type) +{ + if (api_user() === false) { + return false; + } + logger('api_xchan'); - if($_SERVER['REQUEST_METHOD'] === 'POST') { - logger('xchan_store called by ' . api_user()); - $r = xchan_store($_REQUEST); - } - $r = xchan_fetch($_REQUEST); - json_return_and_die($r); - }; + if ($_SERVER['REQUEST_METHOD'] === 'POST') { + logger('xchan_store called by ' . api_user()); + $r = xchan_store($_REQUEST); + } + $r = xchan_fetch($_REQUEST); + json_return_and_die($r); +} - function api_zot_abook_xchan($type) { - logger('api_abook_xchan'); +function api_zot_abook_xchan($type) +{ + logger('api_abook_xchan'); - if(api_user() === false) - return false; + if (api_user() === false) { + return false; + } - $sql_extra = ((array_key_exists('abook_id',$_REQUEST) && intval($_REQUEST['abook_id'])) ? ' and abook_id = ' . intval($_REQUEST['abook_id']) . ' ' : ''); - if($_SERVER['REQUEST_METHOD'] === 'POST') { - // update - } - $r = q("select * from abook left join xchan on abook_xchan = xchan_hash where abook_channel = %d $sql_extra ", - intval(api_user()) - ); + $sql_extra = ((array_key_exists('abook_id', $_REQUEST) && intval($_REQUEST['abook_id'])) ? ' and abook_id = ' . intval($_REQUEST['abook_id']) . ' ' : ''); + if ($_SERVER['REQUEST_METHOD'] === 'POST') { + // update + } + $r = q( + "select * from abook left join xchan on abook_xchan = xchan_hash where abook_channel = %d $sql_extra ", + intval(api_user()) + ); - json_return_and_die($r); - }; + json_return_and_die($r); +} - function api_zot_abconfig($type) { +function api_zot_abconfig($type) +{ - if(api_user() === false) - return false; + if (api_user() === false) { + return false; + } - $sql_extra = ((array_key_exists('abook_id',$_REQUEST) && intval($_REQUEST['abook_id'])) ? ' and abook_id = ' . intval($_REQUEST['abook_id']) . ' ' : ''); - if($_SERVER['REQUEST_METHOD'] === 'POST') { - // update - } - $r = q("select abconfig.* from abconfig left join abook on abook_xchan = abconfig.xchan and abook_channel = abconfig.chan where abconfig.chan = %d $sql_extra ", - intval(api_user()) - ); + $sql_extra = ((array_key_exists('abook_id', $_REQUEST) && intval($_REQUEST['abook_id'])) ? ' and abook_id = ' . intval($_REQUEST['abook_id']) . ' ' : ''); + if ($_SERVER['REQUEST_METHOD'] === 'POST') { + // update + } + $r = q( + "select abconfig.* from abconfig left join abook on abook_xchan = abconfig.xchan and abook_channel = abconfig.chan where abconfig.chan = %d $sql_extra ", + intval(api_user()) + ); - json_return_and_die($r); + json_return_and_die($r); +} - } +function api_zot_perm_allowed($type) +{ + if (api_user() === false) { + return false; + } - function api_zot_perm_allowed($type) { - if(api_user() === false) - return false; + $perm = ((array_key_exists('perm', $_REQUEST)) ? $_REQUEST['perm'] : ''); - $perm = ((array_key_exists('perm',$_REQUEST)) ? $_REQUEST['perm'] : ''); + if (array_key_exists('abook_id', $_REQUEST) && intval($_REQUEST['abook_id'])) { + $r = q( + "select abook_xchan as hash from abook where abook_id = %d and abook_channel = %d limit 1", + intval($_REQUEST['abook_id']), + intval(api_user()) + ); + } else { + $r = xchan_fetch($_REQUEST); + } - if(array_key_exists('abook_id',$_REQUEST) && intval($_REQUEST['abook_id'])) { - $r = q("select abook_xchan as hash from abook where abook_id = %d and abook_channel = %d limit 1", - intval($_REQUEST['abook_id']), - intval(api_user()) - ); - } - else { - $r = xchan_fetch($_REQUEST); - } + $x = false; - $x = false; + if ($r) { + if ($perm) { + $x = [ [ 'perm' => $perm, 'allowed' => perm_is_allowed(api_user(), $r[0]['hash'], $perm)] ]; + } else { + $x = []; + $p = get_all_perms(api_user(), $r[0]['hash']); + if ($p) { + foreach ($p as $k => $v) { + $x[] = [ 'perm' => $k, 'allowed' => $v ]; + } + } + } + } + + json_return_and_die($x); +} - if($r) { - if($perm) - $x = [ [ 'perm' => $perm, 'allowed' => perm_is_allowed(api_user(), $r[0]['hash'], $perm)] ]; - else { - $x = []; - $p = get_all_perms(api_user(),$r[0]['hash']); - if($p) { - foreach($p as $k => $v) - $x[] = [ 'perm' => $k, 'allowed' => $v ]; - } - } - } - - json_return_and_die($x); +function zot_item_update($type) +{ - } + if (api_user() === false) { + logger('api_red_item_store: no user'); + return false; + } - function zot_item_update($type) { - - if (api_user() === false) { - logger('api_red_item_store: no user'); - return false; - } - - logger('api_red_item_store: REQUEST ' . print_r($_REQUEST,true)); - logger('api_red_item_store: FILES ' . print_r($_FILES,true)); + logger('api_red_item_store: REQUEST ' . print_r($_REQUEST, true)); + logger('api_red_item_store: FILES ' . print_r($_FILES, true)); - // set this so that the item_post() function is quiet and doesn't redirect or emit json + // set this so that the item_post() function is quiet and doesn't redirect or emit json - $_REQUEST['api_source'] = true; - $_REQUEST['profile_uid'] = api_user(); + $_REQUEST['api_source'] = true; + $_REQUEST['profile_uid'] = api_user(); - if(x($_FILES,'media')) { - $_FILES['userfile'] = $_FILES['media']; - // upload the image if we have one - $mod = new Zotlabs\Module\Wall_attach(); - $media = $mod->post(); - if($media) - $_REQUEST['body'] .= "\n\n" . $media; - } + if (x($_FILES, 'media')) { + $_FILES['userfile'] = $_FILES['media']; + // upload the image if we have one + $mod = new Zotlabs\Module\Wall_attach(); + $media = $mod->post(); + if ($media) { + $_REQUEST['body'] .= "\n\n" . $media; + } + } - $mod = new Zotlabs\Module\Item(); - $x = $mod->post(); - json_return_and_die($x); - } + $mod = new Zotlabs\Module\Item(); + $x = $mod->post(); + json_return_and_die($x); +} - function red_item($type) { +function red_item($type) +{ - if (api_user() === false) { - logger('api_red_item_full: no user'); - return false; - } + if (api_user() === false) { + logger('api_red_item_full: no user'); + return false; + } - if($_REQUEST['mid']) { - $arr = array('mid' => $_REQUEST['mid']); - } - elseif($_REQUEST['item_id']) { - $arr = array('item_id' => $_REQUEST['item_id']); - } - else - json_return_and_die([]); + if ($_REQUEST['mid']) { + $arr = array('mid' => $_REQUEST['mid']); + } elseif ($_REQUEST['item_id']) { + $arr = array('item_id' => $_REQUEST['item_id']); + } else { + json_return_and_die([]); + } - $arr['start'] = 0; - $arr['records'] = 999999; - $arr['item_type'] = '*'; - - $i = items_fetch($arr,App::get_channel(),get_observer_hash()); - - if(! $i) - json_return_and_die([]); - - $ret = []; - $tmp = []; - foreach($i as $ii) { - $tmp[] = encode_item($ii,true); - } - $ret['item'] = $tmp; - - json_return_and_die($ret); - } + $arr['start'] = 0; + $arr['records'] = 999999; + $arr['item_type'] = '*'; + $i = items_fetch($arr, App::get_channel(), get_observer_hash()); + if (! $i) { + json_return_and_die([]); + } + $ret = []; + $tmp = []; + foreach ($i as $ii) { + $tmp[] = encode_item($ii, true); + } + $ret['item'] = $tmp; + + json_return_and_die($ret); +} diff --git a/include/attach.php b/include/attach.php index 91463573f..c75912518 100644 --- a/include/attach.php +++ b/include/attach.php @@ -1,4 +1,5 @@ 'text/plain', - 'htm' => 'text/html', - 'html' => 'text/html', - 'css' => 'text/css', - 'md' => 'text/markdown', - 'bb' => 'text/bbcode', + 'txt' => 'text/plain', + 'htm' => 'text/html', + 'html' => 'text/html', + 'css' => 'text/css', + 'md' => 'text/markdown', + 'bb' => 'text/bbcode', 'mc' => 'text/x-multicode', 'abc' => 'text/vnd.abc', - 'csv' => 'text/csv', - 'js' => 'application/javascript', - 'json' => 'application/json', - 'xml' => 'application/xml', - 'swf' => 'application/x-shockwave-flash', - 'flv' => 'video/x-flv', - 'epub' => 'application/epub+zip', - 'c' => 'text/plain', - 'h' => 'text/plain', - 'sh' => 'text/plain', - 'py' => 'text/plain', - 'php' => 'text/plain', - 'rb' => 'text/plain', - 'pdl' => 'text/plain', - 'ics' => 'text/calendar', + 'csv' => 'text/csv', + 'js' => 'application/javascript', + 'json' => 'application/json', + 'xml' => 'application/xml', + 'swf' => 'application/x-shockwave-flash', + 'flv' => 'video/x-flv', + 'epub' => 'application/epub+zip', + 'c' => 'text/plain', + 'h' => 'text/plain', + 'sh' => 'text/plain', + 'py' => 'text/plain', + 'php' => 'text/plain', + 'rb' => 'text/plain', + 'pdl' => 'text/plain', + 'ics' => 'text/calendar', - // images + // images - 'png' => 'image/png', - 'jpe' => 'image/jpeg', - 'jpeg' => 'image/jpeg', - 'jpg' => 'image/jpeg', - 'gif' => 'image/gif', - 'bmp' => 'image/bmp', - 'webp' => 'image/webp', - 'ico' => 'image/vnd.microsoft.icon', - 'tiff' => 'image/tiff', - 'tif' => 'image/tiff', - 'svg' => 'image/svg+xml', - 'svgz' => 'image/svg+xml', + 'png' => 'image/png', + 'jpe' => 'image/jpeg', + 'jpeg' => 'image/jpeg', + 'jpg' => 'image/jpeg', + 'gif' => 'image/gif', + 'bmp' => 'image/bmp', + 'webp' => 'image/webp', + 'ico' => 'image/vnd.microsoft.icon', + 'tiff' => 'image/tiff', + 'tif' => 'image/tiff', + 'svg' => 'image/svg+xml', + 'svgz' => 'image/svg+xml', - // archives - 'zip' => 'application/zip', - 'rar' => 'application/x-rar-compressed', - 'exe' => 'application/x-msdownload', - 'msi' => 'application/x-msdownload', - 'cab' => 'application/vnd.ms-cab-compressed', + // archives + 'zip' => 'application/zip', + 'rar' => 'application/x-rar-compressed', + 'exe' => 'application/x-msdownload', + 'msi' => 'application/x-msdownload', + 'cab' => 'application/vnd.ms-cab-compressed', - // audio/video - 'mp3' => 'audio/mpeg', - 'wav' => 'audio/wav', - 'qt' => 'video/quicktime', - 'mov' => 'video/quicktime', - 'ogg' => 'audio/ogg', - 'ogv' => 'video/ogg', - 'ogx' => 'application/ogg', - 'flac' => 'audio/flac', - 'opus' => 'audio/ogg', - 'webm' => 'video/webm', - 'mp4' => 'video/mp4', - 'mkv' => 'video/x-matroska', + // audio/video + 'mp3' => 'audio/mpeg', + 'wav' => 'audio/wav', + 'qt' => 'video/quicktime', + 'mov' => 'video/quicktime', + 'ogg' => 'audio/ogg', + 'ogv' => 'video/ogg', + 'ogx' => 'application/ogg', + 'flac' => 'audio/flac', + 'opus' => 'audio/ogg', + 'webm' => 'video/webm', + 'mp4' => 'video/mp4', + 'mkv' => 'video/x-matroska', - // adobe - 'pdf' => 'application/pdf', - 'psd' => 'image/vnd.adobe.photoshop', - 'ai' => 'application/postscript', - 'eps' => 'application/postscript', - 'ps' => 'application/postscript', + // adobe + 'pdf' => 'application/pdf', + 'psd' => 'image/vnd.adobe.photoshop', + 'ai' => 'application/postscript', + 'eps' => 'application/postscript', + 'ps' => 'application/postscript', - // ms office - 'doc' => 'application/msword', - 'rtf' => 'application/rtf', - 'xls' => 'application/vnd.ms-excel', - 'ppt' => 'application/vnd.ms-powerpoint', + // ms office + 'doc' => 'application/msword', + 'rtf' => 'application/rtf', + 'xls' => 'application/vnd.ms-excel', + 'ppt' => 'application/vnd.ms-powerpoint', - // open office - 'odt' => 'application/vnd.oasis.opendocument.text', - 'ods' => 'application/vnd.oasis.opendocument.spreadsheet', - 'odp' => 'application/vnd.oasis.opendocument.presentation', - 'odg' => 'application/vnd.oasis.opendocument.graphics', - 'odc' => 'application/vnd.oasis.opendocument.chart', - 'odf' => 'application/vnd.oasis.opendocument.formula', - 'odi' => 'application/vnd.oasis.opendocument.image', - 'odm' => 'application/vnd.oasis.opendocument.text-master', - 'odb' => 'application/vnd.oasis.opendocument.base', - 'odb' => 'application/vnd.oasis.opendocument.database', - 'ott' => 'application/vnd.oasis.opendocument.text-template', - 'ots' => 'application/vnd.oasis.opendocument.spreadsheet-template', - 'otp' => 'application/vnd.oasis.opendocument.presentation-template', - 'otg' => 'application/vnd.oasis.opendocument.graphics-template', - 'otc' => 'application/vnd.oasis.opendocument.chart-template', - 'otf' => 'application/vnd.oasis.opendocument.formula-template', - 'oti' => 'application/vnd.oasis.opendocument.image-template', - 'oth' => 'application/vnd.oasis.opendocument.text-web' - ); + // open office + 'odt' => 'application/vnd.oasis.opendocument.text', + 'ods' => 'application/vnd.oasis.opendocument.spreadsheet', + 'odp' => 'application/vnd.oasis.opendocument.presentation', + 'odg' => 'application/vnd.oasis.opendocument.graphics', + 'odc' => 'application/vnd.oasis.opendocument.chart', + 'odf' => 'application/vnd.oasis.opendocument.formula', + 'odi' => 'application/vnd.oasis.opendocument.image', + 'odm' => 'application/vnd.oasis.opendocument.text-master', + 'odb' => 'application/vnd.oasis.opendocument.base', + 'odb' => 'application/vnd.oasis.opendocument.database', + 'ott' => 'application/vnd.oasis.opendocument.text-template', + 'ots' => 'application/vnd.oasis.opendocument.spreadsheet-template', + 'otp' => 'application/vnd.oasis.opendocument.presentation-template', + 'otg' => 'application/vnd.oasis.opendocument.graphics-template', + 'otc' => 'application/vnd.oasis.opendocument.chart-template', + 'otf' => 'application/vnd.oasis.opendocument.formula-template', + 'oti' => 'application/vnd.oasis.opendocument.image-template', + 'oth' => 'application/vnd.oasis.opendocument.text-web' + ); - $last_dot = strrpos($filename, '.'); - if ($last_dot !== false) { - $ext = strtolower(substr($filename, $last_dot + 1)); - if (array_key_exists($ext, $mime_types)) { - return $mime_types[$ext]; - } - } + $last_dot = strrpos($filename, '.'); + if ($last_dot !== false) { + $ext = strtolower(substr($filename, $last_dot + 1)); + if (array_key_exists($ext, $mime_types)) { + return $mime_types[$ext]; + } + } - return 'application/octet-stream'; + return 'application/octet-stream'; } /** @@ -150,35 +152,37 @@ function z_mime_content_type($filename) { * * \e int|boolean \b results amount of found results, or false * * \e string \b message with error messages if any */ -function attach_count_files($channel_id, $observer, $hash = '', $filename = '', $filetype = '') { +function attach_count_files($channel_id, $observer, $hash = '', $filename = '', $filetype = '') +{ - $ret = [ 'success' => false ]; + $ret = [ 'success' => false ]; - if (! perm_is_allowed($channel_id, $observer, 'read_storage')) { - $ret['message'] = t('Permission denied.'); - return $ret; - } + if (! perm_is_allowed($channel_id, $observer, 'read_storage')) { + $ret['message'] = t('Permission denied.'); + return $ret; + } - require_once('include/security.php'); - $sql_extra = permissions_sql($channel_id); + require_once('include/security.php'); + $sql_extra = permissions_sql($channel_id); - if ($hash) { - $sql_extra .= protect_sprintf(" and hash = '" . dbesc($hash) . "' "); - } - if ($filename) { - $sql_extra .= protect_sprintf(" and filename like '@" . dbesc($filename) . "@' "); - } - if ($filetype) { - $sql_extra .= protect_sprintf(" and filetype like '@" . dbesc($filetype) . "@' "); - } - $r = q("select id, uid, folder from attach where uid = %d $sql_extra", - intval($channel_id) - ); + if ($hash) { + $sql_extra .= protect_sprintf(" and hash = '" . dbesc($hash) . "' "); + } + if ($filename) { + $sql_extra .= protect_sprintf(" and filename like '@" . dbesc($filename) . "@' "); + } + if ($filetype) { + $sql_extra .= protect_sprintf(" and filetype like '@" . dbesc($filetype) . "@' "); + } + $r = q( + "select id, uid, folder from attach where uid = %d $sql_extra", + intval($channel_id) + ); - $ret['success'] = ((is_array($r)) ? true : false); - $ret['results'] = ((is_array($r)) ? count($r) : false); + $ret['success'] = ((is_array($r)) ? true : false); + $ret['results'] = ((is_array($r)) ? count($r) : false); - return $ret; + return $ret; } /** @@ -197,50 +201,52 @@ function attach_count_files($channel_id, $observer, $hash = '', $filename = '', * * \e array|boolean \b results array with results, or false * * \e string \b message with error messages if any */ -function attach_list_files($channel_id, $observer, $hash = '', $filename = '', $filetype = '', $orderby = 'created desc', $start = 0, $entries = 0, $since = '', $until = '') { +function attach_list_files($channel_id, $observer, $hash = '', $filename = '', $filetype = '', $orderby = 'created desc', $start = 0, $entries = 0, $since = '', $until = '') +{ - $ret = [ 'success' => false ]; + $ret = [ 'success' => false ]; - if (! perm_is_allowed($channel_id,$observer, 'view_storage')) { - $ret['message'] = t('Permission denied.'); - return $ret; - } + if (! perm_is_allowed($channel_id, $observer, 'view_storage')) { + $ret['message'] = t('Permission denied.'); + return $ret; + } - require_once('include/security.php'); - $sql_extra = permissions_sql($channel_id); + require_once('include/security.php'); + $sql_extra = permissions_sql($channel_id); - if ($hash) { - $sql_extra .= protect_sprintf(" and hash = '" . dbesc($hash) . "' "); - } - if ($filename) { - $sql_extra .= protect_sprintf(" and filename like '%" . dbesc($filename) . "%' "); - } - if ($filetype) { - $sql_extra .= protect_sprintf(" and filetype like '%" . dbesc($filetype) . "%' "); - } - if ($entries) { - $limit = " limit " . intval($start) . ", " . intval($entries) . " "; - } - if (! $since) { - $since = NULL_DATE; - } - if (! $until) { - $until = datetime_convert(); - } - - $sql_extra .= " and created >= '" . dbesc($since) . "' and created <= '" . dbesc($until) . "' "; + if ($hash) { + $sql_extra .= protect_sprintf(" and hash = '" . dbesc($hash) . "' "); + } + if ($filename) { + $sql_extra .= protect_sprintf(" and filename like '%" . dbesc($filename) . "%' "); + } + if ($filetype) { + $sql_extra .= protect_sprintf(" and filetype like '%" . dbesc($filetype) . "%' "); + } + if ($entries) { + $limit = " limit " . intval($start) . ", " . intval($entries) . " "; + } + if (! $since) { + $since = NULL_DATE; + } + if (! $until) { + $until = datetime_convert(); + } - // Retrieve all columns except 'content' + $sql_extra .= " and created >= '" . dbesc($since) . "' and created <= '" . dbesc($until) . "' "; - $r = q("select id, aid, uid, hash, filename, filetype, filesize, revision, folder, os_path, display_path, os_storage, is_dir, is_photo, flags, created, edited, allow_cid, allow_gid, deny_cid, deny_gid from attach where uid = %d $sql_extra ORDER BY $orderby $limit", - intval($channel_id) - ); + // Retrieve all columns except 'content' - $ret['success'] = ((is_array($r)) ? true : false); - $ret['results'] = ((is_array($r)) ? $r : []); + $r = q( + "select id, aid, uid, hash, filename, filetype, filesize, revision, folder, os_path, display_path, os_storage, is_dir, is_photo, flags, created, edited, allow_cid, allow_gid, deny_cid, deny_gid from attach where uid = %d $sql_extra ORDER BY $orderby $limit", + intval($channel_id) + ); - return $ret; + $ret['success'] = ((is_array($r)) ? true : false); + $ret['results'] = ((is_array($r)) ? $r : []); + + return $ret; } /** @@ -255,110 +261,115 @@ function attach_list_files($channel_id, $observer, $hash = '', $filename = '', $ * @param int $rev (optional) Revision default 0 * @return array */ -function attach_by_hash($hash, $observer_hash, $rev = 0) { +function attach_by_hash($hash, $observer_hash, $rev = 0) +{ - $ret = [ 'success' => false ]; + $ret = [ 'success' => false ]; - // Check for existence, which will also provide us the owner uid + // Check for existence, which will also provide us the owner uid - $sql_extra = ''; - if ($rev == (-1)) { - $sql_extra = " order by revision desc "; - } - elseif ($rev) { - $sql_extra = " and revision = " . intval($rev) . " "; - } - - $r = q("SELECT uid FROM attach WHERE hash = '%s' $sql_extra LIMIT 1", - dbesc($hash) - ); - if (! $r) { - $ret['message'] = t('Item was not found.'); - return $ret; - } + $sql_extra = ''; + if ($rev == (-1)) { + $sql_extra = " order by revision desc "; + } elseif ($rev) { + $sql_extra = " and revision = " . intval($rev) . " "; + } - if (! attach_can_view($r[0]['uid'], $observer_hash, $hash)) { - $ret['message'] = t('Permission denied.'); - return $ret; - } + $r = q( + "SELECT uid FROM attach WHERE hash = '%s' $sql_extra LIMIT 1", + dbesc($hash) + ); + if (! $r) { + $ret['message'] = t('Item was not found.'); + return $ret; + } - // We've already checked for existence and permissions + if (! attach_can_view($r[0]['uid'], $observer_hash, $hash)) { + $ret['message'] = t('Permission denied.'); + return $ret; + } - $r = q("SELECT * FROM attach WHERE hash = '%s' and uid = %d $sql_extra LIMIT 1", - dbesc($hash), - intval($r[0]['uid']) - ); + // We've already checked for existence and permissions - if (! $r) { - $ret['message'] = t('Unknown error.'); - return $ret; - } + $r = q( + "SELECT * FROM attach WHERE hash = '%s' and uid = %d $sql_extra LIMIT 1", + dbesc($hash), + intval($r[0]['uid']) + ); - $r[0]['content'] = dbunescbin($r[0]['content']); + if (! $r) { + $ret['message'] = t('Unknown error.'); + return $ret; + } - $ret['success'] = true; - $ret['data'] = array_shift($r); + $r[0]['content'] = dbunescbin($r[0]['content']); - return $ret; + $ret['success'] = true; + $ret['data'] = array_shift($r); + + return $ret; } -function attach_can_view($uid,$ob_hash,$resource,$token = EMPTY_STR) { +function attach_can_view($uid, $ob_hash, $resource, $token = EMPTY_STR) +{ - $sql_extra = permissions_sql($uid,$ob_hash,'',$token); - $hash = $resource; + $sql_extra = permissions_sql($uid, $ob_hash, '', $token); + $hash = $resource; - if (! $token) { - if (! perm_is_allowed($uid,$ob_hash,'view_storage')) { - return false; - } - } + if (! $token) { + if (! perm_is_allowed($uid, $ob_hash, 'view_storage')) { + return false; + } + } - $r = q("select folder from attach where hash = '%s' and uid = %d $sql_extra", - dbesc($hash), - intval($uid) - ); + $r = q( + "select folder from attach where hash = '%s' and uid = %d $sql_extra", + dbesc($hash), + intval($uid) + ); - if (! $r) { - return false; - } + if (! $r) { + return false; + } - // don't perform recursive folder check when using OCAP. Only when using ACL access. - // For OCAP if the token is valid they can see the thing. - - if ($token) { - return true; - } + // don't perform recursive folder check when using OCAP. Only when using ACL access. + // For OCAP if the token is valid they can see the thing. - return attach_can_view_folder($uid,$ob_hash,$r[0]['folder'],$token); + if ($token) { + return true; + } + return attach_can_view_folder($uid, $ob_hash, $r[0]['folder'], $token); } -function attach_can_view_folder($uid,$ob_hash,$folder_hash,$token = EMPTY_STR) { +function attach_can_view_folder($uid, $ob_hash, $folder_hash, $token = EMPTY_STR) +{ - $sql_extra = permissions_sql($uid,$ob_hash,'',$token); - $hash = $folder_hash; - $result = false; + $sql_extra = permissions_sql($uid, $ob_hash, '', $token); + $hash = $folder_hash; + $result = false; - if ((! $folder_hash) && (! $token)) { - return perm_is_allowed($uid,$ob_hash,'view_storage'); - } + if ((! $folder_hash) && (! $token)) { + return perm_is_allowed($uid, $ob_hash, 'view_storage'); + } - do { - $r = q("select folder from attach where hash = '%s' and uid = %d $sql_extra", - dbesc($hash), - intval($uid) - ); - if (! $r) { - return false; - } - $hash = $r[0]['folder']; - } while($hash); + do { + $r = q( + "select folder from attach where hash = '%s' and uid = %d $sql_extra", + dbesc($hash), + intval($uid) + ); + if (! $r) { + return false; + } + $hash = $r[0]['folder']; + } while ($hash); - return true; + return true; } @@ -376,59 +387,61 @@ function attach_can_view_folder($uid,$ob_hash,$folder_hash,$token = EMPTY_STR) { * * \e string \b message (optional) only when success is false * * \e array \b data array of attach DB entry without data component */ -function attach_by_hash_nodata($hash, $observer_hash, $rev = 0) { +function attach_by_hash_nodata($hash, $observer_hash, $rev = 0) +{ - $ret = [ 'success' => false ]; + $ret = [ 'success' => false ]; - // Check for existence, which will also provide us the owner uid + // Check for existence, which will also provide us the owner uid - $sql_extra = ''; - if ($rev == (-1)) { - $sql_extra = " order by revision desc "; - } - elseif ($rev) { - $sql_extra = " and revision = " . intval($rev) . " "; - } + $sql_extra = ''; + if ($rev == (-1)) { + $sql_extra = " order by revision desc "; + } elseif ($rev) { + $sql_extra = " and revision = " . intval($rev) . " "; + } - $r = q("SELECT uid FROM attach WHERE hash = '%s' $sql_extra LIMIT 1", - dbesc($hash) - ); - if (! $r) { - $ret['message'] = t('Item was not found.'); - return $ret; - } + $r = q( + "SELECT uid FROM attach WHERE hash = '%s' $sql_extra LIMIT 1", + dbesc($hash) + ); + if (! $r) { + $ret['message'] = t('Item was not found.'); + return $ret; + } - if (! perm_is_allowed($r[0]['uid'], $observer_hash, 'view_storage')) { - $ret['message'] = t('Permission denied.'); - return $ret; - } + if (! perm_is_allowed($r[0]['uid'], $observer_hash, 'view_storage')) { + $ret['message'] = t('Permission denied.'); + return $ret; + } - $sql_extra = permissions_sql($r[0]['uid'], $observer_hash); + $sql_extra = permissions_sql($r[0]['uid'], $observer_hash); - // Now we'll see if we can access the attachment + // Now we'll see if we can access the attachment - $a = q("select id, aid, uid, hash, creator, filename, filetype, filesize, revision, folder, os_storage, is_photo, os_path, display_path, is_dir, flags, created, edited, allow_cid, allow_gid, deny_cid, deny_gid from attach where uid = %d and hash = '%s' $sql_extra limit 1", - intval($r[0]['uid']), - dbesc($hash) - ); + $a = q( + "select id, aid, uid, hash, creator, filename, filetype, filesize, revision, folder, os_storage, is_photo, os_path, display_path, is_dir, flags, created, edited, allow_cid, allow_gid, deny_cid, deny_gid from attach where uid = %d and hash = '%s' $sql_extra limit 1", + intval($r[0]['uid']), + dbesc($hash) + ); - if (! $a) { - $ret['message'] = t('Permission denied.'); - return $ret; - } + if (! $a) { + $ret['message'] = t('Permission denied.'); + return $ret; + } - if ($a[0]['folder']) { - $x = attach_can_view_folder($a[0]['uid'], $observer_hash, $a[0]['folder']); - if (! $x) { - $ret['message'] = t('Permission denied.'); - return $ret; - } - } + if ($a[0]['folder']) { + $x = attach_can_view_folder($a[0]['uid'], $observer_hash, $a[0]['folder']); + if (! $x) { + $ret['message'] = t('Permission denied.'); + return $ret; + } + } - $ret['success'] = true; - $ret['data'] = array_shift($a); + $ret['success'] = true; + $ret['data'] = array_shift($a); - return $ret; + return $ret; } /** @@ -466,599 +479,633 @@ function attach_by_hash_nodata($hash, $observer_hash, $rev = 0) { * @param array $arr (optional) associative array * @return void|array */ -function attach_store($channel, $observer_hash, $options = '', $arr = null) { - - require_once('include/photos.php'); - - /** - * @hooks photo_upload_begin - * Called when attempting to upload a photo. - */ - call_hooks('photo_upload_begin', $arr); - - $ret = array('success' => false); - $channel_id = $channel['channel_id']; - $sql_options = ''; - $source = (($arr) ? $arr['source'] : ''); - $album = (($arr) ? $arr['album'] : ''); - $newalbum = (($arr) ? $arr['newalbum'] : ''); - $hash = (($arr && $arr['hash']) ? $arr['hash'] : null); - $upload_path = (($arr && $arr['directory']) ? $arr['directory'] : ''); - $visible = (($arr && $arr['visible']) ? $arr['visible'] : ''); - $notify = (($arr && $arr['notify']) ? $arr['notify'] : ''); - $flags = (($arr && array_key_exists('flags',$arr)) ? intval($arr['flags']) : 0); - $observer = []; - - $dosync = ((array_key_exists('nosync',$arr) && $arr['nosync']) ? 0 : 1); - - if($observer_hash) { - $observer = xchan_match([ 'xchan_hash' => $observer_hash ]); - } - - logger('arr: ' . print_r($arr,true), LOGGER_DATA); - - if(! perm_is_allowed($channel_id,$observer_hash, 'write_storage')) { - $ret['message'] = t('Permission denied.'); - return $ret; - } - - $str_group_allow = perms2str($arr['group_allow']); - $str_contact_allow = perms2str($arr['contact_allow']); - $str_group_deny = perms2str($arr['group_deny']); - $str_contact_deny = perms2str($arr['contact_deny']); - - - // The 'update' option sets db values without uploading a new attachment - // 'replace' replaces the existing uploaded data - // 'revision' creates a new revision with new upload data - // Default is to upload a new file - - // revise or update must provide $arr['hash'] of the thing to revise/update - - // By default remove $src when finished - - $remove_when_processed = true; - $import_replace = false; - - if($options === 'import') { - $src = $arr['src']; - $filename = $arr['filename']; - $filesize = @filesize($src); - - $hash = $arr['resource_id']; - - if(array_key_exists('hash',$arr)) - $hash = $arr['hash']; - if(array_key_exists('type',$arr)) - $type = $arr['type']; - - if($arr['preserve_original']) - $remove_when_processed = false; - - if($arr['replace']) - $import_replace = true; - - // if importing a directory, just do it now and go home - we're done. - - if(array_key_exists('is_dir',$arr) && intval($arr['is_dir'])) { - $x = attach_mkdir($channel,$observer_hash,$arr); - if($x['message']) - logger('import_directory: ' . $x['message']); - - return; - } - } - elseif($options !== 'update') { - $f = [ - 'src' => '', - 'filename' => '', - 'filesize' => 0, - 'type' => '' - ]; - - /** - * @hooks photo_upload_file - * Called to generate alternate filenames for an upload. - * * \e string \b src - return value, default empty - * * \e string \b filename - return value, default empty - * * \e number \b filesize - return value, default 0 - * * \e string \b type - return value, default empty - */ - call_hooks('photo_upload_file', $f); - /** - * @hooks attach_upload_file - * Called when uploading a file. - * * \e string \b src - return value, default empty - * * \e string \b filename - return value, default empty - * * \e number \b filesize - return value, default 0 - * * \e string \b type - return value, default empty - */ - call_hooks('attach_upload_file', $f); - - if (x($f,'src') && x($f,'filesize')) { - $src = $f['src']; - $filename = $f['filename']; - $filesize = $f['filesize']; - $type = $f['type']; - } else { - if(! x($_FILES,'userfile')) { - logger('No source file'); - $ret['message'] = t('No source file.'); - return $ret; - } - - $src = $_FILES['userfile']['tmp_name']; - $filename = basename($_FILES['userfile']['name']); - $filesize = intval($_FILES['userfile']['size']); - } - } - - // AndStatus sends jpegs with a non-standard mimetype - if($type === 'image/jpg') - $type = 'image/jpeg'; - - $existing_size = 0; - - if($options === 'replace') { - $x = q("select id, hash, filesize from attach where id = %d and uid = %d limit 1", - intval($arr['id']), - intval($channel_id) - ); - if(! $x) { - $ret['message'] = t('Cannot locate file to replace'); - return $ret; - } - $existing_id = $x[0]['id']; - $existing_size = intval($x[0]['filesize']); - $hash = $x[0]['hash']; - } - - if($options === 'revise' || $options === 'update') { - $sql_options = " order by revision desc "; - if($options === 'update' && $arr && array_key_exists('revision',$arr)) - $sql_options = " and revision = " . intval($arr['revision']) . " "; - - $x = q("select id, aid, uid, filename, filetype, filesize, hash, revision, folder, os_storage, is_photo, os_path, display_path, flags, created, edited, allow_cid, allow_gid, deny_cid, deny_gid from attach where hash = '%s' and uid = %d $sql_options limit 1", - dbesc($arr['hash']), - intval($channel_id) - ); - if(! $x) { - logger('update file source not found'); - $ret['message'] = t('Cannot locate file to revise/update'); - return $ret; - } - $hash = $x[0]['hash']; - } - - $def_extension = ''; - $is_photo = 0; - $gis = false; - - if ($src) { - $gis = @getimagesize($src); - logger('getimagesize: ' . print_r($gis,true), LOGGER_DATA); - } - - if(($gis) && in_array($gis[2], [ IMAGETYPE_GIF, IMAGETYPE_JPEG, IMAGETYPE_PNG, IMAGETYPE_WEBP ])) { - $is_photo = 1; - if($gis[2] === IMAGETYPE_GIF) - $def_extension = '.gif'; - if($gis[2] === IMAGETYPE_JPEG) - $def_extension = '.jpg'; - if($gis[2] === IMAGETYPE_PNG) - $def_extension = '.png'; - if($gis[2] === IMAGETYPE_WEBP) - $def_extension = '.webp'; - } - - // If we know it's a photo, over-ride the type in case the source system could not determine what it was - - if($is_photo) { - $type = $gis['mime']; - } - - $pathname = ''; - - - // If we were called from the Photos module there is a slightly different mechanism - // for setting the parent path than if we were called from the Files (cloud) module. - - if ($source === 'photos') { - if ($newalbum) { - $pathname = filepath_macro($newalbum); - } - elseif (array_key_exists('folder',$arr)) { - $x = q("select filename from attach where hash = '%s' and uid = %d limit 1", - dbesc($arr['folder']), - intval($channel['channel_id']) - ); - if ($x) { - $pathname = $x[0]['filename']; - } - } - else { - $pathname = filepath_macro($album); - } - } - - if (! $pathname) { - $pathname = filepath_macro($upload_path); - } - - $darr = array('pathname' => $pathname); - - // if we need to create a directory, use the channel default permissions. - - $darr['allow_cid'] = $channel['allow_cid']; - $darr['allow_gid'] = $channel['allow_gid']; - $darr['deny_cid'] = $channel['deny_cid']; - $darr['deny_gid'] = $channel['deny_gid']; - - - $direct = null; - - if ($pathname) { - $x = attach_mkdirp($channel, $observer_hash, $darr); - $folder_hash = (($x['success']) ? $x['data']['hash'] : ''); - $direct = (($x['success']) ? $x['data'] : null); - if ((! $str_contact_allow) && (! $str_group_allow) && (! $str_contact_deny) && (! $str_group_deny)) { - $str_contact_allow = $x['data']['allow_cid']; - $str_group_allow = $x['data']['allow_gid']; - $str_contact_deny = $x['data']['deny_cid']; - $str_group_deny = $x['data']['deny_gid']; - } - } - else { - $folder_hash = ((($arr) && array_key_exists('folder',$arr)) ? $arr['folder'] : ''); - } - - if((! $options) || ($options === 'import')) { - - // A freshly uploaded file. Check for duplicate and resolve with the channel's overwrite settings. - - $r = q("select filename, id, hash, filesize from attach where uid = %d and filename = '%s' and folder = '%s' ", - intval($channel_id), - dbesc($filename), - dbesc($folder_hash) - ); - if($r) { - $overwrite = (($import_replace || get_pconfig($channel_id,'system','overwrite_dup_files')) ? true : false); - if(($overwrite) || ($options === 'import')) { - if(! array_key_exists('edited',$arr)) - $arr['edited'] = datetime_convert(); - $options = 'replace'; - $existing_id = $x[0]['id']; - $existing_size = intval($x[0]['filesize']); - $hash = $x[0]['hash']; - } - else { - if(strpos($filename,'.') !== false) { - $basename = substr($filename,0,strrpos($filename,'.')); - $ext = substr($filename,strrpos($filename,'.')); - } - else { - $basename = $filename; - $ext = $def_extension; - } - - $r = q("select filename from attach where uid = %d and ( filename = '%s' OR filename like '%s' ) and folder = '%s' ", - intval($channel_id), - dbesc($basename . $ext), - dbesc($basename . '(%)' . $ext), - dbesc($folder_hash) - ); - - if($r) { - $x = 1; - - do { - $found = false; - foreach($r as $rr) { - if($rr['filename'] === $basename . '(' . $x . ')' . $ext) { - $found = true; - break; - } - } - if($found) - $x++; - } while($found); - $filename = $basename . '(' . $x . ')' . $ext; - } - else - $filename = $basename . $ext; - } - } - } - - if(! $hash) - $hash = new_uuid(); - - // Check storage limits - if($options !== 'update') { - $maxfilesize = get_config('system','maxfilesize'); - - if(($maxfilesize) && ($filesize > $maxfilesize)) { - logger('quota_exceeded'); - $ret['message'] = sprintf( t('File exceeds size limit of %d'), $maxfilesize); - if($remove_when_processed) - @unlink($src); - - /** - * @hooks photo_upload_end - * Called when a photo upload has been processed. - */ - call_hooks('photo_upload_end', $ret); - - return $ret; - } - - $limit = engr_units_to_bytes(service_class_fetch($channel_id, 'attach_upload_limit')); - - if($limit !== false) { - $r = q("select sum(filesize) as total from attach where aid = %d ", - intval($channel['channel_account_id']) - ); - if(($r) && (($r[0]['total'] + $filesize) > ($limit - $existing_size))) { - logger('service_class limit exceeded'); - $ret['message'] = upgrade_message(true) . sprintf(t("You have reached your limit of %1$.0f Mbytes attachment storage."), $limit / 1024000); - if($remove_when_processed) - @unlink($src); - - /** - * @hooks photo_upload_end - * Called when a photo upload has been processed. - */ - call_hooks('photo_upload_end', $ret); - - return $ret; - } - } - $mimetype = ((isset($type) && $type) ? $type : z_mime_content_type($filename)); - } - - $os_basepath = 'store/' . $channel['channel_address'] . '/' ; - $os_relpath = ''; - - if($folder_hash) { - $curr = find_folder_hash_by_attach_hash($channel_id,$folder_hash,true); - if($curr) - $os_relpath .= $curr . '/'; - $os_relpath .= $folder_hash . '/'; - } - - $os_relpath .= $hash; - $os_relpath = ltrim($os_relpath,'/'); - - $os_path = $os_relpath; - $display_path = ltrim($pathname . '/' . $filename,'/'); - - if($src) { - $istream = @fopen($src,'rb'); - $ostream = @fopen($os_basepath . $os_relpath,'wb'); - if($istream && $ostream) { - pipe_streams($istream,$ostream,65535); - fclose($istream); - fclose($ostream); - } - } - - if(array_key_exists('created', $arr)) - $created = $arr['created']; - else - $created = datetime_convert(); - - if(array_key_exists('edited', $arr)) - $edited = $arr['edited']; - else - $edited = $created; - - if($options === 'replace') { - $r = q("update attach set filename = '%s', filetype = '%s', folder = '%s', filesize = %d, os_storage = %d, is_photo = %d, content = '%s', edited = '%s', os_path = '%s', display_path = '%s' where id = %d and uid = %d", - dbesc($filename), - dbesc($mimetype), - dbesc($folder_hash), - intval($filesize), - intval(1), - intval($is_photo), - dbescbin($os_basepath . $os_relpath), - dbesc($created), - dbesc($os_path), - dbesc($display_path), - intval($existing_id), - intval($channel_id) - ); - } - elseif($options === 'revise') { - $r = q("insert into attach ( aid, uid, hash, creator, filename, filetype, folder, filesize, revision, os_storage, is_photo, content, created, edited, os_path, display_path, allow_cid, allow_gid, deny_cid, deny_gid ) +function attach_store($channel, $observer_hash, $options = '', $arr = null) +{ + + require_once('include/photos.php'); + + /** + * @hooks photo_upload_begin + * Called when attempting to upload a photo. + */ + call_hooks('photo_upload_begin', $arr); + + $ret = array('success' => false); + $channel_id = $channel['channel_id']; + $sql_options = ''; + $source = (($arr) ? $arr['source'] : ''); + $album = (($arr) ? $arr['album'] : ''); + $newalbum = (($arr) ? $arr['newalbum'] : ''); + $hash = (($arr && $arr['hash']) ? $arr['hash'] : null); + $upload_path = (($arr && $arr['directory']) ? $arr['directory'] : ''); + $visible = (($arr && $arr['visible']) ? $arr['visible'] : ''); + $notify = (($arr && $arr['notify']) ? $arr['notify'] : ''); + $flags = (($arr && array_key_exists('flags', $arr)) ? intval($arr['flags']) : 0); + $observer = []; + + $dosync = ((array_key_exists('nosync', $arr) && $arr['nosync']) ? 0 : 1); + + if ($observer_hash) { + $observer = xchan_match([ 'xchan_hash' => $observer_hash ]); + } + + logger('arr: ' . print_r($arr, true), LOGGER_DATA); + + if (! perm_is_allowed($channel_id, $observer_hash, 'write_storage')) { + $ret['message'] = t('Permission denied.'); + return $ret; + } + + $str_group_allow = perms2str($arr['group_allow']); + $str_contact_allow = perms2str($arr['contact_allow']); + $str_group_deny = perms2str($arr['group_deny']); + $str_contact_deny = perms2str($arr['contact_deny']); + + + // The 'update' option sets db values without uploading a new attachment + // 'replace' replaces the existing uploaded data + // 'revision' creates a new revision with new upload data + // Default is to upload a new file + + // revise or update must provide $arr['hash'] of the thing to revise/update + + // By default remove $src when finished + + $remove_when_processed = true; + $import_replace = false; + + if ($options === 'import') { + $src = $arr['src']; + $filename = $arr['filename']; + $filesize = @filesize($src); + + $hash = $arr['resource_id']; + + if (array_key_exists('hash', $arr)) { + $hash = $arr['hash']; + } + if (array_key_exists('type', $arr)) { + $type = $arr['type']; + } + + if ($arr['preserve_original']) { + $remove_when_processed = false; + } + + if ($arr['replace']) { + $import_replace = true; + } + + // if importing a directory, just do it now and go home - we're done. + + if (array_key_exists('is_dir', $arr) && intval($arr['is_dir'])) { + $x = attach_mkdir($channel, $observer_hash, $arr); + if ($x['message']) { + logger('import_directory: ' . $x['message']); + } + + return; + } + } elseif ($options !== 'update') { + $f = [ + 'src' => '', + 'filename' => '', + 'filesize' => 0, + 'type' => '' + ]; + + /** + * @hooks photo_upload_file + * Called to generate alternate filenames for an upload. + * * \e string \b src - return value, default empty + * * \e string \b filename - return value, default empty + * * \e number \b filesize - return value, default 0 + * * \e string \b type - return value, default empty + */ + call_hooks('photo_upload_file', $f); + /** + * @hooks attach_upload_file + * Called when uploading a file. + * * \e string \b src - return value, default empty + * * \e string \b filename - return value, default empty + * * \e number \b filesize - return value, default 0 + * * \e string \b type - return value, default empty + */ + call_hooks('attach_upload_file', $f); + + if (x($f, 'src') && x($f, 'filesize')) { + $src = $f['src']; + $filename = $f['filename']; + $filesize = $f['filesize']; + $type = $f['type']; + } else { + if (! x($_FILES, 'userfile')) { + logger('No source file'); + $ret['message'] = t('No source file.'); + return $ret; + } + + $src = $_FILES['userfile']['tmp_name']; + $filename = basename($_FILES['userfile']['name']); + $filesize = intval($_FILES['userfile']['size']); + } + } + + // AndStatus sends jpegs with a non-standard mimetype + if ($type === 'image/jpg') { + $type = 'image/jpeg'; + } + + $existing_size = 0; + + if ($options === 'replace') { + $x = q( + "select id, hash, filesize from attach where id = %d and uid = %d limit 1", + intval($arr['id']), + intval($channel_id) + ); + if (! $x) { + $ret['message'] = t('Cannot locate file to replace'); + return $ret; + } + $existing_id = $x[0]['id']; + $existing_size = intval($x[0]['filesize']); + $hash = $x[0]['hash']; + } + + if ($options === 'revise' || $options === 'update') { + $sql_options = " order by revision desc "; + if ($options === 'update' && $arr && array_key_exists('revision', $arr)) { + $sql_options = " and revision = " . intval($arr['revision']) . " "; + } + + $x = q( + "select id, aid, uid, filename, filetype, filesize, hash, revision, folder, os_storage, is_photo, os_path, display_path, flags, created, edited, allow_cid, allow_gid, deny_cid, deny_gid from attach where hash = '%s' and uid = %d $sql_options limit 1", + dbesc($arr['hash']), + intval($channel_id) + ); + if (! $x) { + logger('update file source not found'); + $ret['message'] = t('Cannot locate file to revise/update'); + return $ret; + } + $hash = $x[0]['hash']; + } + + $def_extension = ''; + $is_photo = 0; + $gis = false; + + if ($src) { + $gis = @getimagesize($src); + logger('getimagesize: ' . print_r($gis, true), LOGGER_DATA); + } + + if (($gis) && in_array($gis[2], [ IMAGETYPE_GIF, IMAGETYPE_JPEG, IMAGETYPE_PNG, IMAGETYPE_WEBP ])) { + $is_photo = 1; + if ($gis[2] === IMAGETYPE_GIF) { + $def_extension = '.gif'; + } + if ($gis[2] === IMAGETYPE_JPEG) { + $def_extension = '.jpg'; + } + if ($gis[2] === IMAGETYPE_PNG) { + $def_extension = '.png'; + } + if ($gis[2] === IMAGETYPE_WEBP) { + $def_extension = '.webp'; + } + } + + // If we know it's a photo, over-ride the type in case the source system could not determine what it was + + if ($is_photo) { + $type = $gis['mime']; + } + + $pathname = ''; + + + // If we were called from the Photos module there is a slightly different mechanism + // for setting the parent path than if we were called from the Files (cloud) module. + + if ($source === 'photos') { + if ($newalbum) { + $pathname = filepath_macro($newalbum); + } elseif (array_key_exists('folder', $arr)) { + $x = q( + "select filename from attach where hash = '%s' and uid = %d limit 1", + dbesc($arr['folder']), + intval($channel['channel_id']) + ); + if ($x) { + $pathname = $x[0]['filename']; + } + } else { + $pathname = filepath_macro($album); + } + } + + if (! $pathname) { + $pathname = filepath_macro($upload_path); + } + + $darr = array('pathname' => $pathname); + + // if we need to create a directory, use the channel default permissions. + + $darr['allow_cid'] = $channel['allow_cid']; + $darr['allow_gid'] = $channel['allow_gid']; + $darr['deny_cid'] = $channel['deny_cid']; + $darr['deny_gid'] = $channel['deny_gid']; + + + $direct = null; + + if ($pathname) { + $x = attach_mkdirp($channel, $observer_hash, $darr); + $folder_hash = (($x['success']) ? $x['data']['hash'] : ''); + $direct = (($x['success']) ? $x['data'] : null); + if ((! $str_contact_allow) && (! $str_group_allow) && (! $str_contact_deny) && (! $str_group_deny)) { + $str_contact_allow = $x['data']['allow_cid']; + $str_group_allow = $x['data']['allow_gid']; + $str_contact_deny = $x['data']['deny_cid']; + $str_group_deny = $x['data']['deny_gid']; + } + } else { + $folder_hash = ((($arr) && array_key_exists('folder', $arr)) ? $arr['folder'] : ''); + } + + if ((! $options) || ($options === 'import')) { + // A freshly uploaded file. Check for duplicate and resolve with the channel's overwrite settings. + + $r = q( + "select filename, id, hash, filesize from attach where uid = %d and filename = '%s' and folder = '%s' ", + intval($channel_id), + dbesc($filename), + dbesc($folder_hash) + ); + if ($r) { + $overwrite = (($import_replace || get_pconfig($channel_id, 'system', 'overwrite_dup_files')) ? true : false); + if (($overwrite) || ($options === 'import')) { + if (! array_key_exists('edited', $arr)) { + $arr['edited'] = datetime_convert(); + } + $options = 'replace'; + $existing_id = $x[0]['id']; + $existing_size = intval($x[0]['filesize']); + $hash = $x[0]['hash']; + } else { + if (strpos($filename, '.') !== false) { + $basename = substr($filename, 0, strrpos($filename, '.')); + $ext = substr($filename, strrpos($filename, '.')); + } else { + $basename = $filename; + $ext = $def_extension; + } + + $r = q( + "select filename from attach where uid = %d and ( filename = '%s' OR filename like '%s' ) and folder = '%s' ", + intval($channel_id), + dbesc($basename . $ext), + dbesc($basename . '(%)' . $ext), + dbesc($folder_hash) + ); + + if ($r) { + $x = 1; + + do { + $found = false; + foreach ($r as $rr) { + if ($rr['filename'] === $basename . '(' . $x . ')' . $ext) { + $found = true; + break; + } + } + if ($found) { + $x++; + } + } while ($found); + $filename = $basename . '(' . $x . ')' . $ext; + } else { + $filename = $basename . $ext; + } + } + } + } + + if (! $hash) { + $hash = new_uuid(); + } + + // Check storage limits + if ($options !== 'update') { + $maxfilesize = get_config('system', 'maxfilesize'); + + if (($maxfilesize) && ($filesize > $maxfilesize)) { + logger('quota_exceeded'); + $ret['message'] = sprintf(t('File exceeds size limit of %d'), $maxfilesize); + if ($remove_when_processed) { + @unlink($src); + } + + /** + * @hooks photo_upload_end + * Called when a photo upload has been processed. + */ + call_hooks('photo_upload_end', $ret); + + return $ret; + } + + $limit = engr_units_to_bytes(service_class_fetch($channel_id, 'attach_upload_limit')); + + if ($limit !== false) { + $r = q( + "select sum(filesize) as total from attach where aid = %d ", + intval($channel['channel_account_id']) + ); + if (($r) && (($r[0]['total'] + $filesize) > ($limit - $existing_size))) { + logger('service_class limit exceeded'); + $ret['message'] = upgrade_message(true) . sprintf(t("You have reached your limit of %1$.0f Mbytes attachment storage."), $limit / 1024000); + if ($remove_when_processed) { + @unlink($src); + } + + /** + * @hooks photo_upload_end + * Called when a photo upload has been processed. + */ + call_hooks('photo_upload_end', $ret); + + return $ret; + } + } + $mimetype = ((isset($type) && $type) ? $type : z_mime_content_type($filename)); + } + + $os_basepath = 'store/' . $channel['channel_address'] . '/' ; + $os_relpath = ''; + + if ($folder_hash) { + $curr = find_folder_hash_by_attach_hash($channel_id, $folder_hash, true); + if ($curr) { + $os_relpath .= $curr . '/'; + } + $os_relpath .= $folder_hash . '/'; + } + + $os_relpath .= $hash; + $os_relpath = ltrim($os_relpath, '/'); + + $os_path = $os_relpath; + $display_path = ltrim($pathname . '/' . $filename, '/'); + + if ($src) { + $istream = @fopen($src, 'rb'); + $ostream = @fopen($os_basepath . $os_relpath, 'wb'); + if ($istream && $ostream) { + pipe_streams($istream, $ostream, 65535); + fclose($istream); + fclose($ostream); + } + } + + if (array_key_exists('created', $arr)) { + $created = $arr['created']; + } else { + $created = datetime_convert(); + } + + if (array_key_exists('edited', $arr)) { + $edited = $arr['edited']; + } else { + $edited = $created; + } + + if ($options === 'replace') { + $r = q( + "update attach set filename = '%s', filetype = '%s', folder = '%s', filesize = %d, os_storage = %d, is_photo = %d, content = '%s', edited = '%s', os_path = '%s', display_path = '%s' where id = %d and uid = %d", + dbesc($filename), + dbesc($mimetype), + dbesc($folder_hash), + intval($filesize), + intval(1), + intval($is_photo), + dbescbin($os_basepath . $os_relpath), + dbesc($created), + dbesc($os_path), + dbesc($display_path), + intval($existing_id), + intval($channel_id) + ); + } elseif ($options === 'revise') { + $r = q( + "insert into attach ( aid, uid, hash, creator, filename, filetype, folder, filesize, revision, os_storage, is_photo, content, created, edited, os_path, display_path, allow_cid, allow_gid, deny_cid, deny_gid ) VALUES ( %d, %d, '%s', '%s', '%s', '%s', '%s', %d, %d, %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s' ) ", - intval($x[0]['aid']), - intval($channel_id), - dbesc($x[0]['hash']), - dbesc($observer_hash), - dbesc($filename), - dbesc($mimetype), - dbesc($folder_hash), - intval($filesize), - intval($x[0]['revision'] + 1), - intval(1), - intval($is_photo), - dbescbin($os_basepath . $os_relpath), - dbesc($created), - dbesc($created), - dbesc($os_path), - dbesc($display_path), - dbesc($x[0]['allow_cid']), - dbesc($x[0]['allow_gid']), - dbesc($x[0]['deny_cid']), - dbesc($x[0]['deny_gid']) - ); - } - elseif($options === 'update') { - $r = q("update attach set filename = '%s', filetype = '%s', folder = '%s', edited = '%s', os_storage = %d, is_photo = %d, os_path = '%s', + intval($x[0]['aid']), + intval($channel_id), + dbesc($x[0]['hash']), + dbesc($observer_hash), + dbesc($filename), + dbesc($mimetype), + dbesc($folder_hash), + intval($filesize), + intval($x[0]['revision'] + 1), + intval(1), + intval($is_photo), + dbescbin($os_basepath . $os_relpath), + dbesc($created), + dbesc($created), + dbesc($os_path), + dbesc($display_path), + dbesc($x[0]['allow_cid']), + dbesc($x[0]['allow_gid']), + dbesc($x[0]['deny_cid']), + dbesc($x[0]['deny_gid']) + ); + } elseif ($options === 'update') { + $r = q( + "update attach set filename = '%s', filetype = '%s', folder = '%s', edited = '%s', os_storage = %d, is_photo = %d, os_path = '%s', display_path = '%s', allow_cid = '%s', allow_gid = '%s', deny_cid = '%s', deny_gid = '%s' where id = %d and uid = %d", - dbesc((array_key_exists('filename',$arr)) ? $arr['filename'] : $x[0]['filename']), - dbesc((array_key_exists('filetype',$arr)) ? $arr['filetype'] : $x[0]['filetype']), - dbesc(($folder_hash) ? $folder_hash : $x[0]['folder']), - dbesc($created), - dbesc((array_key_exists('os_storage',$arr)) ? $arr['os_storage'] : $x[0]['os_storage']), - dbesc((array_key_exists('is_photo',$arr)) ? $arr['is_photo'] : $x[0]['is_photo']), - dbesc((array_key_exists('os_path',$arr)) ? $arr['os_path'] : $x[0]['os_path']), - dbesc((array_key_exists('display_path',$arr)) ? $arr['display_path'] : $x[0]['display_path']), - dbesc((array_key_exists('allow_cid',$arr)) ? $arr['allow_cid'] : $x[0]['allow_cid']), - dbesc((array_key_exists('allow_gid',$arr)) ? $arr['allow_gid'] : $x[0]['allow_gid']), - dbesc((array_key_exists('deny_cid',$arr)) ? $arr['deny_cid'] : $x[0]['deny_cid']), - dbesc((array_key_exists('deny_gid',$arr)) ? $arr['deny_gid'] : $x[0]['deny_gid']), - intval($x[0]['id']), - intval($x[0]['uid']) - ); - } - else { - - $r = q("INSERT INTO attach ( aid, uid, hash, creator, filename, filetype, folder, filesize, revision, os_storage, is_photo, flags, content, created, edited, os_path, display_path, allow_cid, allow_gid,deny_cid, deny_gid ) + dbesc((array_key_exists('filename', $arr)) ? $arr['filename'] : $x[0]['filename']), + dbesc((array_key_exists('filetype', $arr)) ? $arr['filetype'] : $x[0]['filetype']), + dbesc(($folder_hash) ? $folder_hash : $x[0]['folder']), + dbesc($created), + dbesc((array_key_exists('os_storage', $arr)) ? $arr['os_storage'] : $x[0]['os_storage']), + dbesc((array_key_exists('is_photo', $arr)) ? $arr['is_photo'] : $x[0]['is_photo']), + dbesc((array_key_exists('os_path', $arr)) ? $arr['os_path'] : $x[0]['os_path']), + dbesc((array_key_exists('display_path', $arr)) ? $arr['display_path'] : $x[0]['display_path']), + dbesc((array_key_exists('allow_cid', $arr)) ? $arr['allow_cid'] : $x[0]['allow_cid']), + dbesc((array_key_exists('allow_gid', $arr)) ? $arr['allow_gid'] : $x[0]['allow_gid']), + dbesc((array_key_exists('deny_cid', $arr)) ? $arr['deny_cid'] : $x[0]['deny_cid']), + dbesc((array_key_exists('deny_gid', $arr)) ? $arr['deny_gid'] : $x[0]['deny_gid']), + intval($x[0]['id']), + intval($x[0]['uid']) + ); + } else { + $r = q( + "INSERT INTO attach ( aid, uid, hash, creator, filename, filetype, folder, filesize, revision, os_storage, is_photo, flags, content, created, edited, os_path, display_path, allow_cid, allow_gid,deny_cid, deny_gid ) VALUES ( %d, %d, '%s', '%s', '%s', '%s', '%s', %d, %d, %d, %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s' ) ", - intval($channel['channel_account_id']), - intval($channel_id), - dbesc($hash), - dbesc(get_observer_hash()), - dbesc($filename), - dbesc($mimetype), - dbesc($folder_hash), - intval($filesize), - intval(0), - intval(1), - intval($is_photo), - intval($flags), - dbescbin($os_basepath . $os_relpath), - dbesc($created), - dbesc($created), - dbesc($os_path), - dbesc($display_path), - dbesc(($arr && array_key_exists('allow_cid',$arr)) ? $arr['allow_cid'] : $str_contact_allow), - dbesc(($arr && array_key_exists('allow_gid',$arr)) ? $arr['allow_gid'] : $str_group_allow), - dbesc(($arr && array_key_exists('deny_cid',$arr)) ? $arr['deny_cid'] : $str_contact_deny), - dbesc(($arr && array_key_exists('deny_gid',$arr)) ? $arr['deny_gid'] : $str_group_deny) - ); - } + intval($channel['channel_account_id']), + intval($channel_id), + dbesc($hash), + dbesc(get_observer_hash()), + dbesc($filename), + dbesc($mimetype), + dbesc($folder_hash), + intval($filesize), + intval(0), + intval(1), + intval($is_photo), + intval($flags), + dbescbin($os_basepath . $os_relpath), + dbesc($created), + dbesc($created), + dbesc($os_path), + dbesc($display_path), + dbesc(($arr && array_key_exists('allow_cid', $arr)) ? $arr['allow_cid'] : $str_contact_allow), + dbesc(($arr && array_key_exists('allow_gid', $arr)) ? $arr['allow_gid'] : $str_group_allow), + dbesc(($arr && array_key_exists('deny_cid', $arr)) ? $arr['deny_cid'] : $str_contact_deny), + dbesc(($arr && array_key_exists('deny_gid', $arr)) ? $arr['deny_gid'] : $str_group_deny) + ); + } - if ($is_photo) { - - // Call the photos library to generate all the standard thumbnails. - // This may fail if the image format isn't supported by the image library *or* memory is exhausted during the attempt. - // That's why this is called at the end of the upload when all the important work to store as a normal file has been accomplished. - - $args = array( 'source' => $source, 'visible' => $visible, 'resource_id' => $hash, 'album' => $pathname, 'folder' => $folder_hash, 'os_syspath' => $os_basepath . $os_relpath, 'os_path' => $os_path, 'display_path' => $display_path, 'filename' => $filename, 'getimagesize' => $gis, 'directory' => $direct, 'options' => $options ); - if($arr['contact_allow']) - $args['contact_allow'] = $arr['contact_allow']; - if($arr['group_allow']) - $args['group_allow'] = $arr['group_allow']; - if($arr['contact_deny']) - $args['contact_deny'] = $arr['contact_deny']; - if($arr['group_deny']) - $args['group_deny'] = $arr['group_deny']; - if(array_key_exists('allow_cid',$arr)) - $args['allow_cid'] = $arr['allow_cid']; - if(array_key_exists('allow_gid',$arr)) - $args['allow_gid'] = $arr['allow_gid']; - if(array_key_exists('deny_cid',$arr)) - $args['deny_cid'] = $arr['deny_cid']; - if(array_key_exists('deny_gid',$arr)) - $args['deny_gid'] = $arr['deny_gid']; + if ($is_photo) { + // Call the photos library to generate all the standard thumbnails. + // This may fail if the image format isn't supported by the image library *or* memory is exhausted during the attempt. + // That's why this is called at the end of the upload when all the important work to store as a normal file has been accomplished. - $args['created'] = $created; - $args['edited'] = $edited; - if($arr['item']) - $args['item'] = $arr['item']; + $args = array( 'source' => $source, 'visible' => $visible, 'resource_id' => $hash, 'album' => $pathname, 'folder' => $folder_hash, 'os_syspath' => $os_basepath . $os_relpath, 'os_path' => $os_path, 'display_path' => $display_path, 'filename' => $filename, 'getimagesize' => $gis, 'directory' => $direct, 'options' => $options ); + if ($arr['contact_allow']) { + $args['contact_allow'] = $arr['contact_allow']; + } + if ($arr['group_allow']) { + $args['group_allow'] = $arr['group_allow']; + } + if ($arr['contact_deny']) { + $args['contact_deny'] = $arr['contact_deny']; + } + if ($arr['group_deny']) { + $args['group_deny'] = $arr['group_deny']; + } + if (array_key_exists('allow_cid', $arr)) { + $args['allow_cid'] = $arr['allow_cid']; + } + if (array_key_exists('allow_gid', $arr)) { + $args['allow_gid'] = $arr['allow_gid']; + } + if (array_key_exists('deny_cid', $arr)) { + $args['deny_cid'] = $arr['deny_cid']; + } + if (array_key_exists('deny_gid', $arr)) { + $args['deny_gid'] = $arr['deny_gid']; + } - if($arr['description']) - $args['description'] = $arr['description']; + $args['created'] = $created; + $args['edited'] = $edited; + if ($arr['item']) { + $args['item'] = $arr['item']; + } - if($arr['title']) - $args['title'] = $arr['title']; + if ($arr['description']) { + $args['description'] = $arr['description']; + } - if($arr['body']) - $args['body'] = $arr['body']; + if ($arr['title']) { + $args['title'] = $arr['title']; + } + + if ($arr['body']) { + $args['body'] = $arr['body']; + } - $args['deliver'] = $dosync; + $args['deliver'] = $dosync; - $p = photo_upload($channel,$observer,$args); - if($p['success']) { - $ret['body'] = $p['body']; - } - } + $p = photo_upload($channel, $observer, $args); + if ($p['success']) { + $ret['body'] = $p['body']; + } + } - if(($options !== 'update') && ($remove_when_processed)) - @unlink($src); + if (($options !== 'update') && ($remove_when_processed)) { + @unlink($src); + } - if(! $r) { - $ret['message'] = t('File upload failed. Possible system limit or action terminated.'); + if (! $r) { + $ret['message'] = t('File upload failed. Possible system limit or action terminated.'); - /** - * @hooks photo_upload_end - * Called when a photo upload has been processed. - */ - call_hooks('photo_upload_end', $ret); + /** + * @hooks photo_upload_end + * Called when a photo upload has been processed. + */ + call_hooks('photo_upload_end', $ret); - return $ret; - } + return $ret; + } - // Update the folder timestamp @todo recurse to the storage root folder + // Update the folder timestamp @todo recurse to the storage root folder - if($folder_hash) { - q("UPDATE attach set edited = '%s' where hash = '%s' and uid = %d and is_dir = 1", - dbesc($edited), - dbesc($folder_hash), - intval($channel_id) - ); - } + if ($folder_hash) { + q( + "UPDATE attach set edited = '%s' where hash = '%s' and uid = %d and is_dir = 1", + dbesc($edited), + dbesc($folder_hash), + intval($channel_id) + ); + } - // Caution: This re-uses $sql_options set further above + // Caution: This re-uses $sql_options set further above - $r = q("select * from attach where uid = %d and hash = '%s' $sql_options limit 1", - intval($channel_id), - dbesc($hash) - ); + $r = q( + "select * from attach where uid = %d and hash = '%s' $sql_options limit 1", + intval($channel_id), + dbesc($hash) + ); - if(! $r) { - $ret['message'] = t('Stored file could not be verified. Upload failed.'); + if (! $r) { + $ret['message'] = t('Stored file could not be verified. Upload failed.'); - /** - * @hooks photo_upload_end - * Called when a photo upload has been processed. - */ - call_hooks('photo_upload_end', $ret); + /** + * @hooks photo_upload_end + * Called when a photo upload has been processed. + */ + call_hooks('photo_upload_end', $ret); - return $ret; - } + return $ret; + } - $ret['success'] = true; - $ret['data'] = $r[0]; - if(! $is_photo) { - /** - * @hooks photo_upload_end - * Called when a photo upload has been processed. - * This would've been called already with a success result in photos_upload() if it was a photo. - */ - call_hooks('photo_upload_end', $ret); - } + $ret['success'] = true; + $ret['data'] = $r[0]; + if (! $is_photo) { + /** + * @hooks photo_upload_end + * Called when a photo upload has been processed. + * This would've been called already with a success result in photos_upload() if it was a photo. + */ + call_hooks('photo_upload_end', $ret); + } - Run::Summon([ 'Thumbnail' , $hash ]); + Run::Summon([ 'Thumbnail' , $hash ]); - if($dosync) { - $sync = attach_export_data($channel,$hash); + if ($dosync) { + $sync = attach_export_data($channel, $hash); - if($sync) - Libsync::build_sync_packet($channel['channel_id'],array('file' => array($sync))); - } + if ($sync) { + Libsync::build_sync_packet($channel['channel_id'], array('file' => array($sync))); + } + } - if($notify) { - $cloudPath = z_root() . '/cloud/' . $channel['channel_address'] . '/' . $r['0']['display_path']; - $object = get_file_activity_object($channel['channel_id'], $r['0']['hash'], $cloudPath); - file_activity($channel['channel_id'], $object, $r['0']['allow_cid'], $r['0']['allow_gid'], $r['0']['deny_cid'], $r['0']['deny_gid'], 'post', $notify); - } + if ($notify) { + $cloudPath = z_root() . '/cloud/' . $channel['channel_address'] . '/' . $r['0']['display_path']; + $object = get_file_activity_object($channel['channel_id'], $r['0']['hash'], $cloudPath); + file_activity($channel['channel_id'], $object, $r['0']['allow_cid'], $r['0']['allow_gid'], $r['0']['deny_cid'], $r['0']['deny_gid'], 'post', $notify); + } - return $ret; + return $ret; } /** @@ -1066,7 +1113,7 @@ function attach_store($channel, $observer_hash, $options = '', $arr = null) { * * Also checking permissions of all parent components. * - * @param integer $channel_id + * @param int $channel_id * @param string $observer_hash hash of current observer * @param string $pathname * @param string $parent_hash (optional) @@ -1076,49 +1123,51 @@ function attach_store($channel, $observer_hash, $options = '', $arr = null) { * * \e string \b message error message if success is false * * \e array \b data array of attach DB entries without data component */ -function z_readdir($channel_id, $observer_hash, $pathname, $parent_hash = '') { +function z_readdir($channel_id, $observer_hash, $pathname, $parent_hash = '') +{ - $ret = [ 'success' => false ]; + $ret = [ 'success' => false ]; - if (! perm_is_allowed($channel_id, $observer_hash, 'view_storage')) { - $ret['message'] = t('Permission denied.'); - return $ret; - } + if (! perm_is_allowed($channel_id, $observer_hash, 'view_storage')) { + $ret['message'] = t('Permission denied.'); + return $ret; + } - if (strpos($pathname, '/')) { - $paths = explode('/', $pathname); - if (count($paths) > 1) { - $curpath = array_shift($paths); + if (strpos($pathname, '/')) { + $paths = explode('/', $pathname); + if (count($paths) > 1) { + $curpath = array_shift($paths); - $r = q("select hash, id, is_dir from attach where uid = %d and filename = '%s' and is_dir != 0 " . permissions_sql($channel_id,$observer_hash) . " limit 1", - intval($channel_id), - dbesc($curpath) - ); - if (! $r) { - $ret['message'] = t('Path not available.'); - return $ret; - } - // recurse - return z_readdir($channel_id, $observer_hash, implode('/', $paths), $r[0]['hash']); - } - } - else { - $paths = array($pathname); - } - - $r = q("select id, aid, uid, hash, creator, filename, filetype, filesize, revision, folder, os_path, display_path, is_photo, is_dir, os_storage, flags, created, edited, allow_cid, allow_gid, deny_cid, deny_gid from attach where id = %d and folder = '%s' and filename = '%s' and is_dir != 0 " . permissions_sql($channel_id), - intval($channel_id), - dbesc($parent_hash), - dbesc($paths[0]) - ); - if (! $r) { - $ret['message'] = t('Path not available.'); - return $ret; - } - $ret['success'] = true; - $ret['data'] = $r; + $r = q( + "select hash, id, is_dir from attach where uid = %d and filename = '%s' and is_dir != 0 " . permissions_sql($channel_id, $observer_hash) . " limit 1", + intval($channel_id), + dbesc($curpath) + ); + if (! $r) { + $ret['message'] = t('Path not available.'); + return $ret; + } + // recurse + return z_readdir($channel_id, $observer_hash, implode('/', $paths), $r[0]['hash']); + } + } else { + $paths = array($pathname); + } - return $ret; + $r = q( + "select id, aid, uid, hash, creator, filename, filetype, filesize, revision, folder, os_path, display_path, is_photo, is_dir, os_storage, flags, created, edited, allow_cid, allow_gid, deny_cid, deny_gid from attach where id = %d and folder = '%s' and filename = '%s' and is_dir != 0 " . permissions_sql($channel_id), + intval($channel_id), + dbesc($parent_hash), + dbesc($paths[0]) + ); + if (! $r) { + $ret['message'] = t('Path not available.'); + return $ret; + } + $ret['success'] = true; + $ret['data'] = $r; + + return $ret; } /** @@ -1138,159 +1187,161 @@ function z_readdir($channel_id, $observer_hash, $pathname, $parent_hash = '') { * * \e string \b deny_gid * @return array */ -function attach_mkdir($channel, $observer_hash, $arr = null) { +function attach_mkdir($channel, $observer_hash, $arr = null) +{ - $ret = array('success' => false); - $channel_id = $channel['channel_id']; + $ret = array('success' => false); + $channel_id = $channel['channel_id']; - $sql_options = ''; + $sql_options = ''; - $os_basepath = 'store/' . $channel['channel_address']; + $os_basepath = 'store/' . $channel['channel_address']; - logger('basepath: ' . $os_basepath, LOGGER_DEBUG); + logger('basepath: ' . $os_basepath, LOGGER_DEBUG); - if (! is_dir($os_basepath)) { - os_mkdir($os_basepath,STORAGE_DEFAULT_PERMISSIONS, true); - } + if (! is_dir($os_basepath)) { + os_mkdir($os_basepath, STORAGE_DEFAULT_PERMISSIONS, true); + } - $os_basepath .= '/'; + $os_basepath .= '/'; - if (! perm_is_allowed($channel_id, $observer_hash, 'write_storage')) { - $ret['message'] = t('Permission denied.'); - return $ret; - } + if (! perm_is_allowed($channel_id, $observer_hash, 'write_storage')) { + $ret['message'] = t('Permission denied.'); + return $ret; + } - if (! $arr['filename']) { - $ret['message'] = t('Empty pathname'); - return $ret; - } + if (! $arr['filename']) { + $ret['message'] = t('Empty pathname'); + return $ret; + } - $arr['hash'] = (($arr['hash']) ? $arr['hash'] : new_uuid()); + $arr['hash'] = (($arr['hash']) ? $arr['hash'] : new_uuid()); - // Check for duplicate name. - // Check both the filename and the hash as we will be making use of both. + // Check for duplicate name. + // Check both the filename and the hash as we will be making use of both. - $r = q("select id, hash, is_dir, flags from attach where ( filename = '%s' or hash = '%s' ) and folder = '%s' and uid = %d limit 1", - dbesc($arr['filename']), - dbesc($arr['hash']), - dbesc($arr['folder']), - intval($channel['channel_id']) - ); - if ($r) { - if (array_key_exists('force',$arr) && intval($arr['force']) && (intval($r[0]['is_dir']))) { - $ret['success'] = true; - $r = q("select * from attach where id = %d limit 1", - intval($r[0]['id']) - ); - if ($r) { - $ret['data'] = $r[0]; - } - return $ret; - } - $ret['message'] = t('duplicate filename or path'); - return $ret; - } + $r = q( + "select id, hash, is_dir, flags from attach where ( filename = '%s' or hash = '%s' ) and folder = '%s' and uid = %d limit 1", + dbesc($arr['filename']), + dbesc($arr['hash']), + dbesc($arr['folder']), + intval($channel['channel_id']) + ); + if ($r) { + if (array_key_exists('force', $arr) && intval($arr['force']) && (intval($r[0]['is_dir']))) { + $ret['success'] = true; + $r = q( + "select * from attach where id = %d limit 1", + intval($r[0]['id']) + ); + if ($r) { + $ret['data'] = $r[0]; + } + return $ret; + } + $ret['message'] = t('duplicate filename or path'); + return $ret; + } - if ($arr['folder']) { + if ($arr['folder']) { + // Walk the directory tree from parent back to root to make sure the parent is valid and name is unique and we + // have permission to see this path. This implies the root directory itself is public since we won't have permissions + // set on the psuedo-directory. We can however set permissions for anything and everything contained within it. - // Walk the directory tree from parent back to root to make sure the parent is valid and name is unique and we - // have permission to see this path. This implies the root directory itself is public since we won't have permissions - // set on the psuedo-directory. We can however set permissions for anything and everything contained within it. + $lpath = ''; + $lfile = $arr['folder']; + $dpath = ''; - $lpath = ''; - $lfile = $arr['folder']; - $dpath = ''; - - $sql_options = permissions_sql($channel['channel_id']); + $sql_options = permissions_sql($channel['channel_id']); - do { - $r = q("select filename, hash, flags, is_dir, folder, display_path from attach where uid = %d and hash = '%s' and is_dir = 1 + do { + $r = q( + "select filename, hash, flags, is_dir, folder, display_path from attach where uid = %d and hash = '%s' and is_dir = 1 $sql_options limit 1", - intval($channel['channel_id']), - dbesc($lfile) - ); - if (! $r) { - logger('attach_mkdir: hash ' . $lfile . ' not found in ' . $lpath); - $ret['message'] = t('Path not found.'); - return $ret; - } + intval($channel['channel_id']), + dbesc($lfile) + ); + if (! $r) { + logger('attach_mkdir: hash ' . $lfile . ' not found in ' . $lpath); + $ret['message'] = t('Path not found.'); + return $ret; + } - $dpath = $r[0]['filename'] . (($dpath) ? '/' . $dpath : ''); + $dpath = $r[0]['filename'] . (($dpath) ? '/' . $dpath : ''); - if ($lfile) { - $lpath = $r[0]['hash'] . (($lpath) ? '/' . $lpath : ''); - } + if ($lfile) { + $lpath = $r[0]['hash'] . (($lpath) ? '/' . $lpath : ''); + } - $lfile = $r[0]['folder']; + $lfile = $r[0]['folder']; + } while (($r[0]['folder']) && intval($r[0]['is_dir'])); - } while ( ($r[0]['folder']) && intval($r[0]['is_dir'])) ; + $path = $lpath; + } else { + $path = ''; + } - $path = $lpath; - } - else { - $path = ''; - } + $created = datetime_convert(); - $created = datetime_convert(); - - $os_path = ltrim($path . '/' . $arr['hash'],'/'); - $display_path = ltrim($dpath . '/' . $arr['filename'],'/'); + $os_path = ltrim($path . '/' . $arr['hash'], '/'); + $display_path = ltrim($dpath . '/' . $arr['filename'], '/'); - $r = q("INSERT INTO attach ( aid, uid, hash, creator, filename, filetype, filesize, revision, folder, os_storage, is_dir, content, created, edited, os_path, display_path, allow_cid, allow_gid, deny_cid, deny_gid ) + $r = q( + "INSERT INTO attach ( aid, uid, hash, creator, filename, filetype, filesize, revision, folder, os_storage, is_dir, content, created, edited, os_path, display_path, allow_cid, allow_gid, deny_cid, deny_gid ) VALUES ( %d, %d, '%s', '%s', '%s', '%s', %d, %d, '%s', %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s' ) ", - intval($channel['channel_account_id']), - intval($channel_id), - dbesc($arr['hash']), - dbesc(get_observer_hash()), - dbesc($arr['filename']), - dbesc('multipart/mixed'), - intval(0), - intval(0), - dbesc($arr['folder']), - intval(1), - intval(1), - dbescbin($os_basepath . $os_path), - dbesc($created), - dbesc($created), - dbesc($os_path), - dbesc($display_path), - dbesc(($arr && array_key_exists('allow_cid',$arr)) ? $arr['allow_cid'] : $channel['channel_allow_cid']), - dbesc(($arr && array_key_exists('allow_gid',$arr)) ? $arr['allow_gid'] : $channel['channel_allow_gid']), - dbesc(($arr && array_key_exists('deny_cid',$arr)) ? $arr['deny_cid'] : $channel['channel_deny_cid']), - dbesc(($arr && array_key_exists('deny_gid',$arr)) ? $arr['deny_gid'] : $channel['channel_deny_gid']) - ); + intval($channel['channel_account_id']), + intval($channel_id), + dbesc($arr['hash']), + dbesc(get_observer_hash()), + dbesc($arr['filename']), + dbesc('multipart/mixed'), + intval(0), + intval(0), + dbesc($arr['folder']), + intval(1), + intval(1), + dbescbin($os_basepath . $os_path), + dbesc($created), + dbesc($created), + dbesc($os_path), + dbesc($display_path), + dbesc(($arr && array_key_exists('allow_cid', $arr)) ? $arr['allow_cid'] : $channel['channel_allow_cid']), + dbesc(($arr && array_key_exists('allow_gid', $arr)) ? $arr['allow_gid'] : $channel['channel_allow_gid']), + dbesc(($arr && array_key_exists('deny_cid', $arr)) ? $arr['deny_cid'] : $channel['channel_deny_cid']), + dbesc(($arr && array_key_exists('deny_gid', $arr)) ? $arr['deny_gid'] : $channel['channel_deny_gid']) + ); - if ($r) { - if (os_mkdir($os_basepath . $os_path, STORAGE_DEFAULT_PERMISSIONS, true)) { - $ret['success'] = true; + if ($r) { + if (os_mkdir($os_basepath . $os_path, STORAGE_DEFAULT_PERMISSIONS, true)) { + $ret['success'] = true; - // update the parent folder's lastmodified timestamp - $e = q("UPDATE attach SET edited = '%s' WHERE hash = '%s' AND uid = %d", - dbesc($created), - dbesc($arr['folder']), - intval($channel_id) - ); + // update the parent folder's lastmodified timestamp + $e = q( + "UPDATE attach SET edited = '%s' WHERE hash = '%s' AND uid = %d", + dbesc($created), + dbesc($arr['folder']), + intval($channel_id) + ); - $z = q("select * from attach where hash = '%s' and uid = %d and is_dir = 1 limit 1", - dbesc($arr['hash']), - intval($channel_id) - ); - if ($z) { - $ret['data'] = $z[0]; - } - } - else { - logger('attach_mkdir: ' . mkdir . ' ' . $os_basepath . $os_path . ' failed.'); - $ret['message'] = t('mkdir failed.'); - } - } - else { - $ret['message'] = t('database storage failed.'); - } + $z = q( + "select * from attach where hash = '%s' and uid = %d and is_dir = 1 limit 1", + dbesc($arr['hash']), + intval($channel_id) + ); + if ($z) { + $ret['data'] = $z[0]; + } + } else { + logger('attach_mkdir: ' . mkdir . ' ' . $os_basepath . $os_path . ' failed.'); + $ret['message'] = t('mkdir failed.'); + } + } else { + $ret['message'] = t('database storage failed.'); + } - return $ret; + return $ret; } /** @@ -1309,78 +1360,78 @@ function attach_mkdir($channel, $observer_hash, $arr = null) { * * \e string \b deny_gid * @return array */ -function attach_mkdirp($channel, $observer_hash, $arr = null) { +function attach_mkdirp($channel, $observer_hash, $arr = null) +{ - $ret = [ 'success' => false ]; - $channel_id = $channel['channel_id']; + $ret = [ 'success' => false ]; + $channel_id = $channel['channel_id']; - $sql_options = ''; + $sql_options = ''; - $basepath = 'store/' . $channel['channel_address']; + $basepath = 'store/' . $channel['channel_address']; - logger('basepath: ' . $basepath, LOGGER_DEBUG); + logger('basepath: ' . $basepath, LOGGER_DEBUG); - if (! is_dir($basepath)) { - os_mkdir($basepath,STORAGE_DEFAULT_PERMISSIONS, true); - } + if (! is_dir($basepath)) { + os_mkdir($basepath, STORAGE_DEFAULT_PERMISSIONS, true); + } - if (! perm_is_allowed($channel_id, $observer_hash, 'write_storage')) { - $ret['message'] = t('Permission denied.'); - return $ret; - } + if (! perm_is_allowed($channel_id, $observer_hash, 'write_storage')) { + $ret['message'] = t('Permission denied.'); + return $ret; + } - if (! $arr['pathname']) { - $ret['message'] = t('Empty pathname'); - return $ret; - } + if (! $arr['pathname']) { + $ret['message'] = t('Empty pathname'); + return $ret; + } - $paths = explode('/',$arr['pathname']); - if (! $paths) { - $ret['message'] = t('Empty path'); - return $ret; - } + $paths = explode('/', $arr['pathname']); + if (! $paths) { + $ret['message'] = t('Empty path'); + return $ret; + } - $current_parent = ''; + $current_parent = ''; - foreach ($paths as $p) { - if (! $p) { - continue; - } + foreach ($paths as $p) { + if (! $p) { + continue; + } - $arx = [ - 'filename' => $p, - 'folder' => $current_parent, - 'force' => 1 - ]; - - if (array_key_exists('allow_cid',$arr)) { - $arx['allow_cid'] = $arr['allow_cid']; - } - if (array_key_exists('deny_cid',$arr)) { - $arx['deny_cid'] = $arr['deny_cid']; - } - if (array_key_exists('allow_gid',$arr)) { - $arx['allow_gid'] = $arr['allow_gid']; - } - if (array_key_exists('deny_gid',$arr)) { - $arx['deny_gid'] = $arr['deny_gid']; - } + $arx = [ + 'filename' => $p, + 'folder' => $current_parent, + 'force' => 1 + ]; - $x = attach_mkdir($channel, $observer_hash, $arx); - if ($x['success']) { - $current_parent = $x['data']['hash']; - } - else { - $ret['message'] = $x['message']; - return $ret; - } - } - if (isset($x)) { - $ret['success'] = true; - $ret['data'] = $x['data']; - } + if (array_key_exists('allow_cid', $arr)) { + $arx['allow_cid'] = $arr['allow_cid']; + } + if (array_key_exists('deny_cid', $arr)) { + $arx['deny_cid'] = $arr['deny_cid']; + } + if (array_key_exists('allow_gid', $arr)) { + $arx['allow_gid'] = $arr['allow_gid']; + } + if (array_key_exists('deny_gid', $arr)) { + $arx['deny_gid'] = $arr['deny_gid']; + } - return $ret; + $x = attach_mkdir($channel, $observer_hash, $arx); + if ($x['success']) { + $current_parent = $x['data']['hash']; + } else { + $ret['message'] = $x['message']; + return $ret; + } + } + if (isset($x)) { + $ret['success'] = true; + $ret['data'] = $x['data']; + } + + return $ret; } @@ -1394,91 +1445,96 @@ function attach_mkdirp($channel, $observer_hash, $arr = null) { * @param string $allow_gid * @param string $deny_cid * @param string $deny_gid - * @param boolean $recurse (optional) default false - * @param boolean $sync (optional) default false + * @param bool $recurse (optional) default false + * @param bool $sync (optional) default false */ -function attach_change_permissions($channel_id, $resource, $allow_cid, $allow_gid, $deny_cid, $deny_gid, $recurse = false, $sync = false) { +function attach_change_permissions($channel_id, $resource, $allow_cid, $allow_gid, $deny_cid, $deny_gid, $recurse = false, $sync = false) +{ - $channel = channelx_by_n($channel_id); - if (! $channel) { - return; - } + $channel = channelx_by_n($channel_id); + if (! $channel) { + return; + } - $r = q("select hash, flags, is_dir, is_photo, allow_cid from attach where hash = '%s' and uid = %d limit 1", - dbesc($resource), - intval($channel_id) - ); + $r = q( + "select hash, flags, is_dir, is_photo, allow_cid from attach where hash = '%s' and uid = %d limit 1", + dbesc($resource), + intval($channel_id) + ); - if (! $r) { - return; - } + if (! $r) { + return; + } - $private = (($allow_cid || $allow_gid || $deny_cid || $deny_gid) ? true : false); + $private = (($allow_cid || $allow_gid || $deny_cid || $deny_gid) ? true : false); - // preserve any existing tokens that may have been set for this file - // @fixme - we need a way to unconditionally clear these if desired. - - if ($private) { - $token_matches = null; - if (preg_match_all('/\/',$r[0]['allow_cid'],$token_matches, PREG_SET_ORDER)) { - foreach ($token_matches as $m) { - $tok = ''; - if (strpos($allow_cid,$tok) === false) { - $allow_cid .= $tok; - } - } - } - } + // preserve any existing tokens that may have been set for this file + // @fixme - we need a way to unconditionally clear these if desired. - if (intval($r[0]['is_dir'])) { - if ($recurse) { - $r = q("select hash, flags, is_dir from attach where folder = '%s' and uid = %d", - dbesc($resource), - intval($channel_id) - ); - if ($r) { - foreach ($r as $rr) { - attach_change_permissions($channel_id, $rr['hash'], $allow_cid, $allow_gid, $deny_cid, $deny_gid, $recurse, $sync); - } - } - } - } + if ($private) { + $token_matches = null; + if (preg_match_all('/\/', $r[0]['allow_cid'], $token_matches, PREG_SET_ORDER)) { + foreach ($token_matches as $m) { + $tok = ''; + if (strpos($allow_cid, $tok) === false) { + $allow_cid .= $tok; + } + } + } + } - $x = q("update attach set allow_cid = '%s', allow_gid = '%s', deny_cid = '%s', deny_gid = '%s', flags = 0 where hash = '%s' and uid = %d", - dbesc($allow_cid), - dbesc($allow_gid), - dbesc($deny_cid), - dbesc($deny_gid), - dbesc($resource), - intval($channel_id) - ); - if ($r[0]['is_photo']) { - $x = q("update photo set allow_cid = '%s', allow_gid = '%s', deny_cid = '%s', deny_gid = '%s' where resource_id = '%s' and uid = %d", - dbesc($allow_cid), - dbesc($allow_gid), - dbesc($deny_cid), - dbesc($deny_gid), - dbesc($resource), - intval($channel_id) - ); - $x = q("update item set allow_cid = '%s', allow_gid = '%s', deny_cid = '%s', deny_gid = '%s' where resource_id = '%s' and resource_type = 'photo' and uid = %d", - dbesc($allow_cid), - dbesc($allow_gid), - dbesc($deny_cid), - dbesc($deny_gid), - dbesc($resource), - intval($channel_id) - ); + if (intval($r[0]['is_dir'])) { + if ($recurse) { + $r = q( + "select hash, flags, is_dir from attach where folder = '%s' and uid = %d", + dbesc($resource), + intval($channel_id) + ); + if ($r) { + foreach ($r as $rr) { + attach_change_permissions($channel_id, $rr['hash'], $allow_cid, $allow_gid, $deny_cid, $deny_gid, $recurse, $sync); + } + } + } + } - } + $x = q( + "update attach set allow_cid = '%s', allow_gid = '%s', deny_cid = '%s', deny_gid = '%s', flags = 0 where hash = '%s' and uid = %d", + dbesc($allow_cid), + dbesc($allow_gid), + dbesc($deny_cid), + dbesc($deny_gid), + dbesc($resource), + intval($channel_id) + ); + if ($r[0]['is_photo']) { + $x = q( + "update photo set allow_cid = '%s', allow_gid = '%s', deny_cid = '%s', deny_gid = '%s' where resource_id = '%s' and uid = %d", + dbesc($allow_cid), + dbesc($allow_gid), + dbesc($deny_cid), + dbesc($deny_gid), + dbesc($resource), + intval($channel_id) + ); + $x = q( + "update item set allow_cid = '%s', allow_gid = '%s', deny_cid = '%s', deny_gid = '%s' where resource_id = '%s' and resource_type = 'photo' and uid = %d", + dbesc($allow_cid), + dbesc($allow_gid), + dbesc($deny_cid), + dbesc($deny_gid), + dbesc($resource), + intval($channel_id) + ); + } - if ($sync) { - $data = attach_export_data($channel,$resource); + if ($sync) { + $data = attach_export_data($channel, $resource); - if ($data) { - Libsync::build_sync_packet($channel['channel_id'], [ 'file' => [ $data ] ]); - } - } + if ($data) { + Libsync::build_sync_packet($channel['channel_id'], [ 'file' => [ $data ] ]); + } + } } /** @@ -1494,122 +1550,130 @@ function attach_change_permissions($channel_id, $resource, $allow_cid, $allow_gi * @param int $is_photo (optional) default 0 * @return void */ -function attach_delete($channel_id, $resource, $is_photo = 0) { +function attach_delete($channel_id, $resource, $is_photo = 0) +{ - $c = q("SELECT channel_address FROM channel WHERE channel_id = %d LIMIT 1", - intval($channel_id) - ); + $c = q( + "SELECT channel_address FROM channel WHERE channel_id = %d LIMIT 1", + intval($channel_id) + ); - $channel_address = (($c) ? $c[0]['channel_address'] : 'notfound'); - $photo_sql = (($is_photo) ? " and is_photo = 1 " : ''); + $channel_address = (($c) ? $c[0]['channel_address'] : 'notfound'); + $photo_sql = (($is_photo) ? " and is_photo = 1 " : ''); - $r = q("SELECT hash, os_storage, flags, is_dir, is_photo, folder FROM attach WHERE hash = '%s' AND uid = %d $photo_sql limit 1", - dbesc($resource), - intval($channel_id) - ); + $r = q( + "SELECT hash, os_storage, flags, is_dir, is_photo, folder FROM attach WHERE hash = '%s' AND uid = %d $photo_sql limit 1", + dbesc($resource), + intval($channel_id) + ); - if (! $r) { - attach_drop_photo($channel_id,$resource); - $arr = ['channel_id' => $channel_id, 'resource' => $resource, 'is_photo'=>$is_photo]; - call_hooks("attach_delete",$arr); - return; - } + if (! $r) { + attach_drop_photo($channel_id, $resource); + $arr = ['channel_id' => $channel_id, 'resource' => $resource, 'is_photo' => $is_photo]; + call_hooks("attach_delete", $arr); + return; + } - $url = get_cloud_url($channel_id, $channel_address, $resource); - $object = get_file_activity_object($channel_id, $resource, $url); + $url = get_cloud_url($channel_id, $channel_address, $resource); + $object = get_file_activity_object($channel_id, $resource, $url); - // If resource is a directory delete everything in the directory recursive - if (intval($r[0]['is_dir'])) { - $x = q("SELECT hash, os_storage, is_dir, flags FROM attach WHERE folder = '%s' AND uid = %d", - dbesc($resource), - intval($channel_id) - ); - if ($x) { - foreach ($x as $xx) { - attach_delete($channel_id, $xx['hash']); - } - } - } + // If resource is a directory delete everything in the directory recursive + if (intval($r[0]['is_dir'])) { + $x = q( + "SELECT hash, os_storage, is_dir, flags FROM attach WHERE folder = '%s' AND uid = %d", + dbesc($resource), + intval($channel_id) + ); + if ($x) { + foreach ($x as $xx) { + attach_delete($channel_id, $xx['hash']); + } + } + } - // delete a file from filesystem - if (intval($r[0]['os_storage'])) { - $y = q("SELECT content FROM attach WHERE hash = '%s' AND uid = %d LIMIT 1", - dbesc($resource), - intval($channel_id) - ); + // delete a file from filesystem + if (intval($r[0]['os_storage'])) { + $y = q( + "SELECT content FROM attach WHERE hash = '%s' AND uid = %d LIMIT 1", + dbesc($resource), + intval($channel_id) + ); - if ($y) { - $y[0]['content'] = dbunescbin($y[0]['content']); - if (strpos($y[0]['content'],'store') === false) { - $f = 'store/' . $channel_address . '/' . $y[0]['content']; - } - else { - $f = $y[0]['content']; - } + if ($y) { + $y[0]['content'] = dbunescbin($y[0]['content']); + if (strpos($y[0]['content'], 'store') === false) { + $f = 'store/' . $channel_address . '/' . $y[0]['content']; + } else { + $f = $y[0]['content']; + } - if (is_dir($f)) { - @rmdir($f); - } - elseif (file_exists($f)) { - unlink($f); - } - } - } + if (is_dir($f)) { + @rmdir($f); + } elseif (file_exists($f)) { + unlink($f); + } + } + } - // delete from database - $z = q("DELETE FROM attach WHERE hash = '%s' AND uid = %d", - dbesc($resource), - intval($channel_id) - ); + // delete from database + $z = q( + "DELETE FROM attach WHERE hash = '%s' AND uid = %d", + dbesc($resource), + intval($channel_id) + ); - if ($r[0]['is_photo']) { - attach_drop_photo($channel_id,$resource); - } + if ($r[0]['is_photo']) { + attach_drop_photo($channel_id, $resource); + } - // update the parent folder's lastmodified timestamp - $e = q("UPDATE attach SET edited = '%s' WHERE hash = '%s' AND uid = %d", - dbesc(datetime_convert()), - dbesc($r[0]['folder']), - intval($channel_id) - ); + // update the parent folder's lastmodified timestamp + $e = q( + "UPDATE attach SET edited = '%s' WHERE hash = '%s' AND uid = %d", + dbesc(datetime_convert()), + dbesc($r[0]['folder']), + intval($channel_id) + ); - $arr = ['channel_id' => $channel_id, 'resource' => $resource, 'is_photo'=>$is_photo]; - call_hooks("attach_delete",$arr); + $arr = ['channel_id' => $channel_id, 'resource' => $resource, 'is_photo' => $is_photo]; + call_hooks("attach_delete", $arr); - file_activity($channel_id, $object, $object['allow_cid'], $object['allow_gid'], $object['deny_cid'], $object['deny_gid'], 'update', true); + file_activity($channel_id, $object, $object['allow_cid'], $object['allow_gid'], $object['deny_cid'], $object['deny_gid'], 'update', true); - return; + return; } -function attach_drop_photo($channel_id,$resource) { +function attach_drop_photo($channel_id, $resource) +{ - $x = q("select id, item_hidden from item where resource_id = '%s' and resource_type = 'photo' and uid = %d and item_deleted = 0", - dbesc($resource), - intval($channel_id) - ); - if ($x) { - drop_item($x[0]['id'],false,(($x[0]['item_hidden']) ? DROPITEM_NORMAL : DROPITEM_PHASE1),true); - } - $r = q("select content from photo where uid = %d and resource_id = '%s' and os_storage = 1", - intval($channel_id), - dbesc($resource) - ); - if ($r) { - foreach ($r as $rv) { - $p = dbunescbin($rv['content']); - if ($p && file_exists($p)) { - @unlink($p); - } - } - } - - q("DELETE FROM photo WHERE uid = %d AND resource_id = '%s'", - intval($channel_id), - dbesc($resource) - ); + $x = q( + "select id, item_hidden from item where resource_id = '%s' and resource_type = 'photo' and uid = %d and item_deleted = 0", + dbesc($resource), + intval($channel_id) + ); + if ($x) { + drop_item($x[0]['id'], false, (($x[0]['item_hidden']) ? DROPITEM_NORMAL : DROPITEM_PHASE1), true); + } + $r = q( + "select content from photo where uid = %d and resource_id = '%s' and os_storage = 1", + intval($channel_id), + dbesc($resource) + ); + if ($r) { + foreach ($r as $rv) { + $p = dbunescbin($rv['content']); + if ($p && file_exists($p)) { + @unlink($p); + } + } + } + q( + "DELETE FROM photo WHERE uid = %d AND resource_id = '%s'", + intval($channel_id), + dbesc($resource) + ); } @@ -1626,47 +1690,49 @@ function attach_drop_photo($channel_id,$resource) { * @return string * path to the file in cloud/ */ -function get_cloudpath($arr) { - $basepath = 'cloud/'; +function get_cloudpath($arr) +{ + $basepath = 'cloud/'; - if ($arr['uid']) { - $r = q("select channel_address from channel where channel_id = %d limit 1", - intval($arr['uid']) - ); - if ($r) { - $basepath .= $r[0]['channel_address'] . '/'; - } - } + if ($arr['uid']) { + $r = q( + "select channel_address from channel where channel_id = %d limit 1", + intval($arr['uid']) + ); + if ($r) { + $basepath .= $r[0]['channel_address'] . '/'; + } + } - $path = $basepath; + $path = $basepath; - if ($arr['folder']) { - $lpath = ''; - $lfile = $arr['folder']; + if ($arr['folder']) { + $lpath = ''; + $lfile = $arr['folder']; - do { - $r = q("select filename, hash, flags, is_dir, folder from attach where uid = %d and hash = '%s' and is_dir != 0 + do { + $r = q( + "select filename, hash, flags, is_dir, folder from attach where uid = %d and hash = '%s' and is_dir != 0 limit 1", - intval($arr['uid']), - dbesc($lfile) - ); + intval($arr['uid']), + dbesc($lfile) + ); - if (! $r) { - break; - } + if (! $r) { + break; + } - if ($lfile) { - $lpath = $r[0]['filename'] . '/' . $lpath; - } - $lfile = $r[0]['folder']; + if ($lfile) { + $lpath = $r[0]['filename'] . '/' . $lpath; + } + $lfile = $r[0]['folder']; + } while (($r[0]['folder']) && intval($r[0]['is_dir'])); - } while ( ($r[0]['folder']) && intval($r[0]['is_dir'])); + $path .= $lpath; + } + $path .= $arr['filename']; - $path .= $lpath; - } - $path .= $arr['filename']; - - return $path; + return $path; } /** @@ -1680,21 +1746,22 @@ function get_cloudpath($arr) { * @param string $attachHash * @return string with the full folder path */ -function get_cloud_url($channel_id, $channel_name, $attachHash) { - $parentFullPath = ''; - // build directory tree - $parentHash = $attachHash; - do { - $parentHash = find_folder_hash_by_attach_hash($channel_id, $parentHash); - if ($parentHash) { - $parentName = find_filename_by_hash($channel_id, $parentHash); - $parentFullPath = $parentName . '/' . $parentFullPath; - } - } while ($parentHash); +function get_cloud_url($channel_id, $channel_name, $attachHash) +{ + $parentFullPath = ''; + // build directory tree + $parentHash = $attachHash; + do { + $parentHash = find_folder_hash_by_attach_hash($channel_id, $parentHash); + if ($parentHash) { + $parentName = find_filename_by_hash($channel_id, $parentHash); + $parentFullPath = $parentName . '/' . $parentFullPath; + } + } while ($parentHash); - $url = z_root() . '/cloud/' . $channel_name . '/' . $parentFullPath . find_filename_by_hash($channel_id, $attachHash); - return $url; + $url = z_root() . '/cloud/' . $channel_name . '/' . $parentFullPath . find_filename_by_hash($channel_id, $attachHash); + return $url; } /** @@ -1704,29 +1771,30 @@ function get_cloud_url($channel_id, $channel_name, $attachHash) { * The id of the channel * @param string $attachHash * The hash of the attachment - * @param boolean $recurse + * @param bool $recurse * (optional) default false * @return string */ -function find_folder_hash_by_attach_hash($channel_id, $attachHash, $recurse = false) { +function find_folder_hash_by_attach_hash($channel_id, $attachHash, $recurse = false) +{ - logger('attach_hash: ' . $attachHash); + logger('attach_hash: ' . $attachHash); - $r = q("SELECT folder FROM attach WHERE uid = %d AND hash = '%s' LIMIT 1", - intval($channel_id), - dbesc($attachHash) - ); - $hash = EMPTY_STR; - if ($r && $r[0]['folder']) { - if ($recurse) { - $hash = find_folder_hash_by_attach_hash($channel_id,$r[0]['folder'],true) . '/' . $r[0]['folder']; - } - else { - $hash = $r[0]['folder']; - } - } + $r = q( + "SELECT folder FROM attach WHERE uid = %d AND hash = '%s' LIMIT 1", + intval($channel_id), + dbesc($attachHash) + ); + $hash = EMPTY_STR; + if ($r && $r[0]['folder']) { + if ($recurse) { + $hash = find_folder_hash_by_attach_hash($channel_id, $r[0]['folder'], true) . '/' . $r[0]['folder']; + } else { + $hash = $r[0]['folder']; + } + } - return $hash; + return $hash; } /** @@ -1736,37 +1804,38 @@ function find_folder_hash_by_attach_hash($channel_id, $attachHash, $recurse = fa * @param string $path * @return string */ -function find_folder_hash_by_path($channel_id, $path) { +function find_folder_hash_by_path($channel_id, $path) +{ - if (! $path) { - return EMPTY_STR; - } - - $comps = explode('/',$path); - $errors = false; - $parent_hash = ''; + if (! $path) { + return EMPTY_STR; + } - for ($x = 0; $x < count($comps); $x ++) { - $element = $comps[$x]; - $r = q("SELECT hash FROM attach WHERE uid = %d AND filename = '%s' AND folder = '%s' LIMIT 1", - intval($channel_id), - dbesc($element), - dbesc($parent_hash) - ); - if ($r) { - $parent_hash = $r[0]['hash']; - } - else { - $errors ++; - break; - } - } + $comps = explode('/', $path); + $errors = false; + $parent_hash = ''; - if ($errors) { - return EMPTY_STR; - } + for ($x = 0; $x < count($comps); $x++) { + $element = $comps[$x]; + $r = q( + "SELECT hash FROM attach WHERE uid = %d AND filename = '%s' AND folder = '%s' LIMIT 1", + intval($channel_id), + dbesc($element), + dbesc($parent_hash) + ); + if ($r) { + $parent_hash = $r[0]['hash']; + } else { + $errors++; + break; + } + } - return $parent_hash; + if ($errors) { + return EMPTY_STR; + } + + return $parent_hash; } /** @@ -1779,17 +1848,19 @@ function find_folder_hash_by_path($channel_id, $path) { * @return string * The filename of the attachment */ -function find_filename_by_hash($channel_id, $attachHash) { - $r = q("SELECT filename FROM attach WHERE uid = %d AND hash = '%s' LIMIT 1", - intval($channel_id), - dbesc($attachHash) - ); - $filename = ''; - if ($r) { - $filename = $r[0]['filename']; - } +function find_filename_by_hash($channel_id, $attachHash) +{ + $r = q( + "SELECT filename FROM attach WHERE uid = %d AND hash = '%s' LIMIT 1", + intval($channel_id), + dbesc($attachHash) + ); + $filename = ''; + if ($r) { + $filename = $r[0]['filename']; + } - return $filename; + return $filename; } /** @@ -1800,12 +1871,13 @@ function find_filename_by_hash($channel_id, $attachHash) { * @param int $bufsize size of chunk, default 16384 * @return number with the size */ -function pipe_streams($in, $out, $bufsize = 16384) { - $size = 0; - while (!feof($in)) { - $size += fwrite($out, fread($in, $bufsize)); - } - return $size; +function pipe_streams($in, $out, $bufsize = 16384) +{ + $size = 0; + while (!feof($in)) { + $size += fwrite($out, fread($in, $bufsize)); + } + return $size; } /** @@ -1818,169 +1890,170 @@ function pipe_streams($in, $out, $bufsize = 16384) { * @param string $deny_cid * @param string $deny_gid * @param string $verb - * @param boolean $notify + * @param bool $notify */ -function file_activity($channel_id, $object, $allow_cid, $allow_gid, $deny_cid, $deny_gid, $verb, $notify) { +function file_activity($channel_id, $object, $allow_cid, $allow_gid, $deny_cid, $deny_gid, $verb, $notify) +{ - $poster = App::get_observer(); + $poster = App::get_observer(); - // if we got no object something went wrong - if (!$object) { - return; - } + // if we got no object something went wrong + if (!$object) { + return; + } - // turn strings into arrays - $arr_allow_cid = expand_acl($allow_cid); - $arr_allow_gid = expand_acl($allow_gid); - $arr_deny_cid = expand_acl($deny_cid); - $arr_deny_gid = expand_acl($deny_gid); + // turn strings into arrays + $arr_allow_cid = expand_acl($allow_cid); + $arr_allow_gid = expand_acl($allow_gid); + $arr_deny_cid = expand_acl($deny_cid); + $arr_deny_gid = expand_acl($deny_gid); - // filter out receivers which do not have permission to view filestorage - $arr_allow_cid = check_list_permissions($channel_id, $arr_allow_cid, 'view_storage'); + // filter out receivers which do not have permission to view filestorage + $arr_allow_cid = check_list_permissions($channel_id, $arr_allow_cid, 'view_storage'); - $is_dir = (intval($object['is_dir']) ? true : false); + $is_dir = (intval($object['is_dir']) ? true : false); - // do not send activity for folders for now - if ($is_dir) { - return; - } + // do not send activity for folders for now + if ($is_dir) { + return; + } - // check for recursive perms if we are in a folder - if ($object['folder']) { + // check for recursive perms if we are in a folder + if ($object['folder']) { + $folder_hash = $object['folder']; - $folder_hash = $object['folder']; + $r_perms = attach_recursive_perms($arr_allow_cid, $arr_allow_gid, $arr_deny_cid, $arr_deny_gid, $folder_hash); - $r_perms = attach_recursive_perms($arr_allow_cid, $arr_allow_gid, $arr_deny_cid, $arr_deny_gid, $folder_hash); + if ($r_perms === false) { + // nobody has recursive perms - nobody must be notified + return; + } - if ($r_perms === false) { - // nobody has recursive perms - nobody must be notified - return; - } + // split up returned perms + $arr_allow_cid = $r_perms['allow_cid']; + $arr_allow_gid = $r_perms['allow_gid']; + $arr_deny_cid = $r_perms['deny_cid']; + $arr_deny_gid = $r_perms['deny_gid']; - // split up returned perms - $arr_allow_cid = $r_perms['allow_cid']; - $arr_allow_gid = $r_perms['allow_gid']; - $arr_deny_cid = $r_perms['deny_cid']; - $arr_deny_gid = $r_perms['deny_gid']; + // filter out receivers which do not have permission to view filestorage + $arr_allow_cid = check_list_permissions($channel_id, $arr_allow_cid, 'view_storage'); + } - // filter out receivers which do not have permission to view filestorage - $arr_allow_cid = check_list_permissions($channel_id, $arr_allow_cid, 'view_storage'); - } + $uuid = new_uuid(); + $mid = z_root() . '/item/' . $uuid; - $uuid = new_uuid(); - $mid = z_root() . '/item/' . $uuid; + $objtype = 'Document'; - $objtype = 'Document'; + $arr = []; + $arr['aid'] = get_account_id(); + $arr['uid'] = $channel_id; + $arr['uuid'] = $uuid; + $arr['item_wall'] = 1; + $arr['item_origin'] = 1; + $arr['item_unseen'] = 1; + $arr['author_xchan'] = $poster['xchan_hash']; + $arr['owner_xchan'] = $poster['xchan_hash']; //?? + $arr['title'] = $object['filename']; + $arr['item_notshown'] = 1; + $arr['obj_type'] = $objtype; + $arr['resource_id'] = $object['hash']; + $arr['resource_type'] = 'attach'; - $arr = []; - $arr['aid'] = get_account_id(); - $arr['uid'] = $channel_id; - $arr['uuid'] = $uuid; - $arr['item_wall'] = 1; - $arr['item_origin'] = 1; - $arr['item_unseen'] = 1; - $arr['author_xchan'] = $poster['xchan_hash']; - $arr['owner_xchan'] = $poster['xchan_hash']; //?? - $arr['title'] = $object['filename']; - $arr['item_notshown'] = 1; - $arr['obj_type'] = $objtype; - $arr['resource_id'] = $object['hash']; - $arr['resource_type'] = 'attach'; + $private = (($arr_allow_cid[0] || $arr_allow_gid[0] || $arr_deny_cid[0] || $arr_deny_gid[0]) ? 1 : 0); - $private = (($arr_allow_cid[0] || $arr_allow_gid[0] || $arr_deny_cid[0] || $arr_deny_gid[0]) ? 1 : 0); + $obj = [ + 'type' => 'Document', + 'id' => z_root() . '/attach/' . $object['hash'], + 'name' => $object['filename'], + ]; - $obj= [ - 'type' => 'Document', - 'id' => z_root() . '/attach/' . $object['hash'], - 'name' => $object['filename'], - ]; - - $obj['url'] = array_merge($object['url'],[ - 'type' => 'Link', - 'mediaType' => $object['filetype'], - 'href' => z_root() . '/attach/' . $object['hash'] - ]); + $obj['url'] = array_merge($object['url'], [ + 'type' => 'Link', + 'mediaType' => $object['filetype'], + 'href' => z_root() . '/attach/' . $object['hash'] + ]); - $jsonobject = json_encode($obj); + $jsonobject = json_encode($obj); - //check if item for this object exists - $y = q("SELECT mid FROM item WHERE verb = '%s' AND obj_type = '%s' AND resource_id = '%s' AND uid = %d LIMIT 1", - dbesc(ACTIVITY_POST), - dbesc($objtype), - dbesc($object['hash']), - intval(local_channel()) - ); + //check if item for this object exists + $y = q( + "SELECT mid FROM item WHERE verb = '%s' AND obj_type = '%s' AND resource_id = '%s' AND uid = %d LIMIT 1", + dbesc(ACTIVITY_POST), + dbesc($objtype), + dbesc($object['hash']), + intval(local_channel()) + ); - if ($y) { - $update = true; - $object['d_mid'] = $y[0]['mid']; //attach mid of the old object - $u_jsonobject = json_encode($obj); + if ($y) { + $update = true; + $object['d_mid'] = $y[0]['mid']; //attach mid of the old object + $u_jsonobject = json_encode($obj); - // we have got the relevant info - delete the old item before we create the new one - $z = q("DELETE FROM item WHERE obj_type = '%s' AND verb = '%s' AND mid = '%s'", - dbesc(ACTIVITY_OBJ_FILE), - dbesc(ACTIVITY_POST), - dbesc($y[0]['mid']) - ); + // we have got the relevant info - delete the old item before we create the new one + $z = q( + "DELETE FROM item WHERE obj_type = '%s' AND verb = '%s' AND mid = '%s'", + dbesc(ACTIVITY_OBJ_FILE), + dbesc(ACTIVITY_POST), + dbesc($y[0]['mid']) + ); + } - } + // send update activity and create a new one + if ($update && $verb == 'post') { + //updates should be sent to everybody with recursive perms and all eventual former allowed members ($object['allow_cid'] etc.). + $u_arr_allow_cid = array_unique(array_merge($arr_allow_cid, expand_acl($object['allow_cid']))); + $u_arr_allow_gid = array_unique(array_merge($arr_allow_gid, expand_acl($object['allow_gid']))); + $u_arr_deny_cid = array_unique(array_merge($arr_deny_cid, expand_acl($object['deny_cid']))); + $u_arr_deny_gid = array_unique(array_merge($arr_deny_gid, expand_acl($object['deny_gid']))); - // send update activity and create a new one - if ($update && $verb == 'post' ) { - //updates should be sent to everybody with recursive perms and all eventual former allowed members ($object['allow_cid'] etc.). - $u_arr_allow_cid = array_unique(array_merge($arr_allow_cid, expand_acl($object['allow_cid']))); - $u_arr_allow_gid = array_unique(array_merge($arr_allow_gid, expand_acl($object['allow_gid']))); - $u_arr_deny_cid = array_unique(array_merge($arr_deny_cid, expand_acl($object['deny_cid']))); - $u_arr_deny_gid = array_unique(array_merge($arr_deny_gid, expand_acl($object['deny_gid']))); + $private = (($u_arr_allow_cid[0] || $u_arr_allow_gid[0] || $u_arr_deny_cid[0] || $u_arr_deny_gid[0]) ? 1 : 0); - $private = (($u_arr_allow_cid[0] || $u_arr_allow_gid[0] || $u_arr_deny_cid[0] || $u_arr_deny_gid[0]) ? 1 : 0); + $uuid = new_uuid(); + $u_mid = z_root() . '/item/' . $uuid; - $uuid = new_uuid(); - $u_mid = z_root() . '/item/' . $uuid; + $arr['uuid'] = $uuid; + $arr['mid'] = $u_mid; + $arr['parent_mid'] = $u_mid; + $arr['allow_cid'] = perms2str($u_arr_allow_cid); + $arr['allow_gid'] = perms2str($u_arr_allow_gid); + $arr['deny_cid'] = perms2str($u_arr_deny_cid); + $arr['deny_gid'] = perms2str($u_arr_deny_gid); + $arr['item_private'] = $private; + $arr['verb'] = ACTIVITY_UPDATE; + $arr['obj'] = $u_jsonobject; + $arr['body'] = ''; - $arr['uuid'] = $uuid; - $arr['mid'] = $u_mid; - $arr['parent_mid'] = $u_mid; - $arr['allow_cid'] = perms2str($u_arr_allow_cid); - $arr['allow_gid'] = perms2str($u_arr_allow_gid); - $arr['deny_cid'] = perms2str($u_arr_deny_cid); - $arr['deny_gid'] = perms2str($u_arr_deny_gid); - $arr['item_private'] = $private; - $arr['verb'] = ACTIVITY_UPDATE; - $arr['obj'] = $u_jsonobject; - $arr['body'] = ''; + post_activity_item($arr); - post_activity_item($arr); + $update = false; + } - $update = false; - } + // don't create new activity if notify was not enabled + if (! $notify) { + return; + } - // don't create new activity if notify was not enabled - if (! $notify) { - return; - } + //don't create new activity if we have an update request but there is no item to update + //this can e.g. happen when deleting images + if ((! $y) && ($verb == 'update')) { + return; + } - //don't create new activity if we have an update request but there is no item to update - //this can e.g. happen when deleting images - if ((! $y) && ($verb == 'update')) { - return; - } + $arr['mid'] = $mid; + $arr['parent_mid'] = $mid; + $arr['allow_cid'] = perms2str($arr_allow_cid); + $arr['allow_gid'] = perms2str($arr_allow_gid); + $arr['deny_cid'] = perms2str($arr_deny_cid); + $arr['deny_gid'] = perms2str($arr_deny_gid); + $arr['item_private'] = $private; + $arr['verb'] = (($update) ? ACTIVITY_UPDATE : ACTIVITY_POST); + $arr['obj'] = (($update) ? $u_jsonobject : $jsonobject); + $arr['body'] = ''; - $arr['mid'] = $mid; - $arr['parent_mid'] = $mid; - $arr['allow_cid'] = perms2str($arr_allow_cid); - $arr['allow_gid'] = perms2str($arr_allow_gid); - $arr['deny_cid'] = perms2str($arr_deny_cid); - $arr['deny_gid'] = perms2str($arr_deny_gid); - $arr['item_private'] = $private; - $arr['verb'] = (($update) ? ACTIVITY_UPDATE : ACTIVITY_POST); - $arr['obj'] = (($update) ? $u_jsonobject : $jsonobject); - $arr['body'] = ''; + post_activity_item($arr); - post_activity_item($arr); - - return; + return; } /** @@ -1991,34 +2064,36 @@ function file_activity($channel_id, $object, $allow_cid, $allow_gid, $deny_cid, * @param string $url * @return array An associative array for the specified file. */ -function get_file_activity_object($channel_id, $hash, $url) { +function get_file_activity_object($channel_id, $hash, $url) +{ - $x = q("SELECT creator, filename, filetype, filesize, revision, folder, os_storage, is_photo, is_dir, flags, created, edited, allow_cid, allow_gid, deny_cid, deny_gid FROM attach WHERE uid = %d AND hash = '%s' LIMIT 1", - intval($channel_id), - dbesc($hash) - ); - if (! $x) { - return null; - } - - $url = rawurlencode($url); + $x = q( + "SELECT creator, filename, filetype, filesize, revision, folder, os_storage, is_photo, is_dir, flags, created, edited, allow_cid, allow_gid, deny_cid, deny_gid FROM attach WHERE uid = %d AND hash = '%s' LIMIT 1", + intval($channel_id), + dbesc($hash) + ); + if (! $x) { + return null; + } - $links = []; - $links[] = [ - 'rel' => 'alternate', - 'mediaType' => 'text/html', - 'href' => $url - ]; + $url = rawurlencode($url); - $object = array_merge( [ - 'type' => ACTIVITY_OBJ_FILE, - 'title' => $x[0]['filename'], - 'id' => $url, - 'url' => $links, - 'hash' => $hash, - ], $x[0]); + $links = []; + $links[] = [ + 'rel' => 'alternate', + 'mediaType' => 'text/html', + 'href' => $url + ]; - return $object; + $object = array_merge([ + 'type' => ACTIVITY_OBJ_FILE, + 'title' => $x[0]['filename'], + 'id' => $url, + 'url' => $links, + 'hash' => $hash, + ], $x[0]); + + return $object; } /** @@ -2030,228 +2105,236 @@ function get_file_activity_object($channel_id, $hash, $url) { * @param array $arr_deny_gid * @param string $folder_hash */ -function attach_recursive_perms($arr_allow_cid, $arr_allow_gid, $arr_deny_cid, $arr_deny_gid, $folder_hash) { +function attach_recursive_perms($arr_allow_cid, $arr_allow_gid, $arr_deny_cid, $arr_deny_gid, $folder_hash) +{ - $ret = []; - $parent_arr = []; - $count_values = []; - $poster = App::get_observer(); + $ret = []; + $parent_arr = []; + $count_values = []; + $poster = App::get_observer(); - //lookup all channels in sharee group and add them to sharee $arr_allow_cid - if ($arr_allow_gid) { - $in_group = AccessList::expand($arr_allow_gid); - $arr_allow_cid = array_unique(array_merge($arr_allow_cid, $in_group)); - } + //lookup all channels in sharee group and add them to sharee $arr_allow_cid + if ($arr_allow_gid) { + $in_group = AccessList::expand($arr_allow_gid); + $arr_allow_cid = array_unique(array_merge($arr_allow_cid, $in_group)); + } - //count existing parent folders - we will compare to that count later - $count = 0; - while ($folder_hash) { - $x = q("SELECT allow_cid, allow_gid, deny_cid, deny_gid, folder FROM attach WHERE hash = '%s' LIMIT 1", - dbesc($folder_hash) - ); + //count existing parent folders - we will compare to that count later + $count = 0; + while ($folder_hash) { + $x = q( + "SELECT allow_cid, allow_gid, deny_cid, deny_gid, folder FROM attach WHERE hash = '%s' LIMIT 1", + dbesc($folder_hash) + ); - //only process private folders - if ($x[0]['allow_cid'] || $x[0]['allow_gid'] || $x[0]['deny_cid'] || $x[0]['deny_gid']) { - $parent_arr['allow_cid'][] = expand_acl($x[0]['allow_cid']); - $parent_arr['allow_gid'][] = expand_acl($x[0]['allow_gid']); - $parent_arr['deny_cid'][] = expand_acl($x[0]['deny_cid']); - $parent_arr['deny_gid'][] = expand_acl($x[0]['deny_gid']); + //only process private folders + if ($x[0]['allow_cid'] || $x[0]['allow_gid'] || $x[0]['deny_cid'] || $x[0]['deny_gid']) { + $parent_arr['allow_cid'][] = expand_acl($x[0]['allow_cid']); + $parent_arr['allow_gid'][] = expand_acl($x[0]['allow_gid']); + $parent_arr['deny_cid'][] = expand_acl($x[0]['deny_cid']); + $parent_arr['deny_gid'][] = expand_acl($x[0]['deny_gid']); - //this is the number of all existing parent folders - we will compare to that count later - $count++; - } + //this is the number of all existing parent folders - we will compare to that count later + $count++; + } - $folder_hash = $x[0]['folder']; - } + $folder_hash = $x[0]['folder']; + } - //logger(EOL . 'parent_arr: ' . print_r($parent_arr,true)); + //logger(EOL . 'parent_arr: ' . print_r($parent_arr,true)); - //if none of the parent folders is private just return file perms - if (!$parent_arr['allow_cid'] && !$parent_arr['allow_gid'] && !$parent_arr['deny_cid'] && !$parent_arr['deny_gid']) { - $ret['allow_gid'] = $arr_allow_gid; - $ret['allow_cid'] = $arr_allow_cid; - $ret['deny_gid'] = $arr_deny_gid; - $ret['deny_cid'] = $arr_deny_cid; + //if none of the parent folders is private just return file perms + if (!$parent_arr['allow_cid'] && !$parent_arr['allow_gid'] && !$parent_arr['deny_cid'] && !$parent_arr['deny_gid']) { + $ret['allow_gid'] = $arr_allow_gid; + $ret['allow_cid'] = $arr_allow_cid; + $ret['deny_gid'] = $arr_deny_gid; + $ret['deny_cid'] = $arr_deny_cid; - return $ret; - } + return $ret; + } - //if there are no perms on the file we will work with the perms from the first parent folder - if (!$arr_allow_cid && !$arr_allow_gid && !$arr_deny_cid && !$arr_deny_gid) { - $arr_allow_cid = $parent_arr['allow_cid'][0]; - $arr_allow_gid = $parent_arr['allow_gid'][0]; - $arr_deny_cid = $parent_arr['deny_cid'][0]; - $arr_deny_gid = $parent_arr['deny_gid'][0]; - } + //if there are no perms on the file we will work with the perms from the first parent folder + if (!$arr_allow_cid && !$arr_allow_gid && !$arr_deny_cid && !$arr_deny_gid) { + $arr_allow_cid = $parent_arr['allow_cid'][0]; + $arr_allow_gid = $parent_arr['allow_gid'][0]; + $arr_deny_cid = $parent_arr['deny_cid'][0]; + $arr_deny_gid = $parent_arr['deny_gid'][0]; + } - /*** - * - * check if sharee has perms for all parent folders - * - ***/ + /*** + * + * check if sharee has perms for all parent folders + * + ***/ - $r_arr_allow_cid = []; + $r_arr_allow_cid = []; - if ($parent_arr['allow_cid']) { - //check sharee arr_allow_cid against allow_cid of all parent folders - foreach ($parent_arr['allow_cid'] as $folder_arr_allow_cid) { - foreach ($folder_arr_allow_cid as $ac_hash) { - $count_values[$ac_hash]++; - } - } - foreach ($arr_allow_cid as $fac_hash) { - if ($count_values[$fac_hash] == $count) { - $r_arr_allow_cid[] = $fac_hash; - } - } - //logger(EOL . 'r_arr_allow_cid: ' . print_r($r_arr_allow_cid,true)); - } + if ($parent_arr['allow_cid']) { + //check sharee arr_allow_cid against allow_cid of all parent folders + foreach ($parent_arr['allow_cid'] as $folder_arr_allow_cid) { + foreach ($folder_arr_allow_cid as $ac_hash) { + $count_values[$ac_hash]++; + } + } + foreach ($arr_allow_cid as $fac_hash) { + if ($count_values[$fac_hash] == $count) { + $r_arr_allow_cid[] = $fac_hash; + } + } + //logger(EOL . 'r_arr_allow_cid: ' . print_r($r_arr_allow_cid,true)); + } - if ($parent_arr['allow_gid']) { - //check sharee arr_allow_cid against members of allow_gid of all parent folders - foreach ($parent_arr['allow_gid'] as $folder_arr_allow_gid) { - //get the group members - $folder_arr_allow_cid = AccessList::expand($folder_arr_allow_gid); - foreach ($folder_arr_allow_cid as $ac_hash) { - $count_values[$ac_hash]++; - } - } - foreach ($arr_allow_cid as $fac_hash) { - if ($count_values[$fac_hash] == $count) { - $r_arr_allow_cid[] = $fac_hash; - } - } - //logger(EOL . 'groups - r_arr_allow_cid: ' . print_r($r_arr_allow_cid,true)); - } + if ($parent_arr['allow_gid']) { + //check sharee arr_allow_cid against members of allow_gid of all parent folders + foreach ($parent_arr['allow_gid'] as $folder_arr_allow_gid) { + //get the group members + $folder_arr_allow_cid = AccessList::expand($folder_arr_allow_gid); + foreach ($folder_arr_allow_cid as $ac_hash) { + $count_values[$ac_hash]++; + } + } + foreach ($arr_allow_cid as $fac_hash) { + if ($count_values[$fac_hash] == $count) { + $r_arr_allow_cid[] = $fac_hash; + } + } + //logger(EOL . 'groups - r_arr_allow_cid: ' . print_r($r_arr_allow_cid,true)); + } - /*** - * - * check if sharee is denied somewhere in parent folders and deny him if so - * - ***/ + /*** + * + * check if sharee is denied somewhere in parent folders and deny him if so + * + ***/ - //deny_cid - $r_arr_deny_cid = []; + //deny_cid + $r_arr_deny_cid = []; - if ($parent_arr['deny_cid']) { - foreach ($parent_arr['deny_cid'] as $folder_arr_deny_cid) { - $r_arr_deny_cid = array_merge($arr_deny_cid, $folder_arr_deny_cid); - } - $r_arr_deny_cid = array_unique($r_arr_deny_cid); - //logger(EOL . 'r_arr_deny_cid: ' . print_r($r_arr_deny_cid,true)); - } + if ($parent_arr['deny_cid']) { + foreach ($parent_arr['deny_cid'] as $folder_arr_deny_cid) { + $r_arr_deny_cid = array_merge($arr_deny_cid, $folder_arr_deny_cid); + } + $r_arr_deny_cid = array_unique($r_arr_deny_cid); + //logger(EOL . 'r_arr_deny_cid: ' . print_r($r_arr_deny_cid,true)); + } - //deny_gid - $r_arr_deny_gid = []; + //deny_gid + $r_arr_deny_gid = []; - if ($parent_arr['deny_cid']) { - foreach ($parent_arr['deny_gid'] as $folder_arr_deny_gid) { - $r_arr_deny_gid = array_merge($arr_deny_gid, $folder_arr_deny_gid); - } - $r_arr_deny_gid = array_unique($r_arr_deny_gid); - //logger(EOL . 'r_arr_deny_gid: ' . print_r($r_arr_dr_arr_deny_gideny_cid,true)); - } + if ($parent_arr['deny_cid']) { + foreach ($parent_arr['deny_gid'] as $folder_arr_deny_gid) { + $r_arr_deny_gid = array_merge($arr_deny_gid, $folder_arr_deny_gid); + } + $r_arr_deny_gid = array_unique($r_arr_deny_gid); + //logger(EOL . 'r_arr_deny_gid: ' . print_r($r_arr_dr_arr_deny_gideny_cid,true)); + } - //if no channel is allowed return false - if (! $r_arr_allow_cid) { - return false; - } + //if no channel is allowed return false + if (! $r_arr_allow_cid) { + return false; + } - $ret['allow_gid'] = []; // eventual group members are already collected in $r_arr_allow_cid - $ret['allow_cid'] = $r_arr_allow_cid; - $ret['deny_gid'] = $r_arr_deny_gid; - $ret['deny_cid'] = $r_arr_deny_cid; + $ret['allow_gid'] = []; // eventual group members are already collected in $r_arr_allow_cid + $ret['allow_cid'] = $r_arr_allow_cid; + $ret['deny_gid'] = $r_arr_deny_gid; + $ret['deny_cid'] = $r_arr_deny_cid; - return $ret; + return $ret; } -function filepath_macro($s) { - - return str_replace( - [ '%Y', '%m', '%d' ], - [ datetime_convert('UTC',date_default_timezone_get(),'now', 'Y'), - datetime_convert('UTC',date_default_timezone_get(),'now', 'm'), - datetime_convert('UTC',date_default_timezone_get(),'now', 'd') - ], $s); +function filepath_macro($s) +{ + return str_replace( + [ '%Y', '%m', '%d' ], + [ datetime_convert('UTC', date_default_timezone_get(), 'now', 'Y'), + datetime_convert('UTC', date_default_timezone_get(), 'now', 'm'), + datetime_convert('UTC', date_default_timezone_get(), 'now', 'd') + ], + $s + ); } -function attach_export_data($channel, $resource_id, $deleted = false) { +function attach_export_data($channel, $resource_id, $deleted = false) +{ - $ret = []; + $ret = []; - $paths = []; + $paths = []; - $hash_ptr = $resource_id; + $hash_ptr = $resource_id; - $ret['fetch_url'] = z_root() . '/getfile'; - $ret['original_channel'] = $channel['channel_address']; + $ret['fetch_url'] = z_root() . '/getfile'; + $ret['original_channel'] = $channel['channel_address']; - if ($deleted) { - $ret['attach'] = [ [ 'hash' => $resource_id, 'deleted' => 1 ] ]; - return $ret; - } + if ($deleted) { + $ret['attach'] = [ [ 'hash' => $resource_id, 'deleted' => 1 ] ]; + return $ret; + } - do { - $r = q("select * from attach where hash = '%s' and uid = %d limit 1", - dbesc($hash_ptr), - intval($channel['channel_id']) - ); - if (! $r) { - break; - } + do { + $r = q( + "select * from attach where hash = '%s' and uid = %d limit 1", + dbesc($hash_ptr), + intval($channel['channel_id']) + ); + if (! $r) { + break; + } - if ($hash_ptr === $resource_id) { - $attach_ptr = $r[0]; - } - $r[0]['content'] = dbunescbin($r[0]['content']); + if ($hash_ptr === $resource_id) { + $attach_ptr = $r[0]; + } + $r[0]['content'] = dbunescbin($r[0]['content']); - $hash_ptr = $r[0]['folder']; - $paths[] = $r[0]; - } while($hash_ptr); + $hash_ptr = $r[0]['folder']; + $paths[] = $r[0]; + } while ($hash_ptr); - $paths = array_reverse($paths); + $paths = array_reverse($paths); - $ret['attach'] = $paths; + $ret['attach'] = $paths; - if ($attach_ptr['is_photo']) { + if ($attach_ptr['is_photo']) { + $r = q( + "select * from photo where resource_id = '%s' and uid = %d order by imgscale asc", + dbesc($resource_id), + intval($channel['channel_id']) + ); + if ($r) { + for ($x = 0; $x < count($r); $x++) { + $r[$x]['content'] = base64_encode(dbunescbin($r[$x]['content'])); + } + $ret['photo'] = $r; + } - $r = q("select * from photo where resource_id = '%s' and uid = %d order by imgscale asc", - dbesc($resource_id), - intval($channel['channel_id']) - ); - if ($r) { - for ($x = 0; $x < count($r); $x ++) { - $r[$x]['content'] = base64_encode(dbunescbin($r[$x]['content'])); - } - $ret['photo'] = $r; - } + $r = q( + "select * from item where resource_id = '%s' and resource_type = 'photo' and uid = %d ", + dbesc($resource_id), + intval($channel['channel_id']) + ); + if ($r) { + $ret['item'] = []; + $items = q( + "select item.*, item.id as item_id from item where item.parent = %d ", + intval($r[0]['id']) + ); + if ($items) { + xchan_query($items); + $items = fetch_post_tags($items, true); + foreach ($items as $rr) { + $ret['item'][] = encode_item($rr, true); + } + } + } + } - $r = q("select * from item where resource_id = '%s' and resource_type = 'photo' and uid = %d ", - dbesc($resource_id), - intval($channel['channel_id']) - ); - if ($r) { - $ret['item'] = []; - $items = q("select item.*, item.id as item_id from item where item.parent = %d ", - intval($r[0]['id']) - ); - if ($items) { - xchan_query($items); - $items = fetch_post_tags($items,true); - foreach ($items as $rr) { - $ret['item'][] = encode_item($rr,true); - } - } - } - } - - return $ret; + return $ret; } @@ -2261,80 +2344,81 @@ function attach_export_data($channel, $resource_id, $deleted = false) { * @param string $s * @return string */ -function get_attach_binname($s) { - $p = $s; - if (strpos($s, 'store/') === 0) { - $p = substr($s, 6); - $p = substr($p, strpos($p, '/') + 1); - } +function get_attach_binname($s) +{ + $p = $s; + if (strpos($s, 'store/') === 0) { + $p = substr($s, 6); + $p = substr($p, strpos($p, '/') + 1); + } - return $p; + return $p; } -function get_dirpath_by_cloudpath($channel, $path) { +function get_dirpath_by_cloudpath($channel, $path) +{ - $path = notags(trim($path)); + $path = notags(trim($path)); - $h = @parse_url($path); + $h = @parse_url($path); - if (! $h || !x($h, 'path')) { - return null; - } - if (substr($h['path'], -1, 1) === '/') { - $h['path'] = substr($h['path'], 0, -1); - } - if (substr($h['path'],0,1) === '/') { - $h['path'] = substr($h['path'], 1); - } - $folders = explode('/', $h['path']); - $f = array_shift($folders); + if (! $h || !x($h, 'path')) { + return null; + } + if (substr($h['path'], -1, 1) === '/') { + $h['path'] = substr($h['path'], 0, -1); + } + if (substr($h['path'], 0, 1) === '/') { + $h['path'] = substr($h['path'], 1); + } + $folders = explode('/', $h['path']); + $f = array_shift($folders); - $nick = $channel['channel_address']; - //check to see if the absolute path was provided (/cloud/channelname/path/to/folder) - if ($f === 'cloud' ) { - $g = array_shift($folders); - if ($g !== $nick) { - // if nick does not follow "cloud", then the top level folder must be called "cloud" - // and the given path must be relative to "/cloud/channelname/". - $folders = array_unshift(array_unshift($folders, $g), $f); - } - } - else { - array_unshift($folders, $f); - } - $clouddir = 'store/' . $nick . '/' ; - $subdir = '/'; - $valid = true; - while ($folders && $valid && is_dir($clouddir . $subdir) && is_readable($clouddir . $subdir)) { - $valid = false; - $f = array_shift($folders); - $items = array_diff(scandir($clouddir . $subdir), array('.', '..')); // hashed names - foreach ($items as $item) { - $filename = find_filename_by_hash($channel['channel_id'], $item); - if ($filename === $f) { - $subdir .= $item . '/'; - $valid = true; - } - } - } - if (! $valid) { - return null; - } - else { - return $clouddir . $subdir; - } + $nick = $channel['channel_address']; + //check to see if the absolute path was provided (/cloud/channelname/path/to/folder) + if ($f === 'cloud') { + $g = array_shift($folders); + if ($g !== $nick) { + // if nick does not follow "cloud", then the top level folder must be called "cloud" + // and the given path must be relative to "/cloud/channelname/". + $folders = array_unshift(array_unshift($folders, $g), $f); + } + } else { + array_unshift($folders, $f); + } + $clouddir = 'store/' . $nick . '/' ; + $subdir = '/'; + $valid = true; + while ($folders && $valid && is_dir($clouddir . $subdir) && is_readable($clouddir . $subdir)) { + $valid = false; + $f = array_shift($folders); + $items = array_diff(scandir($clouddir . $subdir), array('.', '..')); // hashed names + foreach ($items as $item) { + $filename = find_filename_by_hash($channel['channel_id'], $item); + if ($filename === $f) { + $subdir .= $item . '/'; + $valid = true; + } + } + } + if (! $valid) { + return null; + } else { + return $clouddir . $subdir; + } } -function get_filename_by_cloudname($cloudname, $channel, $storepath) { - $items = array_diff(scandir($storepath), array('.', '..')); // hashed names - foreach ($items as $item) { - $filename = find_filename_by_hash($channel['channel_id'], $item); - if ($filename === $cloudname) { - return $item; - } - } - return null; +function get_filename_by_cloudname($cloudname, $channel, $storepath) +{ + $items = array_diff(scandir($storepath), array('.', '..')); // hashed names + foreach ($items as $item) { + $filename = find_filename_by_hash($channel['channel_id'], $item); + if ($filename === $cloudname) { + return $item; + } + } + return null; } /** @@ -2344,51 +2428,50 @@ function get_filename_by_cloudname($cloudname, $channel, $storepath) { * @param string $observer_hash * @param string $srcpath * @param string $cloudpath - * @return boolean + * @return bool */ -function copy_folder_to_cloudfiles($channel, $observer_hash, $srcpath, $cloudpath) { - if (!is_dir($srcpath) || !is_readable($srcpath)) { - logger('Error reading source path: ' . $srcpath, LOGGER_NORMAL); - return false; - } - $nodes = array_diff(scandir($srcpath), array('.', '..')); - foreach ($nodes as $node) { - $clouddir = $cloudpath . '/' . $node; // Sub-folder in cloud files destination - $nodepath = $srcpath . '/' . $node; // Sub-folder in source path - if (is_dir($nodepath)) { - $x = attach_mkdirp($channel, $observer_hash, array('pathname' => $clouddir)); - if (!$x['success']) { - logger('Error creating cloud path: ' . $clouddir, LOGGER_NORMAL); - return false; - } - // Recursively call this function where the source and destination are the subfolders - $success = copy_folder_to_cloudfiles($channel, $observer_hash, $nodepath, $clouddir); - if (! $success) { - logger('Error copying contents of folder: ' . $nodepath, LOGGER_NORMAL); - return false; - } - } - elseif (is_file($nodepath) && is_readable($nodepath)) { - $x = attach_store($channel, $observer_hash, 'import', [ - 'directory' => $cloudpath, - 'src' => $nodepath, - 'filename' => $node, - 'filesize' => @filesize($nodepath), - 'preserve_original' => true - ]); - if (! $x['success']) { - logger('Error copying file: ' . $nodepath, LOGGER_NORMAL); - logger('Return value: ' . json_encode($x), LOGGER_NORMAL); - return false; - } - } - else { - logger('Error scanning source path', LOGGER_NORMAL); - return false; - } - } +function copy_folder_to_cloudfiles($channel, $observer_hash, $srcpath, $cloudpath) +{ + if (!is_dir($srcpath) || !is_readable($srcpath)) { + logger('Error reading source path: ' . $srcpath, LOGGER_NORMAL); + return false; + } + $nodes = array_diff(scandir($srcpath), array('.', '..')); + foreach ($nodes as $node) { + $clouddir = $cloudpath . '/' . $node; // Sub-folder in cloud files destination + $nodepath = $srcpath . '/' . $node; // Sub-folder in source path + if (is_dir($nodepath)) { + $x = attach_mkdirp($channel, $observer_hash, array('pathname' => $clouddir)); + if (!$x['success']) { + logger('Error creating cloud path: ' . $clouddir, LOGGER_NORMAL); + return false; + } + // Recursively call this function where the source and destination are the subfolders + $success = copy_folder_to_cloudfiles($channel, $observer_hash, $nodepath, $clouddir); + if (! $success) { + logger('Error copying contents of folder: ' . $nodepath, LOGGER_NORMAL); + return false; + } + } elseif (is_file($nodepath) && is_readable($nodepath)) { + $x = attach_store($channel, $observer_hash, 'import', [ + 'directory' => $cloudpath, + 'src' => $nodepath, + 'filename' => $node, + 'filesize' => @filesize($nodepath), + 'preserve_original' => true + ]); + if (! $x['success']) { + logger('Error copying file: ' . $nodepath, LOGGER_NORMAL); + logger('Return value: ' . json_encode($x), LOGGER_NORMAL); + return false; + } + } else { + logger('Error scanning source path', LOGGER_NORMAL); + return false; + } + } - return true; + return true; } /** @@ -2404,235 +2487,241 @@ function copy_folder_to_cloudfiles($channel, $observer_hash, $srcpath, $cloudpat * @param int $channel_id * @param int $resource_id * @param string $new_folder_hash - * @return void|boolean + * @return void|bool */ -function attach_move($channel_id, $resource_id, $new_folder_hash, $newname = '') { +function attach_move($channel_id, $resource_id, $new_folder_hash, $newname = '') +{ - $c = channelx_by_n($channel_id); - if (! ($c && $resource_id)) { - return false; - } + $c = channelx_by_n($channel_id); + if (! ($c && $resource_id)) { + return false; + } - // find the resource to be moved + // find the resource to be moved - $r = q("select * from attach where hash = '%s' and uid = %d limit 1", - dbesc($resource_id), - intval($channel_id) - ); - if (! $r) { - logger('resource_id not found'); - return false; - } + $r = q( + "select * from attach where hash = '%s' and uid = %d limit 1", + dbesc($resource_id), + intval($channel_id) + ); + if (! $r) { + logger('resource_id not found'); + return false; + } - $oldstorepath = dbunescbin($r[0]['content']); + $oldstorepath = dbunescbin($r[0]['content']); - // find the resource we are moving to + // find the resource we are moving to - if ($new_folder_hash) { - $n = q("select * from attach where hash = '%s' and uid = %d and is_dir = 1 limit 1", - dbesc($new_folder_hash), - intval($channel_id) - ); - if (! $n) { - return false; - } + if ($new_folder_hash) { + $n = q( + "select * from attach where hash = '%s' and uid = %d and is_dir = 1 limit 1", + dbesc($new_folder_hash), + intval($channel_id) + ); + if (! $n) { + return false; + } - $newdirname = $n[0]['filename']; - $newalbumname = $n[0]['display_path']; - $newstorepath = dbunescbin($n[0]['content']) . '/' . $resource_id; - } - else { + $newdirname = $n[0]['filename']; + $newalbumname = $n[0]['display_path']; + $newstorepath = dbunescbin($n[0]['content']) . '/' . $resource_id; + } else { + // root directory - // root directory + $newdirname = EMPTY_STR; + $newalbumname = EMPTY_STR; + $newstorepath = 'store/' . $c['channel_address'] . '/' . $resource_id; + } - $newdirname = EMPTY_STR; - $newalbumname = EMPTY_STR; - $newstorepath = 'store/' . $c['channel_address'] . '/' . $resource_id; - } + rename($oldstorepath, $newstorepath); - rename($oldstorepath,$newstorepath); + // duplicate detection. If 'overwrite' is specified, return false because we can't yet do that. - // duplicate detection. If 'overwrite' is specified, return false because we can't yet do that. + $oldfilename = $r[0]['filename']; + $newfilename = (($newname) ? basename($newname) : $oldfilename); - $oldfilename = $r[0]['filename']; - $newfilename = (($newname) ? basename($newname) : $oldfilename); + // don't do duplicate check unless our parent folder has changed. - // don't do duplicate check unless our parent folder has changed. + if ($r[0]['folder'] !== $new_folder_hash) { + $s = q( + "select filename, id, hash, filesize from attach where filename = '%s' and folder = '%s' ", + dbesc($newfilename), + dbesc($new_folder_hash) + ); - if ($r[0]['folder'] !== $new_folder_hash) { + if ($s) { + $overwrite = get_pconfig($channel_id, 'system', 'overwrite_dup_files'); + if ($overwrite) { + /// @fixme + return; + } else { + if (strpos($newfilename, '.') !== false) { + $basename = substr($newfilename, 0, strrpos($newfilename, '.')); + $ext = substr($newfilename, strrpos($newfilename, '.')); + } else { + $basename = $newfilename; + $ext = ''; + } - $s = q("select filename, id, hash, filesize from attach where filename = '%s' and folder = '%s' ", - dbesc($newfilename), - dbesc($new_folder_hash) - ); + $matches = false; + if (preg_match('/(.*?)\([0-9]{1,}\)$/', $basename, $matches)) { + $basename = $matches[1]; + } - if ($s) { - $overwrite = get_pconfig($channel_id,'system','overwrite_dup_files'); - if ($overwrite) { - /// @fixme - return; - } - else { - if (strpos($newfilename,'.') !== false) { - $basename = substr($newfilename,0,strrpos($newfilename,'.')); - $ext = substr($newfilename,strrpos($newfilename,'.')); - } - else { - $basename = $newfilename; - $ext = ''; - } + $v = q( + "select filename from attach where ( filename = '%s' OR filename like '%s' ) and folder = '%s' ", + dbesc($basename . $ext), + dbesc($basename . '(%)' . $ext), + dbesc($new_folder_hash) + ); - $matches = false; - if (preg_match('/(.*?)\([0-9]{1,}\)$/',$basename,$matches)) { - $basename = $matches[1]; - } + if ($v) { + $x = 1; - $v = q("select filename from attach where ( filename = '%s' OR filename like '%s' ) and folder = '%s' ", - dbesc($basename . $ext), - dbesc($basename . '(%)' . $ext), - dbesc($new_folder_hash) - ); + do { + $found = false; + foreach ($v as $vv) { + if ($vv['filename'] === $basename . '(' . $x . ')' . $ext) { + $found = true; + break; + } + } + if ($found) { + $x++; + } + } while ($found); - if ($v) { - $x = 1; + $newfilename = $basename . '(' . $x . ')' . $ext; + } else { + $newfilename = $basename . $ext; + } + } + } + } - do { - $found = false; - foreach ($v as $vv) { - if ($vv['filename'] === $basename . '(' . $x . ')' . $ext) { - $found = true; - break; - } - } - if ($found) { - $x++; - } - } while ($found); - - $newfilename = $basename . '(' . $x . ')' . $ext; - } - else { - $newfilename = $basename . $ext; - } - } - } - } - - $t = q("update attach set content = '%s', folder = '%s', filename = '%s' where id = %d", - dbescbin($newstorepath), - dbesc($new_folder_hash), - dbesc($newfilename), - intval($r[0]['id']) - ); + $t = q( + "update attach set content = '%s', folder = '%s', filename = '%s' where id = %d", + dbescbin($newstorepath), + dbesc($new_folder_hash), + dbesc($newfilename), + intval($r[0]['id']) + ); - $x = attach_syspaths($channel_id,$resource_id); + $x = attach_syspaths($channel_id, $resource_id); - $t1 = q("update attach set os_path = '%s', display_path = '%s' where id = %d", - dbesc($x['os_path']), - dbesc($x['path']), - intval($r[0]['id']) - ); + $t1 = q( + "update attach set os_path = '%s', display_path = '%s' where id = %d", + dbesc($x['os_path']), + dbesc($x['path']), + intval($r[0]['id']) + ); - if ($r[0]['is_photo']) { - $t = q("update photo set album = '%s', filename = '%s', os_path = '%s', display_path = '%s' + if ($r[0]['is_photo']) { + $t = q( + "update photo set album = '%s', filename = '%s', os_path = '%s', display_path = '%s' where resource_id = '%s' and uid = %d", - dbesc($newalbumname), - dbesc($newfilename), - dbesc($x['os_path']), - dbesc($x['path']), - dbesc($resource_id), - intval($channel_id) - ); + dbesc($newalbumname), + dbesc($newfilename), + dbesc($x['os_path']), + dbesc($x['path']), + dbesc($resource_id), + intval($channel_id) + ); - $t = q("update photo set content = '%s' where resource_id = '%s' and uid = %d and imgscale = 0", - dbescbin($newstorepath), - dbesc($resource_id), - intval($channel_id) - ); - } + $t = q( + "update photo set content = '%s' where resource_id = '%s' and uid = %d and imgscale = 0", + dbescbin($newstorepath), + dbesc($resource_id), + intval($channel_id) + ); + } - if ($r[0]['is_dir']) { - $move_success = true; - $x = q("select hash from attach where folder = '%s' and uid = %d", - dbesc($r[0]['hash']), - intval($channel_id) - ); - if ($x) { - foreach ($x as $xv) { - $rs = attach_move($channel_id,$xv['hash'],$r[0]['hash']); - if (! $rs) { - $move_success = false; - break; - } - } - } - return $move_success; - } + if ($r[0]['is_dir']) { + $move_success = true; + $x = q( + "select hash from attach where folder = '%s' and uid = %d", + dbesc($r[0]['hash']), + intval($channel_id) + ); + if ($x) { + foreach ($x as $xv) { + $rs = attach_move($channel_id, $xv['hash'], $r[0]['hash']); + if (! $rs) { + $move_success = false; + break; + } + } + } + return $move_success; + } - return true; + return true; } /** - * Used to generate a select input box of all your folders + * Used to generate a select input box of all your folders */ -function attach_folder_select_list($channel_id) { +function attach_folder_select_list($channel_id) +{ - $r = q("select * from attach where is_dir = 1 and uid = %d", - intval($channel_id) - ); + $r = q( + "select * from attach where is_dir = 1 and uid = %d", + intval($channel_id) + ); - $out = []; - $out[''] = '/'; + $out = []; + $out[''] = '/'; - if ($r) { - foreach ($r as $rv) { - $x = attach_folder_rpaths($r,$rv); - if ($x) { - $out[$x[0]] = $x[1]; - } - } - } + if ($r) { + foreach ($r as $rv) { + $x = attach_folder_rpaths($r, $rv); + if ($x) { + $out[$x[0]] = $x[1]; + } + } + } - return $out; + return $out; } -function attach_folder_rpaths($all_folders,$that_folder) { +function attach_folder_rpaths($all_folders, $that_folder) +{ - $path = '/' . $that_folder['filename']; - $current_hash = $that_folder['hash']; - $parent_hash = $that_folder['folder']; - $error = false; - $found = false; + $path = '/' . $that_folder['filename']; + $current_hash = $that_folder['hash']; + $parent_hash = $that_folder['folder']; + $error = false; + $found = false; - if ($parent_hash) { - do { - foreach ($all_folders as $selected) { - if (! $selected['is_dir']) { - continue; - } - if ($selected['hash'] == $parent_hash) { - $path = '/' . $selected['filename'] . $path; - $parent_hash = $selected['folder']; - $found = true; - break; - } - } - if (! $found) { - $error = true; - } - } - while ((! $found) && (! $error) && ($parent_hash != '')); - } + if ($parent_hash) { + do { + foreach ($all_folders as $selected) { + if (! $selected['is_dir']) { + continue; + } + if ($selected['hash'] == $parent_hash) { + $path = '/' . $selected['filename'] . $path; + $parent_hash = $selected['folder']; + $found = true; + break; + } + } + if (! $found) { + $error = true; + } + } while ((! $found) && (! $error) && ($parent_hash != '')); + } - return (($error) ? false : [ $current_hash , $path ]); + return (($error) ? false : [ $current_hash , $path ]); } /** @@ -2640,61 +2729,65 @@ function attach_folder_rpaths($all_folders,$that_folder) { */ -function attach_syspaths($channel_id,$attach_hash) { +function attach_syspaths($channel_id, $attach_hash) +{ - $os_path = ''; - $path = ''; - do { + $os_path = ''; + $path = ''; + do { + $r = q( + "select folder, filename, hash from attach where hash = '%s' and uid = %d", + dbesc($attach_hash), + intval($channel_id) + ); + if (! $r) { + break; + } - $r = q("select folder, filename, hash from attach where hash = '%s' and uid = %d", - dbesc($attach_hash), - intval($channel_id) - ); - if (! $r) { - break; - } + $os_path = $r[0]['hash'] . (($os_path) ? '/' . $os_path : ''); + $path = $r[0]['filename'] . (($path) ? '/' . $path : ''); + $attach_hash = $r[0]['folder']; + } while ($attach_hash); - $os_path = $r[0]['hash'] . (($os_path) ? '/' . $os_path : ''); - $path = $r[0]['filename'] . (($path) ? '/' . $path : ''); - $attach_hash = $r[0]['folder']; - } while ($attach_hash); - - return [ 'os_path' => $os_path, 'path' => $path ]; + return [ 'os_path' => $os_path, 'path' => $path ]; } /** * in earlier releases we did not fill in os_path and display_path in the attach DB structure. - * (It was not needed or used). Going forward we intend to make use of these fields. + * (It was not needed or used). Going forward we intend to make use of these fields. * A cron task checks for empty values (as older attachments may have arrived at our site - * in a clone operation) and executes attach_syspaths() to generate these field values and correct - * the attach table entry. The operation is limited to 100 DB entries at a time so as not to + * in a clone operation) and executes attach_syspaths() to generate these field values and correct + * the attach table entry. The operation is limited to 100 DB entries at a time so as not to * overload the system in any cron run. Eventually it will catch up with old attach structures * and switch into maintenance mode to correct any that might arrive in clone packets from older * sites. */ -function attach_upgrade() { +function attach_upgrade() +{ - $r = q("select id, uid, hash from attach where os_path = '' and display_path = '' limit 100"); - if ($r) { - foreach ($r as $rv) { - $x = attach_syspaths($rv['uid'],$rv['hash']); - if ($x) { - $w = q("update attach set os_path = '%s', display_path = '%s' where id = %d", - dbesc($x['os_path']), - dbesc($x['path']), - intval($rv['id']) - ); - $y = q("update photo set os_path = '%s', display_path = '%s' where uid = %d and resource_id = '%s'", - dbesc($x['os_path']), - dbesc($x['path']), - intval($rv['uid']), - dbesc($rv['hash']) - ); - } - } - } + $r = q("select id, uid, hash from attach where os_path = '' and display_path = '' limit 100"); + if ($r) { + foreach ($r as $rv) { + $x = attach_syspaths($rv['uid'], $rv['hash']); + if ($x) { + $w = q( + "update attach set os_path = '%s', display_path = '%s' where id = %d", + dbesc($x['os_path']), + dbesc($x['path']), + intval($rv['id']) + ); + $y = q( + "update photo set os_path = '%s', display_path = '%s' where uid = %d and resource_id = '%s'", + dbesc($x['os_path']), + dbesc($x['path']), + intval($rv['uid']), + dbesc($rv['hash']) + ); + } + } + } } @@ -2704,52 +2797,50 @@ function attach_upgrade() { */ -function save_chunk($channel,$start,$end,$len) { +function save_chunk($channel, $start, $end, $len) +{ - $result = []; + $result = []; - $tmp_path = $_FILES['files']['tmp_name']; - $new_base = 'cache/' . $channel['channel_address'] . '/tmp'; + $tmp_path = $_FILES['files']['tmp_name']; + $new_base = 'cache/' . $channel['channel_address'] . '/tmp'; - os_mkdir($new_base,STORAGE_DEFAULT_PERMISSIONS,true); + os_mkdir($new_base, STORAGE_DEFAULT_PERMISSIONS, true); - if (! is_dir($new_base)) { - logger('directory create failed for ' . $new_base); - } + if (! is_dir($new_base)) { + logger('directory create failed for ' . $new_base); + } - $new_path = $new_base . '/' . $_FILES['files']['name'] . '.ftmp'; + $new_path = $new_base . '/' . $_FILES['files']['name'] . '.ftmp'; - if (file_exists($new_path) && intval($start) === 0) { - $result['partial'] = true; - $result['length'] = intval(filesize($new_path)); - return $result; - } + if (file_exists($new_path) && intval($start) === 0) { + $result['partial'] = true; + $result['length'] = intval(filesize($new_path)); + return $result; + } - if (! file_exists($new_path)) { - rename($tmp_path,$new_path); - } - else { - $istream = fopen($tmp_path,'rb'); - $ostream = fopen($new_path,'ab'); - if ($istream && $ostream) { - pipe_streams($istream,$ostream); - fclose($istream); - fclose($ostream); - } - } - if (($len - 1) == $end) { - unlink($tmp_path); - $result['name'] = $_FILES['files']['name']; - $result['type'] = $_FILES['files']['type']; - $result['tmp_name'] = $new_path; - $result['error'] = 0; - $result['size'] = $len; - $result['complete'] = true; - return $result; - } - $result['partial'] = true; - $result['length'] = intval(filesize($new_path)); - return $result; + if (! file_exists($new_path)) { + rename($tmp_path, $new_path); + } else { + $istream = fopen($tmp_path, 'rb'); + $ostream = fopen($new_path, 'ab'); + if ($istream && $ostream) { + pipe_streams($istream, $ostream); + fclose($istream); + fclose($ostream); + } + } + if (($len - 1) == $end) { + unlink($tmp_path); + $result['name'] = $_FILES['files']['name']; + $result['type'] = $_FILES['files']['type']; + $result['tmp_name'] = $new_path; + $result['error'] = 0; + $result['size'] = $len; + $result['complete'] = true; + return $result; + } + $result['partial'] = true; + $result['length'] = intval(filesize($new_path)); + return $result; } - - diff --git a/include/auth.php b/include/auth.php index 49813ada5..36fb9a02d 100644 --- a/include/auth.php +++ b/include/auth.php @@ -1,6 +1,5 @@ null, 'channel' => null, 'xchan' => null ]; - $login = punify($login); + $ret = [ 'account' => null, 'channel' => null, 'xchan' => null ]; + $login = punify($login); - $email_verify = get_config('system', 'verify_email'); - $register_policy = get_config('system', 'register_policy'); + $email_verify = get_config('system', 'verify_email'); + $register_policy = get_config('system', 'register_policy'); - if(! $login) - return null; + if (! $login) { + return null; + } - $account = null; - $channel = null; - $xchan = null; + $account = null; + $channel = null; + $xchan = null; - $addon_auth = [ - 'username' => $login, - 'password' => trim($pass), - 'authenticated' => 0, - 'user_record' => null - ]; + $addon_auth = [ + 'username' => $login, + 'password' => trim($pass), + 'authenticated' => 0, + 'user_record' => null + ]; - /** - * - * A plugin indicates successful login by setting 'authenticated' to non-zero value and returning a user record - * Plugins should never set 'authenticated' except to indicate success - as hooks may be chained - * and later plugins should not interfere with an earlier one that succeeded. - * - */ + /** + * + * A plugin indicates successful login by setting 'authenticated' to non-zero value and returning a user record + * Plugins should never set 'authenticated' except to indicate success - as hooks may be chained + * and later plugins should not interfere with an earlier one that succeeded. + * + */ - call_hooks('authenticate', $addon_auth); + call_hooks('authenticate', $addon_auth); - if(($addon_auth['authenticated']) && is_array($addon_auth['user_record']) && (! empty($addon_auth['user_record']))) { - $ret['account'] = $addon_auth['user_record']; - return $ret; - } - else { - if(! strpos($login,'@')) { - $channel = channelx_by_nick($login); - if(! $channel) { - $x = q("select * from atoken where atoken_name = '%s' and atoken_token = '%s' limit 1", - dbesc($login), - dbesc($pass) - ); - if($x) { - $ret['xchan'] = atoken_xchan($x[0]); - atoken_create_xchan($ret['xchan']); - return $ret; - } - } - } - if($channel) { - $where = " where account_id = " . intval($channel['channel_account_id']) . " "; - } - else { - $where = " where account_email = '" . dbesc($login) . "' "; - } + if (($addon_auth['authenticated']) && is_array($addon_auth['user_record']) && (! empty($addon_auth['user_record']))) { + $ret['account'] = $addon_auth['user_record']; + return $ret; + } else { + if (! strpos($login, '@')) { + $channel = channelx_by_nick($login); + if (! $channel) { + $x = q( + "select * from atoken where atoken_name = '%s' and atoken_token = '%s' limit 1", + dbesc($login), + dbesc($pass) + ); + if ($x) { + $ret['xchan'] = atoken_xchan($x[0]); + atoken_create_xchan($ret['xchan']); + return $ret; + } + } + } + if ($channel) { + $where = " where account_id = " . intval($channel['channel_account_id']) . " "; + } else { + $where = " where account_email = '" . dbesc($login) . "' "; + } - $a = q("select * from account $where"); - if(! $a) { - return null; - } + $a = q("select * from account $where"); + if (! $a) { + return null; + } - $account = $a[0]; + $account = $a[0]; - // Currently we only verify email address if there is an open registration policy. - // This isn't because of any policy - it's because the workflow gets too complicated if - // you have to verify the email and then go through the account approval workflow before - // letting them login. + // Currently we only verify email address if there is an open registration policy. + // This isn't because of any policy - it's because the workflow gets too complicated if + // you have to verify the email and then go through the account approval workflow before + // letting them login. - if(($email_verify) && ($register_policy == REGISTER_OPEN) && ($account['account_flags'] & ACCOUNT_UNVERIFIED)) { - logger('email verification required for ' . $login); - return ( [ 'reason' => 'unvalidated' ] ); - } + if (($email_verify) && ($register_policy == REGISTER_OPEN) && ($account['account_flags'] & ACCOUNT_UNVERIFIED)) { + logger('email verification required for ' . $login); + return ( [ 'reason' => 'unvalidated' ] ); + } - if($channel) { - // Try the authentication plugin again since weve determined we are using the channel login instead of account login - $addon_auth = [ - 'username' => $account['account_email'], - 'password' => trim($pass), - 'authenticated' => 0, - 'user_record' => null - ]; + if ($channel) { + // Try the authentication plugin again since weve determined we are using the channel login instead of account login + $addon_auth = [ + 'username' => $account['account_email'], + 'password' => trim($pass), + 'authenticated' => 0, + 'user_record' => null + ]; - call_hooks('authenticate', $addon_auth); + call_hooks('authenticate', $addon_auth); - if(($addon_auth['authenticated']) && is_array($addon_auth['user_record']) && (! empty($addon_auth['user_record']))) { - $ret['account'] = $addon_auth['user_record']; - return $ret; - } - } + if (($addon_auth['authenticated']) && is_array($addon_auth['user_record']) && (! empty($addon_auth['user_record']))) { + $ret['account'] = $addon_auth['user_record']; + return $ret; + } + } - if(($account['account_flags'] == ACCOUNT_OK) - && (hash('whirlpool',$account['account_salt'] . $pass) === $account['account_password'])) { - logger('password verified for ' . $login); - $ret['account'] = $account; - if($channel) - $ret['channel'] = $channel; - return $ret; - } - } + if ( + ($account['account_flags'] == ACCOUNT_OK) + && (hash('whirlpool', $account['account_salt'] . $pass) === $account['account_password']) + ) { + logger('password verified for ' . $login); + $ret['account'] = $account; + if ($channel) { + $ret['channel'] = $channel; + } + return $ret; + } + } - $error = 'password failed for ' . $login; - logger($error); + $error = 'password failed for ' . $login; + logger($error); - if($account['account_flags'] & ACCOUNT_UNVERIFIED) - logger('Account is unverified. account_flags = ' . $account['account_flags']); - if($account['account_flags'] & ACCOUNT_BLOCKED) - logger('Account is blocked. account_flags = ' . $account['account_flags']); - if($account['account_flags'] & ACCOUNT_EXPIRED) - logger('Account is expired. account_flags = ' . $account['account_flags']); - if($account['account_flags'] & ACCOUNT_REMOVED) - logger('Account is removed. account_flags = ' . $account['account_flags']); - if($account['account_flags'] & ACCOUNT_PENDING) - logger('Account is pending. account_flags = ' . $account['account_flags']); + if ($account['account_flags'] & ACCOUNT_UNVERIFIED) { + logger('Account is unverified. account_flags = ' . $account['account_flags']); + } + if ($account['account_flags'] & ACCOUNT_BLOCKED) { + logger('Account is blocked. account_flags = ' . $account['account_flags']); + } + if ($account['account_flags'] & ACCOUNT_EXPIRED) { + logger('Account is expired. account_flags = ' . $account['account_flags']); + } + if ($account['account_flags'] & ACCOUNT_REMOVED) { + logger('Account is removed. account_flags = ' . $account['account_flags']); + } + if ($account['account_flags'] & ACCOUNT_PENDING) { + logger('Account is pending. account_flags = ' . $account['account_flags']); + } - log_failed_login($error); + log_failed_login($error); - return null; + return null; } /** @@ -168,10 +176,12 @@ function account_verify_password($login, $pass) { * @param string $errormsg * Error message to display for failed login. */ -function log_failed_login($errormsg) { - $authlog = get_config('system', 'authlog'); - if ($authlog) - @file_put_contents($authlog, datetime_convert() . ':' . session_id() . ' ' . $errormsg . PHP_EOL, FILE_APPEND); +function log_failed_login($errormsg) +{ + $authlog = get_config('system', 'authlog'); + if ($authlog) { + @file_put_contents($authlog, datetime_convert() . ':' . session_id() . ' ' . $errormsg . PHP_EOL, FILE_APPEND); + } } /** @@ -180,178 +190,174 @@ function log_failed_login($errormsg) { * also handles logout */ -if((isset($_SESSION)) && (x($_SESSION, 'authenticated')) && - ((! (x($_POST, 'auth-params'))) || ($_POST['auth-params'] !== 'login'))) { +if ( + (isset($_SESSION)) && (x($_SESSION, 'authenticated')) && + ((! (x($_POST, 'auth-params'))) || ($_POST['auth-params'] !== 'login')) +) { + // process a logout request - // process a logout request - - if(((x($_POST, 'auth-params')) && ($_POST['auth-params'] === 'logout')) || (App::$module === 'logout')) { - // process logout request - $args = array('channel_id' => local_channel()); - call_hooks('logging_out', $args); + if (((x($_POST, 'auth-params')) && ($_POST['auth-params'] === 'logout')) || (App::$module === 'logout')) { + // process logout request + $args = array('channel_id' => local_channel()); + call_hooks('logging_out', $args); - if($_SESSION['delegate'] && $_SESSION['delegate_push']) { - $_SESSION = $_SESSION['delegate_push']; - info( t('Delegation session ended.') . EOL); - } - else { - App::$session->nuke(); - info( t('Logged out.') . EOL); - } + if ($_SESSION['delegate'] && $_SESSION['delegate_push']) { + $_SESSION = $_SESSION['delegate_push']; + info(t('Delegation session ended.') . EOL); + } else { + App::$session->nuke(); + info(t('Logged out.') . EOL); + } - goaway(z_root()); - } + goaway(z_root()); + } - // re-validate a visitor, optionally invoke "su" if permitted to do so + // re-validate a visitor, optionally invoke "su" if permitted to do so - if(x($_SESSION, 'visitor_id') && (! x($_SESSION, 'uid'))) { - // if our authenticated guest is allowed to take control of the admin channel, make it so. - $admins = get_config('system', 'remote_admin'); - if($admins && is_array($admins) && in_array($_SESSION['visitor_id'], $admins)) { - $x = q("select * from account where account_email = '%s' and account_email != '' and ( account_flags & %d ) > 0 limit 1", - dbesc(get_config('system', 'admin_email')), - intval(ACCOUNT_ROLE_ADMIN) - ); - if($x) { - App::$session->new_cookie(60 * 60 * 24); // one day - $_SESSION['last_login_date'] = datetime_convert(); - unset($_SESSION['visitor_id']); // no longer a visitor - authenticate_success($x[0], null, true, true); - } - } - if(array_key_exists('atoken',$_SESSION)) { - $y = q("select * from atoken where atoken_id = %d limit 1", - intval($_SESSION['atoken']) - ); - if($y) - $r = array(atoken_xchan($y[0])); - } - else { - $r = q("select * from xchan left join hubloc on xchan_hash = hubloc_hash where xchan_hash = '%s' ", - dbesc($_SESSION['visitor_id']) - ); - } - if($r) { - $r = Libzot::zot_record_preferred($r); - App::set_observer($r); - } - else { - unset($_SESSION['visitor_id']); - unset($_SESSION['authenticated']); - } - App::set_groups(init_groups_visitor($_SESSION['visitor_id'])); - } + if (x($_SESSION, 'visitor_id') && (! x($_SESSION, 'uid'))) { + // if our authenticated guest is allowed to take control of the admin channel, make it so. + $admins = get_config('system', 'remote_admin'); + if ($admins && is_array($admins) && in_array($_SESSION['visitor_id'], $admins)) { + $x = q( + "select * from account where account_email = '%s' and account_email != '' and ( account_flags & %d ) > 0 limit 1", + dbesc(get_config('system', 'admin_email')), + intval(ACCOUNT_ROLE_ADMIN) + ); + if ($x) { + App::$session->new_cookie(60 * 60 * 24); // one day + $_SESSION['last_login_date'] = datetime_convert(); + unset($_SESSION['visitor_id']); // no longer a visitor + authenticate_success($x[0], null, true, true); + } + } + if (array_key_exists('atoken', $_SESSION)) { + $y = q( + "select * from atoken where atoken_id = %d limit 1", + intval($_SESSION['atoken']) + ); + if ($y) { + $r = array(atoken_xchan($y[0])); + } + } else { + $r = q( + "select * from xchan left join hubloc on xchan_hash = hubloc_hash where xchan_hash = '%s' ", + dbesc($_SESSION['visitor_id']) + ); + } + if ($r) { + $r = Libzot::zot_record_preferred($r); + App::set_observer($r); + } else { + unset($_SESSION['visitor_id']); + unset($_SESSION['authenticated']); + } + App::set_groups(init_groups_visitor($_SESSION['visitor_id'])); + } - // already logged in user returning + // already logged in user returning - if(x($_SESSION, 'uid') || x($_SESSION, 'account_id')) { + if (x($_SESSION, 'uid') || x($_SESSION, 'account_id')) { + App::$session->return_check(); - App::$session->return_check(); + $r = q( + "select * from account where account_id = %d limit 1", + intval($_SESSION['account_id']) + ); - $r = q("select * from account where account_id = %d limit 1", - intval($_SESSION['account_id']) - ); + if (($r) && (($r[0]['account_flags'] == ACCOUNT_OK) || ($r[0]['account_flags'] == ACCOUNT_UNVERIFIED))) { + App::$account = $r[0]; + $login_refresh = false; + if (! x($_SESSION, 'last_login_date')) { + $_SESSION['last_login_date'] = datetime_convert('UTC', 'UTC'); + } + if (strcmp(datetime_convert('UTC', 'UTC', 'now - 12 hours'), $_SESSION['last_login_date']) > 0) { + $_SESSION['last_login_date'] = datetime_convert(); + App::$session->extend_cookie(); + $login_refresh = true; + } + $ch = (($_SESSION['uid']) ? channelx_by_n($_SESSION['uid']) : null); + authenticate_success($r[0], null, $ch, false, false, $login_refresh); + } else { + $_SESSION['account_id'] = 0; + App::$session->nuke(); + goaway(z_root()); + } + } // end logged in user returning +} else { + if (isset($_SESSION)) { + App::$session->nuke(); + } - if(($r) && (($r[0]['account_flags'] == ACCOUNT_OK) || ($r[0]['account_flags'] == ACCOUNT_UNVERIFIED))) { - App::$account = $r[0]; - $login_refresh = false; - if(! x($_SESSION,'last_login_date')) { - $_SESSION['last_login_date'] = datetime_convert('UTC','UTC'); - } - if(strcmp(datetime_convert('UTC','UTC','now - 12 hours'), $_SESSION['last_login_date']) > 0 ) { - $_SESSION['last_login_date'] = datetime_convert(); - App::$session->extend_cookie(); - $login_refresh = true; - } - $ch = (($_SESSION['uid']) ? channelx_by_n($_SESSION['uid']) : null); - authenticate_success($r[0], null, $ch, false, false, $login_refresh); - } - else { - $_SESSION['account_id'] = 0; - App::$session->nuke(); - goaway(z_root()); - } - } // end logged in user returning -} -else { + // handle a fresh login request - if(isset($_SESSION)) { - App::$session->nuke(); - } + if ((x($_POST, 'password')) && strlen($_POST['password'])) { + $encrypted = hash('whirlpool', trim($_POST['password'])); + } - // handle a fresh login request + if ((x($_POST, 'auth-params')) && $_POST['auth-params'] === 'login') { + $atoken = null; + $account = null; + $channel = null; - if((x($_POST, 'password')) && strlen($_POST['password'])) - $encrypted = hash('whirlpool', trim($_POST['password'])); + $verify = account_verify_password($_POST['username'], $_POST['password']); + if ($verify && array_key_exists('reason', $verify) && $verify['reason'] === 'unvalidated') { + notice(t('Email validation is incomplete. Please check your email.')); + goaway(z_root() . '/email_validation/' . bin2hex(punify(trim(escape_tags($_POST['username']))))); + } elseif ($verify) { + $atoken = $verify['xchan']; + $channel = $verify['channel']; + $account = App::$account = $verify['account']; + } - if((x($_POST, 'auth-params')) && $_POST['auth-params'] === 'login') { + if (App::$account) { + $_SESSION['account_id'] = App::$account['account_id']; + } elseif ($atoken) { + atoken_login($atoken); + } else { + notice(t('Failed authentication') . EOL); + } - $atoken = null; - $account = null; - $channel = null; + if (! ($account || $atoken)) { + $error = 'authenticate: failed login attempt: ' . notags(trim($_POST['username'])) . ' from IP ' . $_SERVER['REMOTE_ADDR']; + logger($error); + // Also log failed logins to a separate auth log to reduce overhead for server side intrusion prevention + $authlog = get_config('system', 'authlog'); + if ($authlog) { + @file_put_contents($authlog, datetime_convert() . ':' . session_id() . ' ' . $error . "\n", FILE_APPEND); + } + notice(t('Login failed.') . EOL); + goaway(z_root() . '/login'); + } - $verify = account_verify_password($_POST['username'], $_POST['password']); - if($verify && array_key_exists('reason',$verify) && $verify['reason'] === 'unvalidated') { - notice( t('Email validation is incomplete. Please check your email.')); - goaway(z_root() . '/email_validation/' . bin2hex(punify(trim(escape_tags($_POST['username']))))); - } - elseif($verify) { - $atoken = $verify['xchan']; - $channel = $verify['channel']; - $account = App::$account = $verify['account']; - } + // If the user specified to remember the authentication, then change the cookie + // to expire after one year (the default is when the browser is closed). + // If the user did not specify to remember, change the cookie to expire when the + // browser is closed. The reason this is necessary is because if the user + // specifies to remember, then logs out and logs back in without specifying to + // remember, the old "remember" cookie may remain and prevent the session from + // expiring when the browser is closed. + // + // It seems like I should be able to test for the old cookie, but for some reason when + // I read the lifetime value from session_get_cookie_params(), I always get '0' + // (i.e. expire when the browser is closed), even when there's a time expiration + // on the cookie - if(App::$account) { - $_SESSION['account_id'] = App::$account['account_id']; - } - elseif($atoken) { - atoken_login($atoken); - } - else { - notice( t('Failed authentication') . EOL); - } + if (($_POST['remember_me']) || ($_POST['remember'])) { + $_SESSION['remember_me'] = 1; + App::$session->new_cookie(31449600); // one year + } else { + $_SESSION['remember_me'] = 0; + App::$session->new_cookie(0); // 0 means delete on browser exit + } - if(! ($account || $atoken)) { - $error = 'authenticate: failed login attempt: ' . notags(trim($_POST['username'])) . ' from IP ' . $_SERVER['REMOTE_ADDR']; - logger($error); - // Also log failed logins to a separate auth log to reduce overhead for server side intrusion prevention - $authlog = get_config('system', 'authlog'); - if ($authlog) - @file_put_contents($authlog, datetime_convert() . ':' . session_id() . ' ' . $error . "\n", FILE_APPEND); - notice( t('Login failed.') . EOL ); - goaway(z_root() . '/login'); - } + // if we haven't failed up this point, log them in. - // If the user specified to remember the authentication, then change the cookie - // to expire after one year (the default is when the browser is closed). - // If the user did not specify to remember, change the cookie to expire when the - // browser is closed. The reason this is necessary is because if the user - // specifies to remember, then logs out and logs back in without specifying to - // remember, the old "remember" cookie may remain and prevent the session from - // expiring when the browser is closed. - // - // It seems like I should be able to test for the old cookie, but for some reason when - // I read the lifetime value from session_get_cookie_params(), I always get '0' - // (i.e. expire when the browser is closed), even when there's a time expiration - // on the cookie - - if(($_POST['remember_me']) || ($_POST['remember'])) { - $_SESSION['remember_me'] = 1; - App::$session->new_cookie(31449600); // one year - } - else { - $_SESSION['remember_me'] = 0; - App::$session->new_cookie(0); // 0 means delete on browser exit - } - - // if we haven't failed up this point, log them in. - - $_SESSION['last_login_date'] = datetime_convert(); - if(! $atoken) { - authenticate_success($account,$channel,true, true); - } - } + $_SESSION['last_login_date'] = datetime_convert(); + if (! $atoken) { + authenticate_success($account, $channel, true, true); + } + } } @@ -362,19 +368,22 @@ else { * and returns the corresponding channel_id. * * @fixme How do we prevent that an OpenID identity is used more than once? - * + * * @param string $authid * The given openid_identity * @return int|bool * Return channel_id from pconfig or false. */ -function match_openid($authid) { - // Query the uid/channel_id from pconfig for a given value. - $r = q("SELECT uid FROM pconfig WHERE cat = 'system' AND k = 'openid' AND v = '%s' LIMIT 1", - dbesc($authid) - ); - if($r) - return $r[0]['uid']; - return false; +function match_openid($authid) +{ + // Query the uid/channel_id from pconfig for a given value. + $r = q( + "SELECT uid FROM pconfig WHERE cat = 'system' AND k = 'openid' AND v = '%s' LIMIT 1", + dbesc($authid) + ); + if ($r) { + return $r[0]['uid']; + } + return false; } diff --git a/include/bbcode.php b/include/bbcode.php index bd22fb222..0a8390f2c 100644 --- a/include/bbcode.php +++ b/include/bbcode.php @@ -14,136 +14,157 @@ require_once('include/oembed.php'); require_once('include/event.php'); require_once('include/html2plain.php'); -function get_bb_tag_pos($s, $name, $occurance = 1) { +function get_bb_tag_pos($s, $name, $occurance = 1) +{ - if($occurance < 1) - $occurance = 1; + if ($occurance < 1) { + $occurance = 1; + } - $start_open = -1; - for($i = 1; $i <= $occurance; $i++) { - if( $start_open !== false) - $start_open = strpos($s, '[' . $name, $start_open + 1); // allow [name= type tags - } + $start_open = -1; + for ($i = 1; $i <= $occurance; $i++) { + if ($start_open !== false) { + $start_open = strpos($s, '[' . $name, $start_open + 1); // allow [name= type tags + } + } - if( $start_open === false) - return false; + if ($start_open === false) { + return false; + } - $start_equal = strpos($s, '=', $start_open); - $start_close = strpos($s, ']', $start_open); + $start_equal = strpos($s, '=', $start_open); + $start_close = strpos($s, ']', $start_open); - if( $start_close === false) - return false; + if ($start_close === false) { + return false; + } - $start_close++; + $start_close++; - $end_open = strpos($s, '[/' . $name . ']', $start_close); + $end_open = strpos($s, '[/' . $name . ']', $start_close); - if( $end_open === false) - return false; + if ($end_open === false) { + return false; + } - $res = array( 'start' => array('open' => $start_open, 'close' => $start_close), - 'end' => array('open' => $end_open, 'close' => $end_open + strlen('[/' . $name . ']')) ); - if( $start_equal !== false) - $res['start']['equal'] = $start_equal + 1; + $res = array( 'start' => array('open' => $start_open, 'close' => $start_close), + 'end' => array('open' => $end_open, 'close' => $end_open + strlen('[/' . $name . ']')) ); + if ($start_equal !== false) { + $res['start']['equal'] = $start_equal + 1; + } - return $res; + return $res; } -function bb_tag_preg_replace($pattern, $replace, $name, $s) { +function bb_tag_preg_replace($pattern, $replace, $name, $s) +{ - $string = $s; + $string = $s; - $occurance = 1; - $pos = get_bb_tag_pos($string, $name, $occurance); - while($pos !== false && $occurance < 1000) { + $occurance = 1; + $pos = get_bb_tag_pos($string, $name, $occurance); + while ($pos !== false && $occurance < 1000) { + $start = substr($string, 0, $pos['start']['open']); + $subject = substr($string, $pos['start']['open'], $pos['end']['close'] - $pos['start']['open']); + $end = substr($string, $pos['end']['close']); + if ($end === false) { + $end = ''; + } - $start = substr($string, 0, $pos['start']['open']); - $subject = substr($string, $pos['start']['open'], $pos['end']['close'] - $pos['start']['open']); - $end = substr($string, $pos['end']['close']); - if($end === false) - $end = ''; + $subject = preg_replace($pattern, $replace, $subject); + $string = $start . $subject . $end; - $subject = preg_replace($pattern, $replace, $subject); - $string = $start . $subject . $end; + $occurance++; + $pos = get_bb_tag_pos($string, $name, $occurance); + } - $occurance++; - $pos = get_bb_tag_pos($string, $name, $occurance); - } - - return $string; + return $string; } -function tryoembed($match) { - $url = ((count($match) == 2) ? $match[1] : $match[2]); +function tryoembed($match) +{ + $url = ((count($match) == 2) ? $match[1] : $match[2]); - $o = oembed_fetch_url($url); + $o = oembed_fetch_url($url); - if ($o['type'] == 'error') - return $match[0]; + if ($o['type'] == 'error') { + return $match[0]; + } - $html = oembed_format_object($o); - return $html; + $html = oembed_format_object($o); + return $html; } -function nakedoembed($match) { - $url = ((count($match) == 2) ? $match[1] : $match[2]); +function nakedoembed($match) +{ + $url = ((count($match) == 2) ? $match[1] : $match[2]); - $strip_url = strip_escaped_zids($url); + $strip_url = strip_escaped_zids($url); - // this function no longer performs oembed on naked links - // because they author may have created naked links intentionally. - // Now it just strips zids on naked links. - - return str_replace($url,$strip_url,$match[0]); + // this function no longer performs oembed on naked links + // because they author may have created naked links intentionally. + // Now it just strips zids on naked links. + + return str_replace($url, $strip_url, $match[0]); } -function tryzrlaudio($match) { - $link = $match[1]; - $zrl = is_matrix_url($link); - if($zrl) - $link = zid($link); +function tryzrlaudio($match) +{ + $link = $match[1]; + $zrl = is_matrix_url($link); + if ($zrl) { + $link = zid($link); + } - return ''; + return ''; } -function tryzrlvideo($match) { - $link = $match[1]; - $zrl = is_matrix_url($link); - if($zrl) - $link = zid($link); +function tryzrlvideo($match) +{ + $link = $match[1]; + $zrl = is_matrix_url($link); + if ($zrl) { + $link = zid($link); + } - $static_link = get_config('system','video_default_poster','images/video_poster.jpg'); - if($static_link) - $poster = 'poster="' . escape_tags($static_link) . '" ' ; + $static_link = get_config('system', 'video_default_poster', 'images/video_poster.jpg'); + if ($static_link) { + $poster = 'poster="' . escape_tags($static_link) . '" ' ; + } - return ''; + return ''; } -function videowithopts($match) { - $link = $match[2]; - $zrl = is_matrix_url($link); - if($zrl) - $link = zid($link); +function videowithopts($match) +{ + $link = $match[2]; + $zrl = is_matrix_url($link); + if ($zrl) { + $link = zid($link); + } - $attributes = $match[1]; + $attributes = $match[1]; - $poster = ""; + $poster = ""; - preg_match("/poster='(.*?)'/ism", $attributes, $matches); - if (isset($matches[1]) && $matches[1] != "") - $poster = 'poster="' . (($zrl) ? zid($matches[1]) : $matches[1]) . '"'; + preg_match("/poster='(.*?)'/ism", $attributes, $matches); + if (isset($matches[1]) && $matches[1] != "") { + $poster = 'poster="' . (($zrl) ? zid($matches[1]) : $matches[1]) . '"'; + } - preg_match("/poster=\"\;(.*?)\"\;/ism", $attributes, $matches); - if (isset($matches[1]) && $matches[1] != "") - $poster = 'poster="' . (($zrl) ? zid($matches[1]) : $matches[1]) . '"'; + preg_match("/poster=\"\;(.*?)\"\;/ism", $attributes, $matches); + if (isset($matches[1]) && $matches[1] != "") { + $poster = 'poster="' . (($zrl) ? zid($matches[1]) : $matches[1]) . '"'; + } - preg_match("/poster=\\\"(.*?)\\\"/ism", $attributes, $matches); - if (isset($matches[1]) && $matches[1] != "") - $poster = 'poster="' . (($zrl) ? zid($matches[1]) : $matches[1]) . '"'; + preg_match("/poster=\\\"(.*?)\\\"/ism", $attributes, $matches); + if (isset($matches[1]) && $matches[1] != "") { + $poster = 'poster="' . (($zrl) ? zid($matches[1]) : $matches[1]) . '"'; + } - return ''; + return ''; } @@ -153,87 +174,92 @@ function videowithopts($match) { // [noparse][ i ]italic[ /i ][/noparse], // to hide them from parser. -function bb_spacefy($st) { - $whole_match = $st[0]; - $captured = $st[1]; - $spacefied = preg_replace("/\[(.*?)\]/", "[ $1 ]", $captured); - $new_str = str_replace($captured, $spacefied, $whole_match); +function bb_spacefy($st) +{ + $whole_match = $st[0]; + $captured = $st[1]; + $spacefied = preg_replace("/\[(.*?)\]/", "[ $1 ]", $captured); + $new_str = str_replace($captured, $spacefied, $whole_match); - return $new_str; + return $new_str; } // The previously spacefied [noparse][ i ]italic[ /i ][/noparse], // now turns back and the [noparse] tags are trimmed // returning [i]italic[/i] -function bb_unspacefy_and_trim($st) { - //$whole_match = $st[0]; - $captured = $st[1]; - $unspacefied = preg_replace("/\[ (.*?)\ ]/", "[$1]", $captured); +function bb_unspacefy_and_trim($st) +{ + //$whole_match = $st[0]; + $captured = $st[1]; + $unspacefied = preg_replace("/\[ (.*?)\ ]/", "[$1]", $captured); - return $unspacefied; + return $unspacefied; } -function bb_extract_images($body) { +function bb_extract_images($body) +{ - $saved_image = []; - $orig_body = $body; - $new_body = ''; + $saved_image = []; + $orig_body = $body; + $new_body = ''; - $cnt = 0; - $img_start = strpos($orig_body, '[img'); - $img_st_close = ($img_start !== false ? strpos(substr($orig_body, $img_start), ']') : false); - $img_end = ($img_start !== false ? strpos(substr($orig_body, $img_start), '[/img]') : false); - while(($img_st_close !== false) && ($img_end !== false)) { + $cnt = 0; + $img_start = strpos($orig_body, '[img'); + $img_st_close = ($img_start !== false ? strpos(substr($orig_body, $img_start), ']') : false); + $img_end = ($img_start !== false ? strpos(substr($orig_body, $img_start), '[/img]') : false); + while (($img_st_close !== false) && ($img_end !== false)) { + $img_st_close++; // make it point to AFTER the closing bracket + $img_end += $img_start; - $img_st_close++; // make it point to AFTER the closing bracket - $img_end += $img_start; + if (! strcmp(substr($orig_body, $img_start + $img_st_close, 5), 'data:')) { + // This is an embedded image - if(! strcmp(substr($orig_body, $img_start + $img_st_close, 5), 'data:')) { - // This is an embedded image + $saved_image[$cnt] = substr($orig_body, $img_start + $img_st_close, $img_end - ($img_start + $img_st_close)); + $new_body = $new_body . substr($orig_body, 0, $img_start) . '[$#saved_image' . $cnt . '#$]'; - $saved_image[$cnt] = substr($orig_body, $img_start + $img_st_close, $img_end - ($img_start + $img_st_close)); - $new_body = $new_body . substr($orig_body, 0, $img_start) . '[$#saved_image' . $cnt . '#$]'; + $cnt++; + } else { + $new_body = $new_body . substr($orig_body, 0, $img_end + strlen('[/img]')); + } - $cnt++; - } - else - $new_body = $new_body . substr($orig_body, 0, $img_end + strlen('[/img]')); + $orig_body = substr($orig_body, $img_end + strlen('[/img]')); - $orig_body = substr($orig_body, $img_end + strlen('[/img]')); + if ($orig_body === false) { // in case the body ends on a closing image tag + $orig_body = ''; + } - if($orig_body === false) // in case the body ends on a closing image tag - $orig_body = ''; + $img_start = strpos($orig_body, '[img'); + $img_st_close = ($img_start !== false ? strpos(substr($orig_body, $img_start), ']') : false); + $img_end = ($img_start !== false ? strpos(substr($orig_body, $img_start), '[/img]') : false); + } - $img_start = strpos($orig_body, '[img'); - $img_st_close = ($img_start !== false ? strpos(substr($orig_body, $img_start), ']') : false); - $img_end = ($img_start !== false ? strpos(substr($orig_body, $img_start), '[/img]') : false); - } + $new_body = $new_body . $orig_body; - $new_body = $new_body . $orig_body; - - return array('body' => $new_body, 'images' => $saved_image); + return array('body' => $new_body, 'images' => $saved_image); } -function bb_replace_images($body, $images) { +function bb_replace_images($body, $images) +{ - $newbody = $body; - $cnt = 0; + $newbody = $body; + $cnt = 0; - if(! $images) - return $newbody; + if (! $images) { + return $newbody; + } - foreach($images as $image) { - // We're depending on the property of 'foreach' (specified on the PHP website) that - // it loops over the array starting from the first element and going sequentially - // to the last element - $newbody = str_replace('[$#saved_image' . $cnt . '#$]', '' . t('Image/photo') . '', $newbody); - $cnt++; - } -// logger('replace_images: ' . $newbody); - return $newbody; + foreach ($images as $image) { + // We're depending on the property of 'foreach' (specified on the PHP website) that + // it loops over the array starting from the first element and going sequentially + // to the last element + $newbody = str_replace('[$#saved_image' . $cnt . '#$]', '' . t('Image/photo') . '', $newbody); + $cnt++; + } +// logger('replace_images: ' . $newbody); + return $newbody; } /** @@ -243,48 +269,55 @@ function bb_replace_images($body, $images) { * @return string HTML code */ -function bb_parse_crypt($match) { +function bb_parse_crypt($match) +{ - $matches = []; - $attributes = $match[1]; + $matches = []; + $attributes = $match[1]; - $algorithm = ""; + $algorithm = ""; - preg_match("/alg='(.*?)'/ism", $attributes, $matches); - if ($matches[1] != "") - $algorithm = $matches[1]; + preg_match("/alg='(.*?)'/ism", $attributes, $matches); + if ($matches[1] != "") { + $algorithm = $matches[1]; + } - preg_match("/alg=\"\;(.*?)\"\;/ism", $attributes, $matches); - if ($matches[1] != "") - $algorithm = $matches[1]; + preg_match("/alg=\"\;(.*?)\"\;/ism", $attributes, $matches); + if ($matches[1] != "") { + $algorithm = $matches[1]; + } - preg_match("/alg=\\\"(.*?)\\\"/ism", $attributes, $matches); - if ($matches[1] != "") - $algorithm = $matches[1]; + preg_match("/alg=\\\"(.*?)\\\"/ism", $attributes, $matches); + if ($matches[1] != "") { + $algorithm = $matches[1]; + } - $hint = ""; - $matches = []; - - preg_match("/hint='(.*?)'/ism", $attributes, $matches); - if ($matches[1] != "") - $hint = $matches[1]; - preg_match("/hint=\"\;(.*?)\"\;/ism", $attributes, $matches); - if ($matches[1] != "") - $hint = $matches[1]; - preg_match("/hint=\\\"(.*?)\\\"/ism", $attributes, $matches); - if ($matches[1] != "") - $hint = $matches[1]; + $hint = ""; + $matches = []; - $x = random_string(); + preg_match("/hint='(.*?)'/ism", $attributes, $matches); + if ($matches[1] != "") { + $hint = $matches[1]; + } + preg_match("/hint=\"\;(.*?)\"\;/ism", $attributes, $matches); + if ($matches[1] != "") { + $hint = $matches[1]; + } + preg_match("/hint=\\\"(.*?)\\\"/ism", $attributes, $matches); + if ($matches[1] != "") { + $hint = $matches[1]; + } - $f = 'hz_decrypt'; + $x = random_string(); - $onclick = 'onclick="' . $f . '(\'' . $algorithm . '\',\'' . $hint . '\',\'' . $match[2] . '\',\'#' . $x . '\');"'; - $label = t('Encrypted content'); + $f = 'hz_decrypt'; - $Text = '
      ' . $label . '


      ' . bb_parse_b64_crypt($match); + $onclick = 'onclick="' . $f . '(\'' . $algorithm . '\',\'' . $hint . '\',\'' . $match[2] . '\',\'#' . $x . '\');"'; + $label = t('Encrypted content'); - return $Text; + $Text = '
      ' . $label . '


      ' . bb_parse_b64_crypt($match); + + return $Text; } /** @@ -293,357 +326,376 @@ function bb_parse_crypt($match) { * @param array $match * @return string */ -function bb_parse_b64_crypt($match) { +function bb_parse_b64_crypt($match) +{ - if(empty($match[2])) - return; + if (empty($match[2])) { + return; + } - $r .= '----- ENCRYPTED CONTENT -----' . "\n"; - $r .= base64_encode($match[1]) . "." . $match[2] . "\n"; - $r .= '----- END ENCRYPTED CONTENT -----' . "\n"; + $r .= '----- ENCRYPTED CONTENT -----' . "\n"; + $r .= base64_encode($match[1]) . "." . $match[2] . "\n"; + $r .= '----- END ENCRYPTED CONTENT -----' . "\n"; - $r = '' . str_replace("\n",'
      ',wordwrap($r,75,"\n",true)) . '
      '; - - return $r; + $r = '' . str_replace("\n", '
      ', wordwrap($r, 75, "\n", true)) . '
      '; + return $r; } -function bb_parse_app($match) { +function bb_parse_app($match) +{ - $app = Apps::app_decode($match[1]); - if ($app) { - return Apps::app_render($app); - } + $app = Apps::app_decode($match[1]); + if ($app) { + return Apps::app_render($app); + } } -function bb_parse_app_ap($match) { +function bb_parse_app_ap($match) +{ - $app = Apps::app_decode($match[1]); - if ($app) { - return sprintf( t('(Embedded app \'%s\' could not be displayed).'), $app['name']); - } + $app = Apps::app_decode($match[1]); + if ($app) { + return sprintf(t('(Embedded app \'%s\' could not be displayed).'), $app['name']); + } } -function bb_svg($match) { +function bb_svg($match) +{ - $params = str_replace(['
      ', '"'], [ '', '"'],$match[1]); - $Text = str_replace([ '[',']' ], [ '<','>' ], $match[2]); - - $output = '' . str_replace(['
      ', '"', ' '], [ '', '"', ' '],$Text) . ''; + $params = str_replace(['
      ', '"'], [ '', '"'], $match[1]); + $Text = str_replace([ '[',']' ], [ '<','>' ], $match[2]); - $purify = new SvgSanitizer(); - $purify->loadXML($output); - $purify->sanitize(); - $output = $purify->saveSVG(); - $output = preg_replace("/\<\?xml(.*?)\?\>/",'',$output); - return $output; + $output = '' . str_replace(['
      ', '"', ' '], [ '', '"', ' '], $Text) . ''; + + $purify = new SvgSanitizer(); + $purify->loadXML($output); + $purify->sanitize(); + $output = $purify->saveSVG(); + $output = preg_replace("/\<\?xml(.*?)\?\>/", '', $output); + return $output; } -function bb_svg_export($match) { +function bb_svg_export($match) +{ - $params = str_replace(['
      ', '"'], [ '', '"'],$match[1]); - $Text = str_replace([ '[',']' ], [ '<','>' ], $match[2]); - - $output = '' . str_replace(['
      ', '"', ' '], [ '', '"', ' '],$Text) . ''; + $params = str_replace(['
      ', '"'], [ '', '"'], $match[1]); + $Text = str_replace([ '[',']' ], [ '<','>' ], $match[2]); - $purify = new SvgSanitizer(); - $purify->loadXML($output); - $purify->sanitize(); - $output = $purify->saveSVG(); - $output = preg_replace("/\<\?xml(.*?)\?\>/",'',$output); - $output = 'svg'; - return $output; + $output = '' . str_replace(['
      ', '"', ' '], [ '', '"', ' '], $Text) . ''; + + $purify = new SvgSanitizer(); + $purify->loadXML($output); + $purify->sanitize(); + $output = $purify->saveSVG(); + $output = preg_replace("/\<\?xml(.*?)\?\>/", '', $output); + $output = 'svg'; + return $output; } -function bb_parse_element($match) { - $j = json_decode(base64url_decode($match[1]),true); +function bb_parse_element($match) +{ + $j = json_decode(base64url_decode($match[1]), true); - if ($j && local_channel()) { - $text = sprintf( t('Install %1$s element %2$s'), translate_design_element($j['type']), $j['pagetitle']); - $o = EOL . '' . EOL; - } - else { - $text = sprintf( t('This post contains an installable %s element, however you lack permissions to install it on this site.' ), translate_design_element($j['type'])) . $j['pagetitle']; - $o = EOL . $text . EOL; - } + if ($j && local_channel()) { + $text = sprintf(t('Install %1$s element %2$s'), translate_design_element($j['type']), $j['pagetitle']); + $o = EOL . '' . EOL; + } else { + $text = sprintf(t('This post contains an installable %s element, however you lack permissions to install it on this site.'), translate_design_element($j['type'])) . $j['pagetitle']; + $o = EOL . $text . EOL; + } - return $o; + return $o; } -function translate_design_element($type) { - switch($type) { - case 'webpage': - $ret = t('webpage'); - break; - case 'layout': - $ret = t('layout'); - break; - case 'block': - $ret = t('block'); - break; - case 'menu': - $ret = t('menu'); - break; - } +function translate_design_element($type) +{ + switch ($type) { + case 'webpage': + $ret = t('webpage'); + break; + case 'layout': + $ret = t('layout'); + break; + case 'block': + $ret = t('block'); + break; + case 'menu': + $ret = t('menu'); + break; + } - return $ret; + return $ret; } -function bb_format_attachdata($body) { +function bb_format_attachdata($body) +{ - $data = getAttachmentData($body); - - - if($data) { - $txt = ''; - if($data['url'] && $data['title']) { - $txt .= "\n\n" . '[url=' . $data['url'] . ']' . $data['title'] . '[/url]'; - } - else { - if($data['url']) { - $txt .= "\n\n" . $data['url']; - } - if($data['title']) { - $txt .= "\n\n" . $data['title']; - } - } - if($data['preview']) { - $txt .= "\n\n" . '[img]' . $data['preview'] . '[/img]'; - } - if($data['image']) { - $txt .= "\n\n" . '[img]' . $data['image'] . '[/img]'; - } + $data = getAttachmentData($body); - $txt .= "\n\n" . $data['text']; - return preg_replace('/\[attachment(.*?)\](.*?)\[\/attachment\]/ism',$txt,$body); - } - return $body; + if ($data) { + $txt = ''; + if ($data['url'] && $data['title']) { + $txt .= "\n\n" . '[url=' . $data['url'] . ']' . $data['title'] . '[/url]'; + } else { + if ($data['url']) { + $txt .= "\n\n" . $data['url']; + } + if ($data['title']) { + $txt .= "\n\n" . $data['title']; + } + } + if ($data['preview']) { + $txt .= "\n\n" . '[img]' . $data['preview'] . '[/img]'; + } + if ($data['image']) { + $txt .= "\n\n" . '[img]' . $data['image'] . '[/img]'; + } + + + $txt .= "\n\n" . $data['text']; + return preg_replace('/\[attachment(.*?)\](.*?)\[\/attachment\]/ism', $txt, $body); + } + return $body; } -function getAttachmentData($body) { - $data = []; +function getAttachmentData($body) +{ + $data = []; - if (! preg_match("/\[attachment(.*?)\](.*?)\[\/attachment\]/ism", $body, $match)) { - return null; - } + if (! preg_match("/\[attachment(.*?)\](.*?)\[\/attachment\]/ism", $body, $match)) { + return null; + } - $attributes = $match[1]; + $attributes = $match[1]; - $data["text"] = trim($match[2]); + $data["text"] = trim($match[2]); - $type = ""; - preg_match("/type='(.*?)'/ism", $attributes, $matches); + $type = ""; + preg_match("/type='(.*?)'/ism", $attributes, $matches); - if (x($matches, 1)) { - $type = strtolower($matches[1]); - } + if (x($matches, 1)) { + $type = strtolower($matches[1]); + } - preg_match('/type=\"\;(.*?)\"\;/ism', $attributes, $matches); - if (x($matches, 1)) { - $type = strtolower($matches[1]); - } + preg_match('/type=\"\;(.*?)\"\;/ism', $attributes, $matches); + if (x($matches, 1)) { + $type = strtolower($matches[1]); + } - preg_match('/type=\\\"(.*?)\\\"/ism', $attributes, $matches); - if (x($matches, 1)) { - $type = strtolower($matches[1]); - } + preg_match('/type=\\\"(.*?)\\\"/ism', $attributes, $matches); + if (x($matches, 1)) { + $type = strtolower($matches[1]); + } - if ($type == "") { - return []; - } + if ($type == "") { + return []; + } - if (!in_array($type, ["link", "audio", "photo", "video"])) { - return []; - } + if (!in_array($type, ["link", "audio", "photo", "video"])) { + return []; + } - if ($type != "") { - $data["type"] = $type; - } - $url = ""; - preg_match("/url='(.*?)'/ism", $attributes, $matches); - if (x($matches, 1)) { - $url = $matches[1]; - } + if ($type != "") { + $data["type"] = $type; + } + $url = ""; + preg_match("/url='(.*?)'/ism", $attributes, $matches); + if (x($matches, 1)) { + $url = $matches[1]; + } - preg_match('/url=\"\;(.*?)\"\;/ism', $attributes, $matches); - if (x($matches, 1)) { - $url = $matches[1]; - } + preg_match('/url=\"\;(.*?)\"\;/ism', $attributes, $matches); + if (x($matches, 1)) { + $url = $matches[1]; + } - preg_match('/url=\\\"(.*?)\\\"/ism', $attributes, $matches); - if (x($matches, 1)) { - $url = $matches[1]; - } + preg_match('/url=\\\"(.*?)\\\"/ism', $attributes, $matches); + if (x($matches, 1)) { + $url = $matches[1]; + } - if ($url != "") { - $data["url"] = html_entity_decode($url, ENT_QUOTES, 'UTF-8'); - } + if ($url != "") { + $data["url"] = html_entity_decode($url, ENT_QUOTES, 'UTF-8'); + } - $title = ""; - preg_match("/title='(.*?)'/ism", $attributes, $matches); - if (x($matches, 1)) { - $title = $matches[1]; - } + $title = ""; + preg_match("/title='(.*?)'/ism", $attributes, $matches); + if (x($matches, 1)) { + $title = $matches[1]; + } - preg_match('/title=\"\;(.*?)\"\;/ism', $attributes, $matches); - if (x($matches, 1)) { - $title = $matches[1]; - } + preg_match('/title=\"\;(.*?)\"\;/ism', $attributes, $matches); + if (x($matches, 1)) { + $title = $matches[1]; + } - preg_match('/title=\\\"(.*?)\\\"/ism', $attributes, $matches); - if (x($matches, 1)) { - $title = $matches[1]; - } + preg_match('/title=\\\"(.*?)\\\"/ism', $attributes, $matches); + if (x($matches, 1)) { + $title = $matches[1]; + } - if ($title != "") { - $title = html_entity_decode($title, ENT_QUOTES, 'UTF-8'); - $title = str_replace(["[", "]"], ["[", "]"], $title); - $data["title"] = $title; - } + if ($title != "") { + $title = html_entity_decode($title, ENT_QUOTES, 'UTF-8'); + $title = str_replace(["[", "]"], ["[", "]"], $title); + $data["title"] = $title; + } - $image = ""; - preg_match("/image='(.*?)'/ism", $attributes, $matches); - if (x($matches, 1)) { - $image = $matches[1]; - } + $image = ""; + preg_match("/image='(.*?)'/ism", $attributes, $matches); + if (x($matches, 1)) { + $image = $matches[1]; + } - preg_match('/image=\"\;(.*?)\"\;/ism', $attributes, $matches); - if (x($matches, 1)) { - $image = $matches[1]; - } + preg_match('/image=\"\;(.*?)\"\;/ism', $attributes, $matches); + if (x($matches, 1)) { + $image = $matches[1]; + } - preg_match('/image=\\\"(.*?)\\\"/ism', $attributes, $matches); - if (x($matches, 1)) { - $image = $matches[1]; - } + preg_match('/image=\\\"(.*?)\\\"/ism', $attributes, $matches); + if (x($matches, 1)) { + $image = $matches[1]; + } - if ($image != "") { - $data["image"] = html_entity_decode($image, ENT_QUOTES, 'UTF-8'); - } + if ($image != "") { + $data["image"] = html_entity_decode($image, ENT_QUOTES, 'UTF-8'); + } - $preview = ""; - preg_match("/preview='(.*?)'/ism", $attributes, $matches); - if (x($matches, 1)) { - $preview = $matches[1]; - } + $preview = ""; + preg_match("/preview='(.*?)'/ism", $attributes, $matches); + if (x($matches, 1)) { + $preview = $matches[1]; + } - preg_match('/preview=\"\;(.*?)\"\;/ism', $attributes, $matches); - if (x($matches, 1)) { - $preview = $matches[1]; - } + preg_match('/preview=\"\;(.*?)\"\;/ism', $attributes, $matches); + if (x($matches, 1)) { + $preview = $matches[1]; + } - preg_match('/preview=\\\"(.*?)\\\"/ism', $attributes, $matches); - if (x($matches, 1)) { - $preview = $matches[1]; - } + preg_match('/preview=\\\"(.*?)\\\"/ism', $attributes, $matches); + if (x($matches, 1)) { + $preview = $matches[1]; + } - if ($preview != "") { - $data["preview"] = html_entity_decode($preview, ENT_QUOTES, 'UTF-8'); - } + if ($preview != "") { + $data["preview"] = html_entity_decode($preview, ENT_QUOTES, 'UTF-8'); + } - $data["description"] = trim($match[3]); + $data["description"] = trim($match[3]); - $data["after"] = trim($match[4]); + $data["after"] = trim($match[4]); - return $data; + return $data; } -function bb_ShareAttributes($match) { +function bb_ShareAttributes($match) +{ - $matches = []; - $attributes = $match[1]; + $matches = []; + $attributes = $match[1]; - $author = ""; - preg_match("/author='(.*?)'/ism", $attributes, $matches); - if ($matches[1] != "") - $author = urldecode($matches[1]); + $author = ""; + preg_match("/author='(.*?)'/ism", $attributes, $matches); + if ($matches[1] != "") { + $author = urldecode($matches[1]); + } - $link = ""; - preg_match("/link='(.*?)'/ism", $attributes, $matches); - if ($matches[1] != "") - $link = $matches[1]; + $link = ""; + preg_match("/link='(.*?)'/ism", $attributes, $matches); + if ($matches[1] != "") { + $link = $matches[1]; + } - $avatar = ""; - preg_match("/avatar='(.*?)'/ism", $attributes, $matches); - if ($matches[1] != "") - $avatar = $matches[1]; + $avatar = ""; + preg_match("/avatar='(.*?)'/ism", $attributes, $matches); + if ($matches[1] != "") { + $avatar = $matches[1]; + } - $profile = ""; - preg_match("/profile='(.*?)'/ism", $attributes, $matches); - if ($matches[1] != "") - $profile = $matches[1]; + $profile = ""; + preg_match("/profile='(.*?)'/ism", $attributes, $matches); + if ($matches[1] != "") { + $profile = $matches[1]; + } - $posted = ""; - preg_match("/posted='(.*?)'/ism", $attributes, $matches); - if ($matches[1] != "") - $posted = $matches[1]; + $posted = ""; + preg_match("/posted='(.*?)'/ism", $attributes, $matches); + if ($matches[1] != "") { + $posted = $matches[1]; + } - $auth = ""; - preg_match("/auth='(.*?)'/ism", $attributes, $matches); - if ($matches[1] != "") { - if($matches[1] === 'true') - $auth = true; - else - $auth = false; - } + $auth = ""; + preg_match("/auth='(.*?)'/ism", $attributes, $matches); + if ($matches[1] != "") { + if ($matches[1] === 'true') { + $auth = true; + } else { + $auth = false; + } + } - if($auth === EMPTY_STR) { - $auth = is_matrix_url($profile); - } + if ($auth === EMPTY_STR) { + $auth = is_matrix_url($profile); + } - // message_id is never used, do we still need it? - $message_id = ""; - preg_match("/message_id='(.*?)'/ism", $attributes, $matches); - if ($matches[1] != "") - $message_id = $matches[1]; + // message_id is never used, do we still need it? + $message_id = ""; + preg_match("/message_id='(.*?)'/ism", $attributes, $matches); + if ($matches[1] != "") { + $message_id = $matches[1]; + } - if(! $message_id) { - preg_match("/guid='(.*?)'/ism", $attributes, $matches); - if ($matches[1] != "") - $message_id = $matches[1]; - } + if (! $message_id) { + preg_match("/guid='(.*?)'/ism", $attributes, $matches); + if ($matches[1] != "") { + $message_id = $matches[1]; + } + } - $reldate = '' . datetime_convert('UTC', date_default_timezone_get(), $posted, 'r') . ''; + $reldate = '' . datetime_convert('UTC', date_default_timezone_get(), $posted, 'r') . ''; - $headline = '
      '; + $headline = '
      '; - if ($avatar != "") { - $headline .= '' . htmlspecialchars($author,ENT_COMPAT,'UTF-8',false) . ''; - } - - if(strpos($link,'/cards/')) - $type = t('card'); - elseif(strpos($link,'/articles/')) - $type = t('article'); - else - $type = t('post'); + if ($avatar != "") { + $headline .= '' . htmlspecialchars($author, ENT_COMPAT, 'UTF-8', false) . ''; + } - // Bob Smith wrote the following post 2 hours ago + if (strpos($link, '/cards/')) { + $type = t('card'); + } elseif (strpos($link, '/articles/')) { + $type = t('article'); + } else { + $type = t('post'); + } - $fmt = sprintf( t('%1$s wrote the following %2$s %3$s'), - '' . $author . '', - '' . $type . '', - $reldate - ); + // Bob Smith wrote the following post 2 hours ago - $headline .= '' . $fmt . '
      '; + $fmt = sprintf( + t('%1$s wrote the following %2$s %3$s'), + '' . $author . '', + '' . $type . '', + $reldate + ); - $text = $headline . '
      ' . trim($match[2]) . '
      '; + $headline .= '' . $fmt . '
      '; - return $text; + $text = $headline . '
      ' . trim($match[2]) . '
      '; + + return $text; } -function bb_location($match) { - // not yet implemented +function bb_location($match) +{ + // not yet implemented } /** @@ -652,135 +704,164 @@ function bb_location($match) { * @param array $match * @return string HTML iframe with content of $match[1] */ -function bb_iframe($match) { +function bb_iframe($match) +{ - $sandbox = ((strpos($match[1], App::get_hostname())) ? ' sandbox="allow-scripts" ' : ''); + $sandbox = ((strpos($match[1], App::get_hostname())) ? ' sandbox="allow-scripts" ' : ''); - return ''; + return ''; } -function bb_ShareAttributesSimple($match) { +function bb_ShareAttributesSimple($match) +{ - $matches = []; - $attributes = $match[1]; + $matches = []; + $attributes = $match[1]; - $author = ""; - preg_match("/author='(.*?)'/ism", $attributes, $matches); - if ($matches[1] != "") - $author = html_entity_decode($matches[1],ENT_QUOTES,'UTF-8'); + $author = ""; + preg_match("/author='(.*?)'/ism", $attributes, $matches); + if ($matches[1] != "") { + $author = html_entity_decode($matches[1], ENT_QUOTES, 'UTF-8'); + } - preg_match('/author="(.*?)"/ism', $attributes, $matches); - if ($matches[1] != "") - $author = $matches[1]; + preg_match('/author="(.*?)"/ism', $attributes, $matches); + if ($matches[1] != "") { + $author = $matches[1]; + } - $profile = ""; - preg_match("/profile='(.*?)'/ism", $attributes, $matches); - if ($matches[1] != "") - $profile = $matches[1]; + $profile = ""; + preg_match("/profile='(.*?)'/ism", $attributes, $matches); + if ($matches[1] != "") { + $profile = $matches[1]; + } - preg_match('/profile="(.*?)"/ism', $attributes, $matches); - if ($matches[1] != "") - $profile = $matches[1]; + preg_match('/profile="(.*?)"/ism', $attributes, $matches); + if ($matches[1] != "") { + $profile = $matches[1]; + } - $text = html_entity_decode("♲ ", ENT_QUOTES, 'UTF-8') . ' ' . $author . ':
      ' . $match[2] . '
      '; + $text = html_entity_decode("♲ ", ENT_QUOTES, 'UTF-8') . ' ' . $author . ':
      ' . $match[2] . '
      '; - return($text); + return($text); } -function rpost_callback($match) { - if ($match[2]) { - return str_replace($match[0], Libzot::get_rpost_path(App::get_observer()) . '&title=' . urlencode($match[2]) . '&body=' . urlencode($match[3]), $match[0]); - } else { - return str_replace($match[0], Libzot::get_rpost_path(App::get_observer()) . '&body=' . urlencode($match[3]), $match[0]); - } +function rpost_callback($match) +{ + if ($match[2]) { + return str_replace($match[0], Libzot::get_rpost_path(App::get_observer()) . '&title=' . urlencode($match[2]) . '&body=' . urlencode($match[3]), $match[0]); + } else { + return str_replace($match[0], Libzot::get_rpost_path(App::get_observer()) . '&body=' . urlencode($match[3]), $match[0]); + } } -function bb_map_coords($match) { - // the extra space in the following line is intentional - return str_replace($match[0],'
      ' . generate_map(str_replace('/',' ',$match[1])) . '
      ', $match[0]); +function bb_map_coords($match) +{ + // the extra space in the following line is intentional + return str_replace($match[0], '
      ' . generate_map(str_replace('/', ' ', $match[1])) . '
      ', $match[0]); } -function bb_map_location($match) { - // the extra space in the following line is intentional - return str_replace($match[0],'
      ' . generate_named_map($match[1]) . '
      ', $match[0]); +function bb_map_location($match) +{ + // the extra space in the following line is intentional + return str_replace($match[0], '
      ' . generate_named_map($match[1]) . '
      ', $match[0]); } -function bb_opentag($match) { - $openclose = (($match[2]) ? '' . $match[1] . '' : t('Click to open/close')); - $text = (($match[2]) ? $match[2] : $match[1]); - $rnd = mt_rand(); +function bb_opentag($match) +{ + $openclose = (($match[2]) ? '' . $match[1] . '' : t('Click to open/close')); + $text = (($match[2]) ? $match[2] : $match[1]); + $rnd = mt_rand(); - return ''; + return ''; } -function bb_spoilertag($match) { - $openclose = (($match[2]) ? '' . $match[1] . ' ' . t('spoiler') . '' : t('Click to open/close')); - $text = (($match[2]) ? $match[2] : $match[1]); - $rnd = mt_rand(); +function bb_spoilertag($match) +{ + $openclose = (($match[2]) ? '' . $match[1] . ' ' . t('spoiler') . '' : t('Click to open/close')); + $text = (($match[2]) ? $match[2] : $match[1]); + $rnd = mt_rand(); - return ''; + return ''; } -function bb_summary($match) { - $rnd1 = mt_rand(); - $rnd2 = mt_rand(); - $rnd3 = mt_rand(); - $rnd4 = mt_rand(); +function bb_summary($match) +{ + $rnd1 = mt_rand(); + $rnd2 = mt_rand(); + $rnd3 = mt_rand(); + $rnd4 = mt_rand(); - return $match[1] . $match[2] . EOL . EOL . $match[3]; + return $match[1] . $match[2] . EOL . EOL . $match[3]; -// return $match[1] . '
      ' . $match[2] . '
      ' . t('View article') . '
      '; +// return $match[1] . '
      ' . $match[2] . '
      ' . t('View article') . '
      '; } -function bb_definitionList($match) { - // $match[1] is the markup styles for the "terms" in the definition list. - // $match[2] is the content between the [dl]...[/dl] tags +function bb_definitionList($match) +{ + // $match[1] is the markup styles for the "terms" in the definition list. + // $match[2] is the content between the [dl]...[/dl] tags - $classes = ''; - if (stripos($match[1], "b") !== false) $classes .= 'dl-terms-bold '; - if (stripos($match[1], "i") !== false) $classes .= 'dl-terms-italic '; - if (stripos($match[1], "u") !== false) $classes .= 'dl-terms-underline '; - if (stripos($match[1], "l") !== false) $classes .= 'dl-terms-large '; - if (stripos($match[1], "m") !== false) $classes .= 'dl-terms-monospace '; - if (stripos($match[1], "h") !== false) $classes .= 'dl-horizontal '; // dl-horizontal is already provided by bootstrap - if (strlen($classes) === 0) $classes = "dl-terms-plain"; + $classes = ''; + if (stripos($match[1], "b") !== false) { + $classes .= 'dl-terms-bold '; + } + if (stripos($match[1], "i") !== false) { + $classes .= 'dl-terms-italic '; + } + if (stripos($match[1], "u") !== false) { + $classes .= 'dl-terms-underline '; + } + if (stripos($match[1], "l") !== false) { + $classes .= 'dl-terms-large '; + } + if (stripos($match[1], "m") !== false) { + $classes .= 'dl-terms-monospace '; + } + if (stripos($match[1], "h") !== false) { + $classes .= 'dl-horizontal '; // dl-horizontal is already provided by bootstrap + } + if (strlen($classes) === 0) { + $classes = "dl-terms-plain"; + } - // The bbcode transformation will be: - // [*=term-text] description-text =>
      term-text
      description-text - // then after all replacements have been made, the extra
      at the start of the - // first line can be removed. HTML5 allows the tag to be missing from the end of the last line. - // Using '(?\n"; - $eatLeadingSpaces = '(?: |[ \t])*'; // prevent spaces infront of [*= from adding another line to the previous element - $listElements = preg_replace('/^(\n|
      )/', '', $match[2]); // ltrim the first newline - $listElements = preg_replace( - '/' . $eatLeadingSpaces . '\[\*=([[:print:]]*?)(?$1
      ', - $listElements - ); - // Unescape any \] inside the
      tags - $listElements = preg_replace_callback('/
      (.*?)<\/dt>/ism', 'bb_definitionList_unescapeBraces', $listElements); - - // Remove the extra at the start of the string, if there is one. - $firstOpenTag = strpos($listElements, '
      '); - $firstCloseTag = strpos($listElements, $closeDescriptionTag); - if ($firstCloseTag !== false && ($firstOpenTag === false || ($firstCloseTag < $firstOpenTag))) { - $listElements = preg_replace( '/<\/dd>/ism', '', $listElements, 1); - } + // The bbcode transformation will be: + // [*=term-text] description-text =>
      term-text
      description-text + // then after all replacements have been made, the extra
      at the start of the + // first line can be removed. HTML5 allows the tag to be missing from the end of the last line. + // Using '(?\n"; + $eatLeadingSpaces = '(?: |[ \t])*'; // prevent spaces infront of [*= from adding another line to the previous element + $listElements = preg_replace('/^(\n|
      )/', '', $match[2]); // ltrim the first newline + $listElements = preg_replace( + '/' . $eatLeadingSpaces . '\[\*=([[:print:]]*?)(?$1
      ', + $listElements + ); + // Unescape any \] inside the
      tags + $listElements = preg_replace_callback('/
      (.*?)<\/dt>/ism', 'bb_definitionList_unescapeBraces', $listElements); - return '
      ' . $listElements . '
      ';; + // Remove the extra at the start of the string, if there is one. + $firstOpenTag = strpos($listElements, '
      '); + $firstCloseTag = strpos($listElements, $closeDescriptionTag); + if ($firstCloseTag !== false && ($firstOpenTag === false || ($firstCloseTag < $firstOpenTag))) { + $listElements = preg_replace('/<\/dd>/ism', '', $listElements, 1); + } + + return '
      ' . $listElements . '
      '; } -function bb_definitionList_unescapeBraces($match) { - return '
      ' . str_replace('\]', ']', $match[1]) . '
      '; +function bb_definitionList_unescapeBraces($match) +{ + return '
      ' . str_replace('\]', ']', $match[1]) . '
      '; } -function bb_checklist($match) { - $str = $match[1]; - $str = str_replace("[]", "
    • ", $str); - $str = str_replace("[x]", "
    • ", $str); - return '
        ' . $str . '
      '; +function bb_checklist($match) +{ + $str = $match[1]; + $str = str_replace("[]", "
    • ", $str); + $str = str_replace("[x]", "
    • ", $str); + return '
        ' . $str . '
      '; } @@ -790,1295 +871,1316 @@ function bb_checklist($match) { * @param array|string $input * @return string A HTML span tag with the styles. */ -function bb_sanitize_style($input) { - // whitelist array: property => limits (0 = no limitation) - $w = array( - // color properties - "color" => 0, - "background-color" => 0, - // box properties - "padding" => array("px"=>100, "%"=>0, "em"=>2, "ex"=>2, "mm"=>0, "cm"=>0, "in"=>0, "pt"=>0, "pc"=>0), - "margin" => array("px"=>100, "%"=>0, "em"=>2, "ex"=>2, "mm"=>0, "cm"=>0, "in"=>0, "pt"=>0, "pc"=>0), - "border" => array("px"=>100, "%"=>0, "em"=>2, "ex"=>2, "mm"=>0, "cm"=>0, "in"=>0, "pt"=>0, "pc"=>0), - "float" => 0, - "clear" => 0, - // text properties - "text-decoration" => 0, - ); +function bb_sanitize_style($input) +{ + // whitelist array: property => limits (0 = no limitation) + $w = array( + // color properties + "color" => 0, + "background-color" => 0, + // box properties + "padding" => array("px" => 100, "%" => 0, "em" => 2, "ex" => 2, "mm" => 0, "cm" => 0, "in" => 0, "pt" => 0, "pc" => 0), + "margin" => array("px" => 100, "%" => 0, "em" => 2, "ex" => 2, "mm" => 0, "cm" => 0, "in" => 0, "pt" => 0, "pc" => 0), + "border" => array("px" => 100, "%" => 0, "em" => 2, "ex" => 2, "mm" => 0, "cm" => 0, "in" => 0, "pt" => 0, "pc" => 0), + "float" => 0, + "clear" => 0, + // text properties + "text-decoration" => 0, + ); - // determine if input is string or array - - $input_is_array = is_array($input); - $css = []; - $css_string = (($input_is_array) ? $input[1] : $input); - $a = explode(';', $css_string); + // determine if input is string or array - foreach($a as $parts){ - list($k, $v) = explode(':', $parts); - $css[ trim($k) ] = trim($v); - } + $input_is_array = is_array($input); + $css = []; + $css_string = (($input_is_array) ? $input[1] : $input); + $a = explode(';', $css_string); - // sanitize properties - $b = array_merge(array_diff_key($css, $w), array_diff_key($w, $css)); - $css = array_diff_key($css, $b); - $css_string_san = ''; + foreach ($a as $parts) { + list($k, $v) = explode(':', $parts); + $css[ trim($k) ] = trim($v); + } - foreach ($css as $key => $value) { - if ($w[$key] != null) { - foreach ($w[$key] as $limit_key => $limit_value) { - //sanitize values - if (strpos($value, $limit_key)) { - $value = preg_replace_callback( - "/(\S.*?)$limit_key/ism", - function($match) use($limit_value, $limit_key) { - if ($match[1] > $limit_value) { - return $limit_value . $limit_key; - } else { - return $match[1] . $limit_key; - } - }, - $value - ); - } - } - } - $css_string_san .= $key . ":" . $value ."; "; - } + // sanitize properties + $b = array_merge(array_diff_key($css, $w), array_diff_key($w, $css)); + $css = array_diff_key($css, $b); + $css_string_san = ''; - if ($input_is_array) { - return '' . $input[2] . ''; - } + foreach ($css as $key => $value) { + if ($w[$key] != null) { + foreach ($w[$key] as $limit_key => $limit_value) { + //sanitize values + if (strpos($value, $limit_key)) { + $value = preg_replace_callback( + "/(\S.*?)$limit_key/ism", + function ($match) use ($limit_value, $limit_key) { + if ($match[1] > $limit_value) { + return $limit_value . $limit_key; + } else { + return $match[1] . $limit_key; + } + }, + $value + ); + } + } + } + $css_string_san .= $key . ":" . $value . "; "; + } - return $css_string_san; + if ($input_is_array) { + return '' . $input[2] . ''; + } + + return $css_string_san; } -function oblanguage_callback($matches) { - if(strlen($matches[1]) == 2) { - $compare = strtolower(substr(\App::$language,0,2)); - } - else { - $compare = strtolower(\App::$language); - } +function oblanguage_callback($matches) +{ + if (strlen($matches[1]) == 2) { + $compare = strtolower(substr(App::$language, 0, 2)); + } else { + $compare = strtolower(App::$language); + } - if($compare === strtolower($matches[1])) - return $matches[2]; + if ($compare === strtolower($matches[1])) { + return $matches[2]; + } - return ''; + return ''; } -function oblanguage_necallback($matches) { - if(strlen($matches[1]) == 2) { - $compare = strtolower(substr(\App::$language,0,2)); - } - else { - $compare = strtolower(\App::$language); - } +function oblanguage_necallback($matches) +{ + if (strlen($matches[1]) == 2) { + $compare = strtolower(substr(App::$language, 0, 2)); + } else { + $compare = strtolower(App::$language); + } - if($compare !== strtolower($matches[1])) - return $matches[2]; + if ($compare !== strtolower($matches[1])) { + return $matches[2]; + } - return ''; + return ''; } -function bb_observer($Text) { +function bb_observer($Text) +{ - $observer = App::get_observer(); + $observer = App::get_observer(); - if ((strpos($Text,'[/observer]') !== false) || (strpos($Text,'[/rpost]') !== false)) { + if ((strpos($Text, '[/observer]') !== false) || (strpos($Text, '[/rpost]') !== false)) { + if ($observer) { + $Text = preg_replace("/\[observer\=1\](.*?)\[\/observer\]/ism", '$1', $Text); + $Text = preg_replace("/\[observer\=0\].*?\[\/observer\]/ism", '', $Text); + $Text = preg_replace_callback("/\[rpost(=(.*?))?\](.*?)\[\/rpost\]/ism", 'rpost_callback', $Text); + } else { + $Text = preg_replace("/\[observer\=1\].*?\[\/observer\]/ism", '', $Text); + $Text = preg_replace("/\[observer\=0\](.*?)\[\/observer\]/ism", '$1', $Text); + $Text = preg_replace("/\[rpost(=.*?)?\](.*?)\[\/rpost\]/ism", '', $Text); + } + } - if ($observer) { - $Text = preg_replace("/\[observer\=1\](.*?)\[\/observer\]/ism", '$1', $Text); - $Text = preg_replace("/\[observer\=0\].*?\[\/observer\]/ism", '', $Text); - $Text = preg_replace_callback("/\[rpost(=(.*?))?\](.*?)\[\/rpost\]/ism", 'rpost_callback', $Text); - } else { - $Text = preg_replace("/\[observer\=1\].*?\[\/observer\]/ism", '', $Text); - $Text = preg_replace("/\[observer\=0\](.*?)\[\/observer\]/ism", '$1', $Text); - $Text = preg_replace("/\[rpost(=.*?)?\](.*?)\[\/rpost\]/ism", '', $Text); - } - } + $channel = App::get_channel(); - $channel = App::get_channel(); + if (strpos($Text, '[/channel]') !== false) { + if ($channel) { + $Text = preg_replace("/\[channel\=1\](.*?)\[\/channel\]/ism", '$1', $Text); + $Text = preg_replace("/\[channel\=0\].*?\[\/channel\]/ism", '', $Text); + } else { + $Text = preg_replace("/\[channel\=1\].*?\[\/channel\]/ism", '', $Text); + $Text = preg_replace("/\[channel\=0\](.*?)\[\/channel\]/ism", '$1', $Text); + } + } - if (strpos($Text,'[/channel]') !== false) { - if ($channel) { - $Text = preg_replace("/\[channel\=1\](.*?)\[\/channel\]/ism", '$1', $Text); - $Text = preg_replace("/\[channel\=0\].*?\[\/channel\]/ism", '', $Text); - } else { - $Text = preg_replace("/\[channel\=1\].*?\[\/channel\]/ism", '', $Text); - $Text = preg_replace("/\[channel\=0\](.*?)\[\/channel\]/ism", '$1', $Text); - } - } - - return $Text; + return $Text; } -function bb_imgoptions($match) { +function bb_imgoptions($match) +{ - // $Text = preg_replace_callback("/\[([zi])mg([ \=])(.*?)\](.*?)\[\/[zi]mg\]/ism",'bb_imgoptions',$Text); - // alt text cannot contain ']' - - // [img|zmg=wwwxhhh float=left|right alt=alt text]url[/img|zmg] - // [img|zmg width="nnn" height="nnn" alt="xyz" style="float: abc;"]url[/img|zmg] - - $local_match = null; - $width = 0; - $float = false; - $alt = false; + // $Text = preg_replace_callback("/\[([zi])mg([ \=])(.*?)\](.*?)\[\/[zi]mg\]/ism",'bb_imgoptions',$Text); + // alt text cannot contain ']' - $style = EMPTY_STR; + // [img|zmg=wwwxhhh float=left|right alt=alt text]url[/img|zmg] + // [img|zmg width="nnn" height="nnn" alt="xyz" style="float: abc;"]url[/img|zmg] - $attributes = $match[3]; + $local_match = null; + $width = 0; + $float = false; + $alt = false; - $x = preg_match("/alt='(.*?)'/ism", $attributes, $matches); - if ($x) { - $alt = $matches[1]; - } - - $x = preg_match("/alt=\"\;(.*?)\"\;/ism", $attributes, $matches); - if ($x) { - $alt = $matches[1]; - } + $style = EMPTY_STR; - $x = preg_match("/alt=\\\"(.*?)\\\"/ism", $attributes, $matches); - if ($x) { - $alt = $matches[1]; - } + $attributes = $match[3]; - $x = preg_match("/width=([0-9]*)/ism", $attributes, $matches); - if ($x) { - $width = bb_xss($matches[1]); - } + $x = preg_match("/alt='(.*?)'/ism", $attributes, $matches); + if ($x) { + $alt = $matches[1]; + } - $x = preg_match("/width='(.*?)'/ism", $attributes, $matches); - if ($x) { - $width = bb_xss($matches[1]); - } - - $x = preg_match("/width=\"\;(.*?)\"\;/ism", $attributes, $matches); - if ($x) { - $width = bb_xss($matches[1]); - } + $x = preg_match("/alt=\"\;(.*?)\"\;/ism", $attributes, $matches); + if ($x) { + $alt = $matches[1]; + } - $x = preg_match("/width=\\\"(.*?)\\\"/ism", $attributes, $matches); - if ($x) { - $width = bb_xss($matches[1]); - } + $x = preg_match("/alt=\\\"(.*?)\\\"/ism", $attributes, $matches); + if ($x) { + $alt = $matches[1]; + } - $x = preg_match("/height=([0-9]*)/ism", $attributes, $matches); - if ($x) { - $height = bb_xss($matches[1]); - } + $x = preg_match("/width=([0-9]*)/ism", $attributes, $matches); + if ($x) { + $width = bb_xss($matches[1]); + } - $x = preg_match("/height='(.*?)'/ism", $attributes, $matches); - if ($x) { - $height = bb_xss($matches[1]); - } - - $x = preg_match("/height=\"\;(.*?)\"\;/ism", $attributes, $matches); - if ($x) { - $height = bb_xss($matches[1]); - } - - $x = preg_match("/height=\\\"(.*?)\\\"/ism", $attributes, $matches); - if ($x) { - $height = bb_xss($matches[1]); - } + $x = preg_match("/width='(.*?)'/ism", $attributes, $matches); + if ($x) { + $width = bb_xss($matches[1]); + } - $x = preg_match("/style='(.*?)'/ism", $attributes, $matches); - if ($x) { - $style = bb_sanitize_style($matches[1]); - } - - $x = preg_match("/style=\"\;(.*?)\"\;/ism", $attributes, $matches); - if ($x) { - $style = bb_sanitize_style($matches[1]); - } + $x = preg_match("/width=\"\;(.*?)\"\;/ism", $attributes, $matches); + if ($x) { + $width = bb_xss($matches[1]); + } - $x = preg_match("/style=\\\"(.*?)\\\"/ism", $attributes, $matches); - if ($x) { - $style = bb_sanitize_style($matches[1]); - } + $x = preg_match("/width=\\\"(.*?)\\\"/ism", $attributes, $matches); + if ($x) { + $width = bb_xss($matches[1]); + } - // legacy img options - - if ($match[2] === '=') { - // pull out (optional) legacy size declarations first - if (preg_match("/([0-9]*)x([0-9]*)/ism",$match[3],$local_match)) { - $width = intval($local_match[1]); - } - $match[3] = substr($match[3],strpos($match[3],' ')); - } + $x = preg_match("/height=([0-9]*)/ism", $attributes, $matches); + if ($x) { + $height = bb_xss($matches[1]); + } - // then (optional) legacy float specifiers - if ($n = strpos($match[3],'float=left') !== false) { - $float = 'left'; - $match[3] = substr($match[3],$n + 10); - } - if ($n = strpos($match[3],'float=right') !== false) { - $float = 'right'; - $match[3] = substr($match[3],$n + 11); - } - - // finally alt text which extends to the close of the tag - if ((! $alt) && ($n = strpos($match[3],'alt=') !== false)) { - $alt = substr($match[3],$n + 4); - } + $x = preg_match("/height='(.*?)'/ism", $attributes, $matches); + if ($x) { + $height = bb_xss($matches[1]); + } - // now assemble the resulting img tag from these components - - $output = ''; - - return $output; - + $x = preg_match("/height=\\\"(.*?)\\\"/ism", $attributes, $matches); + if ($x) { + $height = bb_xss($matches[1]); + } + + $x = preg_match("/style='(.*?)'/ism", $attributes, $matches); + if ($x) { + $style = bb_sanitize_style($matches[1]); + } + + $x = preg_match("/style=\"\;(.*?)\"\;/ism", $attributes, $matches); + if ($x) { + $style = bb_sanitize_style($matches[1]); + } + + $x = preg_match("/style=\\\"(.*?)\\\"/ism", $attributes, $matches); + if ($x) { + $style = bb_sanitize_style($matches[1]); + } + + // legacy img options + + if ($match[2] === '=') { + // pull out (optional) legacy size declarations first + if (preg_match("/([0-9]*)x([0-9]*)/ism", $match[3], $local_match)) { + $width = intval($local_match[1]); + } + $match[3] = substr($match[3], strpos($match[3], ' ')); + } + + // then (optional) legacy float specifiers + if ($n = strpos($match[3], 'float=left') !== false) { + $float = 'left'; + $match[3] = substr($match[3], $n + 10); + } + if ($n = strpos($match[3], 'float=right') !== false) { + $float = 'right'; + $match[3] = substr($match[3], $n + 11); + } + + // finally alt text which extends to the close of the tag + if ((! $alt) && ($n = strpos($match[3], 'alt=') !== false)) { + $alt = substr($match[3], $n + 4); + } + + // now assemble the resulting img tag from these components + + $output = ''; + + return $output; } -function multicode_purify($s) { +function multicode_purify($s) +{ - $s = preg_replace_callback("/\[code(.*?)\](.*?)\[\/code\]/ism", function ($match) { - return '[code' . $match[1] . ']' . bb_code_protect($match[2]) . '[/code]'; - }, $s); + $s = preg_replace_callback("/\[code(.*?)\](.*?)\[\/code\]/ism", function ($match) { + return '[code' . $match[1] . ']' . bb_code_protect($match[2]) . '[/code]'; + }, $s); - $s = preg_replace_callback('#(^|\n)([`~]{3,})(?: *\.?([a-zA-Z0-9\-.]+))?\n+([\s\S]+?)\n+\2(\n|$)#', function ($match) { - return $match[1] . $match[2] . "\n" . bb_code_protect($match[4]) . "\n" . $match[2] . (($match[5]) ? $match[5] : "\n"); - }, $s); + $s = preg_replace_callback('#(^|\n)([`~]{3,})(?: *\.?([a-zA-Z0-9\-.]+))?\n+([\s\S]+?)\n+\2(\n|$)#', function ($match) { + return $match[1] . $match[2] . "\n" . bb_code_protect($match[4]) . "\n" . $match[2] . (($match[5]) ? $match[5] : "\n"); + }, $s); -// if (strpos($s,'<') !== false || strpos($s,'>') !== false) { - $s = purify_html($s, [ 'escape' ]); -// } - - return bb_code_unprotect($s); +// if (strpos($s,'<') !== false || strpos($s,'>') !== false) { + $s = purify_html($s, [ 'escape' ]); +// } + return bb_code_unprotect($s); } -function bb_code_preprotect($matches) { - return '[code' . $matches[1] . ']' . 'b64.^8e%.' . base64_encode(str_replace('
      ','|+br+|',$matches[2])) . '.b64.$8e%' . '[/code]'; +function bb_code_preprotect($matches) +{ + return '[code' . $matches[1] . ']' . 'b64.^8e%.' . base64_encode(str_replace('
      ', '|+br+|', $matches[2])) . '.b64.$8e%' . '[/code]'; } -function bb_code_preunprotect($s) { - return preg_replace_callback('|b64\.\^8e\%\.(.*?)\.b64\.\$8e\%|ism','bb_code_unprotect_sub',$s); +function bb_code_preunprotect($s) +{ + return preg_replace_callback('|b64\.\^8e\%\.(.*?)\.b64\.\$8e\%|ism', 'bb_code_unprotect_sub', $s); } -function bb_code_protect($s) { - return 'b64.^9e%.' . base64_encode(str_replace('
      ','|+br+|',$s)) . '.b64.$9e%'; +function bb_code_protect($s) +{ + return 'b64.^9e%.' . base64_encode(str_replace('
      ', '|+br+|', $s)) . '.b64.$9e%'; } -function bb_code_unprotect($s) { - return preg_replace_callback('|b64\.\^9e\%\.(.*?)\.b64\.\$9e\%|ism','bb_code_unprotect_sub',$s); +function bb_code_unprotect($s) +{ + return preg_replace_callback('|b64\.\^9e\%\.(.*?)\.b64\.\$9e\%|ism', 'bb_code_unprotect_sub', $s); } -function bb_code_unprotect_sub($match) { - $x = str_replace( [ '<', '>' ], [ '<', '>' ], base64_decode($match[1])); - return str_replace('|+br+|','
      ', $x); +function bb_code_unprotect_sub($match) +{ + $x = str_replace([ '<', '>' ], [ '<', '>' ], base64_decode($match[1])); + return str_replace('|+br+|', '
      ', $x); } -function bb_colorbox($match) { - if (strpos($match[1],'zrl')) { - $url = zid($match[2]); - } - else { - $url = $match[2]; - } - return 'censored'; +function bb_colorbox($match) +{ + if (strpos($match[1], 'zrl')) { + $url = zid($match[2]); + } else { + $url = $match[2]; + } + return 'censored'; } -function bb_code($match) { - if(strpos($match[0], "
      ")) - return '
      ' . bb_code_protect(trim($match[1])) . '
      '; - else - return '' . bb_code_protect(trim($match[1])) . ''; +function bb_code($match) +{ + if (strpos($match[0], "
      ")) { + return '
      ' . bb_code_protect(trim($match[1])) . '
      '; + } else { + return '' . bb_code_protect(trim($match[1])) . ''; + } } -function bb_code_options($match) { - if(strpos($match[0], "
      ")) { - $class = ""; - $pre = true; - } else { - $class = "inline-code"; - $pre = false; - } - if(strpos($match[1], 'nowrap')) { - $style = "overflow-x: auto; white-space: pre;"; - } else { - $style = ""; - } - if($pre) { - return '
      ' . bb_code_protect(trim($match[2])) . '
      '; - } else { - return '' . bb_code_protect(trim($match[2])) . ''; - } +function bb_code_options($match) +{ + if (strpos($match[0], "
      ")) { + $class = ""; + $pre = true; + } else { + $class = "inline-code"; + $pre = false; + } + if (strpos($match[1], 'nowrap')) { + $style = "overflow-x: auto; white-space: pre;"; + } else { + $style = ""; + } + if ($pre) { + return '
      ' . bb_code_protect(trim($match[2])) . '
      '; + } else { + return '' . bb_code_protect(trim($match[2])) . ''; + } } -function md_protect($match) { - return bb_code_protect($match[1]); +function md_protect($match) +{ + return bb_code_protect($match[1]); } -function html_protect($match) { - return str_replace(['<','>'],['<','>'],$match[1]); +function html_protect($match) +{ + return str_replace(['<','>'], ['<','>'], $match[1]); } -function md_header($content) { +function md_header($content) +{ - $headingLevel = strlen($content[1]); - $header = trim($content[2]); - // Build anchor without space, numbers. + $headingLevel = strlen($content[1]); + $header = trim($content[2]); + // Build anchor without space, numbers. $anchor = preg_replace('#[^a-z?!]#', '', strtolower($header)); return sprintf('%s', $headingLevel, $anchor, $header, $headingLevel); - } -function md_codeblock($content) { +function md_codeblock($content) +{ - $language = !empty($content[3]) ? filter_var($content[3], FILTER_SANITIZE_STRING) : ''; - $class = !empty($language) ? sprintf(' class="%s language-%s"', $language, $language) : ''; - // Build one block so that we not render each paragraph separately. - $content = str_replace("\n", '
      ', $content[4]); + $language = !empty($content[3]) ? filter_var($content[3], FILTER_SANITIZE_STRING) : ''; + $class = !empty($language) ? sprintf(' class="%s language-%s"', $language, $language) : ''; + // Build one block so that we not render each paragraph separately. + $content = str_replace("\n", '
      ', $content[4]); - return sprintf('
      %s
      ', $class, bb_code_protect($content)); + return sprintf('
      %s
      ', $class, bb_code_protect($content)); } -function md_italic($content) { +function md_italic($content) +{ - return '' . $content[1] . $content[3] . ''; + return '' . $content[1] . $content[3] . ''; } -function md_bold($content) { +function md_bold($content) +{ - return '' . $content[1] . $content[3] . ''; + return '' . $content[1] . $content[3] . ''; } -function md_bolditalic($content) { +function md_bolditalic($content) +{ - return '' . $content[1] . $content[3] . ''; + return '' . $content[1] . $content[3] . ''; } -function md_image($content) { - $url = filter_var($content[1], FILTER_SANITIZE_URL); - $alt = ''; - if (isset($content[2])) { - $content[2] = str_replace('"', '', $content[2]); - $alt = ' alt="' . filter_var($content[2], FILTER_SANITIZE_STRING) . '"'; - } +function md_image($content) +{ + $url = filter_var($content[1], FILTER_SANITIZE_URL); + $alt = ''; + if (isset($content[2])) { + $content[2] = str_replace('"', '', $content[2]); + $alt = ' alt="' . filter_var($content[2], FILTER_SANITIZE_STRING) . '"'; + } - return sprintf('', $url, $alt); + return sprintf('', $url, $alt); } -function md_topheader($matches) { - // Terrible hack to check we haven't found an empty list item. - if ($matches[2] == '-' && preg_match('{^-(?: |$)}', $matches[1])) { - return $matches[0]; - } +function md_topheader($matches) +{ + // Terrible hack to check we haven't found an empty list item. + if ($matches[2] == '-' && preg_match('{^-(?: |$)}', $matches[1])) { + return $matches[0]; + } - $level = $matches[2][0] == '=' ? 1 : 2; - - return "" . $matches[1] . "" . "\n"; + $level = $matches[2][0] == '=' ? 1 : 2; + return "" . $matches[1] . "" . "\n"; } -function bb_fixtable_lf($match) { +function bb_fixtable_lf($match) +{ - // bbcode version (1 arg) - // remove extraneous whitespace between table element tags since newlines will all - // be converted to '
      ' and turn your neatly crafted tables into a whole lot of - // empty space. - - $x = preg_replace("/\]\s+\[/",'][',$match[1]); - $x = str_replace("\\\n","\n",$x); - return '[table]' . $x . '[/table]'; + // bbcode version (1 arg) + // remove extraneous whitespace between table element tags since newlines will all + // be converted to '
      ' and turn your neatly crafted tables into a whole lot of + // empty space. + $x = preg_replace("/\]\s+\[/", '][', $match[1]); + $x = str_replace("\\\n", "\n", $x); + return '[table]' . $x . '[/table]'; } -function ht_fixtable_lf($match) { +function ht_fixtable_lf($match) +{ - // HTML version (2 args) - // remove extraneous whitespace between table element tags since newlines will all - // be converted to '
      ' and turn your neatly crafted tables into a whole lot of - // empty space. - - $x = preg_replace("/\>\s+\<',$match[2]); - return '' . $x . ''; + // HTML version (2 args) + // remove extraneous whitespace between table element tags since newlines will all + // be converted to '
      ' and turn your neatly crafted tables into a whole lot of + // empty space. + $x = preg_replace("/\>\s+\<', $match[2]); + return '' . $x . ''; } -function bb_colortag($matches) { - return '' . $matches[2] . ''; +function bb_colortag($matches) +{ + return '' . $matches[2] . ''; } -function bb_fonttag($matches) { - return '' . $matches[2] . ''; +function bb_fonttag($matches) +{ + return '' . $matches[2] . ''; } -function bb_sizetag($matches) { - return '' . $matches[2] . ''; - +function bb_sizetag($matches) +{ + return '' . $matches[2] . ''; } -function bb_hltag($matches) { - return '' . $matches[2] . ''; +function bb_hltag($matches) +{ + return '' . $matches[2] . ''; } -function bb_xss($s) { - // don't allow functions of any kind - $s = str_replace( [ '(', ')' ], [ '', '' ], $s); +function bb_xss($s) +{ + // don't allow functions of any kind + $s = str_replace([ '(', ')' ], [ '', '' ], $s); - // don't allow injection of multiple params + // don't allow injection of multiple params - if (strpos($s,';') !== false) { - return substr($s,0,strpos($s,';')); - } - return $s; + if (strpos($s, ';') !== false) { + return substr($s, 0, strpos($s, ';')); + } + return $s; } -function bbtopoll($s) { +function bbtopoll($s) +{ - $pl = []; + $pl = []; - $match = ''; - if(! preg_match("/\[poll=(.*?)\](.*?)\[\/poll\]/ism",$s,$match)) { - return null; - } - $pl['poll_id'] = $match[1]; - $pl['poll_question'] = $match[2]; + $match = ''; + if (! preg_match("/\[poll=(.*?)\](.*?)\[\/poll\]/ism", $s, $match)) { + return null; + } + $pl['poll_id'] = $match[1]; + $pl['poll_question'] = $match[2]; - $match = ''; - if(preg_match_all("/\[poll\-answer=(.*?)\](.*?)\[\/poll\-answer\]/is",$s,$match,PREG_SET_ORDER)) { - $pl['answer'] = []; - foreach($match as $m) { - $ans = [ 'answer_id' => $m[1], 'answer_text' => $m[2] ]; - $pl['answer'][] = $ans; - } - } - - return $pl; + $match = ''; + if (preg_match_all("/\[poll\-answer=(.*?)\](.*?)\[\/poll\-answer\]/is", $s, $match, PREG_SET_ORDER)) { + $pl['answer'] = []; + foreach ($match as $m) { + $ans = [ 'answer_id' => $m[1], 'answer_text' => $m[2] ]; + $pl['answer'][] = $ans; + } + } + return $pl; } -function parseIdentityAwareHTML($Text) { +function parseIdentityAwareHTML($Text) +{ - // Hide all [noparse] contained bbtags by spacefying them - if (strpos($Text,'[noparse]') !== false) { - $Text = preg_replace_callback("/\[noparse\](.*?)\[\/noparse\]/ism", 'bb_spacefy',$Text); - } - if (strpos($Text,'[nobb]') !== false) { - $Text = preg_replace_callback("/\[nobb\](.*?)\[\/nobb\]/ism", 'bb_spacefy',$Text); - } - if (strpos($Text,'[pre]') !== false) { - $Text = preg_replace_callback("/\[pre\](.*?)\[\/pre\]/ism", 'bb_spacefy',$Text); - } - // process [observer] tags before we do anything else because we might - // be stripping away stuff that then doesn't need to be worked on anymore + // Hide all [noparse] contained bbtags by spacefying them + if (strpos($Text, '[noparse]') !== false) { + $Text = preg_replace_callback("/\[noparse\](.*?)\[\/noparse\]/ism", 'bb_spacefy', $Text); + } + if (strpos($Text, '[nobb]') !== false) { + $Text = preg_replace_callback("/\[nobb\](.*?)\[\/nobb\]/ism", 'bb_spacefy', $Text); + } + if (strpos($Text, '[pre]') !== false) { + $Text = preg_replace_callback("/\[pre\](.*?)\[\/pre\]/ism", 'bb_spacefy', $Text); + } + // process [observer] tags before we do anything else because we might + // be stripping away stuff that then doesn't need to be worked on anymore $observer = App::get_observer(); - if ((strpos($Text,'[/observer]') !== false) || (strpos($Text,'[/rpost]') !== false)) { + if ((strpos($Text, '[/observer]') !== false) || (strpos($Text, '[/rpost]') !== false)) { + $Text = preg_replace_callback("/\[observer\.language\=(.*?)\](.*?)\[\/observer\]/ism", 'oblanguage_callback', $Text); - $Text = preg_replace_callback("/\[observer\.language\=(.*?)\](.*?)\[\/observer\]/ism",'oblanguage_callback', $Text); + $Text = preg_replace_callback("/\[observer\.language\!\=(.*?)\](.*?)\[\/observer\]/ism", 'oblanguage_necallback', $Text); - $Text = preg_replace_callback("/\[observer\.language\!\=(.*?)\](.*?)\[\/observer\]/ism",'oblanguage_necallback', $Text); + if ($observer) { + $Text = preg_replace("/\[observer\=1\](.*?)\[\/observer\]/ism", '$1', $Text); + $Text = preg_replace("/\[observer\=0\].*?\[\/observer\]/ism", '', $Text); + $Text = preg_replace_callback("/\[rpost(=(.*?))?\](.*?)\[\/rpost\]/ism", 'rpost_callback', $Text); + } else { + $Text = preg_replace("/\[observer\=1\].*?\[\/observer\]/ism", '', $Text); + $Text = preg_replace("/\[observer\=0\](.*?)\[\/observer\]/ism", '$1', $Text); + $Text = preg_replace("/\[rpost(=.*?)?\](.*?)\[\/rpost\]/ism", '', $Text); + } + } + // replace [observer.baseurl] + if ($observer) { + $s1 = ''; + $s2 = ''; + $obsBaseURL = $observer['xchan_connurl']; + $obsBaseURL = preg_replace("/\/poco\/.*$/", '', $obsBaseURL); + $Text = str_replace('[observer.baseurl]', $obsBaseURL, $Text); + $Text = str_replace('[observer.url]', $observer['xchan_url'], $Text); + $Text = str_replace('[observer.name]', $s1 . $observer['xchan_name'] . $s2, $Text); + $Text = str_replace('[observer.address]', $s1 . $observer['xchan_addr'] . $s2, $Text); + $Text = str_replace('[observer.webname]', substr($observer['xchan_addr'], 0, strpos($observer['xchan_addr'], '@')), $Text); + $Text = str_replace('[observer.photo]', $s1 . '[zmg]' . $observer['xchan_photo_l'] . '[/zmg]' . $s2, $Text); + $Text = str_replace('[observer.baseurl/]', $obsBaseURL, $Text); + $Text = str_replace('[observer.url/]', $observer['xchan_url'], $Text); + $Text = str_replace('[observer.name/]', $s1 . $observer['xchan_name'] . $s2, $Text); + $Text = str_replace('[observer.address/]', $s1 . $observer['xchan_addr'] . $s2, $Text); + $Text = str_replace('[observer.webname/]', substr($observer['xchan_addr'], 0, strpos($observer['xchan_addr'], '@')), $Text); + $Text = str_replace('[observer.photo/]', $s1 . '[zmg]' . $observer['xchan_photo_l'] . '[/zmg]' . $s2, $Text); + } else { + $Text = str_replace('[observer.baseurl]', '', $Text); + $Text = str_replace('[observer.url]', '', $Text); + $Text = str_replace('[observer.name]', '', $Text); + $Text = str_replace('[observer.address]', '', $Text); + $Text = str_replace('[observer.webname]', '', $Text); + $Text = str_replace('[observer.photo]', '', $Text); + $Text = str_replace('[observer.baseurl/]', '', $Text); + $Text = str_replace('[observer.url/]', '', $Text); + $Text = str_replace('[observer.name/]', '', $Text); + $Text = str_replace('[observer.address/]', '', $Text); + $Text = str_replace('[observer.webname/]', '', $Text); + $Text = str_replace('[observer.photo/]', '', $Text); + } - if ($observer) { - $Text = preg_replace("/\[observer\=1\](.*?)\[\/observer\]/ism", '$1', $Text); - $Text = preg_replace("/\[observer\=0\].*?\[\/observer\]/ism", '', $Text); - $Text = preg_replace_callback("/\[rpost(=(.*?))?\](.*?)\[\/rpost\]/ism", 'rpost_callback', $Text); - } else { - $Text = preg_replace("/\[observer\=1\].*?\[\/observer\]/ism", '', $Text); - $Text = preg_replace("/\[observer\=0\](.*?)\[\/observer\]/ism", '$1', $Text); - $Text = preg_replace("/\[rpost(=.*?)?\](.*?)\[\/rpost\]/ism", '', $Text); - } - } - // replace [observer.baseurl] - if ($observer) { - $s1 = ''; - $s2 = ''; - $obsBaseURL = $observer['xchan_connurl']; - $obsBaseURL = preg_replace("/\/poco\/.*$/", '', $obsBaseURL); - $Text = str_replace('[observer.baseurl]', $obsBaseURL, $Text); - $Text = str_replace('[observer.url]',$observer['xchan_url'], $Text); - $Text = str_replace('[observer.name]',$s1 . $observer['xchan_name'] . $s2, $Text); - $Text = str_replace('[observer.address]',$s1 . $observer['xchan_addr'] . $s2, $Text); - $Text = str_replace('[observer.webname]', substr($observer['xchan_addr'],0,strpos($observer['xchan_addr'],'@')), $Text); - $Text = str_replace('[observer.photo]',$s1 . '[zmg]'.$observer['xchan_photo_l'].'[/zmg]' . $s2, $Text); - $Text = str_replace('[observer.baseurl/]', $obsBaseURL, $Text); - $Text = str_replace('[observer.url/]',$observer['xchan_url'], $Text); - $Text = str_replace('[observer.name/]',$s1 . $observer['xchan_name'] . $s2, $Text); - $Text = str_replace('[observer.address/]',$s1 . $observer['xchan_addr'] . $s2, $Text); - $Text = str_replace('[observer.webname/]', substr($observer['xchan_addr'],0,strpos($observer['xchan_addr'],'@')), $Text); - $Text = str_replace('[observer.photo/]',$s1 . '[zmg]'.$observer['xchan_photo_l'].'[/zmg]' . $s2, $Text); + $Text = str_replace(array('[baseurl]','[baseurl/]','[sitename]','[sitename/]'), array(z_root(),z_root(), get_config('system', 'sitename'),get_config('system', 'sitename')), $Text); - } else { - $Text = str_replace('[observer.baseurl]', '', $Text); - $Text = str_replace('[observer.url]','', $Text); - $Text = str_replace('[observer.name]','', $Text); - $Text = str_replace('[observer.address]','', $Text); - $Text = str_replace('[observer.webname]','',$Text); - $Text = str_replace('[observer.photo]','', $Text); - $Text = str_replace('[observer.baseurl/]', '', $Text); - $Text = str_replace('[observer.url/]','', $Text); - $Text = str_replace('[observer.name/]','', $Text); - $Text = str_replace('[observer.address/]','', $Text); - $Text = str_replace('[observer.webname/]','',$Text); - $Text = str_replace('[observer.photo/]','', $Text); - } - - $Text = str_replace(array('[baseurl]','[baseurl/]','[sitename]','[sitename/]'),array(z_root(),z_root(), get_config('system','sitename'),get_config('system','sitename')),$Text); - - // Unhide all [noparse] contained bbtags unspacefying them - // and triming the [noparse] tag. - if (strpos($Text,'[noparse]') !== false) { - $Text = preg_replace_callback("/\[noparse\](.*?)\[\/noparse\]/ism", 'bb_unspacefy_and_trim', $Text); - } - if (strpos($Text,'[nobb]') !== false) { - $Text = preg_replace_callback("/\[nobb\](.*?)\[\/nobb\]/ism", 'bb_unspacefy_and_trim', $Text); - } - if (strpos($Text,'[pre]') !== false) { - $Text = preg_replace_callback("/\[pre\](.*?)\[\/pre\]/ism", 'bb_unspacefy_and_trim', $Text); - } - return $Text; + // Unhide all [noparse] contained bbtags unspacefying them + // and triming the [noparse] tag. + if (strpos($Text, '[noparse]') !== false) { + $Text = preg_replace_callback("/\[noparse\](.*?)\[\/noparse\]/ism", 'bb_unspacefy_and_trim', $Text); + } + if (strpos($Text, '[nobb]') !== false) { + $Text = preg_replace_callback("/\[nobb\](.*?)\[\/nobb\]/ism", 'bb_unspacefy_and_trim', $Text); + } + if (strpos($Text, '[pre]') !== false) { + $Text = preg_replace_callback("/\[pre\](.*?)\[\/pre\]/ism", 'bb_unspacefy_and_trim', $Text); + } + return $Text; } -function bbcode($Text, $options = []) { +function bbcode($Text, $options = []) +{ - if(! is_array($options)) { - $options = []; - } + if (! is_array($options)) { + $options = []; + } - if(is_array($Text)) { - btlogger('Text is array: ' . print_r($Text,true)); - } + if (is_array($Text)) { + btlogger('Text is array: ' . print_r($Text, true)); + } - $cache = ((array_key_exists('cache',$options)) ? $options['cache'] : false); - $newwin = ((array_key_exists('newwin',$options)) ? $options['newwin'] : true); - $export = ((array_key_exists('export',$options)) ? $options['export'] : false); - $activitypub = ((array_key_exists('activitypub',$options)) ? $options['activitypub'] : false); - $censored = ((array_key_exists('censored',$options)) ? $options['censored'] : false); - $plain = ((array_key_exists('plain',$options)) ? $options['plain'] : false); - $bbonly = ((array_key_exists('bbonly',$options)) ? $options['bbonly'] : false); - - if ($activitypub) { - $export = true; - } + $cache = ((array_key_exists('cache', $options)) ? $options['cache'] : false); + $newwin = ((array_key_exists('newwin', $options)) ? $options['newwin'] : true); + $export = ((array_key_exists('export', $options)) ? $options['export'] : false); + $activitypub = ((array_key_exists('activitypub', $options)) ? $options['activitypub'] : false); + $censored = ((array_key_exists('censored', $options)) ? $options['censored'] : false); + $plain = ((array_key_exists('plain', $options)) ? $options['plain'] : false); + $bbonly = ((array_key_exists('bbonly', $options)) ? $options['bbonly'] : false); - $target = (($newwin) ? ' target="_blank" ' : ''); + if ($activitypub) { + $export = true; + } - call_hooks('bbcode_filter', $Text); + $target = (($newwin) ? ' target="_blank" ' : ''); + call_hooks('bbcode_filter', $Text); - // Hide all [noparse] contained bbtags by spacefying them - if (strpos($Text,'[noparse]') !== false) { - $Text = preg_replace_callback("/\[noparse\](.*?)\[\/noparse\]/ism", 'bb_spacefy',$Text); - } - if (strpos($Text,'[nobb]') !== false) { - $Text = preg_replace_callback("/\[nobb\](.*?)\[\/nobb\]/ism", 'bb_spacefy',$Text); - } - if (strpos($Text,'[pre]') !== false) { - $Text = preg_replace_callback("/\[pre\](.*?)\[\/pre\]/ism", 'bb_spacefy',$Text); - } - $Text = bb_format_attachdata($Text); + // Hide all [noparse] contained bbtags by spacefying them + if (strpos($Text, '[noparse]') !== false) { + $Text = preg_replace_callback("/\[noparse\](.*?)\[\/noparse\]/ism", 'bb_spacefy', $Text); + } + if (strpos($Text, '[nobb]') !== false) { + $Text = preg_replace_callback("/\[nobb\](.*?)\[\/nobb\]/ism", 'bb_spacefy', $Text); + } + if (strpos($Text, '[pre]') !== false) { + $Text = preg_replace_callback("/\[pre\](.*?)\[\/pre\]/ism", 'bb_spacefy', $Text); + } - // If we find any event code, turn it into an event. - // After we're finished processing the bbcode we'll - // replace all of the event code with a reformatted version. + $Text = bb_format_attachdata($Text); - $ev = bbtoevent($Text); + // If we find any event code, turn it into an event. + // After we're finished processing the bbcode we'll + // replace all of the event code with a reformatted version. - // and the same with polls + $ev = bbtoevent($Text); - $pl = bbtopoll($Text); + // and the same with polls + $pl = bbtopoll($Text); - // process [observer] tags before we do anything else because we might - // be stripping away stuff that then doesn't need to be worked on anymore - if($cache || $export) - $observer = false; - else - $observer = App::get_observer(); + // process [observer] tags before we do anything else because we might + // be stripping away stuff that then doesn't need to be worked on anymore - if ((strpos($Text,'[/observer]') !== false) || (strpos($Text,'[/rpost]') !== false)) { - $Text = preg_replace_callback("/\[observer\.language\=(.*?)\](.*?)\[\/observer\]/ism",'oblanguage_callback', $Text); - $Text = preg_replace_callback("/\[observer\.language\!\=(.*?)\](.*?)\[\/observer\]/ism",'oblanguage_necallback', $Text); + if ($cache || $export) { + $observer = false; + } else { + $observer = App::get_observer(); + } - if ($observer) { - $Text = preg_replace("/\[observer\=1\](.*?)\[\/observer\]/ism", '$1', $Text); - $Text = preg_replace("/\[observer\=0\].*?\[\/observer\]/ism", '', $Text); - $Text = preg_replace_callback("/\[rpost(=(.*?))?\](.*?)\[\/rpost\]/ism", 'rpost_callback', $Text); - } else { - $Text = preg_replace("/\[observer\=1\].*?\[\/observer\]/ism", '', $Text); - $Text = preg_replace("/\[observer\=0\](.*?)\[\/observer\]/ism", '$1', $Text); - $Text = preg_replace("/\[rpost(=.*?)?\](.*?)\[\/rpost\]/ism", '', $Text); - } - } + if ((strpos($Text, '[/observer]') !== false) || (strpos($Text, '[/rpost]') !== false)) { + $Text = preg_replace_callback("/\[observer\.language\=(.*?)\](.*?)\[\/observer\]/ism", 'oblanguage_callback', $Text); + $Text = preg_replace_callback("/\[observer\.language\!\=(.*?)\](.*?)\[\/observer\]/ism", 'oblanguage_necallback', $Text); - if($cache || $export) - $channel = false; - else - $channel = App::get_channel(); + if ($observer) { + $Text = preg_replace("/\[observer\=1\](.*?)\[\/observer\]/ism", '$1', $Text); + $Text = preg_replace("/\[observer\=0\].*?\[\/observer\]/ism", '', $Text); + $Text = preg_replace_callback("/\[rpost(=(.*?))?\](.*?)\[\/rpost\]/ism", 'rpost_callback', $Text); + } else { + $Text = preg_replace("/\[observer\=1\].*?\[\/observer\]/ism", '', $Text); + $Text = preg_replace("/\[observer\=0\](.*?)\[\/observer\]/ism", '$1', $Text); + $Text = preg_replace("/\[rpost(=.*?)?\](.*?)\[\/rpost\]/ism", '', $Text); + } + } - if (strpos($Text,'[/channel]') !== false) { - if ($channel) { - $Text = preg_replace("/\[channel\=1\](.*?)\[\/channel\]/ism", '$1', $Text); - $Text = preg_replace("/\[channel\=0\].*?\[\/channel\]/ism", '', $Text); - } else { - $Text = preg_replace("/\[channel\=1\].*?\[\/channel\]/ism", '', $Text); - $Text = preg_replace("/\[channel\=0\](.*?)\[\/channel\]/ism", '$1', $Text); - } - } + if ($cache || $export) { + $channel = false; + } else { + $channel = App::get_channel(); + } + if (strpos($Text, '[/channel]') !== false) { + if ($channel) { + $Text = preg_replace("/\[channel\=1\](.*?)\[\/channel\]/ism", '$1', $Text); + $Text = preg_replace("/\[channel\=0\].*?\[\/channel\]/ism", '', $Text); + } else { + $Text = preg_replace("/\[channel\=1\].*?\[\/channel\]/ism", '', $Text); + $Text = preg_replace("/\[channel\=0\](.*?)\[\/channel\]/ism", '$1', $Text); + } + } - $x = bb_extract_images($Text); - $Text = $x['body']; - $saved_images = $x['images']; - if(! $export) - $Text = str_replace(array('[baseurl]','[baseurl/]','[sitename]','[sitename/]'),array(z_root(),z_root(), get_config('system','sitename'),get_config('system','sitename')),$Text); + $x = bb_extract_images($Text); + $Text = $x['body']; + $saved_images = $x['images']; + if (! $export) { + $Text = str_replace(array('[baseurl]','[baseurl/]','[sitename]','[sitename/]'), array(z_root(),z_root(), get_config('system', 'sitename'),get_config('system', 'sitename')), $Text); + } - // Replace any html brackets with HTML Entities to prevent executing HTML or script - // Don't use strip_tags here because it breaks [url] search by replacing & with amp - // These are no longer needed since we run the content through purify_html() - // $Text = str_replace("<", "<", $Text); - // $Text = str_replace(">", ">", $Text); + // Replace any html brackets with HTML Entities to prevent executing HTML or script + // Don't use strip_tags here because it breaks [url] search by replacing & with amp + // These are no longer needed since we run the content through purify_html() + // $Text = str_replace("<", "<", $Text); + // $Text = str_replace(">", ">", $Text); - // Check for [code] text here, before the linefeeds are messed with. - // The highlighter will unescape and re-escape the content. - if (strpos($Text,'[code=') !== false) { - $Text = preg_replace_callback("/\[code=(.*?)\](.*?)\[\/code\]/ism", function ($match) use ($options) { - return bb_code_protect(text_highlight($match[2],strtolower($match[1]),$options)); - }, $Text); - } + // Check for [code] text here, before the linefeeds are messed with. + // The highlighter will unescape and re-escape the content. - $Text = preg_replace_callback("/\[table\](.*?)\[\/table\]/ism",'bb_fixtable_lf',$Text); - $Text = preg_replace_callback("/\(.*?)\<\/table\>/ism",'ht_fixtable_lf',$Text); + if (strpos($Text, '[code=') !== false) { + $Text = preg_replace_callback("/\[code=(.*?)\](.*?)\[\/code\]/ism", function ($match) use ($options) { + return bb_code_protect(text_highlight($match[2], strtolower($match[1]), $options)); + }, $Text); + } - $Text = str_replace("\r\n", "\n", $Text); + $Text = preg_replace_callback("/\[table\](.*?)\[\/table\]/ism", 'bb_fixtable_lf', $Text); + $Text = preg_replace_callback("/\(.*?)\<\/table\>/ism", 'ht_fixtable_lf', $Text); - if ($bbonly) { - $Text = purify_html($Text); - } - else { + $Text = str_replace("\r\n", "\n", $Text); - // escape some frequently encountered false positives with a zero-width space + if ($bbonly) { + $Text = purify_html($Text); + } else { + // escape some frequently encountered false positives with a zero-width space - // Here we are catching things like [quote](something)[/quote] and [b](something)[/b] and preventing them from turning into broken markdown links [text](url) - // We'll do this with a zero-width space between ] and ( - $Text = preg_replace("/\[(.*?)\]\((.*?)\)\[\/(.*?)\]/ism", '[$1]' . html_entity_decode('​') . '($2)[/$3]', $Text); + // Here we are catching things like [quote](something)[/quote] and [b](something)[/b] and preventing them from turning into broken markdown links [text](url) + // We'll do this with a zero-width space between ] and ( + $Text = preg_replace("/\[(.*?)\]\((.*?)\)\[\/(.*?)\]/ism", '[$1]' . html_entity_decode('​') . '($2)[/$3]', $Text); - // save code blocks from being interpreted as markdown + // save code blocks from being interpreted as markdown - $Text = preg_replace_callback("/\[code(.*?)\](.*?)\[\/code\]/ism", 'bb_code_preprotect', $Text); + $Text = preg_replace_callback("/\[code(.*?)\](.*?)\[\/code\]/ism", 'bb_code_preprotect', $Text); - // Quick but flawed fix for performance regression after purification - // was moved to rendering code to allow multiple code formats - // A proper fix would be to escape any code blocks before purification, - // restore them and store the resultant intermediate multicode. - // This is now accomplished using multicode_purify() - - // if (strpbrk($Text,'<>') !== false) { - // $Text = purify_html($Text, [ 'escape' ]); - // } - - // the bbcode tag 'nomd' will bypass markdown processing for any given text region - - $Text = preg_replace_callback('#\[nomd\](.*?)\[\/nomd\]#ism','md_protect',$Text); - - // and for completeness, there's 'nohtml' - - - $Text = preg_replace_callback('#\[nohtml\](.*?)\[\/nohtml\]#ism','html_protect',$Text); - - - // Perform some markdown conversions before translating linefeeds so as to keep the regexes manageable - // The preceding character check in bold/italic sequences is so we don't mistake underscore/asterisk in the middle of conversational text as an italic trigger. - - $Text = preg_replace_callback('#(^|\n| )(?$3',$Text); - // markdown inline code blocks must be preceded by space or linebreak - $Text = preg_replace('#(^|\n| )(?$2', $Text); - // strip backslash escape for inline code - $Text = preg_replace('#(\\\)`#','`',$Text); - $Text = preg_replace('#<\/code><\/pre>\n
      | .*?>)#','
      ',$Text); - - // blockquotes - $Text = preg_replace('#^(>)+ +(.*?)$#m','
      $2
      ',$Text); - $Text = preg_replace('#^(\>)+ +(.*?)$#m','
      $2
      ',$Text); - $Text = preg_replace('#\n
      #',"\n", $Text); - - // links - $Text = preg_replace_callback('#!\[[^\]]*\]\((.*?)(?=\"|\))(\".*\")?\)(?!`)#','md_image',$Text); - $Text = preg_replace('#\[([^\[]+)\]\((?:javascript:)?([^\)]+)\)(?!`)#','$1',$Text); - - // unordered lists - $Text = preg_replace('#^(?
    • $1
    ',$Text); - // strip the backslash escape if present - $Text = preg_replace('#^(\\\)([*\-+]) #m','$2',$Text); - // order lists - $Text = preg_replace('#^\d+[\.\)] +(.*?)$#m','
    1. $1
    ',$Text); - - $Text = preg_replace('/\s*<\/(ol|ul)>\n+<\1>\s*/',"\n",$Text); - - $Text = bb_code_preunprotect($Text); - } - - - // Convert new line chars to html
    tags - - $Text = str_replace(array("\r", "\n"), array('
    ', '
    '), $Text); - $Text = str_replace(array("\t", " "), array("    ", "  "), $Text); - - // Check for [code] text - if (strpos($Text,'[code]') !== false) { - $Text = preg_replace_callback("/\[code\](.*?)\[\/code\]/ism", 'bb_code', $Text); - } - - // Check for [code options] text - if (strpos($Text,'[code ') !== false) { - $Text = preg_replace_callback("/\[code(.*?)\](.*?)\[\/code\]/ism", 'bb_code_options', $Text); - } - - // Set up the parameters for a URL search string - $URLSearchString = "^\[\]"; - // Set up the parameters for a MAIL search string - $MAILSearchString = $URLSearchString; - - // replace [observer.baseurl] - if ($observer) { - $s1 = ''; - $s2 = ''; - $obsBaseURL = $observer['xchan_connurl']; - $obsBaseURL = preg_replace("/\/poco\/.*$/", '', $obsBaseURL); - $Text = str_replace('[observer.baseurl]', $obsBaseURL, $Text); - $Text = str_replace('[observer.url]',$observer['xchan_url'], $Text); - $Text = str_replace('[observer.name]',$s1 . $observer['xchan_name'] . $s2, $Text); - $Text = str_replace('[observer.address]',$s1 . $observer['xchan_addr'] . $s2, $Text); - $Text = str_replace('[observer.webname]', substr($observer['xchan_addr'],0,strpos($observer['xchan_addr'],'@')), $Text); - $Text = str_replace('[observer.photo]',$s1 . '[zmg]'.$observer['xchan_photo_l'].'[/zmg]' . $s2, $Text); - $Text = str_replace('[observer.baseurl/]', $obsBaseURL, $Text); - $Text = str_replace('[observer.url/]',$observer['xchan_url'], $Text); - $Text = str_replace('[observer.name/]',$s1 . $observer['xchan_name'] . $s2, $Text); - $Text = str_replace('[observer.address/]',$s1 . $observer['xchan_addr'] . $s2, $Text); - $Text = str_replace('[observer.webname/]', substr($observer['xchan_addr'],0,strpos($observer['xchan_addr'],'@')), $Text); - $Text = str_replace('[observer.photo/]',$s1 . '[zmg]'.$observer['xchan_photo_l'].'[/zmg]' . $s2, $Text); - } else { - $Text = str_replace('[observer.baseurl]', '', $Text); - $Text = str_replace('[observer.url]','', $Text); - $Text = str_replace('[observer.name]','', $Text); - $Text = str_replace('[observer.address]','', $Text); - $Text = str_replace('[observer.webname]','',$Text); - $Text = str_replace('[observer.photo]','', $Text); - $Text = str_replace('[observer.baseurl/]', '', $Text); - $Text = str_replace('[observer.url/]','', $Text); - $Text = str_replace('[observer.name/]','', $Text); - $Text = str_replace('[observer.address/]','', $Text); - $Text = str_replace('[observer.webname/]','',$Text); - $Text = str_replace('[observer.photo/]','', $Text); - } - - - - // Perform URL Search - - $urlchars = '[a-zA-Z0-9\pL\:\/\-\?\&\;\.\=\_\~\#\%\$\!\+\,\@\(\)]'; - - if (strpos($Text,'http') !== false) { - $Text = preg_replace("/([^\]\='".'"'."\;\/])(https?\:\/\/$urlchars+)/ismu", '$1$2', $Text); - } - - $count = 0; - while (strpos($Text,'[/share]') !== false && $count < 10) { - $Text = preg_replace_callback("/\[share(.*?)\](.*?)\[\/share\]/ism", 'bb_ShareAttributes', $Text); - $count ++; - } - - if (strpos($Text,'[/url]') !== false) { - $Text = preg_replace("/\#\^\[url\]([$URLSearchString]*)\[\/url\]/ism", '$1', $Text); - $Text = preg_replace("/\#\^\[url\=([$URLSearchString]*)\](.*?)\[\/url\]/ism", '$2', $Text); - $Text = preg_replace("/\[url\]([$URLSearchString]*)\[\/url\]/ism", '$1', $Text); - $Text = preg_replace("/\@(\!?)\[url\=([$URLSearchString]*)\](.*?)\[\/url\]/ism", '@$1$3', $Text); - $Text = preg_replace("/\[url\=([$URLSearchString]*)\](.*?)\[\/url\]/ism", '$2', $Text); - } - - if (strpos($Text,'[/zrl]') !== false) { - // render hubzilla bookmarks as normal links - $Text = preg_replace("/\#\^\[zrl\]([$URLSearchString]*)\[\/zrl\]/ism", '$1', $Text); - $Text = preg_replace("/\#\^\[zrl\=([$URLSearchString]*)\](.*?)\[\/zrl\]/ism", '$2', $Text); - $Text = preg_replace("/\[zrl\]([$URLSearchString]*)\[\/zrl\]/ism", '$1', $Text); - $Text = preg_replace("/\@(\!?)\[zrl\=([$URLSearchString]*)\](.*?)\[\/zrl\]/ism", '@$1$3', $Text); - $Text = preg_replace("/\[zrl\=([$URLSearchString]*)\](.*?)\[\/zrl\]/ism", '$2', $Text); - } - - // named anchors do not work well in conversational text, as it is often collapsed by a "showmore" script. - // Included here for completeness. - - if (strpos($Text,'[/anchor]') !== false) { - $Text = preg_replace("/\[anchor\](.*?)\[\/anchor\]/ism", '', $Text); - } - - if (strpos($Text,'[/goto]') !== false) { - $Text = preg_replace("/\[goto=(.*?)\](.*?)\[\/goto\]/ism", '$2', $Text); - } - - // Perform MAIL Search - if (strpos($Text,'[/mail]') !== false) { - $Text = preg_replace("/\[mail\]([$MAILSearchString]*)\[\/mail\]/", '$1', $Text); - $Text = preg_replace("/\[mail\=([$MAILSearchString]*)\](.*?)\[\/mail\]/", '$2', $Text); - } - - - // leave open the posibility of [map=something] - // this is replaced in prepare_body() which has knowledge of the item location - - if ($export) { - $Text = str_replace( [ '[map]','[/map]' ], [ '','' ] , $Text); - $Text = preg_replace("/\[map=(.*?)[, ](.*?)\]/ism", 'geo:$1,$2', $Text); - } - else { - if (strpos($Text,'[/map]') !== false) { - $Text = preg_replace_callback("/\[map\](.*?)\[\/map\]/ism", 'bb_map_location', $Text); - } - if (strpos($Text,'[map=') !== false) { - $Text = preg_replace_callback("/\[map=(.*?)\/\]/ism", 'bb_map_coords', $Text); - $Text = preg_replace_callback("/\[map=(.*?)\]/ism", 'bb_map_coords', $Text); - } - if (strpos($Text,'[map]') !== false) { - $Text = preg_replace("/\[map\/\]/", '
    ', $Text); - $Text = preg_replace("/\[map\]/", '
    ', $Text); - } - } - - // Check for bold text - if (strpos($Text,'[b]') !== false) { - $Text = preg_replace("(\[b\](.*?)\[\/b\])ism", '$1', $Text); - } - // Check for Italics text - if (strpos($Text,'[i]') !== false) { - $Text = preg_replace("(\[i\](.*?)\[\/i\])ism", '$1', $Text); - } - // Check for Underline text - if (strpos($Text,'[u]') !== false) { - $Text = preg_replace("(\[u\](.*?)\[\/u\])ism", '$1', $Text); - } - // Check for strike-through text - if (strpos($Text,'[s]') !== false) { - $Text = preg_replace("(\[s\](.*?)\[\/s\])ism", '$1', $Text); - } - // Check for over-line text - if (strpos($Text,'[o]') !== false) { - $Text = preg_replace("(\[o\](.*?)\[\/o\])ism", '$1', $Text); - } - if (strpos($Text,'[sup]') !== false) { - $Text = preg_replace("(\[sup\](.*?)\[\/sup\])ism", '$1', $Text); - } - if (strpos($Text,'[sub]') !== false) { - $Text = preg_replace("(\[sub\](.*?)\[\/sub\])ism", '$1', $Text); - } - - // Check for colored text - if (strpos($Text,'[/color]') !== false) { - $Text = preg_replace_callback("(\[color=(.*?)\](.*?)\[\/color\])ism", 'bb_colortag', $Text); - } - // Check for highlighted text - if (strpos($Text,'[/hl]') !== false) { - $Text = preg_replace("(\[hl\](.*?)\[\/hl\])ism", "$1", $Text); - $Text = preg_replace_callback("(\[mark=(.*?)\](.*?)\[\/mark\])ism", 'bb_hltag', $Text); - } - // Check for highlighted text - if (strpos($Text,'[/mark]') !== false) { - $Text = preg_replace("(\[mark\](.*?)\[\/mark\])ism", "$1", $Text); - $Text = preg_replace_callback("(\[mark=(.*?)\](.*?)\[\/mark\])ism", 'bb_hltag', $Text); - } - - // Check for sized text - // [size=50] --> font-size: 50px (with the unit). - if (strpos($Text,'[/size]') !== false) { - $Text = preg_replace("(\[size=(\d*?)\](.*?)\[\/size\])ism", "$2", $Text); - $Text = preg_replace_callback("(\[size=(.*?)\](.*?)\[\/size\])ism", 'bb_sizetag', $Text); - } - // Check for h1 - if (strpos($Text,'[h1]') !== false) { - $Text = preg_replace("(\[h1\](.*?)\[\/h1\])ism",'

    $1

    ',$Text); - $Text = str_replace('
    ', '', $Text); - } - // Check for h2 - if (strpos($Text,'[h2]') !== false) { - $Text = preg_replace("(\[h2\](.*?)\[\/h2\])ism",'

    $1

    ',$Text); - $Text = str_replace('
    ', '', $Text); - } - // Check for h3 - if (strpos($Text,'[h3]') !== false) { - $Text = preg_replace("(\[h3\](.*?)\[\/h3\])ism",'

    $1

    ',$Text); - $Text = str_replace('
    ', '', $Text); - } - // Check for h4 - if (strpos($Text,'[h4]') !== false) { - $Text = preg_replace("(\[h4\](.*?)\[\/h4\])ism",'

    $1

    ',$Text); - $Text = str_replace('
    ', '', $Text); - } - // Check for h5 - if (strpos($Text,'[h5]') !== false) { - $Text = preg_replace("(\[h5\](.*?)\[\/h5\])ism",'
    $1
    ',$Text); - $Text = str_replace('
    ', '', $Text); - } - // Check for h6 - if (strpos($Text,'[h6]') !== false) { - $Text = preg_replace("(\[h6\](.*?)\[\/h6\])ism",'
    $1
    ',$Text); - $Text = str_replace('
    ', '', $Text); - } - - // Check for table of content without params - while(strpos($Text,'[toc]') !== false) { - $toc_id = 'toc-' . random_string(10); - $Text = preg_replace("/\[toc\]/ism", '
      ', $Text, 1); - $Text = preg_replace("/\[toc\/\]/ism", '
        ', $Text, 1); - } - // Check for table of content with params - while(strpos($Text,'[toc') !== false) { - $toc_id = 'toc-' . random_string(10); - $Text = preg_replace("/\[toc([^\]]+?)\/\]/ism", '
          ', $Text, 1); - $Text = preg_replace("/\[toc([^\]]+?)\]/ism", '
            ', $Text, 1); - } - // Check for centered text - if (strpos($Text,'[/center]') !== false) { - $Text = preg_replace("(\[center\](.*?)\[\/center\])ism", "
            $1
            ", $Text); - } - // Check for footer - if (strpos($Text,'[/footer]') !== false) { - $Text = preg_replace("(\[footer\](.*?)\[\/footer\])ism", "
            $1
            ", $Text); - } - - // Check for bdi - if (strpos($Text,'[/bdi]') !== false) { - $Text = preg_replace("(\[bdi\](.*?)\[\/bdi\])ism", "$1", $Text); - } - - - // Check for list text - - $Text = preg_replace("/
            \[\*\]/ism","[*]",$Text); - $Text = str_replace("[*]", "
          • ", $Text); - - // handle nested lists - $endlessloop = 0; - - while ((((strpos($Text, "[/list]") !== false) && (strpos($Text, "[list") !== false)) || - ((strpos($Text, "[/ol]") !== false) && (strpos($Text, "[ol]") !== false)) || - ((strpos($Text, "[/ul]") !== false) && (strpos($Text, "[ul]") !== false)) || - ((strpos($Text, "[/dl]") !== false) && (strpos($Text, "[dl") !== false)) || - ((strpos($Text, "[/li]") !== false) && (strpos($Text, "[li]") !== false))) && (++$endlessloop < 20)) { - $Text = preg_replace("/\[list\](.*?)\[\/list\]/ism", '
              $1
            ', $Text); - $Text = preg_replace("/\[list=\](.*?)\[\/list\]/ism", '
              $1
            ', $Text); - $Text = preg_replace("/\[list=1\](.*?)\[\/list\]/ism", '
              $1
            ', $Text); - $Text = preg_replace("/\[list=((?-i)i)\](.*?)\[\/list\]/ism",'
              $2
            ', $Text); - $Text = preg_replace("/\[list=((?-i)I)\](.*?)\[\/list\]/ism", '
              $2
            ', $Text); - $Text = preg_replace("/\[list=((?-i)a)\](.*?)\[\/list\]/ism", '
              $2
            ', $Text); - $Text = preg_replace("/\[list=((?-i)A)\](.*?)\[\/list\]/ism", '
              $2
            ', $Text); - $Text = preg_replace("/\[ul\](.*?)\[\/ul\]/ism", '
              $1
            ', $Text); - $Text = preg_replace("/\[ol\](.*?)\[\/ol\]/ism", '
              $1
            ', $Text); - $Text = preg_replace("/\[\/li\]
            \[li\]/ism",'[/li][li]',$Text); - $Text = preg_replace("/\[li\](.*?)\[\/li\]/ism", '
          • $1
          • ', $Text); - - // [dl] tags have an optional [dl terms="bi"] form where bold/italic/underline/mono/large - // etc. style may be specified for the "terms" in the definition list. The quotation marks - // are also optional. The regex looks intimidating, but breaks down as: - // "[dl" "]" "[/dl]" - // where optional-termStyles are: "terms=" - $Text = preg_replace_callback('/\[dl[[:space:]]*(?:terms=(?:"|")?([a-zA-Z]+)(?:"|")?)?\](.*?)\[\/dl\]/ism', 'bb_definitionList', $Text); - - } - - // Friendica generates this - if (strpos($Text,'[/abstract]') !== false) { - $Text = preg_replace("/\[abstract\](.*?)\[\/abstract\]/ism", '

            $1

            ', $Text); - } - - if (strpos($Text,'[checklist]') !== false) { - $Text = preg_replace_callback("/\[checklist\](.*?)\[\/checklist\]/ism", 'bb_checklist', $Text); - } - - - $loop = 0; - while (strpos($Text,'[/table]') !== false && strpos($Text,"[table") !== false && ++$loop < 20) { - $Text = preg_replace("/\[table\](.*?)\[\/table\]/ism", '$1
            ', $Text); - $Text = preg_replace("/\[table border=1\](.*?)\[\/table\]/ism", '$1
            ', $Text); - $Text = preg_replace("/\[table border=0\](.*?)\[\/table\]/ism", '$1
            ', $Text); - } - if (strpos($Text,'[th]') !== false) { - $Text = preg_replace("/\[th\](.*?)\[\/th\]/ism", '$1', $Text); - } - if (strpos($Text,'[td]') !== false) { - $Text = preg_replace("/\[td\](.*?)\[\/td\]/ism", '$1', $Text); - } - if (strpos($Text,'[tr]') !== false) { - $Text = preg_replace("/\[tr\](.*?)\[\/tr\]/ism", '$1', $Text); - } - if (strpos($Text,'[tbody]') !== false) { - $Text = preg_replace("/\[tbody\](.*?)\[\/tbody\]/ism", '$1', $Text); - } - - - $Text = str_replace('
            ', "\n", $Text); - $Text = str_replace('[hr]', '
            ', $Text); - - // This is actually executed in prepare_body() - - $Text = str_replace('[nosmile]', '', $Text); - - // Check for font change text - if (strpos($Text,'[/font]') !== false) { - $Text = preg_replace_callback("/\[font=(.*?)\](.*?)\[\/font\]/sm", 'bb_fonttag', $Text); - } - - if(strpos($Text,'[/summary]') !== false) { - $Text = preg_replace_callback("/^(.*?)\[summary\](.*?)\[\/summary\](.*?)$/ism", 'bb_summary', $Text); - } - - // Check for [spoiler] text - $endlessloop = 0; - while ((strpos($Text, "[/spoiler]")!== false) && (strpos($Text, "[spoiler]") !== false) && (++$endlessloop < 20)) { - $Text = preg_replace_callback("/\[spoiler\](.*?)\[\/spoiler\]/ism", 'bb_spoilertag', $Text); - } - - // Check for [spoiler=Author] text - $endlessloop = 0; - while ((strpos($Text, "[/spoiler]")!== false) && (strpos($Text, "[spoiler=") !== false) && (++$endlessloop < 20)) { - $Text = preg_replace_callback("/\[spoiler=(.*?)\](.*?)\[\/spoiler\]/ism", 'bb_spoilertag', $Text); - } - - // Check for [open] text - $endlessloop = 0; - while ((strpos($Text, "[/open]")!== false) && (strpos($Text, "[open]") !== false) && (++$endlessloop < 20)) { - $Text = preg_replace_callback("/\[open\](.*?)\[\/open\]/ism", 'bb_opentag', $Text); - } - - // Check for [open=Title] text - $endlessloop = 0; - while ((strpos($Text, "[/open]")!== false) && (strpos($Text, "[open=") !== false) && (++$endlessloop < 20)) { - $Text = preg_replace_callback("/\[open=(.*?)\](.*?)\[\/open\]/ism", 'bb_opentag', $Text); - } - - - // Declare the format for [quote] layout - $QuoteLayout = '
            $1
            '; - - - // Check for [quote] text - // handle nested quotes - $endlessloop = 0; - while ((strpos($Text, "[/quote]") !== false) && (strpos($Text, "[quote]") !== false) && (++$endlessloop < 20)) - $Text = preg_replace("/\[quote\](.*?)\[\/quote\]/ism", "$QuoteLayout", $Text); - - // Check for [quote=Author] text - - $t_wrote = t('$1 wrote:'); - - // handle nested quotes - $endlessloop = 0; - while ((strpos($Text, "[/quote]")!== false) && (strpos($Text, "[quote=") !== false) && (++$endlessloop < 20)) - $Text = preg_replace("/\[quote=[\"\']*(.*?)[\"\']*\](.*?)\[\/quote\]/ism", - "" . $t_wrote . "
            $2
            ", - $Text); - - if ($plain) { - $Text = str_replace([ '
            ','
            ' ], [ '“', '”' ], $Text); - } - - - // Images - - // [img]pathtoimage[/img] - if (strpos($Text,'[/img]') !== false) { - $Text = preg_replace("/\[img\](.*?)\[\/img\]/ism", '' . t('Image/photo') . '', $Text); - // Friendica's modified bbcode img tags - $Text = preg_replace("/\[img=http(.*?)\](.*?)\[\/img\]/ism", '' . t('Image/photo') . '', $Text); - } - if (strpos($Text,'[/zmg]') !== false) { - $Text = preg_replace("/\[zmg\](.*?)\[\/zmg\]/ism", '' . t('Image/photo') . '', $Text); - } - - $Text = preg_replace_callback("/\[([zi])mg([ \=])(.*?)\](.*?)\[\/[zi]mg\]/ism",'bb_imgoptions',$Text); - - if($censored) { - $Text = separate_img_links($Text); - $Text = preg_replace_callback("/\/ism","bb_colorbox",$Text); - } - - // style (sanitized) - if (strpos($Text,'[/style]') !== false) { - $Text = preg_replace_callback("(\[style=(.*?)\](.*?)\[\/style\])ism", "bb_sanitize_style", $Text); - } - - // crypt - if (strpos($Text,'[/crypt]') !== false) { - if ($activitypub) { - $Text = preg_replace_callback("/\[crypt (.*?)\](.*?)\[\/crypt\]/ism", 'bb_parse_b64_crypt', $Text); - } - else { - $Text = preg_replace_callback("/\[crypt (.*?)\](.*?)\[\/crypt\]/ism", 'bb_parse_crypt', $Text); - } - } - - if (strpos($Text,'[/app]') !== false) { - if ($activitypub) { - $Text = preg_replace_callback("/\[app\](.*?)\[\/app\]/ism",'bb_parse_app_ap', $Text); - } - else { - $Text = preg_replace_callback("/\[app\](.*?)\[\/app\]/ism",'bb_parse_app', $Text); - } - } - - if(strpos($Text,'[/element]') !== false) { - $Text = preg_replace_callback("/\[element\](.*?)\[\/element\]/ism",'bb_parse_element', $Text); - } - - // html5 video and audio - if (strpos($Text,'[/video]') !== false) { - $Text = preg_replace_callback("/\[video (.*?)\](.*?)\[\/video\]/ism", 'videowithopts', $Text); - $Text = preg_replace_callback("/\[video\](.*?)\[\/video\]/ism", 'tryzrlvideo', $Text); - } - if (strpos($Text,'[/audio]') !== false) { - $Text = preg_replace_callback("/\[audio\](.*?)\[\/audio\]/ism", 'tryzrlaudio', $Text); - } - if (strpos($Text,'[/zvideo]') !== false) { - $Text = preg_replace_callback("/\[zvideo (.*?)\](.*?)\[\/zvideo\]/ism", 'videowithopts', $Text); - $Text = preg_replace_callback("/\[zvideo\](.*?)\[\/zvideo\]/ism", 'tryzrlvideo', $Text); - } - if (strpos($Text,'[/zaudio]') !== false) { - $Text = preg_replace_callback("/\[zaudio\](.*?)\[\/zaudio\]/ism", 'tryzrlaudio', $Text); - } - - // if video couldn't be embedded, link to it instead. - if (strpos($Text,'[/video]') !== false) { - $Text = preg_replace("/\[video\](.*?)\[\/video\]/", '$1', $Text); - } - if (strpos($Text,'[/audio]') !== false) { - $Text = preg_replace("/\[audio\](.*?)\[\/audio\]/", '$1', $Text); - } - - if (strpos($Text,'[/zvideo]') !== false) { - $Text = preg_replace("/\[zvideo\](.*?)\[\/zvideo\]/", '$1', $Text); - } - if (strpos($Text,'[/zaudio]') !== false) { - $Text = preg_replace("/\[zaudio\](.*?)\[\/zaudio\]/", '$1', $Text); - } - - // SVG stuff - - if ($activitypub) { - $Text = preg_replace_callback("/\[svg(.*?)\](.*?)\[\/svg\]/ism", 'bb_svg_export', $Text); - } - else { - $Text = preg_replace_callback("/\[svg(.*?)\](.*?)\[\/svg\]/ism", 'bb_svg', $Text); - } - - // oembed tag - if(! $export) - $Text = oembed_bbcode2html($Text); - - // Avoid triple linefeeds through oembed - $Text = str_replace("


            ", "

            ", $Text); - - // If we found an event earlier, strip out all the event code and replace with a reformatted version. - // Replace the event-start section with the entire formatted event. The other bbcode is stripped. - // Summary (e.g. title) is required, earlier revisions only required description (in addition to - // start which is always required). Allow desc with a missing summary for compatibility. - - if ((x($ev,'desc') || x($ev,'summary')) && x($ev,'dtstart')) { - - $sub = format_event_html($ev); - - $sub = str_replace('$',"\0",$sub); - - $Text = preg_replace("/\[event\-start\](.*?)\[\/event\-start\]/ism",$sub,$Text); - - $Text = preg_replace("/\[event\](.*?)\[\/event\]/ism",'',$Text); - $Text = preg_replace("/\[event\-summary\](.*?)\[\/event\-summary\]/ism",'',$Text); - $Text = preg_replace("/\[event\-description\](.*?)\[\/event\-description\]/ism",'',$Text); - $Text = preg_replace("/\[event\-finish\](.*?)\[\/event\-finish\]/ism",'',$Text); - $Text = preg_replace("/\[event\-id\](.*?)\[\/event\-id\]/ism",'',$Text); - $Text = preg_replace("/\[event\-location\](.*?)\[\/event\-location\]/ism",'',$Text); - $Text = preg_replace("/\[event\-timezone\](.*?)\[\/event\-timezone\]/ism",'',$Text); - $Text = preg_replace("/\[event\-adjust\](.*?)\[\/event\-adjust\]/ism",'',$Text); - - $Text = str_replace("\0",'$',$Text); - - } - - // Unhide all [noparse] contained bbtags unspacefying them - // and triming the [noparse] tag. - if (strpos($Text,'[noparse]') !== false) { - $Text = preg_replace_callback("/\[noparse\](.*?)\[\/noparse\]/ism", 'bb_unspacefy_and_trim', $Text); - } - if (strpos($Text,'[nobb]') !== false) { - $Text = preg_replace_callback("/\[nobb\](.*?)\[\/nobb\]/ism", 'bb_unspacefy_and_trim', $Text); - } - if (strpos($Text,'[pre]') !== false) { - $Text = preg_replace_callback("/\[pre\](.*?)\[\/pre\]/ism", 'bb_unspacefy_and_trim', $Text); - } - - // replace escaped links in code= blocks - $Text = str_replace('%eY9-!','http', $Text); - $Text = bb_code_unprotect($Text); - - // This lets you use HTML entities in posts - just wrap them in brackets. For instance [©] to display a copyright symbol. - - $Text = preg_replace('/\[\&\;([#a-z0-9]+)\;\]/', '&$1;', $Text); - - // fix any escaped ampersands that may have been converted into links - - if(strpos($Text,'&') !== false) - $Text = preg_replace("/\<(.*?)(src|href)=(.*?)\&\;(.*?)\>/ism", '<$1$2=$3&$4>', $Text); - - // This is subtle - it's an XSS filter. It only accepts links with a protocol scheme and where - // the scheme begins with z (zhttp), h (http(s)), f (ftp(s)), g (gemini), m (mailto|magnet), t (tel) and named anchors. - // data: urls are allowed if exporting to activitypub which allows inline svg to federate, but not - // to be used for local display - - if ($activitypub) { - $Text = preg_replace("/\<(.*?)(src|href)=\"[^dzghfmt#](.*?)\>/ism", '<$1$2="">', $Text); - } - else { - $Text = preg_replace("/\<(.*?)(src|href)=\"[^zhgfmt#](.*?)\>/ism", '<$1$2="">', $Text); - } - - $Text = bb_replace_images($Text, $saved_images); - - $args = [ 'text' => $Text, 'options' => $options ]; - call_hooks('bbcode', $args); - - return $args['text']; + // Quick but flawed fix for performance regression after purification + // was moved to rendering code to allow multiple code formats + // A proper fix would be to escape any code blocks before purification, + // restore them and store the resultant intermediate multicode. + // This is now accomplished using multicode_purify() + + // if (strpbrk($Text,'<>') !== false) { + // $Text = purify_html($Text, [ 'escape' ]); + // } + + // the bbcode tag 'nomd' will bypass markdown processing for any given text region + + $Text = preg_replace_callback('#\[nomd\](.*?)\[\/nomd\]#ism', 'md_protect', $Text); + + // and for completeness, there's 'nohtml' + + + $Text = preg_replace_callback('#\[nohtml\](.*?)\[\/nohtml\]#ism', 'html_protect', $Text); + + + // Perform some markdown conversions before translating linefeeds so as to keep the regexes manageable + // The preceding character check in bold/italic sequences is so we don't mistake underscore/asterisk in the middle of conversational text as an italic trigger. + + $Text = preg_replace_callback('#(^|\n| )(?$3',$Text); + // markdown inline code blocks must be preceded by space or linebreak + $Text = preg_replace('#(^|\n| )(?$2', $Text); + // strip backslash escape for inline code + $Text = preg_replace('#(\\\)`#', '`', $Text); + $Text = preg_replace('#<\/code><\/pre>\n
            | .*?>)#', '
            ', $Text); + + // blockquotes + $Text = preg_replace('#^(>)+ +(.*?)$#m', '
            $2
            ', $Text); + $Text = preg_replace('#^(\>)+ +(.*?)$#m', '
            $2
            ', $Text); + $Text = preg_replace('#\n
            #', "\n", $Text); + + // links + $Text = preg_replace_callback('#!\[[^\]]*\]\((.*?)(?=\"|\))(\".*\")?\)(?!`)#', 'md_image', $Text); + $Text = preg_replace('#\[([^\[]+)\]\((?:javascript:)?([^\)]+)\)(?!`)#', '$1', $Text); + + // unordered lists + $Text = preg_replace('#^(?
          • $1
          • ', $Text); + // strip the backslash escape if present + $Text = preg_replace('#^(\\\)([*\-+]) #m', '$2', $Text); + // order lists + $Text = preg_replace('#^\d+[\.\)] +(.*?)$#m', '
            1. $1
            ', $Text); + + $Text = preg_replace('/\s*<\/(ol|ul)>\n+<\1>\s*/', "\n", $Text); + + $Text = bb_code_preunprotect($Text); + } + + + // Convert new line chars to html
            tags + + $Text = str_replace(array("\r", "\n"), array('
            ', '
            '), $Text); + $Text = str_replace(array("\t", " "), array("    ", "  "), $Text); + + // Check for [code] text + if (strpos($Text, '[code]') !== false) { + $Text = preg_replace_callback("/\[code\](.*?)\[\/code\]/ism", 'bb_code', $Text); + } + + // Check for [code options] text + if (strpos($Text, '[code ') !== false) { + $Text = preg_replace_callback("/\[code(.*?)\](.*?)\[\/code\]/ism", 'bb_code_options', $Text); + } + + // Set up the parameters for a URL search string + $URLSearchString = "^\[\]"; + // Set up the parameters for a MAIL search string + $MAILSearchString = $URLSearchString; + + // replace [observer.baseurl] + if ($observer) { + $s1 = ''; + $s2 = ''; + $obsBaseURL = $observer['xchan_connurl']; + $obsBaseURL = preg_replace("/\/poco\/.*$/", '', $obsBaseURL); + $Text = str_replace('[observer.baseurl]', $obsBaseURL, $Text); + $Text = str_replace('[observer.url]', $observer['xchan_url'], $Text); + $Text = str_replace('[observer.name]', $s1 . $observer['xchan_name'] . $s2, $Text); + $Text = str_replace('[observer.address]', $s1 . $observer['xchan_addr'] . $s2, $Text); + $Text = str_replace('[observer.webname]', substr($observer['xchan_addr'], 0, strpos($observer['xchan_addr'], '@')), $Text); + $Text = str_replace('[observer.photo]', $s1 . '[zmg]' . $observer['xchan_photo_l'] . '[/zmg]' . $s2, $Text); + $Text = str_replace('[observer.baseurl/]', $obsBaseURL, $Text); + $Text = str_replace('[observer.url/]', $observer['xchan_url'], $Text); + $Text = str_replace('[observer.name/]', $s1 . $observer['xchan_name'] . $s2, $Text); + $Text = str_replace('[observer.address/]', $s1 . $observer['xchan_addr'] . $s2, $Text); + $Text = str_replace('[observer.webname/]', substr($observer['xchan_addr'], 0, strpos($observer['xchan_addr'], '@')), $Text); + $Text = str_replace('[observer.photo/]', $s1 . '[zmg]' . $observer['xchan_photo_l'] . '[/zmg]' . $s2, $Text); + } else { + $Text = str_replace('[observer.baseurl]', '', $Text); + $Text = str_replace('[observer.url]', '', $Text); + $Text = str_replace('[observer.name]', '', $Text); + $Text = str_replace('[observer.address]', '', $Text); + $Text = str_replace('[observer.webname]', '', $Text); + $Text = str_replace('[observer.photo]', '', $Text); + $Text = str_replace('[observer.baseurl/]', '', $Text); + $Text = str_replace('[observer.url/]', '', $Text); + $Text = str_replace('[observer.name/]', '', $Text); + $Text = str_replace('[observer.address/]', '', $Text); + $Text = str_replace('[observer.webname/]', '', $Text); + $Text = str_replace('[observer.photo/]', '', $Text); + } + + + + // Perform URL Search + + $urlchars = '[a-zA-Z0-9\pL\:\/\-\?\&\;\.\=\_\~\#\%\$\!\+\,\@\(\)]'; + + if (strpos($Text, 'http') !== false) { + $Text = preg_replace("/([^\]\='" . '"' . "\;\/])(https?\:\/\/$urlchars+)/ismu", '$1$2', $Text); + } + + $count = 0; + while (strpos($Text, '[/share]') !== false && $count < 10) { + $Text = preg_replace_callback("/\[share(.*?)\](.*?)\[\/share\]/ism", 'bb_ShareAttributes', $Text); + $count++; + } + + if (strpos($Text, '[/url]') !== false) { + $Text = preg_replace("/\#\^\[url\]([$URLSearchString]*)\[\/url\]/ism", '$1', $Text); + $Text = preg_replace("/\#\^\[url\=([$URLSearchString]*)\](.*?)\[\/url\]/ism", '$2', $Text); + $Text = preg_replace("/\[url\]([$URLSearchString]*)\[\/url\]/ism", '$1', $Text); + $Text = preg_replace("/\@(\!?)\[url\=([$URLSearchString]*)\](.*?)\[\/url\]/ism", '@$1$3', $Text); + $Text = preg_replace("/\[url\=([$URLSearchString]*)\](.*?)\[\/url\]/ism", '$2', $Text); + } + + if (strpos($Text, '[/zrl]') !== false) { + // render hubzilla bookmarks as normal links + $Text = preg_replace("/\#\^\[zrl\]([$URLSearchString]*)\[\/zrl\]/ism", '$1', $Text); + $Text = preg_replace("/\#\^\[zrl\=([$URLSearchString]*)\](.*?)\[\/zrl\]/ism", '$2', $Text); + $Text = preg_replace("/\[zrl\]([$URLSearchString]*)\[\/zrl\]/ism", '$1', $Text); + $Text = preg_replace("/\@(\!?)\[zrl\=([$URLSearchString]*)\](.*?)\[\/zrl\]/ism", '@$1$3', $Text); + $Text = preg_replace("/\[zrl\=([$URLSearchString]*)\](.*?)\[\/zrl\]/ism", '$2', $Text); + } + + // named anchors do not work well in conversational text, as it is often collapsed by a "showmore" script. + // Included here for completeness. + + if (strpos($Text, '[/anchor]') !== false) { + $Text = preg_replace("/\[anchor\](.*?)\[\/anchor\]/ism", '', $Text); + } + + if (strpos($Text, '[/goto]') !== false) { + $Text = preg_replace("/\[goto=(.*?)\](.*?)\[\/goto\]/ism", '$2', $Text); + } + + // Perform MAIL Search + if (strpos($Text, '[/mail]') !== false) { + $Text = preg_replace("/\[mail\]([$MAILSearchString]*)\[\/mail\]/", '$1', $Text); + $Text = preg_replace("/\[mail\=([$MAILSearchString]*)\](.*?)\[\/mail\]/", '$2', $Text); + } + + + // leave open the posibility of [map=something] + // this is replaced in prepare_body() which has knowledge of the item location + + if ($export) { + $Text = str_replace([ '[map]','[/map]' ], [ '','' ], $Text); + $Text = preg_replace("/\[map=(.*?)[, ](.*?)\]/ism", 'geo:$1,$2', $Text); + } else { + if (strpos($Text, '[/map]') !== false) { + $Text = preg_replace_callback("/\[map\](.*?)\[\/map\]/ism", 'bb_map_location', $Text); + } + if (strpos($Text, '[map=') !== false) { + $Text = preg_replace_callback("/\[map=(.*?)\/\]/ism", 'bb_map_coords', $Text); + $Text = preg_replace_callback("/\[map=(.*?)\]/ism", 'bb_map_coords', $Text); + } + if (strpos($Text, '[map]') !== false) { + $Text = preg_replace("/\[map\/\]/", '
            ', $Text); + $Text = preg_replace("/\[map\]/", '
            ', $Text); + } + } + + // Check for bold text + if (strpos($Text, '[b]') !== false) { + $Text = preg_replace("(\[b\](.*?)\[\/b\])ism", '$1', $Text); + } + // Check for Italics text + if (strpos($Text, '[i]') !== false) { + $Text = preg_replace("(\[i\](.*?)\[\/i\])ism", '$1', $Text); + } + // Check for Underline text + if (strpos($Text, '[u]') !== false) { + $Text = preg_replace("(\[u\](.*?)\[\/u\])ism", '$1', $Text); + } + // Check for strike-through text + if (strpos($Text, '[s]') !== false) { + $Text = preg_replace("(\[s\](.*?)\[\/s\])ism", '$1', $Text); + } + // Check for over-line text + if (strpos($Text, '[o]') !== false) { + $Text = preg_replace("(\[o\](.*?)\[\/o\])ism", '$1', $Text); + } + if (strpos($Text, '[sup]') !== false) { + $Text = preg_replace("(\[sup\](.*?)\[\/sup\])ism", '$1', $Text); + } + if (strpos($Text, '[sub]') !== false) { + $Text = preg_replace("(\[sub\](.*?)\[\/sub\])ism", '$1', $Text); + } + + // Check for colored text + if (strpos($Text, '[/color]') !== false) { + $Text = preg_replace_callback("(\[color=(.*?)\](.*?)\[\/color\])ism", 'bb_colortag', $Text); + } + // Check for highlighted text + if (strpos($Text, '[/hl]') !== false) { + $Text = preg_replace("(\[hl\](.*?)\[\/hl\])ism", "$1", $Text); + $Text = preg_replace_callback("(\[mark=(.*?)\](.*?)\[\/mark\])ism", 'bb_hltag', $Text); + } + // Check for highlighted text + if (strpos($Text, '[/mark]') !== false) { + $Text = preg_replace("(\[mark\](.*?)\[\/mark\])ism", "$1", $Text); + $Text = preg_replace_callback("(\[mark=(.*?)\](.*?)\[\/mark\])ism", 'bb_hltag', $Text); + } + + // Check for sized text + // [size=50] --> font-size: 50px (with the unit). + if (strpos($Text, '[/size]') !== false) { + $Text = preg_replace("(\[size=(\d*?)\](.*?)\[\/size\])ism", "$2", $Text); + $Text = preg_replace_callback("(\[size=(.*?)\](.*?)\[\/size\])ism", 'bb_sizetag', $Text); + } + // Check for h1 + if (strpos($Text, '[h1]') !== false) { + $Text = preg_replace("(\[h1\](.*?)\[\/h1\])ism", '

            $1

            ', $Text); + $Text = str_replace('
            ', '', $Text); + } + // Check for h2 + if (strpos($Text, '[h2]') !== false) { + $Text = preg_replace("(\[h2\](.*?)\[\/h2\])ism", '

            $1

            ', $Text); + $Text = str_replace('
            ', '', $Text); + } + // Check for h3 + if (strpos($Text, '[h3]') !== false) { + $Text = preg_replace("(\[h3\](.*?)\[\/h3\])ism", '

            $1

            ', $Text); + $Text = str_replace('
            ', '', $Text); + } + // Check for h4 + if (strpos($Text, '[h4]') !== false) { + $Text = preg_replace("(\[h4\](.*?)\[\/h4\])ism", '

            $1

            ', $Text); + $Text = str_replace('
            ', '', $Text); + } + // Check for h5 + if (strpos($Text, '[h5]') !== false) { + $Text = preg_replace("(\[h5\](.*?)\[\/h5\])ism", '
            $1
            ', $Text); + $Text = str_replace('
            ', '', $Text); + } + // Check for h6 + if (strpos($Text, '[h6]') !== false) { + $Text = preg_replace("(\[h6\](.*?)\[\/h6\])ism", '
            $1
            ', $Text); + $Text = str_replace('
            ', '', $Text); + } + + // Check for table of content without params + while (strpos($Text, '[toc]') !== false) { + $toc_id = 'toc-' . random_string(10); + $Text = preg_replace("/\[toc\]/ism", '
              ', $Text, 1); + $Text = preg_replace("/\[toc\/\]/ism", '
                ', $Text, 1); + } + // Check for table of content with params + while (strpos($Text, '[toc') !== false) { + $toc_id = 'toc-' . random_string(10); + $Text = preg_replace("/\[toc([^\]]+?)\/\]/ism", '
                  ', $Text, 1); + $Text = preg_replace("/\[toc([^\]]+?)\]/ism", '
                    ', $Text, 1); + } + // Check for centered text + if (strpos($Text, '[/center]') !== false) { + $Text = preg_replace("(\[center\](.*?)\[\/center\])ism", "
                    $1
                    ", $Text); + } + // Check for footer + if (strpos($Text, '[/footer]') !== false) { + $Text = preg_replace("(\[footer\](.*?)\[\/footer\])ism", "
                    $1
                    ", $Text); + } + + // Check for bdi + if (strpos($Text, '[/bdi]') !== false) { + $Text = preg_replace("(\[bdi\](.*?)\[\/bdi\])ism", "$1", $Text); + } + + + // Check for list text + + $Text = preg_replace("/
                    \[\*\]/ism", "[*]", $Text); + $Text = str_replace("[*]", "
                  • ", $Text); + + // handle nested lists + $endlessloop = 0; + + while ( + (((strpos($Text, "[/list]") !== false) && (strpos($Text, "[list") !== false)) || + ((strpos($Text, "[/ol]") !== false) && (strpos($Text, "[ol]") !== false)) || + ((strpos($Text, "[/ul]") !== false) && (strpos($Text, "[ul]") !== false)) || + ((strpos($Text, "[/dl]") !== false) && (strpos($Text, "[dl") !== false)) || + ((strpos($Text, "[/li]") !== false) && (strpos($Text, "[li]") !== false))) && (++$endlessloop < 20) + ) { + $Text = preg_replace("/\[list\](.*?)\[\/list\]/ism", '
                      $1
                    ', $Text); + $Text = preg_replace("/\[list=\](.*?)\[\/list\]/ism", '
                      $1
                    ', $Text); + $Text = preg_replace("/\[list=1\](.*?)\[\/list\]/ism", '
                      $1
                    ', $Text); + $Text = preg_replace("/\[list=((?-i)i)\](.*?)\[\/list\]/ism", '
                      $2
                    ', $Text); + $Text = preg_replace("/\[list=((?-i)I)\](.*?)\[\/list\]/ism", '
                      $2
                    ', $Text); + $Text = preg_replace("/\[list=((?-i)a)\](.*?)\[\/list\]/ism", '
                      $2
                    ', $Text); + $Text = preg_replace("/\[list=((?-i)A)\](.*?)\[\/list\]/ism", '
                      $2
                    ', $Text); + $Text = preg_replace("/\[ul\](.*?)\[\/ul\]/ism", '
                      $1
                    ', $Text); + $Text = preg_replace("/\[ol\](.*?)\[\/ol\]/ism", '
                      $1
                    ', $Text); + $Text = preg_replace("/\[\/li\]
                    \[li\]/ism", '[/li][li]', $Text); + $Text = preg_replace("/\[li\](.*?)\[\/li\]/ism", '
                  • $1
                  • ', $Text); + + // [dl] tags have an optional [dl terms="bi"] form where bold/italic/underline/mono/large + // etc. style may be specified for the "terms" in the definition list. The quotation marks + // are also optional. The regex looks intimidating, but breaks down as: + // "[dl" "]" "[/dl]" + // where optional-termStyles are: "terms=" + $Text = preg_replace_callback('/\[dl[[:space:]]*(?:terms=(?:"|")?([a-zA-Z]+)(?:"|")?)?\](.*?)\[\/dl\]/ism', 'bb_definitionList', $Text); + } + + // Friendica generates this + if (strpos($Text, '[/abstract]') !== false) { + $Text = preg_replace("/\[abstract\](.*?)\[\/abstract\]/ism", '

                    $1

                    ', $Text); + } + + if (strpos($Text, '[checklist]') !== false) { + $Text = preg_replace_callback("/\[checklist\](.*?)\[\/checklist\]/ism", 'bb_checklist', $Text); + } + + + $loop = 0; + while (strpos($Text, '[/table]') !== false && strpos($Text, "[table") !== false && ++$loop < 20) { + $Text = preg_replace("/\[table\](.*?)\[\/table\]/ism", '$1
                    ', $Text); + $Text = preg_replace("/\[table border=1\](.*?)\[\/table\]/ism", '$1
                    ', $Text); + $Text = preg_replace("/\[table border=0\](.*?)\[\/table\]/ism", '$1
                    ', $Text); + } + if (strpos($Text, '[th]') !== false) { + $Text = preg_replace("/\[th\](.*?)\[\/th\]/ism", '$1', $Text); + } + if (strpos($Text, '[td]') !== false) { + $Text = preg_replace("/\[td\](.*?)\[\/td\]/ism", '$1', $Text); + } + if (strpos($Text, '[tr]') !== false) { + $Text = preg_replace("/\[tr\](.*?)\[\/tr\]/ism", '$1', $Text); + } + if (strpos($Text, '[tbody]') !== false) { + $Text = preg_replace("/\[tbody\](.*?)\[\/tbody\]/ism", '$1', $Text); + } + + + $Text = str_replace('
                    ', "\n", $Text); + $Text = str_replace('[hr]', '
                    ', $Text); + + // This is actually executed in prepare_body() + + $Text = str_replace('[nosmile]', '', $Text); + + // Check for font change text + if (strpos($Text, '[/font]') !== false) { + $Text = preg_replace_callback("/\[font=(.*?)\](.*?)\[\/font\]/sm", 'bb_fonttag', $Text); + } + + if (strpos($Text, '[/summary]') !== false) { + $Text = preg_replace_callback("/^(.*?)\[summary\](.*?)\[\/summary\](.*?)$/ism", 'bb_summary', $Text); + } + + // Check for [spoiler] text + $endlessloop = 0; + while ((strpos($Text, "[/spoiler]") !== false) && (strpos($Text, "[spoiler]") !== false) && (++$endlessloop < 20)) { + $Text = preg_replace_callback("/\[spoiler\](.*?)\[\/spoiler\]/ism", 'bb_spoilertag', $Text); + } + + // Check for [spoiler=Author] text + $endlessloop = 0; + while ((strpos($Text, "[/spoiler]") !== false) && (strpos($Text, "[spoiler=") !== false) && (++$endlessloop < 20)) { + $Text = preg_replace_callback("/\[spoiler=(.*?)\](.*?)\[\/spoiler\]/ism", 'bb_spoilertag', $Text); + } + + // Check for [open] text + $endlessloop = 0; + while ((strpos($Text, "[/open]") !== false) && (strpos($Text, "[open]") !== false) && (++$endlessloop < 20)) { + $Text = preg_replace_callback("/\[open\](.*?)\[\/open\]/ism", 'bb_opentag', $Text); + } + + // Check for [open=Title] text + $endlessloop = 0; + while ((strpos($Text, "[/open]") !== false) && (strpos($Text, "[open=") !== false) && (++$endlessloop < 20)) { + $Text = preg_replace_callback("/\[open=(.*?)\](.*?)\[\/open\]/ism", 'bb_opentag', $Text); + } + + + // Declare the format for [quote] layout + $QuoteLayout = '
                    $1
                    '; + + + // Check for [quote] text + // handle nested quotes + $endlessloop = 0; + while ((strpos($Text, "[/quote]") !== false) && (strpos($Text, "[quote]") !== false) && (++$endlessloop < 20)) { + $Text = preg_replace("/\[quote\](.*?)\[\/quote\]/ism", "$QuoteLayout", $Text); + } + + // Check for [quote=Author] text + + $t_wrote = t('$1 wrote:'); + + // handle nested quotes + $endlessloop = 0; + while ((strpos($Text, "[/quote]") !== false) && (strpos($Text, "[quote=") !== false) && (++$endlessloop < 20)) { + $Text = preg_replace( + "/\[quote=[\"\']*(.*?)[\"\']*\](.*?)\[\/quote\]/ism", + "" . $t_wrote . "
                    $2
                    ", + $Text + ); + } + + if ($plain) { + $Text = str_replace([ '
                    ','
                    ' ], [ '“', '”' ], $Text); + } + + + // Images + + // [img]pathtoimage[/img] + if (strpos($Text, '[/img]') !== false) { + $Text = preg_replace("/\[img\](.*?)\[\/img\]/ism", '' . t('Image/photo') . '', $Text); + // Friendica's modified bbcode img tags + $Text = preg_replace("/\[img=http(.*?)\](.*?)\[\/img\]/ism", '' . t('Image/photo') . '', $Text); + } + if (strpos($Text, '[/zmg]') !== false) { + $Text = preg_replace("/\[zmg\](.*?)\[\/zmg\]/ism", '' . t('Image/photo') . '', $Text); + } + + $Text = preg_replace_callback("/\[([zi])mg([ \=])(.*?)\](.*?)\[\/[zi]mg\]/ism", 'bb_imgoptions', $Text); + + if ($censored) { + $Text = separate_img_links($Text); + $Text = preg_replace_callback("/\/ism", "bb_colorbox", $Text); + } + + // style (sanitized) + if (strpos($Text, '[/style]') !== false) { + $Text = preg_replace_callback("(\[style=(.*?)\](.*?)\[\/style\])ism", "bb_sanitize_style", $Text); + } + + // crypt + if (strpos($Text, '[/crypt]') !== false) { + if ($activitypub) { + $Text = preg_replace_callback("/\[crypt (.*?)\](.*?)\[\/crypt\]/ism", 'bb_parse_b64_crypt', $Text); + } else { + $Text = preg_replace_callback("/\[crypt (.*?)\](.*?)\[\/crypt\]/ism", 'bb_parse_crypt', $Text); + } + } + + if (strpos($Text, '[/app]') !== false) { + if ($activitypub) { + $Text = preg_replace_callback("/\[app\](.*?)\[\/app\]/ism", 'bb_parse_app_ap', $Text); + } else { + $Text = preg_replace_callback("/\[app\](.*?)\[\/app\]/ism", 'bb_parse_app', $Text); + } + } + + if (strpos($Text, '[/element]') !== false) { + $Text = preg_replace_callback("/\[element\](.*?)\[\/element\]/ism", 'bb_parse_element', $Text); + } + + // html5 video and audio + if (strpos($Text, '[/video]') !== false) { + $Text = preg_replace_callback("/\[video (.*?)\](.*?)\[\/video\]/ism", 'videowithopts', $Text); + $Text = preg_replace_callback("/\[video\](.*?)\[\/video\]/ism", 'tryzrlvideo', $Text); + } + if (strpos($Text, '[/audio]') !== false) { + $Text = preg_replace_callback("/\[audio\](.*?)\[\/audio\]/ism", 'tryzrlaudio', $Text); + } + if (strpos($Text, '[/zvideo]') !== false) { + $Text = preg_replace_callback("/\[zvideo (.*?)\](.*?)\[\/zvideo\]/ism", 'videowithopts', $Text); + $Text = preg_replace_callback("/\[zvideo\](.*?)\[\/zvideo\]/ism", 'tryzrlvideo', $Text); + } + if (strpos($Text, '[/zaudio]') !== false) { + $Text = preg_replace_callback("/\[zaudio\](.*?)\[\/zaudio\]/ism", 'tryzrlaudio', $Text); + } + + // if video couldn't be embedded, link to it instead. + if (strpos($Text, '[/video]') !== false) { + $Text = preg_replace("/\[video\](.*?)\[\/video\]/", '$1', $Text); + } + if (strpos($Text, '[/audio]') !== false) { + $Text = preg_replace("/\[audio\](.*?)\[\/audio\]/", '$1', $Text); + } + + if (strpos($Text, '[/zvideo]') !== false) { + $Text = preg_replace("/\[zvideo\](.*?)\[\/zvideo\]/", '$1', $Text); + } + if (strpos($Text, '[/zaudio]') !== false) { + $Text = preg_replace("/\[zaudio\](.*?)\[\/zaudio\]/", '$1', $Text); + } + + // SVG stuff + + if ($activitypub) { + $Text = preg_replace_callback("/\[svg(.*?)\](.*?)\[\/svg\]/ism", 'bb_svg_export', $Text); + } else { + $Text = preg_replace_callback("/\[svg(.*?)\](.*?)\[\/svg\]/ism", 'bb_svg', $Text); + } + + // oembed tag + if (! $export) { + $Text = oembed_bbcode2html($Text); + } + + // Avoid triple linefeeds through oembed + $Text = str_replace("


                    ", "

                    ", $Text); + + // If we found an event earlier, strip out all the event code and replace with a reformatted version. + // Replace the event-start section with the entire formatted event. The other bbcode is stripped. + // Summary (e.g. title) is required, earlier revisions only required description (in addition to + // start which is always required). Allow desc with a missing summary for compatibility. + + if ((x($ev, 'desc') || x($ev, 'summary')) && x($ev, 'dtstart')) { + $sub = format_event_html($ev); + + $sub = str_replace('$', "\0", $sub); + + $Text = preg_replace("/\[event\-start\](.*?)\[\/event\-start\]/ism", $sub, $Text); + + $Text = preg_replace("/\[event\](.*?)\[\/event\]/ism", '', $Text); + $Text = preg_replace("/\[event\-summary\](.*?)\[\/event\-summary\]/ism", '', $Text); + $Text = preg_replace("/\[event\-description\](.*?)\[\/event\-description\]/ism", '', $Text); + $Text = preg_replace("/\[event\-finish\](.*?)\[\/event\-finish\]/ism", '', $Text); + $Text = preg_replace("/\[event\-id\](.*?)\[\/event\-id\]/ism", '', $Text); + $Text = preg_replace("/\[event\-location\](.*?)\[\/event\-location\]/ism", '', $Text); + $Text = preg_replace("/\[event\-timezone\](.*?)\[\/event\-timezone\]/ism", '', $Text); + $Text = preg_replace("/\[event\-adjust\](.*?)\[\/event\-adjust\]/ism", '', $Text); + + $Text = str_replace("\0", '$', $Text); + } + + // Unhide all [noparse] contained bbtags unspacefying them + // and triming the [noparse] tag. + if (strpos($Text, '[noparse]') !== false) { + $Text = preg_replace_callback("/\[noparse\](.*?)\[\/noparse\]/ism", 'bb_unspacefy_and_trim', $Text); + } + if (strpos($Text, '[nobb]') !== false) { + $Text = preg_replace_callback("/\[nobb\](.*?)\[\/nobb\]/ism", 'bb_unspacefy_and_trim', $Text); + } + if (strpos($Text, '[pre]') !== false) { + $Text = preg_replace_callback("/\[pre\](.*?)\[\/pre\]/ism", 'bb_unspacefy_and_trim', $Text); + } + + // replace escaped links in code= blocks + $Text = str_replace('%eY9-!', 'http', $Text); + $Text = bb_code_unprotect($Text); + + // This lets you use HTML entities in posts - just wrap them in brackets. For instance [©] to display a copyright symbol. + + $Text = preg_replace('/\[\&\;([#a-z0-9]+)\;\]/', '&$1;', $Text); + + // fix any escaped ampersands that may have been converted into links + + if (strpos($Text, '&') !== false) { + $Text = preg_replace("/\<(.*?)(src|href)=(.*?)\&\;(.*?)\>/ism", '<$1$2=$3&$4>', $Text); + } + + // This is subtle - it's an XSS filter. It only accepts links with a protocol scheme and where + // the scheme begins with z (zhttp), h (http(s)), f (ftp(s)), g (gemini), m (mailto|magnet), t (tel) and named anchors. + // data: urls are allowed if exporting to activitypub which allows inline svg to federate, but not + // to be used for local display + + if ($activitypub) { + $Text = preg_replace("/\<(.*?)(src|href)=\"[^dzghfmt#](.*?)\>/ism", '<$1$2="">', $Text); + } else { + $Text = preg_replace("/\<(.*?)(src|href)=\"[^zhgfmt#](.*?)\>/ism", '<$1$2="">', $Text); + } + + $Text = bb_replace_images($Text, $saved_images); + + $args = [ 'text' => $Text, 'options' => $options ]; + call_hooks('bbcode', $args); + + return $args['text']; } - diff --git a/include/channel.php b/include/channel.php index 1779defd1..fb7d48b60 100644 --- a/include/channel.php +++ b/include/channel.php @@ -1,10 +1,10 @@ false, 'message' => ''); +function identity_check_service_class($account_id) +{ + $ret = array('success' => false, 'message' => ''); - $r = q("select count(channel_id) as total from channel where channel_account_id = %d and channel_removed = 0 ", - intval($account_id) - ); - if(! ($r && count($r))) { - $ret['total_identities'] = 0; - $ret['message'] = t('Unable to obtain identity information from database'); - return $ret; - } + $r = q( + "select count(channel_id) as total from channel where channel_account_id = %d and channel_removed = 0 ", + intval($account_id) + ); + if (! ($r && count($r))) { + $ret['total_identities'] = 0; + $ret['message'] = t('Unable to obtain identity information from database'); + return $ret; + } - $ret['total_identities'] = intval($r[0]['total']); + $ret['total_identities'] = intval($r[0]['total']); - if (! account_service_class_allows($account_id, 'total_identities', $r[0]['total'])) { - $ret['message'] .= upgrade_message(); - return $ret; - } + if (! account_service_class_allows($account_id, 'total_identities', $r[0]['total'])) { + $ret['message'] .= upgrade_message(); + return $ret; + } - $ret['success'] = true; + $ret['success'] = true; - return $ret; + return $ret; } @@ -74,25 +76,29 @@ function identity_check_service_class($account_id) { * @param string $name * @return string describing the error state, or nil return if name is valid */ -function validate_channelname($name) { +function validate_channelname($name) +{ - if (! $name) - return t('Empty name'); + if (! $name) { + return t('Empty name'); + } - if (mb_strlen($name) > 191) - return t('Name too long'); + if (mb_strlen($name) > 191) { + return t('Name too long'); + } - $arr = ['name' => $name]; - /** - * @hooks validate_channelname - * Used to validate the names used by a channel. - * * \e string \b name - * * \e string \b message - return error message - */ - call_hooks('validate_channelname', $arr); + $arr = ['name' => $name]; + /** + * @hooks validate_channelname + * Used to validate the names used by a channel. + * * \e string \b name + * * \e string \b message - return error message + */ + call_hooks('validate_channelname', $arr); - if (x($arr, 'message')) - return $arr['message']; + if (x($arr, 'message')) { + return $arr['message']; + } } @@ -100,101 +106,107 @@ function validate_channelname($name) { * @brief Create a system channel - which has no account attached. * */ -function create_sys_channel() { +function create_sys_channel() +{ - // Ensure that there is a host keypair. + // Ensure that there is a host keypair. - if ((! get_config('system', 'pubkey')) || (! get_config('system', 'prvkey'))) { - $hostkey = Crypto::new_keypair(4096); - set_config('system', 'pubkey', $hostkey['pubkey']); - set_config('system', 'prvkey', $hostkey['prvkey']); - } + if ((! get_config('system', 'pubkey')) || (! get_config('system', 'prvkey'))) { + $hostkey = Crypto::new_keypair(4096); + set_config('system', 'pubkey', $hostkey['pubkey']); + set_config('system', 'prvkey', $hostkey['prvkey']); + } - $sys = get_sys_channel(); + $sys = get_sys_channel(); - if ($sys) { - if (isset($sys['channel_pubkey']) && $sys['channel_pubkey'] && $sys['channel_pubkey'] === get_config('system','pubkey')) { - return; - } - else { - // upgrade the sys channel and return - $pubkey = get_config('system','pubkey'); - $prvkey = get_config('system','prvkey'); - $guid_sig = Libzot::sign($sys['channel_guid'],$prvkey); - $hash = Libzot::make_xchan_hash($sys['channel_guid'],$pubkey); + if ($sys) { + if (isset($sys['channel_pubkey']) && $sys['channel_pubkey'] && $sys['channel_pubkey'] === get_config('system', 'pubkey')) { + return; + } else { + // upgrade the sys channel and return + $pubkey = get_config('system', 'pubkey'); + $prvkey = get_config('system', 'prvkey'); + $guid_sig = Libzot::sign($sys['channel_guid'], $prvkey); + $hash = Libzot::make_xchan_hash($sys['channel_guid'], $pubkey); - q("update channel set channel_guid_sig = '%s', channel_hash = '%s', channel_pubkey = '%s', channel_prvkey = '%s' where channel_id = %d", - dbesc($guid_sig), - dbesc($hash), - dbesc($pubkey), - dbesc($prvkey), - dbesc($sys['channel_id']) - ); + q( + "update channel set channel_guid_sig = '%s', channel_hash = '%s', channel_pubkey = '%s', channel_prvkey = '%s' where channel_id = %d", + dbesc($guid_sig), + dbesc($hash), + dbesc($pubkey), + dbesc($prvkey), + dbesc($sys['channel_id']) + ); - q("update xchan set xchan_guid_sig = '%s', xchan_hash = '%s', xchan_pubkey = '%s', xchan_url = '%s' where xchan_hash = '%s'", - dbesc($guid_sig), - dbesc($hash), - dbesc($pubkey), - dbesc(z_root()), - dbesc($sys['channel_hash']) - ); - q("update hubloc set hubloc_guid_sig = '%s', hubloc_hash = '%s', hubloc_id_url = '%s', hubloc_url_sig = '%s', hubloc_url = '%s', hubloc_callback = '%s', hubloc_site_id = '%s', hubloc_orphancheck = 0, hubloc_error = 0, hubloc_deleted = 0 where hubloc_hash = '%s'", - dbesc($guid_sig), - dbesc($hash), - dbesc(z_root()), - dbesc(Libzot::sign(z_root(),$prvkey)), - dbesc(z_root()), - dbesc(z_root() . '/zot'), - dbesc(Libzot::make_xchan_hash(z_root(),$pubkey)), - dbesc($sys['channel_hash']) - ); + q( + "update xchan set xchan_guid_sig = '%s', xchan_hash = '%s', xchan_pubkey = '%s', xchan_url = '%s' where xchan_hash = '%s'", + dbesc($guid_sig), + dbesc($hash), + dbesc($pubkey), + dbesc(z_root()), + dbesc($sys['channel_hash']) + ); + q( + "update hubloc set hubloc_guid_sig = '%s', hubloc_hash = '%s', hubloc_id_url = '%s', hubloc_url_sig = '%s', hubloc_url = '%s', hubloc_callback = '%s', hubloc_site_id = '%s', hubloc_orphancheck = 0, hubloc_error = 0, hubloc_deleted = 0 where hubloc_hash = '%s'", + dbesc($guid_sig), + dbesc($hash), + dbesc(z_root()), + dbesc(Libzot::sign(z_root(), $prvkey)), + dbesc(z_root()), + dbesc(z_root() . '/zot'), + dbesc(Libzot::make_xchan_hash(z_root(), $pubkey)), + dbesc($sys['channel_hash']) + ); - q("update abook set abook_xchan = '%s' where abook_xchan = '%s'", - dbesc($hash), - dbesc($sys['channel_hash']) - ); + q( + "update abook set abook_xchan = '%s' where abook_xchan = '%s'", + dbesc($hash), + dbesc($sys['channel_hash']) + ); - q("update abconfig set xchan = '%s' where xchan = '%s'", - dbesc($hash), - dbesc($sys['channel_hash']) - ); + q( + "update abconfig set xchan = '%s' where xchan = '%s'", + dbesc($hash), + dbesc($sys['channel_hash']) + ); - return; - } - } + return; + } + } - create_identity([ - 'account_id' => 'xxx', // Typecast trickery: account_id is required. This will create an identity with an (integer) account_id of 0 - 'nickname' => 'sys', - 'name' => 'System', - 'permissions_role' => 'social', - 'pageflags' => 0, - 'publish' => 0, - 'system' => 1 - ]); + create_identity([ + 'account_id' => 'xxx', // Typecast trickery: account_id is required. This will create an identity with an (integer) account_id of 0 + 'nickname' => 'sys', + 'name' => 'System', + 'permissions_role' => 'social', + 'pageflags' => 0, + 'publish' => 0, + 'system' => 1 + ]); } /** * @brief Returns the sys channel. * - * @return array|boolean + * @return array|bool */ -function get_sys_channel() { +function get_sys_channel() +{ - // App::$sys_channel caches this lookup - - if (is_array(App::$sys_channel)) { - return App::$sys_channel; - } - - $r = q("select * from channel left join xchan on channel_hash = xchan_hash where channel_system = 1 limit 1"); + // App::$sys_channel caches this lookup - if ($r) { - App::$sys_channel = array_shift($r); - return App::$sys_channel; - } - return false; + if (is_array(App::$sys_channel)) { + return App::$sys_channel; + } + + $r = q("select * from channel left join xchan on channel_hash = xchan_hash where channel_system = 1 limit 1"); + + if ($r) { + App::$sys_channel = array_shift($r); + return App::$sys_channel; + } + return false; } @@ -202,14 +214,15 @@ function get_sys_channel() { * @brief Checks if $channel_id is sys channel. * * @param int $channel_id - * @return boolean + * @return bool */ -function is_sys_channel($channel_id) { - $s = get_sys_channel(); - if ($s) { - return (intval($s['channel_id']) === intval($channel_id)); - } - return false; +function is_sys_channel($channel_id) +{ + $s = get_sys_channel(); + if ($s) { + return (intval($s['channel_id']) === intval($channel_id)); + } + return false; } @@ -221,13 +234,15 @@ function is_sys_channel($channel_id) { * @returns int|booleean * on error returns boolean false */ -function channel_total() { - $r = q("select channel_id from channel where channel_removed = 0"); +function channel_total() +{ + $r = q("select channel_id from channel where channel_removed = 0"); - if (is_array($r)) - return count($r); + if (is_array($r)) { + return count($r); + } - return false; + return false; } @@ -248,544 +263,571 @@ function channel_total() { * 'message' => optional error text if success is false * 'channel' => if successful the created channel array */ -function create_identity($arr) { +function create_identity($arr) +{ - $ret = array('success' => false); + $ret = array('success' => false); - if(! $arr['account_id']) { - $ret['message'] = t('No account identifier'); - return $ret; - } - $ret = identity_check_service_class($arr['account_id']); - if (!$ret['success']) { - return $ret; - } - // save this for auto_friending - $total_identities = $ret['total_identities']; + if (! $arr['account_id']) { + $ret['message'] = t('No account identifier'); + return $ret; + } + $ret = identity_check_service_class($arr['account_id']); + if (!$ret['success']) { + return $ret; + } + // save this for auto_friending + $total_identities = $ret['total_identities']; - $nick = mb_strtolower(trim($arr['nickname'])); - if(! $nick) { - $ret['message'] = t('Nickname is required.'); - return $ret; - } + $nick = mb_strtolower(trim($arr['nickname'])); + if (! $nick) { + $ret['message'] = t('Nickname is required.'); + return $ret; + } - $name = escape_tags($arr['name']); - $pageflags = ((x($arr,'pageflags')) ? intval($arr['pageflags']) : PAGE_NORMAL); - $system = ((x($arr,'system')) ? intval($arr['system']) : 0); - $name_error = validate_channelname($arr['name']); - if($name_error) { - $ret['message'] = $name_error; - return $ret; - } + $name = escape_tags($arr['name']); + $pageflags = ((x($arr, 'pageflags')) ? intval($arr['pageflags']) : PAGE_NORMAL); + $system = ((x($arr, 'system')) ? intval($arr['system']) : 0); + $name_error = validate_channelname($arr['name']); + if ($name_error) { + $ret['message'] = $name_error; + return $ret; + } - if($nick === 'sys' && (! $system)) { - $ret['message'] = t('Reserved nickname. Please choose another.'); - return $ret; - } + if ($nick === 'sys' && (! $system)) { + $ret['message'] = t('Reserved nickname. Please choose another.'); + return $ret; + } - if(check_webbie(array($nick)) !== $nick) { - $ret['message'] = t('Nickname has unsupported characters or is already being used on this site.'); - return $ret; - } + if (check_webbie(array($nick)) !== $nick) { + $ret['message'] = t('Nickname has unsupported characters or is already being used on this site.'); + return $ret; + } - $guid = Libzot::new_uid($nick); + $guid = Libzot::new_uid($nick); - if ($system) { - $key = [ 'pubkey' => get_config('system','pubkey'), 'prvkey' => get_config('system','prvkey') ]; - } - else { - $key = Crypto::new_keypair(4096); - } - - $sig = Libzot::sign($guid,$key['prvkey']); - $hash = Libzot::make_xchan_hash($guid,$key['pubkey']); + if ($system) { + $key = [ 'pubkey' => get_config('system', 'pubkey'), 'prvkey' => get_config('system', 'prvkey') ]; + } else { + $key = Crypto::new_keypair(4096); + } - // Force a few things on the short term until we can provide a theme or app with choice + $sig = Libzot::sign($guid, $key['prvkey']); + $hash = Libzot::make_xchan_hash($guid, $key['pubkey']); - $publish = 1; + // Force a few things on the short term until we can provide a theme or app with choice - if(array_key_exists('publish', $arr)) - $publish = intval($arr['publish']); + $publish = 1; - $role_permissions = null; - $parent_channel_hash = EMPTY_STR; + if (array_key_exists('publish', $arr)) { + $publish = intval($arr['publish']); + } - if(array_key_exists('permissions_role',$arr) && $arr['permissions_role']) { - $role_permissions = PermissionRoles::role_perms($arr['permissions_role']); - if(isset($role_permissions['channel_type']) && $role_permissions['channel_type'] === 'collection') { - $parent_channel_hash = $arr['parent_hash']; - } - } + $role_permissions = null; + $parent_channel_hash = EMPTY_STR; - if($role_permissions && array_key_exists('directory_publish',$role_permissions)) - $publish = intval($role_permissions['directory_publish']); + if (array_key_exists('permissions_role', $arr) && $arr['permissions_role']) { + $role_permissions = PermissionRoles::role_perms($arr['permissions_role']); + if (isset($role_permissions['channel_type']) && $role_permissions['channel_type'] === 'collection') { + $parent_channel_hash = $arr['parent_hash']; + } + } - $primary = true; + if ($role_permissions && array_key_exists('directory_publish', $role_permissions)) { + $publish = intval($role_permissions['directory_publish']); + } - if(array_key_exists('primary', $arr)) - $primary = intval($arr['primary']); + $primary = true; - $expire = 0; + if (array_key_exists('primary', $arr)) { + $primary = intval($arr['primary']); + } - $r = channel_store_lowlevel( - [ - 'channel_account_id' => intval($arr['account_id']), - 'channel_primary' => intval($primary), - 'channel_name' => $name, - 'channel_parent' => $parent_channel_hash, - 'channel_address' => $nick, - 'channel_guid' => $guid, - 'channel_guid_sig' => $sig, - 'channel_hash' => $hash, - 'channel_prvkey' => $key['prvkey'], - 'channel_pubkey' => $key['pubkey'], - 'channel_pageflags' => intval($pageflags), - 'channel_system' => intval($system), - 'channel_expire_days' => intval($expire), - 'channel_timezone' => App::$timezone - ] - ); + $expire = 0; - $r = q("select * from channel where channel_account_id = %d + $r = channel_store_lowlevel( + [ + 'channel_account_id' => intval($arr['account_id']), + 'channel_primary' => intval($primary), + 'channel_name' => $name, + 'channel_parent' => $parent_channel_hash, + 'channel_address' => $nick, + 'channel_guid' => $guid, + 'channel_guid_sig' => $sig, + 'channel_hash' => $hash, + 'channel_prvkey' => $key['prvkey'], + 'channel_pubkey' => $key['pubkey'], + 'channel_pageflags' => intval($pageflags), + 'channel_system' => intval($system), + 'channel_expire_days' => intval($expire), + 'channel_timezone' => App::$timezone + ] + ); + + $r = q( + "select * from channel where channel_account_id = %d and channel_guid = '%s' limit 1", - intval($arr['account_id']), - dbesc($guid) - ); + intval($arr['account_id']), + dbesc($guid) + ); - if(! $r) { - $ret['message'] = t('Unable to retrieve created identity'); - return $ret; - } + if (! $r) { + $ret['message'] = t('Unable to retrieve created identity'); + return $ret; + } - $a = q("select * from account where account_id = %d", - intval($arr['account_id']) - ); + $a = q( + "select * from account where account_id = %d", + intval($arr['account_id']) + ); - $photo_type = null; + $photo_type = null; - $z = [ - 'account' => $a[0], - 'channel' => $r[0], - 'photo_url' => '' - ]; - /** - * @hooks create_channel_photo - * * \e array \b account - * * \e array \b channel - * * \e string \b photo_url - Return value - */ - call_hooks('create_channel_photo', $z); + $z = [ + 'account' => $a[0], + 'channel' => $r[0], + 'photo_url' => '' + ]; + /** + * @hooks create_channel_photo + * * \e array \b account + * * \e array \b channel + * * \e string \b photo_url - Return value + */ + call_hooks('create_channel_photo', $z); - if($z['photo_url']) { - $photo_type = import_channel_photo_from_url($z['photo_url'],$arr['account_id'],$r[0]['channel_id']); - } + if ($z['photo_url']) { + $photo_type = import_channel_photo_from_url($z['photo_url'], $arr['account_id'], $r[0]['channel_id']); + } - if($role_permissions && array_key_exists('limits',$role_permissions)) - $perm_limits = $role_permissions['limits']; - else - $perm_limits = site_default_perms(); + if ($role_permissions && array_key_exists('limits', $role_permissions)) { + $perm_limits = $role_permissions['limits']; + } else { + $perm_limits = site_default_perms(); + } - foreach($perm_limits as $p => $v) - PermissionLimits::Set($r[0]['channel_id'],$p,$v); + foreach ($perm_limits as $p => $v) { + PermissionLimits::Set($r[0]['channel_id'], $p, $v); + } - if($role_permissions && array_key_exists('perms_auto',$role_permissions)) - set_pconfig($r[0]['channel_id'],'system','autoperms',intval($role_permissions['perms_auto'])); + if ($role_permissions && array_key_exists('perms_auto', $role_permissions)) { + set_pconfig($r[0]['channel_id'], 'system', 'autoperms', intval($role_permissions['perms_auto'])); + } - $ret['channel'] = $r[0]; + $ret['channel'] = $r[0]; - if(intval($arr['account_id'])) - set_default_login_identity($arr['account_id'],$ret['channel']['channel_id'],false); + if (intval($arr['account_id'])) { + set_default_login_identity($arr['account_id'], $ret['channel']['channel_id'], false); + } - // Create a verified hub location pointing to this site. + // Create a verified hub location pointing to this site. - $r = hubloc_store_lowlevel( - [ - 'hubloc_guid' => $guid, - 'hubloc_guid_sig' => $sig, - 'hubloc_id_url' => (($system) ? z_root() : channel_url($ret['channel'])), - 'hubloc_hash' => $hash, - 'hubloc_addr' => channel_reddress($ret['channel']), - 'hubloc_primary' => intval($primary), - 'hubloc_url' => z_root(), - 'hubloc_url_sig' => Libzot::sign(z_root(),$ret['channel']['channel_prvkey']), - 'hubloc_site_id' => Libzot::make_xchan_hash(z_root(),get_config('system','pubkey')), - 'hubloc_host' => App::get_hostname(), - 'hubloc_callback' => z_root() . '/zot', - 'hubloc_sitekey' => get_config('system','pubkey'), - 'hubloc_network' => 'nomad', - 'hubloc_updated' => datetime_convert() - ] - ); - if(! $r) - logger('Unable to store hub location'); + $r = hubloc_store_lowlevel( + [ + 'hubloc_guid' => $guid, + 'hubloc_guid_sig' => $sig, + 'hubloc_id_url' => (($system) ? z_root() : channel_url($ret['channel'])), + 'hubloc_hash' => $hash, + 'hubloc_addr' => channel_reddress($ret['channel']), + 'hubloc_primary' => intval($primary), + 'hubloc_url' => z_root(), + 'hubloc_url_sig' => Libzot::sign(z_root(), $ret['channel']['channel_prvkey']), + 'hubloc_site_id' => Libzot::make_xchan_hash(z_root(), get_config('system', 'pubkey')), + 'hubloc_host' => App::get_hostname(), + 'hubloc_callback' => z_root() . '/zot', + 'hubloc_sitekey' => get_config('system', 'pubkey'), + 'hubloc_network' => 'nomad', + 'hubloc_updated' => datetime_convert() + ] + ); + if (! $r) { + logger('Unable to store hub location'); + } - $newuid = $ret['channel']['channel_id']; + $newuid = $ret['channel']['channel_id']; - $r = xchan_store_lowlevel( - [ - 'xchan_hash' => $hash, - 'xchan_guid' => $guid, - 'xchan_guid_sig' => $sig, - 'xchan_pubkey' => $key['pubkey'], - 'xchan_photo_mimetype' => (($photo_type) ? $photo_type : 'image/png'), - 'xchan_photo_l' => z_root() . "/photo/profile/l/{$newuid}", - 'xchan_photo_m' => z_root() . "/photo/profile/m/{$newuid}", - 'xchan_photo_s' => z_root() . "/photo/profile/s/{$newuid}", - 'xchan_addr' => channel_reddress($ret['channel']), - 'xchan_url' => (($system) ? z_root() : channel_url($ret['channel'])), - 'xchan_follow' => z_root() . '/follow?f=&url=%s', - 'xchan_connurl' => z_root() . '/poco/' . $ret['channel']['channel_address'], - 'xchan_name' => $ret['channel']['channel_name'], - 'xchan_network' => 'nomad', - 'xchan_updated' => datetime_convert(), - 'xchan_photo_date' => datetime_convert(), - 'xchan_name_date' => datetime_convert(), - 'xchan_system' => $system - ] - ); + $r = xchan_store_lowlevel( + [ + 'xchan_hash' => $hash, + 'xchan_guid' => $guid, + 'xchan_guid_sig' => $sig, + 'xchan_pubkey' => $key['pubkey'], + 'xchan_photo_mimetype' => (($photo_type) ? $photo_type : 'image/png'), + 'xchan_photo_l' => z_root() . "/photo/profile/l/{$newuid}", + 'xchan_photo_m' => z_root() . "/photo/profile/m/{$newuid}", + 'xchan_photo_s' => z_root() . "/photo/profile/s/{$newuid}", + 'xchan_addr' => channel_reddress($ret['channel']), + 'xchan_url' => (($system) ? z_root() : channel_url($ret['channel'])), + 'xchan_follow' => z_root() . '/follow?f=&url=%s', + 'xchan_connurl' => z_root() . '/poco/' . $ret['channel']['channel_address'], + 'xchan_name' => $ret['channel']['channel_name'], + 'xchan_network' => 'nomad', + 'xchan_updated' => datetime_convert(), + 'xchan_photo_date' => datetime_convert(), + 'xchan_name_date' => datetime_convert(), + 'xchan_system' => $system + ] + ); - // Not checking return value. - // It's ok for this to fail if it's an imported channel, and therefore the hash is a duplicate + // Not checking return value. + // It's ok for this to fail if it's an imported channel, and therefore the hash is a duplicate - $r = profile_store_lowlevel( - [ - 'aid' => intval($ret['channel']['channel_account_id']), - 'uid' => intval($newuid), - 'profile_guid' => new_uuid(), - 'profile_name' => t('Default Profile'), - 'is_default' => 1, - 'publish' => $publish, - 'fullname' => $ret['channel']['channel_name'], - 'photo' => z_root() . "/photo/profile/l/{$newuid}", - 'thumb' => z_root() . "/photo/profile/m/{$newuid}" - ] - ); + $r = profile_store_lowlevel( + [ + 'aid' => intval($ret['channel']['channel_account_id']), + 'uid' => intval($newuid), + 'profile_guid' => new_uuid(), + 'profile_name' => t('Default Profile'), + 'is_default' => 1, + 'publish' => $publish, + 'fullname' => $ret['channel']['channel_name'], + 'photo' => z_root() . "/photo/profile/l/{$newuid}", + 'thumb' => z_root() . "/photo/profile/m/{$newuid}" + ] + ); - if($role_permissions) { - $myperms = ((array_key_exists('perms_connect',$role_permissions)) ? $role_permissions['perms_connect'] : [] ); - } - else { - $x = PermissionRoles::role_perms('social'); - $myperms = $x['perms_connect']; - } + if ($role_permissions) { + $myperms = ((array_key_exists('perms_connect', $role_permissions)) ? $role_permissions['perms_connect'] : [] ); + } else { + $x = PermissionRoles::role_perms('social'); + $myperms = $x['perms_connect']; + } - $r = abook_store_lowlevel( - [ - 'abook_account' => intval($ret['channel']['channel_account_id']), - 'abook_channel' => intval($newuid), - 'abook_xchan' => $hash, - 'abook_closeness' => 0, - 'abook_created' => datetime_convert(), - 'abook_updated' => datetime_convert(), - 'abook_self' => 1 - ] - ); + $r = abook_store_lowlevel( + [ + 'abook_account' => intval($ret['channel']['channel_account_id']), + 'abook_channel' => intval($newuid), + 'abook_xchan' => $hash, + 'abook_closeness' => 0, + 'abook_created' => datetime_convert(), + 'abook_updated' => datetime_convert(), + 'abook_self' => 1 + ] + ); - $x = Permissions::serialise(Permissions::FilledPerms($myperms)); - set_abconfig($newuid,$hash,'system','my_perms',$x); + $x = Permissions::serialise(Permissions::FilledPerms($myperms)); + set_abconfig($newuid, $hash, 'system', 'my_perms', $x); - if(intval($ret['channel']['channel_account_id'])) { + if (intval($ret['channel']['channel_account_id'])) { + // Save our permissions role so we can perhaps call it up and modify it later. - // Save our permissions role so we can perhaps call it up and modify it later. + if ($role_permissions) { + set_pconfig($newuid, 'system', 'permissions_role', $arr['permissions_role']); + if (array_key_exists('online', $role_permissions)) { + set_pconfig($newuid, 'system', 'hide_presence', 1 - intval($role_permissions['online'])); + } + if (array_key_exists('perms_auto', $role_permissions)) { + $autoperms = intval($role_permissions['perms_auto']); + set_pconfig($newuid, 'system', 'autoperms', $autoperms); + } + } - if($role_permissions) { - set_pconfig($newuid,'system','permissions_role',$arr['permissions_role']); - if(array_key_exists('online',$role_permissions)) - set_pconfig($newuid,'system','hide_presence',1-intval($role_permissions['online'])); - if(array_key_exists('perms_auto',$role_permissions)) { - $autoperms = intval($role_permissions['perms_auto']); - set_pconfig($newuid,'system','autoperms',$autoperms); - } - } + // Create a group with yourself as a member. This allows somebody to use it + // right away as a default group for new contacts. - // Create a group with yourself as a member. This allows somebody to use it - // right away as a default group for new contacts. + $group_hash = AccessList::add($newuid, t('Friends')); + if ($group_hash) { + AccessList::member_add($newuid, t('Friends'), $ret['channel']['channel_hash']); - $group_hash = AccessList::add($newuid, t('Friends')); - if ($group_hash) { - AccessList::member_add($newuid,t('Friends'),$ret['channel']['channel_hash']); + // if our role_permissions indicate that we're using a default collection ACL, add it. - // if our role_permissions indicate that we're using a default collection ACL, add it. + if (is_array($role_permissions) && $role_permissions['default_collection']) { + $default_collection_str = '<' . $group_hash . '>'; + } + q( + "update channel set channel_default_group = '%s', channel_allow_gid = '%s' where channel_id = %d", + dbesc($group_hash), + dbesc(($default_collection_str) ? $default_collection_str : EMPTY_STR), + intval($newuid) + ); + } - if(is_array($role_permissions) && $role_permissions['default_collection']) { - $default_collection_str = '<' . $group_hash . '>'; - } - q("update channel set channel_default_group = '%s', channel_allow_gid = '%s' where channel_id = %d", - dbesc($group_hash), - dbesc(($default_collection_str) ? $default_collection_str : EMPTY_STR), - intval($newuid) - ); - } + set_pconfig($ret['channel']['channel_id'], 'system', 'photo_path', '%Y/%Y-%m'); + set_pconfig($ret['channel']['channel_id'], 'system', 'attach_path', '%Y/%Y-%m'); - set_pconfig($ret['channel']['channel_id'],'system','photo_path', '%Y/%Y-%m'); - set_pconfig($ret['channel']['channel_id'],'system','attach_path','%Y/%Y-%m'); - - // If this channel has a parent, auto follow them. + // If this channel has a parent, auto follow them. - if($parent_channel_hash) { - $ch = channelx_by_hash($parent_channel_hash); - if($ch) { - connect_and_sync($ret['channel'],channel_reddress($ch), true); - } - } + if ($parent_channel_hash) { + $ch = channelx_by_hash($parent_channel_hash); + if ($ch) { + connect_and_sync($ret['channel'], channel_reddress($ch), true); + } + } - // auto-follow any of the hub's pre-configured channel choices. - // Only do this if it's the first channel for this account; - // otherwise it could get annoying. Don't make this list too big - // or it will impact registration time. + // auto-follow any of the hub's pre-configured channel choices. + // Only do this if it's the first channel for this account; + // otherwise it could get annoying. Don't make this list too big + // or it will impact registration time. - $accts = get_config('system','auto_follow'); - if(($accts) && (! $total_identities)) { - if(! is_array($accts)) - $accts = array($accts); + $accts = get_config('system', 'auto_follow'); + if (($accts) && (! $total_identities)) { + if (! is_array($accts)) { + $accts = array($accts); + } - foreach($accts as $acct) { - if(trim($acct)) { - $f = connect_and_sync($ret['channel'],trim($acct)); - if($f['success']) { + foreach ($accts as $acct) { + if (trim($acct)) { + $f = connect_and_sync($ret['channel'], trim($acct)); + if ($f['success']) { + $can_view_stream = their_perms_contains($ret['channel']['channel_id'], $f['abook']['abook_xchan'], 'view_stream'); - $can_view_stream = their_perms_contains($ret['channel']['channel_id'],$f['abook']['abook_xchan'],'view_stream'); + // If we can view their stream, pull in some posts - // If we can view their stream, pull in some posts + if (($can_view_stream) || ($f['abook']['xchan_network'] === 'rss')) { + Run::Summon([ 'Onepoll',$f['abook']['abook_id'] ]); + } + } + } + } + } - if(($can_view_stream) || ($f['abook']['xchan_network'] === 'rss')) { - Run::Summon([ 'Onepoll',$f['abook']['abook_id'] ]); - } - } - } - } + /** + * @hooks create_identity + * Called when creating a channel. + * * \e int - The UID of the created identity + */ - } + call_hooks('create_identity', $newuid); - /** - * @hooks create_identity - * Called when creating a channel. - * * \e int - The UID of the created identity - */ + Run::Summon([ 'Directory', $ret['channel']['channel_id'] ]); + } - call_hooks('create_identity', $newuid); - - Run::Summon( [ 'Directory', $ret['channel']['channel_id'] ] ); - } - - $ret['success'] = true; - return $ret; + $ret['success'] = true; + return $ret; } -function connect_and_sync($channel,$address, $sub_channel = false) { +function connect_and_sync($channel, $address, $sub_channel = false) +{ - if ((! $channel) || (! $address)) { - return false; - } + if ((! $channel) || (! $address)) { + return false; + } - $f = Connect::connect($channel,$address, $sub_channel); - if ($f['success']) { - $clone = []; - foreach ($f['abook'] as $k => $v) { - if (strpos($k,'abook_') === 0) { - $clone[$k] = $v; - } - } - unset($clone['abook_id']); - unset($clone['abook_account']); - unset($clone['abook_channel']); - - $abconfig = load_abconfig($channel['channel_id'],$clone['abook_xchan']); - if ($abconfig) { - $clone['abconfig'] = $abconfig; - } + $f = Connect::connect($channel, $address, $sub_channel); + if ($f['success']) { + $clone = []; + foreach ($f['abook'] as $k => $v) { + if (strpos($k, 'abook_') === 0) { + $clone[$k] = $v; + } + } + unset($clone['abook_id']); + unset($clone['abook_account']); + unset($clone['abook_channel']); - Libsync::build_sync_packet($channel['channel_id'], [ 'abook' => [ $clone ] ], true); - return $f; - } - return false; -} + $abconfig = load_abconfig($channel['channel_id'], $clone['abook_xchan']); + if ($abconfig) { + $clone['abconfig'] = $abconfig; + } - -function change_channel_keys($channel) { - - $ret = array('success' => false); - - $stored = []; - - $key = Crypto::new_keypair(4096); - - $sig = Libzot::sign($channel['channel_guid'],$key['prvkey']); - $hash = Libzot::make_xchan_hash($channel['channel_guid'],$channel['channel_pubkey']); - - $stored['old_guid'] = $channel['channel_guid']; - $stored['old_guid_sig'] = $channel['channel_guid_sig']; - $stored['old_key'] = $channel['channel_pubkey']; - $stored['old_hash'] = $channel['channel_hash']; - - $stored['new_key'] = $key['pubkey']; - $stored['new_sig'] = Libzot::sign($key['pubkey'],$channel['channel_prvkey']); - - // Save this info for the notifier to collect - - set_pconfig($channel['channel_id'],'system','keychange',$stored); - - $r = q("update channel set channel_prvkey = '%s', channel_pubkey = '%s', channel_guid_sig = '%s', channel_hash = '%s' where channel_id = %d", - dbesc($key['prvkey']), - dbesc($key['pubkey']), - dbesc($sig), - dbesc($hash), - intval($channel['channel_id']) - ); - if (! $r) { - return $ret; - } - - $r = q("select * from channel where channel_id = %d", - intval($channel['channel_id']) - ); - - if (! $r) { - $ret['message'] = t('Unable to retrieve modified identity'); - return $ret; - } - - $modified = $r[0]; - - $h = q("select * from hubloc where hubloc_hash = '%s' and hubloc_url = '%s' ", - dbesc($stored['old_hash']), - dbesc(z_root()) - ); - - if($h) { - foreach($h as $hv) { - $hv['hubloc_guid_sig'] = $sig; - $hv['hubloc_hash'] = $hash; - $hv['hubloc_url_sig'] = Libzot::sign(z_root(),$modifed['channel_prvkey']); - hubloc_store_lowlevel($hv); - } - } - - $x = q("select * from xchan where xchan_hash = '%s' ", - dbesc($stored['old_hash']) - ); - - $check = q("select * from xchan where xchan_hash = '%s'", - dbesc($hash) - ); - - if (($x) && (! $check)) { - $oldxchan = $x[0]; - foreach ($x as $xv) { - $xv['xchan_guid_sig'] = $sig; - $xv['xchan_hash'] = $hash; - $xv['xchan_pubkey'] = $key['pubkey']; - $xv['xchan_updated'] = datetime_convert(); - xchan_store_lowlevel($xv); - $newxchan = $xv; - } - } - - Libsync::build_sync_packet($channel['channel_id'], [ 'keychange' => $stored ]); - - $a = q("select * from abook where abook_xchan = '%s' and abook_self = 1", - dbesc($stored['old_hash']) - ); - - if ($a) { - q("update abook set abook_xchan = '%s' where abook_id = %d", - dbesc($hash), - intval($a[0]['abook_id']) - ); - } - - xchan_change_key($oldxchan,$newxchan,$stored); - - Run::Summon([ 'Notifier', 'keychange', $channel['channel_id'] ]); - - $ret['success'] = true; - return $ret; + Libsync::build_sync_packet($channel['channel_id'], [ 'abook' => [ $clone ] ], true); + return $f; + } + return false; } -function channel_change_address($channel,$new_address) { - $ret = array('success' => false); +function change_channel_keys($channel) +{ - $old_address = $channel['channel_address']; + $ret = array('success' => false); - if ($new_address === 'sys') { - $ret['message'] = t('Reserved nickname. Please choose another.'); - return $ret; - } + $stored = []; - if (check_webbie(array($new_address)) !== $new_address) { - $ret['message'] = t('Nickname has unsupported characters or is already being used on this site.'); - return $ret; - } + $key = Crypto::new_keypair(4096); - $r = q("update channel set channel_address = '%s' where channel_id = %d", - dbesc($new_address), - intval($channel['channel_id']) - ); - if (! $r) { - return $ret; - } + $sig = Libzot::sign($channel['channel_guid'], $key['prvkey']); + $hash = Libzot::make_xchan_hash($channel['channel_guid'], $channel['channel_pubkey']); - $r = q("select * from channel where channel_id = %d", - intval($channel['channel_id']) - ); + $stored['old_guid'] = $channel['channel_guid']; + $stored['old_guid_sig'] = $channel['channel_guid_sig']; + $stored['old_key'] = $channel['channel_pubkey']; + $stored['old_hash'] = $channel['channel_hash']; - if (! $r) { - $ret['message'] = t('Unable to retrieve modified identity'); - return $ret; - } + $stored['new_key'] = $key['pubkey']; + $stored['new_sig'] = Libzot::sign($key['pubkey'], $channel['channel_prvkey']); - $r = q("update xchan set xchan_addr = '%s' where xchan_hash = '%s'", - dbesc($new_address . '@' . App::get_hostname()), - dbesc($channel['channel_hash']) - ); + // Save this info for the notifier to collect - $h = q("select * from hubloc where hubloc_hash = '%s' and hubloc_url = '%s' ", - dbesc($channel['channel_hash']), - dbesc(z_root()) - ); + set_pconfig($channel['channel_id'], 'system', 'keychange', $stored); - if ($h) { - foreach ($h as $hv) { - if ($hv['hubloc_primary']) { - q("update hubloc set hubloc_primary = 0 where hubloc_id = %d", - intval($hv['hubloc_id']) - ); - } - q("update hubloc set hubloc_deleted = 1 where hubloc_id = %d", - intval($hv['hubloc_id']) - ); + $r = q( + "update channel set channel_prvkey = '%s', channel_pubkey = '%s', channel_guid_sig = '%s', channel_hash = '%s' where channel_id = %d", + dbesc($key['prvkey']), + dbesc($key['pubkey']), + dbesc($sig), + dbesc($hash), + intval($channel['channel_id']) + ); + if (! $r) { + return $ret; + } - unset($hv['hubloc_id']); - $hv['hubloc_addr'] = $new_address . '@' . App::get_hostname(); - hubloc_store_lowlevel($hv); - } - } + $r = q( + "select * from channel where channel_id = %d", + intval($channel['channel_id']) + ); - // fix apps which were stored with the actual name rather than a macro + if (! $r) { + $ret['message'] = t('Unable to retrieve modified identity'); + return $ret; + } - $r = q("select * from app where app_channel = %d and app_system = 1", - intval($channel['channel_id']) - ); - if ($r) { - foreach ($r as $rv) { - $replace = preg_replace('/([\=\/])(' . $old_address . ')($|[\%\/])/ism','$1' . $new_address . '$3',$rv['app_url']); - if ($replace != $rv['app_url']) { - q("update app set app_url = '%s' where id = %d", - dbesc($replace), - intval($rv['id']) - ); - } - } - } + $modified = $r[0]; - Run::Summon( [ 'Notifier', 'refresh_all', $channel['channel_id'] ] ); + $h = q( + "select * from hubloc where hubloc_hash = '%s' and hubloc_url = '%s' ", + dbesc($stored['old_hash']), + dbesc(z_root()) + ); - $ret['success'] = true; - return $ret; + if ($h) { + foreach ($h as $hv) { + $hv['hubloc_guid_sig'] = $sig; + $hv['hubloc_hash'] = $hash; + $hv['hubloc_url_sig'] = Libzot::sign(z_root(), $modifed['channel_prvkey']); + hubloc_store_lowlevel($hv); + } + } + + $x = q( + "select * from xchan where xchan_hash = '%s' ", + dbesc($stored['old_hash']) + ); + + $check = q( + "select * from xchan where xchan_hash = '%s'", + dbesc($hash) + ); + + if (($x) && (! $check)) { + $oldxchan = $x[0]; + foreach ($x as $xv) { + $xv['xchan_guid_sig'] = $sig; + $xv['xchan_hash'] = $hash; + $xv['xchan_pubkey'] = $key['pubkey']; + $xv['xchan_updated'] = datetime_convert(); + xchan_store_lowlevel($xv); + $newxchan = $xv; + } + } + + Libsync::build_sync_packet($channel['channel_id'], [ 'keychange' => $stored ]); + + $a = q( + "select * from abook where abook_xchan = '%s' and abook_self = 1", + dbesc($stored['old_hash']) + ); + + if ($a) { + q( + "update abook set abook_xchan = '%s' where abook_id = %d", + dbesc($hash), + intval($a[0]['abook_id']) + ); + } + + xchan_change_key($oldxchan, $newxchan, $stored); + + Run::Summon([ 'Notifier', 'keychange', $channel['channel_id'] ]); + + $ret['success'] = true; + return $ret; +} + +function channel_change_address($channel, $new_address) +{ + + $ret = array('success' => false); + + $old_address = $channel['channel_address']; + + if ($new_address === 'sys') { + $ret['message'] = t('Reserved nickname. Please choose another.'); + return $ret; + } + + if (check_webbie(array($new_address)) !== $new_address) { + $ret['message'] = t('Nickname has unsupported characters or is already being used on this site.'); + return $ret; + } + + $r = q( + "update channel set channel_address = '%s' where channel_id = %d", + dbesc($new_address), + intval($channel['channel_id']) + ); + if (! $r) { + return $ret; + } + + $r = q( + "select * from channel where channel_id = %d", + intval($channel['channel_id']) + ); + + if (! $r) { + $ret['message'] = t('Unable to retrieve modified identity'); + return $ret; + } + + $r = q( + "update xchan set xchan_addr = '%s' where xchan_hash = '%s'", + dbesc($new_address . '@' . App::get_hostname()), + dbesc($channel['channel_hash']) + ); + + $h = q( + "select * from hubloc where hubloc_hash = '%s' and hubloc_url = '%s' ", + dbesc($channel['channel_hash']), + dbesc(z_root()) + ); + + if ($h) { + foreach ($h as $hv) { + if ($hv['hubloc_primary']) { + q( + "update hubloc set hubloc_primary = 0 where hubloc_id = %d", + intval($hv['hubloc_id']) + ); + } + q( + "update hubloc set hubloc_deleted = 1 where hubloc_id = %d", + intval($hv['hubloc_id']) + ); + + unset($hv['hubloc_id']); + $hv['hubloc_addr'] = $new_address . '@' . App::get_hostname(); + hubloc_store_lowlevel($hv); + } + } + + // fix apps which were stored with the actual name rather than a macro + + $r = q( + "select * from app where app_channel = %d and app_system = 1", + intval($channel['channel_id']) + ); + if ($r) { + foreach ($r as $rv) { + $replace = preg_replace('/([\=\/])(' . $old_address . ')($|[\%\/])/ism', '$1' . $new_address . '$3', $rv['app_url']); + if ($replace != $rv['app_url']) { + q( + "update app set app_url = '%s' where id = %d", + dbesc($replace), + intval($rv['id']) + ); + } + } + } + + Run::Summon([ 'Notifier', 'refresh_all', $channel['channel_id'] ]); + + $ret['success'] = true; + return $ret; } @@ -796,22 +838,25 @@ function channel_change_address($channel,$new_address) { * login account * @param int $channel_id * channel id to set as default for this account - * @param boolean $force (optional) default true + * @param bool $force (optional) default true * - if true, set this default unconditionally * - if $force is false only do this if there is no existing default */ -function set_default_login_identity($account_id, $channel_id, $force = true) { - $r = q("select account_default_channel from account where account_id = %d limit 1", - intval($account_id) - ); - if ($r) { - if ((intval($r[0]['account_default_channel']) == 0) || ($force)) { - $r = q("update account set account_default_channel = %d where account_id = %d", - intval($channel_id), - intval($account_id) - ); - } - } +function set_default_login_identity($account_id, $channel_id, $force = true) +{ + $r = q( + "select account_default_channel from account where account_id = %d limit 1", + intval($account_id) + ); + if ($r) { + if ((intval($r[0]['account_default_channel']) == 0) || ($force)) { + $r = q( + "update account set account_default_channel = %d where account_id = %d", + intval($channel_id), + intval($account_id) + ); + } + } } /** @@ -819,25 +864,26 @@ function set_default_login_identity($account_id, $channel_id, $force = true) { * * @return array with default section names to export */ -function get_default_export_sections() { - $sections = [ - 'channel', - 'connections', - 'config', - 'apps', - 'chatrooms', - 'events' - ]; +function get_default_export_sections() +{ + $sections = [ + 'channel', + 'connections', + 'config', + 'apps', + 'chatrooms', + 'events' + ]; - $cb = [ 'sections' => $sections ]; - /** - * @hooks get_default_export_sections - * Called to get the default list of functional data groups to export in identity_basic_export(). - * * \e array \b sections - return value - */ - call_hooks('get_default_export_sections', $cb); + $cb = [ 'sections' => $sections ]; + /** + * @hooks get_default_export_sections + * Called to get the default list of functional data groups to export in identity_basic_export(). + * * \e array \b sections - return value + */ + call_hooks('get_default_export_sections', $cb); - return $cb['sections']; + return $cb['sections']; } @@ -853,273 +899,293 @@ function get_default_export_sections() { * @return array * See function for details */ -function identity_basic_export($channel_id, $sections = null) { +function identity_basic_export($channel_id, $sections = null) +{ - /* - * basic channel export - */ + /* + * basic channel export + */ - if(! $sections) { - $sections = get_default_export_sections(); - } + if (! $sections) { + $sections = get_default_export_sections(); + } - $ret = []; + $ret = []; - // use constants here as otherwise we will have no idea if we can import from a site - // with a non-standard platform and version. + // use constants here as otherwise we will have no idea if we can import from a site + // with a non-standard platform and version. - $ret['compatibility'] = [ - 'project' => PLATFORM_NAME, - 'codebase' => 'zap', - 'version' => STD_VERSION, - 'database' => DB_UPDATE_VERSION - ]; + $ret['compatibility'] = [ + 'project' => PLATFORM_NAME, + 'codebase' => 'zap', + 'version' => STD_VERSION, + 'database' => DB_UPDATE_VERSION + ]; - /* - * Process channel information regardless of it is one of the sections desired - * because we need the channel relocation information in all export files/streams. - */ + /* + * Process channel information regardless of it is one of the sections desired + * because we need the channel relocation information in all export files/streams. + */ - $r = q("select * from channel where channel_id = %d limit 1", - intval($channel_id) - ); - if ($r) { - $ret['relocate'] = [ 'channel_address' => $r[0]['channel_address'], 'url' => z_root()]; - if (in_array('channel',$sections)) { - $ret['channel'] = $r[0]; - unset($ret['channel']['channel_password']); - unset($ret['channel']['channel_salt']); - } - } + $r = q( + "select * from channel where channel_id = %d limit 1", + intval($channel_id) + ); + if ($r) { + $ret['relocate'] = [ 'channel_address' => $r[0]['channel_address'], 'url' => z_root()]; + if (in_array('channel', $sections)) { + $ret['channel'] = $r[0]; + unset($ret['channel']['channel_password']); + unset($ret['channel']['channel_salt']); + } + } - if (in_array('channel',$sections) || in_array('profile',$sections)) { - $r = q("select * from profile where uid = %d", - intval($channel_id) - ); - if ($r) { - $ret['profile'] = $r; - } + if (in_array('channel', $sections) || in_array('profile', $sections)) { + $r = q( + "select * from profile where uid = %d", + intval($channel_id) + ); + if ($r) { + $ret['profile'] = $r; + } - $r = q("select mimetype, content, os_storage from photo + $r = q( + "select mimetype, content, os_storage from photo where imgscale = 4 and photo_usage = %d and uid = %d limit 1", - intval(PHOTO_PROFILE), - intval($channel_id) - ); + intval(PHOTO_PROFILE), + intval($channel_id) + ); - if ($r) { - $ret['photo'] = [ - 'type' => $r[0]['mimetype'], - 'data' => (($r[0]['os_storage']) - ? base64url_encode(file_get_contents($r[0]['content'])) : base64url_encode(dbunescbin($r[0]['content']))) - ]; - } - } + if ($r) { + $ret['photo'] = [ + 'type' => $r[0]['mimetype'], + 'data' => (($r[0]['os_storage']) + ? base64url_encode(file_get_contents($r[0]['content'])) : base64url_encode(dbunescbin($r[0]['content']))) + ]; + } + } - if (in_array('connections',$sections)) { - $r = q("select * from atoken where atoken_uid = %d", - intval($channel_id) - ); - if ($r) { - $ret['atoken'] = $r; - } - - $xchans = []; - $r = q("select * from abook where abook_channel = %d ", - intval($channel_id) - ); - if ($r) { - $ret['abook'] = $r; + if (in_array('connections', $sections)) { + $r = q( + "select * from atoken where atoken_uid = %d", + intval($channel_id) + ); + if ($r) { + $ret['atoken'] = $r; + } - for ($x = 0; $x < count($ret['abook']); $x ++) { - $xchans[] = $ret['abook'][$x]['abook_xchan']; - $abconfig = load_abconfig($channel_id,$ret['abook'][$x]['abook_xchan']); - if ($abconfig) { - $ret['abook'][$x]['abconfig'] = $abconfig; - } - } - stringify_array_elms($xchans); - } + $xchans = []; + $r = q( + "select * from abook where abook_channel = %d ", + intval($channel_id) + ); + if ($r) { + $ret['abook'] = $r; - if($xchans) { - $r = q("select * from xchan where xchan_hash in ( " . implode(',',$xchans) . " ) "); - if ($r) { - $ret['xchan'] = $r; - } + for ($x = 0; $x < count($ret['abook']); $x++) { + $xchans[] = $ret['abook'][$x]['abook_xchan']; + $abconfig = load_abconfig($channel_id, $ret['abook'][$x]['abook_xchan']); + if ($abconfig) { + $ret['abook'][$x]['abconfig'] = $abconfig; + } + } + stringify_array_elms($xchans); + } - $r = q("select * from hubloc where hubloc_hash in ( " . implode(',',$xchans) . " ) "); - if ($r) { - $ret['hubloc'] = $r; - } - } + if ($xchans) { + $r = q("select * from xchan where xchan_hash in ( " . implode(',', $xchans) . " ) "); + if ($r) { + $ret['xchan'] = $r; + } - $r = q("select * from pgrp where uid = %d ", - intval($channel_id) - ); + $r = q("select * from hubloc where hubloc_hash in ( " . implode(',', $xchans) . " ) "); + if ($r) { + $ret['hubloc'] = $r; + } + } - if ($r) { - $ret['group'] = $r; - } + $r = q( + "select * from pgrp where uid = %d ", + intval($channel_id) + ); - $r = q("select * from pgrp_member where uid = %d ", - intval($channel_id) - ); - if ($r) { - $ret['group_member'] = $r; - } + if ($r) { + $ret['group'] = $r; + } - $r = q("select * from xign where uid = %d ", - intval($channel_id) - ); - if ($r) { - $ret['xign'] = $r; - } + $r = q( + "select * from pgrp_member where uid = %d ", + intval($channel_id) + ); + if ($r) { + $ret['group_member'] = $r; + } - $r = q("select * from block where block_channel_id = %d ", - intval($channel_id) - ); - if ($r) { - $ret['block'] = $r; - } + $r = q( + "select * from xign where uid = %d ", + intval($channel_id) + ); + if ($r) { + $ret['xign'] = $r; + } + $r = q( + "select * from block where block_channel_id = %d ", + intval($channel_id) + ); + if ($r) { + $ret['block'] = $r; + } + } - } + if (in_array('config', $sections)) { + $r = q( + "select * from pconfig where uid = %d", + intval($channel_id) + ); + if ($r) { + $ret['config'] = $r; + } - if (in_array('config',$sections)) { - $r = q("select * from pconfig where uid = %d", - intval($channel_id) - ); - if ($r) { - $ret['config'] = $r; - } + // All other term types will be included in items, if requested. - // All other term types will be included in items, if requested. + $r = q( + "select * from term where ttype in (%d,%d) and uid = %d", + intval(TERM_SAVEDSEARCH), + intval(TERM_THING), + intval($channel_id) + ); + if ($r) { + $ret['term'] = $r; + } + // add psuedo-column obj_baseurl to aid in relocations - $r = q("select * from term where ttype in (%d,%d) and uid = %d", - intval(TERM_SAVEDSEARCH), - intval(TERM_THING), - intval($channel_id) - ); - if ($r) { - $ret['term'] = $r; - } - // add psuedo-column obj_baseurl to aid in relocations + $r = q( + "select obj.*, '%s' as obj_baseurl from obj where obj_channel = %d", + dbesc(z_root()), + intval($channel_id) + ); - $r = q("select obj.*, '%s' as obj_baseurl from obj where obj_channel = %d", - dbesc(z_root()), - intval($channel_id) - ); + if ($r) { + $ret['obj'] = $r; + } - if ($r) { - $ret['obj'] = $r; - } + $r = q( + "select * from likes where channel_id = %d", + intval($channel_id) + ); - $r = q("select * from likes where channel_id = %d", - intval($channel_id) - ); + if ($r) { + $ret['likes'] = $r; + } + } - if ($r) { - $ret['likes'] = $r; - } - } + if (in_array('apps', $sections)) { + $r = q( + "select * from app where app_channel = %d and app_system = 0", + intval($channel_id) + ); + if ($r) { + for ($x = 0; $x < count($r); $x++) { + $r[$x]['term'] = q( + "select * from term where otype = %d and oid = %d", + intval(TERM_OBJ_APP), + intval($r[$x]['id']) + ); + } + $ret['app'] = $r; + } + $r = q( + "select * from app where app_channel = %d and app_system = 1", + intval($channel_id) + ); + if ($r) { + for ($x = 0; $x < count($r); $x++) { + $r[$x]['term'] = q( + "select * from term where otype = %d and oid = %d", + intval(TERM_OBJ_APP), + intval($r[$x]['id']) + ); + } + $ret['sysapp'] = $r; + } + } - if (in_array('apps',$sections)) { - $r = q("select * from app where app_channel = %d and app_system = 0", - intval($channel_id) - ); - if ($r) { - for ($x = 0; $x < count($r); $x ++) { - $r[$x]['term'] = q("select * from term where otype = %d and oid = %d", - intval(TERM_OBJ_APP), - intval($r[$x]['id']) - ); - } - $ret['app'] = $r; - } - $r = q("select * from app where app_channel = %d and app_system = 1", - intval($channel_id) - ); - if ($r) { - for ($x = 0; $x < count($r); $x ++) { - $r[$x]['term'] = q("select * from term where otype = %d and oid = %d", - intval(TERM_OBJ_APP), - intval($r[$x]['id']) - ); - } - $ret['sysapp'] = $r; - } - } + if (in_array('chatrooms', $sections)) { + $r = q( + "select * from chatroom where cr_uid = %d", + intval($channel_id) + ); + if ($r) { + $ret['chatroom'] = $r; + } + } - if (in_array('chatrooms',$sections)) { - $r = q("select * from chatroom where cr_uid = %d", - intval($channel_id) - ); - if ($r) { - $ret['chatroom'] = $r; - } - } + if (in_array('events', $sections)) { + $r = q( + "select * from event where uid = %d", + intval($channel_id) + ); + if ($r) { + $ret['event'] = $r; + } - if (in_array('events',$sections)) { - $r = q("select * from event where uid = %d", - intval($channel_id) - ); - if ($r) { - $ret['event'] = $r; - } + $r = q( + "select * from item where resource_type = 'event' and uid = %d", + intval($channel_id) + ); + if ($r) { + $ret['event_item'] = []; + xchan_query($r); + $r = fetch_post_tags($r, true); + foreach ($r as $rr) { + $ret['event_item'][] = encode_item($rr, true); + } + } + } - $r = q("select * from item where resource_type = 'event' and uid = %d", - intval($channel_id) - ); - if ($r) { - $ret['event_item'] = []; - xchan_query($r); - $r = fetch_post_tags($r,true); - foreach ($r as $rr) { - $ret['event_item'][] = encode_item($rr,true); - } - } - } + if (in_array('items', $sections)) { + /** @warning this may run into memory limits on smaller systems */ - if (in_array('items', $sections)) { - /** @warning this may run into memory limits on smaller systems */ + /** export three months of posts. If you want to export and import all posts you have to start with + * the first year and export/import them in ascending order. + * + * Don't export linked resource items. we'll have to pull those out separately. + */ - /** export three months of posts. If you want to export and import all posts you have to start with - * the first year and export/import them in ascending order. - * - * Don't export linked resource items. we'll have to pull those out separately. - */ - - $r = q("select * from item where item_wall = 1 and item_deleted = 0 and uid = %d + $r = q( + "select * from item where item_wall = 1 and item_deleted = 0 and uid = %d and created > %s - INTERVAL %s and resource_type = '' order by created", - intval($channel_id), - db_utcnow(), - db_quoteinterval('3 MONTH') - ); - if ($r) { - $ret['item'] = []; - xchan_query($r); - $r = fetch_post_tags($r,true); - foreach ($r as $rr) { - $ret['item'][] = encode_item($rr,true); - } - } - } + intval($channel_id), + db_utcnow(), + db_quoteinterval('3 MONTH') + ); + if ($r) { + $ret['item'] = []; + xchan_query($r); + $r = fetch_post_tags($r, true); + foreach ($r as $rr) { + $ret['item'][] = encode_item($rr, true); + } + } + } - $addon = [ - 'channel_id' => $channel_id, - 'sections' => $sections, - 'data' => $ret - ]; - /** - * @hooks identity_basic_export - * Called when exporting a channel's basic information for backup or transfer. - * * \e number \b channel_id - The channel ID - * * \e array \b sections - * * \e array \b data - The data will get returned - */ - call_hooks('identity_basic_export', $addon); - $ret = $addon['data']; + $addon = [ + 'channel_id' => $channel_id, + 'sections' => $sections, + 'data' => $ret + ]; + /** + * @hooks identity_basic_export + * Called when exporting a channel's basic information for backup or transfer. + * * \e number \b channel_id - The channel ID + * * \e array \b sections + * * \e array \b data - The data will get returned + */ + call_hooks('identity_basic_export', $addon); + $ret = $addon['data']; - return $ret; + return $ret; } /** @@ -1132,30 +1198,28 @@ function identity_basic_export($channel_id, $sections = null) { * * \e array \b relocate - (optional) * * \e array \b item - array with items encoded_item() */ -function identity_export_year($channel_id, $year, $month = 0) { +function identity_export_year($channel_id, $year, $month = 0) +{ - if (! $year) { - return []; - } + if (! $year) { + return []; + } - if ($month && $month <= 12) { - $target_month = sprintf('%02d', $month); - $target_month_plus = sprintf('%02d', $month+1); - } - else { - $target_month = '01'; - } + if ($month && $month <= 12) { + $target_month = sprintf('%02d', $month); + $target_month_plus = sprintf('%02d', $month + 1); + } else { + $target_month = '01'; + } - $mindate = datetime_convert('UTC', 'UTC', $year . '-' . $target_month . '-01 00:00:00'); - if ($month && $month < 12) { - $maxdate = datetime_convert('UTC', 'UTC', $year . '-' . $target_month_plus . '-01 00:00:00'); - } - else { - $maxdate = datetime_convert('UTC', 'UTC', $year+1 . '-01-01 00:00:00'); - } - - return channel_export_items_date($channel_id,$mindate,$maxdate); + $mindate = datetime_convert('UTC', 'UTC', $year . '-' . $target_month . '-01 00:00:00'); + if ($month && $month < 12) { + $maxdate = datetime_convert('UTC', 'UTC', $year . '-' . $target_month_plus . '-01 00:00:00'); + } else { + $maxdate = datetime_convert('UTC', 'UTC', $year + 1 . '-01-01 00:00:00'); + } + return channel_export_items_date($channel_id, $mindate, $maxdate); } /** @@ -1169,46 +1233,47 @@ function identity_export_year($channel_id, $year, $month = 0) { * @return array */ -function channel_export_items_date($channel_id, $start, $finish) { +function channel_export_items_date($channel_id, $start, $finish) +{ - if (! $start) { - return []; - } - else { - $start = datetime_convert('UTC', 'UTC', $start); - } - - $finish = datetime_convert('UTC', 'UTC', (($finish) ? $finish : 'now')); - if ($finish < $start) { - return []; - } + if (! $start) { + return []; + } else { + $start = datetime_convert('UTC', 'UTC', $start); + } - $ret = []; + $finish = datetime_convert('UTC', 'UTC', (($finish) ? $finish : 'now')); + if ($finish < $start) { + return []; + } - $ch = channelx_by_n($channel_id); - if ($ch) { - $ret['relocate'] = [ 'channel_address' => $ch['channel_address'], 'url' => z_root()]; - } + $ret = []; - $ret['compatibility']['codebase'] = 'zap'; + $ch = channelx_by_n($channel_id); + if ($ch) { + $ret['relocate'] = [ 'channel_address' => $ch['channel_address'], 'url' => z_root()]; + } - $r = q("select * from item where ( item_wall = 1 or item_type != %d ) and item_deleted = 0 and uid = %d and created >= '%s' and created <= '%s' and resource_type = '' order by created", - intval(ITEM_TYPE_POST), - intval($channel_id), - dbesc($start), - dbesc($finish) - ); + $ret['compatibility']['codebase'] = 'zap'; - if ($r) { - $ret['item'] = []; - xchan_query($r); - $r = fetch_post_tags($r, true); - foreach ($r as $rr) { - $ret['item'][] = encode_item($rr, true); - } - } + $r = q( + "select * from item where ( item_wall = 1 or item_type != %d ) and item_deleted = 0 and uid = %d and created >= '%s' and created <= '%s' and resource_type = '' order by created", + intval(ITEM_TYPE_POST), + intval($channel_id), + dbesc($start), + dbesc($finish) + ); - return $ret; + if ($r) { + $ret['item'] = []; + xchan_query($r); + $r = fetch_post_tags($r, true); + foreach ($r as $rr) { + $ret['item'][] = encode_item($rr, true); + } + } + + return $ret; } @@ -1223,87 +1288,90 @@ function channel_export_items_date($channel_id, $start, $finish) { * @return array */ -function channel_export_items_page($channel_id, $start, $finish, $page = 0, $limit = 50) { +function channel_export_items_page($channel_id, $start, $finish, $page = 0, $limit = 50) +{ - if (intval($page) < 1) { - $page = 0; - } + if (intval($page) < 1) { + $page = 0; + } - if (intval($limit) < 1) { - $limit = 1; - } + if (intval($limit) < 1) { + $limit = 1; + } - if (intval($limit) > 5000) { - $limit = 5000; - } + if (intval($limit) > 5000) { + $limit = 5000; + } - if (! $start) { - $start = NULL_DATE; - } - else { - $start = datetime_convert('UTC', 'UTC', $start); - } - - $finish = datetime_convert('UTC', 'UTC', (($finish) ? $finish : 'now')); - if ($finish < $start) { - return []; - } + if (! $start) { + $start = NULL_DATE; + } else { + $start = datetime_convert('UTC', 'UTC', $start); + } - $offset = intval($limit) * intval($page); + $finish = datetime_convert('UTC', 'UTC', (($finish) ? $finish : 'now')); + if ($finish < $start) { + return []; + } - $ret = []; + $offset = intval($limit) * intval($page); - $ch = channelx_by_n($channel_id); - if ($ch) { - $ret['relocate'] = [ 'channel_address' => $ch['channel_address'], 'url' => z_root()]; - } + $ret = []; - $ret['compatibility']['codebase'] = 'zap'; + $ch = channelx_by_n($channel_id); + if ($ch) { + $ret['relocate'] = [ 'channel_address' => $ch['channel_address'], 'url' => z_root()]; + } + + $ret['compatibility']['codebase'] = 'zap'; - $r = q("select * from item where ( item_wall = 1 or item_type != %d ) and item_deleted = 0 and uid = %d and resource_type = '' and created >= '%s' and created <= '%s' order by created limit %d offset %d", - intval(ITEM_TYPE_POST), - intval($channel_id), - dbesc($start), - dbesc($finish), - intval($limit), - intval($offset) - ); + $r = q( + "select * from item where ( item_wall = 1 or item_type != %d ) and item_deleted = 0 and uid = %d and resource_type = '' and created >= '%s' and created <= '%s' order by created limit %d offset %d", + intval(ITEM_TYPE_POST), + intval($channel_id), + dbesc($start), + dbesc($finish), + intval($limit), + intval($offset) + ); - if ($r) { - $ret['item'] = []; - xchan_query($r); - $r = fetch_post_tags($r, true); - foreach ($r as $rr) { - $ret['item'][] = encode_item($rr, true); - } - } + if ($r) { + $ret['item'] = []; + xchan_query($r); + $r = fetch_post_tags($r, true); + foreach ($r as $rr) { + $ret['item'][] = encode_item($rr, true); + } + } - return $ret; + return $ret; } -function get_my_url() { - if (x($_SESSION, 'zrl_override')) { - return $_SESSION['zrl_override']; - } - if (x($_SESSION, 'my_url')) { - return $_SESSION['my_url']; - } +function get_my_url() +{ + if (x($_SESSION, 'zrl_override')) { + return $_SESSION['zrl_override']; + } + if (x($_SESSION, 'my_url')) { + return $_SESSION['my_url']; + } - return false; + return false; } -function get_my_address() { - if (x($_SESSION, 'zid_override')) { - return $_SESSION['zid_override']; - } - if (x($_SESSION, 'my_address')) { - return $_SESSION['my_address']; - } +function get_my_address() +{ + if (x($_SESSION, 'zid_override')) { + return $_SESSION['zid_override']; + } + if (x($_SESSION, 'my_address')) { + return $_SESSION['my_address']; + } - return false; + return false; } /** @@ -1313,75 +1381,81 @@ function get_my_address() { * don't have it already. * And if they aren't already authenticated here, attempt reverse magic auth. */ -function zid_init() { - $tmp_str = get_my_address(); - if (validate_email($tmp_str)) { - $arr = [ - 'zid' => $tmp_str, - 'url' => App::$cmd - ]; - /** - * @hooks zid_init - * * \e string \b zid - their zid - * * \e string \b url - the destination url - */ - call_hooks('zid_init', $arr); +function zid_init() +{ + $tmp_str = get_my_address(); + if (validate_email($tmp_str)) { + $arr = [ + 'zid' => $tmp_str, + 'url' => App::$cmd + ]; + /** + * @hooks zid_init + * * \e string \b zid - their zid + * * \e string \b url - the destination url + */ + call_hooks('zid_init', $arr); - if (! local_channel()) { - $r = q("select * from hubloc where hubloc_addr = '%s' order by hubloc_connected desc limit 1", - dbesc($tmp_str) - ); - if (! $r) { - Run::Summon( [ 'Gprobe', bin2hex($tmp_str) ] ); - } - if ($r && remote_channel() && remote_channel() === $r[0]['hubloc_hash']) { - return; - } + if (! local_channel()) { + $r = q( + "select * from hubloc where hubloc_addr = '%s' order by hubloc_connected desc limit 1", + dbesc($tmp_str) + ); + if (! $r) { + Run::Summon([ 'Gprobe', bin2hex($tmp_str) ]); + } + if ($r && remote_channel() && remote_channel() === $r[0]['hubloc_hash']) { + return; + } - logger('Not authenticated. Invoking reverse magic-auth for ' . $tmp_str); - // try to avoid recursion - but send them home to do a proper magic auth - $query = App::$query_string; - $query = str_replace(array('?zid=','&zid='),array('?rzid=','&rzid='),$query); - $dest = '/' . $query; - if ($r && ($r[0]['hubloc_url'] != z_root()) && (! strstr($dest,'/magic')) && (! strstr($dest,'/rmagic'))) { - goaway($r[0]['hubloc_url'] . '/magic' . '?f=&rev=1&owa=1&bdest=' . bin2hex(z_root() . $dest)); - } - else - logger('No hubloc found.'); - } - } + logger('Not authenticated. Invoking reverse magic-auth for ' . $tmp_str); + // try to avoid recursion - but send them home to do a proper magic auth + $query = App::$query_string; + $query = str_replace(array('?zid=','&zid='), array('?rzid=','&rzid='), $query); + $dest = '/' . $query; + if ($r && ($r[0]['hubloc_url'] != z_root()) && (! strstr($dest, '/magic')) && (! strstr($dest, '/rmagic'))) { + goaway($r[0]['hubloc_url'] . '/magic' . '?f=&rev=1&owa=1&bdest=' . bin2hex(z_root() . $dest)); + } else { + logger('No hubloc found.'); + } + } + } } /** * @brief If somebody arrives at our site using a zat, authenticate them. * */ -function zat_init() { - if (local_channel() || remote_channel()) { - return; - } +function zat_init() +{ + if (local_channel() || remote_channel()) { + return; + } - $r = q("select * from atoken where atoken_token = '%s' limit 1", - dbesc($_REQUEST['zat']) - ); - if ($r) { - $xchan = atoken_xchan($r[0]); -// atoken_create_xchan($xchan); - atoken_login($xchan); - } + $r = q( + "select * from atoken where atoken_token = '%s' limit 1", + dbesc($_REQUEST['zat']) + ); + if ($r) { + $xchan = atoken_xchan($r[0]); +// atoken_create_xchan($xchan); + atoken_login($xchan); + } } -function atoken_delete_and_sync($channel_id,$atoken_guid) { - $r = q("select * from atoken where atoken_guid = '%s' and atoken_uid = %d", - dbesc($atoken_guid), - intval($channel_id) - ); - if ($r) { - $atok = array_shift($r); - $atok['deleted'] = true; - atoken_delete($atok['atoken_id']); - Libsync::build_sync_packet($channel_id, [ 'atoken' => [ $atok ] ] ); - } +function atoken_delete_and_sync($channel_id, $atoken_guid) +{ + $r = q( + "select * from atoken where atoken_guid = '%s' and atoken_uid = %d", + dbesc($atoken_guid), + intval($channel_id) + ); + if ($r) { + $atok = array_shift($r); + $atok['deleted'] = true; + atoken_delete($atok['atoken_id']); + Libsync::build_sync_packet($channel_id, [ 'atoken' => [ $atok ] ]); + } } /** @@ -1394,21 +1468,22 @@ function atoken_delete_and_sync($channel_id,$atoken_guid) { * * @return int */ -function get_theme_uid() { - $uid = ((isset($_REQUEST['puid'])) ? intval($_REQUEST['puid']) : 0); - if (local_channel()) { - if ((get_pconfig(local_channel(),'system','always_my_theme')) || (! $uid)) { - return local_channel(); - } - } - if (! $uid) { - $x = get_sys_channel(); - if ($x) { - return $x['channel_id']; - } - } +function get_theme_uid() +{ + $uid = ((isset($_REQUEST['puid'])) ? intval($_REQUEST['puid']) : 0); + if (local_channel()) { + if ((get_pconfig(local_channel(), 'system', 'always_my_theme')) || (! $uid)) { + return local_channel(); + } + } + if (! $uid) { + $x = get_sys_channel(); + if ($x) { + return $x['channel_id']; + } + } - return $uid; + return $uid; } /** @@ -1419,24 +1494,24 @@ function get_theme_uid() { * one of (300, 80, 48) * @return string with path to profile photo */ -function get_default_profile_photo($size = 300) { - $scheme = get_config('system','default_profile_photo'); - if (! $scheme) { - $scheme = 'rainbow_man'; - } +function get_default_profile_photo($size = 300) +{ + $scheme = get_config('system', 'default_profile_photo'); + if (! $scheme) { + $scheme = 'rainbow_man'; + } - if (! is_dir('images/default_profile_photos/' . $scheme)) { - $x = [ 'scheme' => $scheme, 'size' => $size, 'url' => '' ]; - call_hooks('default_profile_photo',$x); - if ($x['url']) { - return $x['url']; - } - else { - $scheme = 'rainbow_man'; - } - } + if (! is_dir('images/default_profile_photos/' . $scheme)) { + $x = [ 'scheme' => $scheme, 'size' => $size, 'url' => '' ]; + call_hooks('default_profile_photo', $x); + if ($x['url']) { + return $x['url']; + } else { + $scheme = 'rainbow_man'; + } + } - return 'images/default_profile_photos/' . $scheme . '/' . $size . '.png'; + return 'images/default_profile_photos/' . $scheme . '/' . $size . '.png'; } /** @@ -1444,10 +1519,11 @@ function get_default_profile_photo($size = 300) { * * @param string $s * xchan_hash of the identity in question - * @return boolean true or false + * @return bool true or false */ -function is_foreigner($s) { - return((strpbrk($s, '.:@')) ? true : false); +function is_foreigner($s) +{ + return((strpbrk($s, '.:@')) ? true : false); } /** @@ -1455,10 +1531,11 @@ function is_foreigner($s) { * * @param string $s * xchan_hash of the identity in question - * @return boolean true or false + * @return bool true or false */ -function is_member($s) { - return((is_foreigner($s)) ? false : true); +function is_member($s) +{ + return((is_foreigner($s)) ? false : true); } /** @@ -1468,31 +1545,34 @@ function is_member($s) { * @return array An associative array with * * \e bool \b result */ -function get_online_status($nick) { +function get_online_status($nick) +{ - $ret = array('result' => false); + $ret = array('result' => false); - if (observer_prohibited()) { - return $ret; - } + if (observer_prohibited()) { + return $ret; + } - $r = q("select channel_id, channel_hash from channel where channel_address = '%s' limit 1", - dbesc($nick) - ); - if ($r) { - $hide = get_pconfig($r[0]['channel_id'],'system','hide_online_status'); - if ($hide) { - return $ret; - } - $x = q("select cp_status from chatpresence where cp_xchan = '%s' and cp_room = 0 limit 1", - dbesc($r[0]['channel_hash']) - ); - if ($x) { - $ret['result'] = $x[0]['cp_status']; - } - } + $r = q( + "select channel_id, channel_hash from channel where channel_address = '%s' limit 1", + dbesc($nick) + ); + if ($r) { + $hide = get_pconfig($r[0]['channel_id'], 'system', 'hide_online_status'); + if ($hide) { + return $ret; + } + $x = q( + "select cp_status from chatpresence where cp_xchan = '%s' and cp_room = 0 limit 1", + dbesc($r[0]['channel_hash']) + ); + if ($x) { + $ret['result'] = $x[0]['cp_status']; + } + } - return $ret; + return $ret; } @@ -1500,28 +1580,30 @@ function get_online_status($nick) { * @brief * * @param string $webbie - * @return array|boolean|string + * @return array|bool|string */ -function remote_online_status($webbie) { +function remote_online_status($webbie) +{ - $result = false; - $r = q("select * from hubloc where hubloc_addr = '%s' limit 1", - dbesc($webbie) - ); - if (! $r) { - return $result; - } - $url = $r[0]['hubloc_url'] . '/online/' . substr($webbie,0,strpos($webbie,'@')); + $result = false; + $r = q( + "select * from hubloc where hubloc_addr = '%s' limit 1", + dbesc($webbie) + ); + if (! $r) { + return $result; + } + $url = $r[0]['hubloc_url'] . '/online/' . substr($webbie, 0, strpos($webbie, '@')); - $x = z_fetch_url($url); - if ($x['success']) { - $j = json_decode($x['body'],true); - if ($j) { - $result = (($j['result']) ? $j['result'] : false); - } - } + $x = z_fetch_url($url); + if ($x['success']) { + $j = json_decode($x['body'], true); + if ($j) { + $result = (($j['result']) ? $j['result'] : false); + } + } - return $result; + return $result; } @@ -1530,82 +1612,89 @@ function remote_online_status($webbie) { * * @return string with parsed HTML channel_id_selet template */ -function identity_selector() { - if (local_channel()) { - $r = q("select channel.*, xchan.* from channel left join xchan on channel.channel_hash = xchan.xchan_hash where channel.channel_account_id = %d and channel_removed = 0 order by channel_name ", - intval(get_account_id()) - ); - if ($r && count($r) > 1) { - $o = replace_macros(get_markup_template('channel_id_select.tpl'), array( - '$channels' => $r, - '$selected' => local_channel() - )); +function identity_selector() +{ + if (local_channel()) { + $r = q( + "select channel.*, xchan.* from channel left join xchan on channel.channel_hash = xchan.xchan_hash where channel.channel_account_id = %d and channel_removed = 0 order by channel_name ", + intval(get_account_id()) + ); + if ($r && count($r) > 1) { + $o = replace_macros(get_markup_template('channel_id_select.tpl'), array( + '$channels' => $r, + '$selected' => local_channel() + )); - return $o; - } - } + return $o; + } + } - return ''; + return ''; } -function is_public_profile() { - if (! local_channel()) { - return false; - } +function is_public_profile() +{ + if (! local_channel()) { + return false; + } - if (intval(get_config('system','block_public'))) { - return false; - } + if (intval(get_config('system', 'block_public'))) { + return false; + } - $channel = App::get_channel(); - if ($channel) { - $perm = PermissionLimits::Get($channel['channel_id'],'view_profile'); - if ($perm == PERMS_PUBLIC) { - return true; - } - } + $channel = App::get_channel(); + if ($channel) { + $perm = PermissionLimits::Get($channel['channel_id'], 'view_profile'); + if ($perm == PERMS_PUBLIC) { + return true; + } + } - return false; + return false; } -function get_profile_fields_basic($filter = 0) { +function get_profile_fields_basic($filter = 0) +{ - $profile_fields_basic = (($filter == 0) ? get_config('system','profile_fields_basic') : null); + $profile_fields_basic = (($filter == 0) ? get_config('system', 'profile_fields_basic') : null); - if (! $profile_fields_basic) { - $profile_fields_basic = array('fullname','pdesc','chandesc','basic_gender','pronouns','dob','dob_tz','region','country_name','marital','sexual','homepage','hometown','keywords','about','contact'); - } - - $x = []; - if ($profile_fields_basic) - foreach ($profile_fields_basic as $f) - $x[$f] = 1; + if (! $profile_fields_basic) { + $profile_fields_basic = array('fullname','pdesc','chandesc','basic_gender','pronouns','dob','dob_tz','region','country_name','marital','sexual','homepage','hometown','keywords','about','contact'); + } - return $x; + $x = []; + if ($profile_fields_basic) { + foreach ($profile_fields_basic as $f) { + $x[$f] = 1; + } + } + + return $x; } -function get_profile_fields_advanced($filter = 0) { - $basic = get_profile_fields_basic($filter); - $profile_fields_advanced = (($filter == 0) ? get_config('system','profile_fields_advanced') : null); - if (! $profile_fields_advanced) { - $profile_fields_advanced = array('comms', 'address','locality','postal_code','advanced_gender', 'partner','howlong','politic','religion','likes','dislikes','interest','channels','music','book','film','tv','romance','employment','education'); - } - $x = []; - if ($basic) { - foreach ($basic as $f => $v) { - $x[$f] = $v; - } - } +function get_profile_fields_advanced($filter = 0) +{ + $basic = get_profile_fields_basic($filter); + $profile_fields_advanced = (($filter == 0) ? get_config('system', 'profile_fields_advanced') : null); + if (! $profile_fields_advanced) { + $profile_fields_advanced = array('comms', 'address','locality','postal_code','advanced_gender', 'partner','howlong','politic','religion','likes','dislikes','interest','channels','music','book','film','tv','romance','employment','education'); + } + $x = []; + if ($basic) { + foreach ($basic as $f => $v) { + $x[$f] = $v; + } + } - if ($profile_fields_advanced) { - foreach ($profile_fields_advanced as $f) { - $x[$f] = 1; - } - } + if ($profile_fields_advanced) { + foreach ($profile_fields_advanced as $f) { + $x[$f] = 1; + } + } - return $x; + return $x; } /** @@ -1620,132 +1709,150 @@ function get_profile_fields_advanced($filter = 0) { * Current notification flag value. Send this to notifications_on() to restore the channel settings when finished * with the activity requiring notifications_off(); */ -function notifications_off($channel_id) { - $r = q("select channel_notifyflags from channel where channel_id = %d limit 1", - intval($channel_id) - ); - q("update channel set channel_notifyflags = 0 where channel_id = %d", - intval($channel_id) - ); +function notifications_off($channel_id) +{ + $r = q( + "select channel_notifyflags from channel where channel_id = %d limit 1", + intval($channel_id) + ); + q( + "update channel set channel_notifyflags = 0 where channel_id = %d", + intval($channel_id) + ); - return intval($r[0]['channel_notifyflags']); + return intval($r[0]['channel_notifyflags']); } -function notifications_on($channel_id, $value) { - $x = q("update channel set channel_notifyflags = %d where channel_id = %d", - intval($value), - intval($channel_id) - ); +function notifications_on($channel_id, $value) +{ + $x = q( + "update channel set channel_notifyflags = %d where channel_id = %d", + intval($value), + intval($channel_id) + ); - return $x; + return $x; } -function get_channel_default_perms($uid) { +function get_channel_default_perms($uid) +{ - $ret = []; + $ret = []; - $r = q("select abook_xchan from abook where abook_channel = %d and abook_self = 1 limit 1", - intval($uid) - ); - if ($r) { - $ret = Permissions::FilledPerms(get_abconfig($uid,$r[0]['abook_xchan'],'system','my_perms',EMPTY_STR)); - } + $r = q( + "select abook_xchan from abook where abook_channel = %d and abook_self = 1 limit 1", + intval($uid) + ); + if ($r) { + $ret = Permissions::FilledPerms(get_abconfig($uid, $r[0]['abook_xchan'], 'system', 'my_perms', EMPTY_STR)); + } - return $ret; + return $ret; } -function profiles_build_sync($channel_id,$send = true) { - $r = q("select * from profile where uid = %d", - intval($channel_id) - ); - if ($r) { - if($send) { - Libsync::build_sync_packet($channel_id,array('profile' => $r)); - } - else { - return $r; - } - } +function profiles_build_sync($channel_id, $send = true) +{ + $r = q( + "select * from profile where uid = %d", + intval($channel_id) + ); + if ($r) { + if ($send) { + Libsync::build_sync_packet($channel_id, array('profile' => $r)); + } else { + return $r; + } + } } -function auto_channel_create($account_id) { +function auto_channel_create($account_id) +{ - if (! $account_id) { - return false; - } + if (! $account_id) { + return false; + } - $arr = []; - $arr['account_id'] = $account_id; - $arr['name'] = get_aconfig($account_id,'register','channel_name'); - $arr['nickname'] = legal_webbie(get_aconfig($account_id,'register','channel_address')); - $arr['permissions_role'] = get_aconfig($account_id,'register','permissions_role'); + $arr = []; + $arr['account_id'] = $account_id; + $arr['name'] = get_aconfig($account_id, 'register', 'channel_name'); + $arr['nickname'] = legal_webbie(get_aconfig($account_id, 'register', 'channel_address')); + $arr['permissions_role'] = get_aconfig($account_id, 'register', 'permissions_role'); - del_aconfig($account_id,'register','channel_name'); - del_aconfig($account_id,'register','channel_address'); - del_aconfig($account_id,'register','permissions_role'); + del_aconfig($account_id, 'register', 'channel_name'); + del_aconfig($account_id, 'register', 'channel_address'); + del_aconfig($account_id, 'register', 'permissions_role'); - if((! $arr['name']) || (! $arr['nickname'])) { - $x = q("select * from account where account_id = %d limit 1", - intval($account_id) - ); - if($x) { - if(! $arr['name']) - $arr['name'] = substr($x[0]['account_email'],0,strpos($x[0]['account_email'],'@')); - if(! $arr['nickname']) - $arr['nickname'] = legal_webbie(substr($x[0]['account_email'],0,strpos($x[0]['account_email'],'@'))); - } - } - if(! $arr['permissions_role']) - $arr['permissions_role'] = 'social'; + if ((! $arr['name']) || (! $arr['nickname'])) { + $x = q( + "select * from account where account_id = %d limit 1", + intval($account_id) + ); + if ($x) { + if (! $arr['name']) { + $arr['name'] = substr($x[0]['account_email'], 0, strpos($x[0]['account_email'], '@')); + } + if (! $arr['nickname']) { + $arr['nickname'] = legal_webbie(substr($x[0]['account_email'], 0, strpos($x[0]['account_email'], '@'))); + } + } + } + if (! $arr['permissions_role']) { + $arr['permissions_role'] = 'social'; + } - if(validate_channelname($arr['name'])) - return false; - if($arr['nickname'] === 'sys') - $arr['nickname'] = $arr['nickname'] . mt_rand(1000,9999); + if (validate_channelname($arr['name'])) { + return false; + } + if ($arr['nickname'] === 'sys') { + $arr['nickname'] = $arr['nickname'] . mt_rand(1000, 9999); + } - $arr['nickname'] = check_webbie(array($arr['nickname'], $arr['nickname'] . mt_rand(1000,9999))); + $arr['nickname'] = check_webbie(array($arr['nickname'], $arr['nickname'] . mt_rand(1000, 9999))); - return create_identity($arr); + return create_identity($arr); } -function get_cover_photo($channel_id,$format = 'bbcode', $res = PHOTO_RES_COVER_1200) { +function get_cover_photo($channel_id, $format = 'bbcode', $res = PHOTO_RES_COVER_1200) +{ - $r = q("select height, width, resource_id, edited, mimetype from photo where uid = %d and imgscale = %d and photo_usage = %d", - intval($channel_id), - intval($res), - intval(PHOTO_COVER) - ); - if(! $r) - return false; + $r = q( + "select height, width, resource_id, edited, mimetype from photo where uid = %d and imgscale = %d and photo_usage = %d", + intval($channel_id), + intval($res), + intval(PHOTO_COVER) + ); + if (! $r) { + return false; + } - $output = false; + $output = false; - $url = z_root() . '/photo/' . $r[0]['resource_id'] . '-' . $res ; + $url = z_root() . '/photo/' . $r[0]['resource_id'] . '-' . $res ; - switch($format) { - case 'bbcode': - $output = '[zrl=' . $r[0]['width'] . 'x' . $r[0]['height'] . ']' . $url . '[/zrl]'; - break; - case 'html': - $output = '' . t('cover photo') . ''; - break; - case 'array': - default: - $output = array( - 'width' => $r[0]['width'], - 'height' => $r[0]['height'], - 'type' => $r[0]['mimetype'], - 'updated' => $r[0]['edited'], - 'url' => $url - ); - break; - } + switch ($format) { + case 'bbcode': + $output = '[zrl=' . $r[0]['width'] . 'x' . $r[0]['height'] . ']' . $url . '[/zrl]'; + break; + case 'html': + $output = '' . t('cover photo') . ''; + break; + case 'array': + default: + $output = array( + 'width' => $r[0]['width'], + 'height' => $r[0]['height'], + 'type' => $r[0]['mimetype'], + 'updated' => $r[0]['edited'], + 'url' => $url + ); + break; + } - return $output; + return $output; } @@ -1757,69 +1864,71 @@ function get_cover_photo($channel_id,$format = 'bbcode', $res = PHOTO_RES_COVER_ * @param array $args (optional) * @return string parsed HTML from \e zcard template */ -function get_zcard($channel, $observer_hash = '', $args = []) { +function get_zcard($channel, $observer_hash = '', $args = []) +{ - logger('get_zcard'); + logger('get_zcard'); - $maxwidth = (($args['width']) ? intval($args['width']) : 0); - $maxheight = (($args['height']) ? intval($args['height']) : 0); + $maxwidth = (($args['width']) ? intval($args['width']) : 0); + $maxheight = (($args['height']) ? intval($args['height']) : 0); - if(($maxwidth > 1200) || ($maxwidth < 1)) { - $maxwidth = 1200; - $cover_width = 1200; - } + if (($maxwidth > 1200) || ($maxwidth < 1)) { + $maxwidth = 1200; + $cover_width = 1200; + } - if($maxwidth <= 425) { - $width = 425; - $cover_width = 425; - $size = 'hz_small'; - $cover_size = PHOTO_RES_COVER_425; - $pphoto = array('mimetype' => $channel['xchan_photo_mimetype'], 'width' => 80 , 'height' => 80, 'href' => $channel['xchan_photo_m'].'?rev='.strtotime($channel['xchan_photo_date'])); - } elseif($maxwidth <= 900) { - $width = 900; - $cover_width = 850; - $size = 'hz_medium'; - $cover_size = PHOTO_RES_COVER_850; - $pphoto = array('mimetype' => $channel['xchan_photo_mimetype'], 'width' => 160 , 'height' => 160, 'href' => $channel['xchan_photo_l'].'?rev='.strtotime($channel['xchan_photo_date'])); - } elseif($maxwidth <= 1200) { - $width = 1200; - $cover_width = 1200; - $size = 'hz_large'; - $cover_size = PHOTO_RES_COVER_1200; - $pphoto = array('mimetype' => $channel['xchan_photo_mimetype'], 'width' => 300 , 'height' => 300, 'href' => $channel['xchan_photo_l'].'?rev='.strtotime($channel['xchan_photo_date'])); - } + if ($maxwidth <= 425) { + $width = 425; + $cover_width = 425; + $size = 'hz_small'; + $cover_size = PHOTO_RES_COVER_425; + $pphoto = array('mimetype' => $channel['xchan_photo_mimetype'], 'width' => 80 , 'height' => 80, 'href' => $channel['xchan_photo_m'] . '?rev=' . strtotime($channel['xchan_photo_date'])); + } elseif ($maxwidth <= 900) { + $width = 900; + $cover_width = 850; + $size = 'hz_medium'; + $cover_size = PHOTO_RES_COVER_850; + $pphoto = array('mimetype' => $channel['xchan_photo_mimetype'], 'width' => 160 , 'height' => 160, 'href' => $channel['xchan_photo_l'] . '?rev=' . strtotime($channel['xchan_photo_date'])); + } elseif ($maxwidth <= 1200) { + $width = 1200; + $cover_width = 1200; + $size = 'hz_large'; + $cover_size = PHOTO_RES_COVER_1200; + $pphoto = array('mimetype' => $channel['xchan_photo_mimetype'], 'width' => 300 , 'height' => 300, 'href' => $channel['xchan_photo_l'] . '?rev=' . strtotime($channel['xchan_photo_date'])); + } -// $scale = (float) $maxwidth / $width; -// $translate = intval(($scale / 1.0) * 100); +// $scale = (float) $maxwidth / $width; +// $translate = intval(($scale / 1.0) * 100); - $channel['channel_addr'] = channel_reddress($channel); - $zcard = array('chan' => $channel); + $channel['channel_addr'] = channel_reddress($channel); + $zcard = array('chan' => $channel); - $r = q("select height, width, resource_id, imgscale, mimetype from photo where uid = %d and imgscale = %d and photo_usage = %d", - intval($channel['channel_id']), - intval($cover_size), - intval(PHOTO_COVER) - ); + $r = q( + "select height, width, resource_id, imgscale, mimetype from photo where uid = %d and imgscale = %d and photo_usage = %d", + intval($channel['channel_id']), + intval($cover_size), + intval(PHOTO_COVER) + ); - if($r) { - $cover = $r[0]; - $cover['href'] = z_root() . '/photo/' . $r[0]['resource_id'] . '-' . $r[0]['imgscale']; - } else { - $default_cover = get_config('system','default_cover_photo','pexels-94622'); - $cover = [ 'href' => z_root() . '/images/default_cover_photos/' . $default_cover . '/' . $cover_width . '.jpg' ]; - } + if ($r) { + $cover = $r[0]; + $cover['href'] = z_root() . '/photo/' . $r[0]['resource_id'] . '-' . $r[0]['imgscale']; + } else { + $default_cover = get_config('system', 'default_cover_photo', 'pexels-94622'); + $cover = [ 'href' => z_root() . '/images/default_cover_photos/' . $default_cover . '/' . $cover_width . '.jpg' ]; + } - $o .= replace_macros(get_markup_template('zcard.tpl'), array( - '$maxwidth' => $maxwidth, - '$scale' => $scale, - '$translate' => $translate, - '$size' => $size, - '$cover' => $cover, - '$pphoto' => $pphoto, - '$zcard' => $zcard - )); + $o .= replace_macros(get_markup_template('zcard.tpl'), array( + '$maxwidth' => $maxwidth, + '$scale' => $scale, + '$translate' => $translate, + '$size' => $size, + '$cover' => $cover, + '$pphoto' => $pphoto, + '$zcard' => $zcard + )); - return $o; + return $o; } @@ -1831,125 +1940,132 @@ function get_zcard($channel, $observer_hash = '', $args = []) { * @param array $args (optional) * @return string parsed HTML from \e zcard_embed template */ -function get_zcard_embed($channel, $observer_hash = '', $args = []) { +function get_zcard_embed($channel, $observer_hash = '', $args = []) +{ - logger('get_zcard_embed'); + logger('get_zcard_embed'); - $maxwidth = (($args['width']) ? intval($args['width']) : 0); - $maxheight = (($args['height']) ? intval($args['height']) : 0); + $maxwidth = (($args['width']) ? intval($args['width']) : 0); + $maxheight = (($args['height']) ? intval($args['height']) : 0); - if(($maxwidth > 1200) || ($maxwidth < 1)) { - $maxwidth = 1200; - $cover_width = 1200; - } + if (($maxwidth > 1200) || ($maxwidth < 1)) { + $maxwidth = 1200; + $cover_width = 1200; + } - if($maxwidth <= 425) { - $width = 425; - $cover_width = 425; - $size = 'hz_small'; - $cover_size = PHOTO_RES_COVER_425; - $pphoto = array('mimetype' => $channel['xchan_photo_mimetype'], 'width' => 80 , 'height' => 80, 'href' => $channel['xchan_photo_m']); - } - elseif($maxwidth <= 900) { - $width = 900; - $cover_width = 850; - $size = 'hz_medium'; - $cover_size = PHOTO_RES_COVER_850; - $pphoto = array('mimetype' => $channel['xchan_photo_mimetype'], 'width' => 160 , 'height' => 160, 'href' => $channel['xchan_photo_l']); - } - elseif($maxwidth <= 1200) { - $width = 1200; - $cover_width = 1200; - $size = 'hz_large'; - $cover_size = PHOTO_RES_COVER_1200; - $pphoto = array('mimetype' => $channel['xchan_photo_mimetype'], 'width' => 300 , 'height' => 300, 'href' => $channel['xchan_photo_l']); - } + if ($maxwidth <= 425) { + $width = 425; + $cover_width = 425; + $size = 'hz_small'; + $cover_size = PHOTO_RES_COVER_425; + $pphoto = array('mimetype' => $channel['xchan_photo_mimetype'], 'width' => 80 , 'height' => 80, 'href' => $channel['xchan_photo_m']); + } elseif ($maxwidth <= 900) { + $width = 900; + $cover_width = 850; + $size = 'hz_medium'; + $cover_size = PHOTO_RES_COVER_850; + $pphoto = array('mimetype' => $channel['xchan_photo_mimetype'], 'width' => 160 , 'height' => 160, 'href' => $channel['xchan_photo_l']); + } elseif ($maxwidth <= 1200) { + $width = 1200; + $cover_width = 1200; + $size = 'hz_large'; + $cover_size = PHOTO_RES_COVER_1200; + $pphoto = array('mimetype' => $channel['xchan_photo_mimetype'], 'width' => 300 , 'height' => 300, 'href' => $channel['xchan_photo_l']); + } - $channel['channel_addr'] = channel_reddress($channel); - $zcard = array('chan' => $channel); + $channel['channel_addr'] = channel_reddress($channel); + $zcard = array('chan' => $channel); - $r = q("select height, width, resource_id, imgscale, mimetype from photo where uid = %d and imgscale = %d and photo_usage = %d", - intval($channel['channel_id']), - intval($cover_size), - intval(PHOTO_COVER) - ); + $r = q( + "select height, width, resource_id, imgscale, mimetype from photo where uid = %d and imgscale = %d and photo_usage = %d", + intval($channel['channel_id']), + intval($cover_size), + intval(PHOTO_COVER) + ); - if($r) { - $cover = $r[0]; - $cover['href'] = z_root() . '/photo/' . $r[0]['resource_id'] . '-' . $r[0]['imgscale']; - } - else { - $default_cover = get_config('system','default_cover_photo','pexels-94622'); - $cover = [ 'href' => z_root() . '/images/default_cover_photos/' . $default_cover . '/' . $cover_width . '.jpg' ]; - } + if ($r) { + $cover = $r[0]; + $cover['href'] = z_root() . '/photo/' . $r[0]['resource_id'] . '-' . $r[0]['imgscale']; + } else { + $default_cover = get_config('system', 'default_cover_photo', 'pexels-94622'); + $cover = [ 'href' => z_root() . '/images/default_cover_photos/' . $default_cover . '/' . $cover_width . '.jpg' ]; + } - $o .= replace_macros(get_markup_template('zcard_embed.tpl'),array( - '$maxwidth' => $maxwidth, - '$scale' => $scale, - '$translate' => $translate, - '$size' => $size, - '$cover' => $cover, - '$pphoto' => $pphoto, - '$zcard' => $zcard - )); + $o .= replace_macros(get_markup_template('zcard_embed.tpl'), array( + '$maxwidth' => $maxwidth, + '$scale' => $scale, + '$translate' => $translate, + '$size' => $size, + '$cover' => $cover, + '$pphoto' => $pphoto, + '$zcard' => $zcard + )); - return $o; + return $o; } /** * @brief Get a channel array from a channel nickname. * * @param string $nick - A channel_address nickname - * @return array|boolean + * @return array|bool * - array with channel entry * - false if no channel with $nick was found */ -function channelx_by_nick($nick,$include_removed = false) { +function channelx_by_nick($nick, $include_removed = false) +{ - $sql_extra = (($include_removed) ? "" : " and channel_removed = 0 "); - - // If we are provided a Unicode nickname convert to IDN + $sql_extra = (($include_removed) ? "" : " and channel_removed = 0 "); - $nick = punify($nick); + // If we are provided a Unicode nickname convert to IDN - // return a cached copy if there is a cached copy and it's a match. - // Also check that there is an xchan_hash to validate the App::$channel data is complete - // and that columns from both joined tables are present - - if (App::$channel && is_array(App::$channel) && array_key_exists('channel_address',App::$channel) - && array_key_exists('xchan_hash',App::$channel) && App::$channel['channel_address'] === $nick) { - return App::$channel; - } + $nick = punify($nick); - $r = q("SELECT * FROM channel left join xchan on channel_hash = xchan_hash WHERE channel_address = '%s' $sql_extra LIMIT 1", - dbesc($nick) - ); + // return a cached copy if there is a cached copy and it's a match. + // Also check that there is an xchan_hash to validate the App::$channel data is complete + // and that columns from both joined tables are present - return(($r) ? array_shift($r) : false); + if ( + App::$channel && is_array(App::$channel) && array_key_exists('channel_address', App::$channel) + && array_key_exists('xchan_hash', App::$channel) && App::$channel['channel_address'] === $nick + ) { + return App::$channel; + } + + $r = q( + "SELECT * FROM channel left join xchan on channel_hash = xchan_hash WHERE channel_address = '%s' $sql_extra LIMIT 1", + dbesc($nick) + ); + + return(($r) ? array_shift($r) : false); } /** * @brief Get a channel array by a channel_hash. * * @param string $hash - * @return array|boolean false if channel ID not found, otherwise the channel array + * @return array|bool false if channel ID not found, otherwise the channel array */ -function channelx_by_hash($hash, $include_removed = false) { +function channelx_by_hash($hash, $include_removed = false) +{ - $sql_extra = (($include_removed) ? "" : " and channel_removed = 0 "); + $sql_extra = (($include_removed) ? "" : " and channel_removed = 0 "); - if (App::$channel && is_array(App::$channel) && array_key_exists('channel_hash',App::$channel) - && array_key_exists('xchan_hash',App::$channel) && App::$channel['channel_hash'] === $hash) { - return App::$channel; - } + if ( + App::$channel && is_array(App::$channel) && array_key_exists('channel_hash', App::$channel) + && array_key_exists('xchan_hash', App::$channel) && App::$channel['channel_hash'] === $hash + ) { + return App::$channel; + } - $r = q("SELECT * FROM channel left join xchan on channel_hash = xchan_hash WHERE channel_hash = '%s' $sql_extra LIMIT 1", - dbesc($hash) - ); + $r = q( + "SELECT * FROM channel left join xchan on channel_hash = xchan_hash WHERE channel_hash = '%s' $sql_extra LIMIT 1", + dbesc($hash) + ); - return(($r) ? array_shift($r) : false); + return(($r) ? array_shift($r) : false); } @@ -1957,23 +2073,27 @@ function channelx_by_hash($hash, $include_removed = false) { * @brief Get a channel array by a channel ID. * * @param int $id A channel ID - * @return array|boolean false if channel ID not found, otherwise the channel array + * @return array|bool false if channel ID not found, otherwise the channel array */ -function channelx_by_n($id, $include_removed = false) { +function channelx_by_n($id, $include_removed = false) +{ - $sql_extra = (($include_removed) ? "" : " and channel_removed = 0 "); + $sql_extra = (($include_removed) ? "" : " and channel_removed = 0 "); - if (App::$channel && is_array(App::$channel) && array_key_exists('channel_id',App::$channel) - && array_key_exists('xchan_hash',App::$channel) && intval(App::$channel['channel_id']) === intval($id)) { - return App::$channel; - } + if ( + App::$channel && is_array(App::$channel) && array_key_exists('channel_id', App::$channel) + && array_key_exists('xchan_hash', App::$channel) && intval(App::$channel['channel_id']) === intval($id) + ) { + return App::$channel; + } - $r = q("SELECT * FROM channel LEFT JOIN xchan ON channel_hash = xchan_hash WHERE channel_id = %d $sql_extra LIMIT 1", - dbesc($id) - ); + $r = q( + "SELECT * FROM channel LEFT JOIN xchan ON channel_hash = xchan_hash WHERE channel_id = %d $sql_extra LIMIT 1", + dbesc($id) + ); - return(($r) ? array_shift($r) : false); + return(($r) ? array_shift($r) : false); } /** @@ -1983,11 +2103,13 @@ function channelx_by_n($id, $include_removed = false) { * @return string */ -function channel_reddress($channel) { - if(! ($channel && array_key_exists('channel_address', $channel))) - return ''; +function channel_reddress($channel) +{ + if (! ($channel && array_key_exists('channel_address', $channel))) { + return ''; + } - return strtolower($channel['channel_address'] . '@' . App::get_hostname()); + return strtolower($channel['channel_address'] . '@' . App::get_hostname()); } @@ -2001,10 +2123,11 @@ function channel_reddress($channel) { * @return int */ -function channel_manual_conv_update($channel_id) { +function channel_manual_conv_update($channel_id) +{ - $x = get_pconfig($channel_id, 'system', 'manual_conversation_update', get_config('system', 'manual_conversation_update', 1)); - return intval($x); + $x = get_pconfig($channel_id, 'system', 'manual_conversation_update', get_config('system', 'manual_conversation_update', 1)); + return intval($x); } @@ -2013,108 +2136,111 @@ function channel_manual_conv_update($channel_id) { * * @return string with parsed HTML from \e remote_login template */ -function remote_login() { - $o = replace_macros(get_markup_template('remote_login.tpl'),array( - '$title' => t('Remote Authentication'), - '$desc' => t('Enter your channel address (e.g. channel@example.com)'), - '$submit' => t('Authenticate') - )); +function remote_login() +{ + $o = replace_macros(get_markup_template('remote_login.tpl'), array( + '$title' => t('Remote Authentication'), + '$desc' => t('Enter your channel address (e.g. channel@example.com)'), + '$submit' => t('Authenticate') + )); - return $o; + return $o; } -function channel_store_lowlevel($arr) { - $store = [ - 'channel_account_id' => ((array_key_exists('channel_account_id',$arr)) ? $arr['channel_account_id'] : '0'), - 'channel_primary' => ((array_key_exists('channel_primary',$arr)) ? $arr['channel_primary'] : '0'), - 'channel_name' => ((array_key_exists('channel_name',$arr)) ? $arr['channel_name'] : ''), - 'channel_parent' => ((array_key_exists('channel_parent',$arr)) ? $arr['channel_parent'] : ''), - 'channel_address' => ((array_key_exists('channel_address',$arr)) ? $arr['channel_address'] : ''), - 'channel_guid' => ((array_key_exists('channel_guid',$arr)) ? $arr['channel_guid'] : ''), - 'channel_guid_sig' => ((array_key_exists('channel_guid_sig',$arr)) ? $arr['channel_guid_sig'] : ''), - 'channel_hash' => ((array_key_exists('channel_hash',$arr)) ? $arr['channel_hash'] : ''), - 'channel_timezone' => ((array_key_exists('channel_timezone',$arr)) ? $arr['channel_timezone'] : 'UTC'), - 'channel_location' => ((array_key_exists('channel_location',$arr)) ? $arr['channel_location'] : ''), - 'channel_theme' => ((array_key_exists('channel_theme',$arr)) ? $arr['channel_theme'] : ''), - 'channel_startpage' => ((array_key_exists('channel_startpage',$arr)) ? $arr['channel_startpage'] : ''), - 'channel_pubkey' => ((array_key_exists('channel_pubkey',$arr)) ? $arr['channel_pubkey'] : ''), - 'channel_prvkey' => ((array_key_exists('channel_prvkey',$arr)) ? $arr['channel_prvkey'] : ''), - 'channel_notifyflags' => ((array_key_exists('channel_notifyflags',$arr)) ? $arr['channel_notifyflags'] : '65535'), - 'channel_pageflags' => ((array_key_exists('channel_pageflags',$arr)) ? $arr['channel_pageflags'] : '0'), - 'channel_dirdate' => ((array_key_exists('channel_dirdate',$arr)) ? $arr['channel_dirdate'] : NULL_DATE), - 'channel_lastpost' => ((array_key_exists('channel_lastpost',$arr)) ? $arr['channel_lastpost'] : NULL_DATE), - 'channel_deleted' => ((array_key_exists('channel_deleted',$arr)) ? $arr['channel_deleted'] : NULL_DATE), - 'channel_active' => ((array_key_exists('channel_active',$arr)) ? $arr['channel_active'] : NULL_DATE), - 'channel_max_anon_mail' => ((array_key_exists('channel_max_anon_mail',$arr)) ? $arr['channel_max_anon_mail'] : '10'), - 'channel_max_friend_req' => ((array_key_exists('channel_max_friend_req',$arr)) ? $arr['channel_max_friend_req'] : '10'), - 'channel_expire_days' => ((array_key_exists('channel_expire_days',$arr)) ? $arr['channel_expire_days'] : '0'), - 'channel_passwd_reset' => ((array_key_exists('channel_passwd_reset',$arr)) ? $arr['channel_passwd_reset'] : ''), - 'channel_default_group' => ((array_key_exists('channel_default_group',$arr)) ? $arr['channel_default_group'] : ''), - 'channel_allow_cid' => ((array_key_exists('channel_allow_cid',$arr)) ? $arr['channel_allow_cid'] : ''), - 'channel_allow_gid' => ((array_key_exists('channel_allow_gid',$arr)) ? $arr['channel_allow_gid'] : ''), - 'channel_deny_cid' => ((array_key_exists('channel_deny_cid',$arr)) ? $arr['channel_deny_cid'] : ''), - 'channel_deny_gid' => ((array_key_exists('channel_deny_gid',$arr)) ? $arr['channel_deny_gid'] : ''), - 'channel_removed' => ((array_key_exists('channel_removed',$arr)) ? $arr['channel_removed'] : '0'), - 'channel_system' => ((array_key_exists('channel_system',$arr)) ? $arr['channel_system'] : '0'), - 'channel_moved' => ((array_key_exists('channel_moved',$arr)) ? $arr['channel_moved'] : ''), - 'channel_password' => ((array_key_exists('channel_password',$arr)) ? $arr['channel_password'] : ''), - 'channel_salt' => ((array_key_exists('channel_salt',$arr)) ? $arr['channel_salt'] : '') - ]; +function channel_store_lowlevel($arr) +{ + $store = [ + 'channel_account_id' => ((array_key_exists('channel_account_id', $arr)) ? $arr['channel_account_id'] : '0'), + 'channel_primary' => ((array_key_exists('channel_primary', $arr)) ? $arr['channel_primary'] : '0'), + 'channel_name' => ((array_key_exists('channel_name', $arr)) ? $arr['channel_name'] : ''), + 'channel_parent' => ((array_key_exists('channel_parent', $arr)) ? $arr['channel_parent'] : ''), + 'channel_address' => ((array_key_exists('channel_address', $arr)) ? $arr['channel_address'] : ''), + 'channel_guid' => ((array_key_exists('channel_guid', $arr)) ? $arr['channel_guid'] : ''), + 'channel_guid_sig' => ((array_key_exists('channel_guid_sig', $arr)) ? $arr['channel_guid_sig'] : ''), + 'channel_hash' => ((array_key_exists('channel_hash', $arr)) ? $arr['channel_hash'] : ''), + 'channel_timezone' => ((array_key_exists('channel_timezone', $arr)) ? $arr['channel_timezone'] : 'UTC'), + 'channel_location' => ((array_key_exists('channel_location', $arr)) ? $arr['channel_location'] : ''), + 'channel_theme' => ((array_key_exists('channel_theme', $arr)) ? $arr['channel_theme'] : ''), + 'channel_startpage' => ((array_key_exists('channel_startpage', $arr)) ? $arr['channel_startpage'] : ''), + 'channel_pubkey' => ((array_key_exists('channel_pubkey', $arr)) ? $arr['channel_pubkey'] : ''), + 'channel_prvkey' => ((array_key_exists('channel_prvkey', $arr)) ? $arr['channel_prvkey'] : ''), + 'channel_notifyflags' => ((array_key_exists('channel_notifyflags', $arr)) ? $arr['channel_notifyflags'] : '65535'), + 'channel_pageflags' => ((array_key_exists('channel_pageflags', $arr)) ? $arr['channel_pageflags'] : '0'), + 'channel_dirdate' => ((array_key_exists('channel_dirdate', $arr)) ? $arr['channel_dirdate'] : NULL_DATE), + 'channel_lastpost' => ((array_key_exists('channel_lastpost', $arr)) ? $arr['channel_lastpost'] : NULL_DATE), + 'channel_deleted' => ((array_key_exists('channel_deleted', $arr)) ? $arr['channel_deleted'] : NULL_DATE), + 'channel_active' => ((array_key_exists('channel_active', $arr)) ? $arr['channel_active'] : NULL_DATE), + 'channel_max_anon_mail' => ((array_key_exists('channel_max_anon_mail', $arr)) ? $arr['channel_max_anon_mail'] : '10'), + 'channel_max_friend_req' => ((array_key_exists('channel_max_friend_req', $arr)) ? $arr['channel_max_friend_req'] : '10'), + 'channel_expire_days' => ((array_key_exists('channel_expire_days', $arr)) ? $arr['channel_expire_days'] : '0'), + 'channel_passwd_reset' => ((array_key_exists('channel_passwd_reset', $arr)) ? $arr['channel_passwd_reset'] : ''), + 'channel_default_group' => ((array_key_exists('channel_default_group', $arr)) ? $arr['channel_default_group'] : ''), + 'channel_allow_cid' => ((array_key_exists('channel_allow_cid', $arr)) ? $arr['channel_allow_cid'] : ''), + 'channel_allow_gid' => ((array_key_exists('channel_allow_gid', $arr)) ? $arr['channel_allow_gid'] : ''), + 'channel_deny_cid' => ((array_key_exists('channel_deny_cid', $arr)) ? $arr['channel_deny_cid'] : ''), + 'channel_deny_gid' => ((array_key_exists('channel_deny_gid', $arr)) ? $arr['channel_deny_gid'] : ''), + 'channel_removed' => ((array_key_exists('channel_removed', $arr)) ? $arr['channel_removed'] : '0'), + 'channel_system' => ((array_key_exists('channel_system', $arr)) ? $arr['channel_system'] : '0'), + 'channel_moved' => ((array_key_exists('channel_moved', $arr)) ? $arr['channel_moved'] : ''), + 'channel_password' => ((array_key_exists('channel_password', $arr)) ? $arr['channel_password'] : ''), + 'channel_salt' => ((array_key_exists('channel_salt', $arr)) ? $arr['channel_salt'] : '') + ]; - return create_table_from_array('channel', $store); + return create_table_from_array('channel', $store); } -function profile_store_lowlevel($arr) { +function profile_store_lowlevel($arr) +{ $store = [ - 'profile_guid' => ((array_key_exists('profile_guid',$arr)) ? $arr['profile_guid'] : ''), - 'aid' => ((array_key_exists('aid',$arr)) ? $arr['aid'] : 0), - 'uid' => ((array_key_exists('uid',$arr)) ? $arr['uid'] : 0), - 'profile_name' => ((array_key_exists('profile_name',$arr)) ? $arr['profile_name'] : ''), - 'is_default' => ((array_key_exists('is_default',$arr)) ? $arr['is_default'] : 0), - 'hide_friends' => ((array_key_exists('hide_friends',$arr)) ? $arr['hide_friends'] : 0), - 'fullname' => ((array_key_exists('fullname',$arr)) ? $arr['fullname'] : ''), - 'pdesc' => ((array_key_exists('pdesc',$arr)) ? $arr['pdesc'] : ''), - 'chandesc' => ((array_key_exists('chandesc',$arr)) ? $arr['chandesc'] : ''), - 'dob' => ((array_key_exists('dob',$arr)) ? $arr['dob'] : ''), - 'dob_tz' => ((array_key_exists('dob_tz',$arr)) ? $arr['dob_tz'] : ''), - 'address' => ((array_key_exists('address',$arr)) ? $arr['address'] : ''), - 'locality' => ((array_key_exists('locality',$arr)) ? $arr['locality'] : ''), - 'region' => ((array_key_exists('region',$arr)) ? $arr['region'] : ''), - 'postal_code' => ((array_key_exists('postal_code',$arr)) ? $arr['postal_code'] : ''), - 'country_name' => ((array_key_exists('country_name',$arr)) ? $arr['country_name'] : ''), - 'hometown' => ((array_key_exists('hometown',$arr)) ? $arr['hometown'] : ''), - 'gender' => ((array_key_exists('gender',$arr)) ? $arr['gender'] : ''), - 'marital' => ((array_key_exists('marital',$arr)) ? $arr['marital'] : ''), - 'partner' => ((array_key_exists('partner',$arr)) ? $arr['partner'] : ''), - 'howlong' => ((array_key_exists('howlong',$arr)) ? $arr['howlong'] : NULL_DATE), - 'sexual' => ((array_key_exists('sexual',$arr)) ? $arr['sexual'] : ''), - 'pronouns' => ((array_key_exists('pronouns',$arr)) ? $arr['pronouns'] : ''), - 'politic' => ((array_key_exists('politic',$arr)) ? $arr['politic'] : ''), - 'religion' => ((array_key_exists('religion',$arr)) ? $arr['religion'] : ''), - 'keywords' => ((array_key_exists('keywords',$arr)) ? $arr['keywords'] : ''), - 'likes' => ((array_key_exists('likes',$arr)) ? $arr['likes'] : ''), - 'dislikes' => ((array_key_exists('dislikes',$arr)) ? $arr['dislikes'] : ''), - 'about' => ((array_key_exists('about',$arr)) ? $arr['about'] : ''), - 'summary' => ((array_key_exists('summary',$arr)) ? $arr['summary'] : ''), - 'music' => ((array_key_exists('music',$arr)) ? $arr['music'] : ''), - 'book' => ((array_key_exists('book',$arr)) ? $arr['book'] : ''), - 'tv' => ((array_key_exists('tv',$arr)) ? $arr['tv'] : ''), - 'film' => ((array_key_exists('film',$arr)) ? $arr['film'] : ''), - 'interest' => ((array_key_exists('interest',$arr)) ? $arr['interest'] : ''), - 'romance' => ((array_key_exists('romance',$arr)) ? $arr['romance'] : ''), - 'employment' => ((array_key_exists('employment',$arr)) ? $arr['employment'] : ''), - 'education' => ((array_key_exists('education',$arr)) ? $arr['education'] : ''), - 'contact' => ((array_key_exists('contact',$arr)) ? $arr['contact'] : ''), - 'channels' => ((array_key_exists('channels',$arr)) ? $arr['channels'] : ''), - 'homepage' => ((array_key_exists('homepage',$arr)) ? $arr['homepage'] : ''), - 'photo' => ((array_key_exists('photo',$arr)) ? $arr['photo'] : ''), - 'thumb' => ((array_key_exists('thumb',$arr)) ? $arr['thumb'] : ''), - 'publish' => ((array_key_exists('publish',$arr)) ? $arr['publish'] : 0), - 'profile_vcard' => ((array_key_exists('profile_vcard',$arr)) ? $arr['profile_vcard'] : '') - ]; + 'profile_guid' => ((array_key_exists('profile_guid', $arr)) ? $arr['profile_guid'] : ''), + 'aid' => ((array_key_exists('aid', $arr)) ? $arr['aid'] : 0), + 'uid' => ((array_key_exists('uid', $arr)) ? $arr['uid'] : 0), + 'profile_name' => ((array_key_exists('profile_name', $arr)) ? $arr['profile_name'] : ''), + 'is_default' => ((array_key_exists('is_default', $arr)) ? $arr['is_default'] : 0), + 'hide_friends' => ((array_key_exists('hide_friends', $arr)) ? $arr['hide_friends'] : 0), + 'fullname' => ((array_key_exists('fullname', $arr)) ? $arr['fullname'] : ''), + 'pdesc' => ((array_key_exists('pdesc', $arr)) ? $arr['pdesc'] : ''), + 'chandesc' => ((array_key_exists('chandesc', $arr)) ? $arr['chandesc'] : ''), + 'dob' => ((array_key_exists('dob', $arr)) ? $arr['dob'] : ''), + 'dob_tz' => ((array_key_exists('dob_tz', $arr)) ? $arr['dob_tz'] : ''), + 'address' => ((array_key_exists('address', $arr)) ? $arr['address'] : ''), + 'locality' => ((array_key_exists('locality', $arr)) ? $arr['locality'] : ''), + 'region' => ((array_key_exists('region', $arr)) ? $arr['region'] : ''), + 'postal_code' => ((array_key_exists('postal_code', $arr)) ? $arr['postal_code'] : ''), + 'country_name' => ((array_key_exists('country_name', $arr)) ? $arr['country_name'] : ''), + 'hometown' => ((array_key_exists('hometown', $arr)) ? $arr['hometown'] : ''), + 'gender' => ((array_key_exists('gender', $arr)) ? $arr['gender'] : ''), + 'marital' => ((array_key_exists('marital', $arr)) ? $arr['marital'] : ''), + 'partner' => ((array_key_exists('partner', $arr)) ? $arr['partner'] : ''), + 'howlong' => ((array_key_exists('howlong', $arr)) ? $arr['howlong'] : NULL_DATE), + 'sexual' => ((array_key_exists('sexual', $arr)) ? $arr['sexual'] : ''), + 'pronouns' => ((array_key_exists('pronouns', $arr)) ? $arr['pronouns'] : ''), + 'politic' => ((array_key_exists('politic', $arr)) ? $arr['politic'] : ''), + 'religion' => ((array_key_exists('religion', $arr)) ? $arr['religion'] : ''), + 'keywords' => ((array_key_exists('keywords', $arr)) ? $arr['keywords'] : ''), + 'likes' => ((array_key_exists('likes', $arr)) ? $arr['likes'] : ''), + 'dislikes' => ((array_key_exists('dislikes', $arr)) ? $arr['dislikes'] : ''), + 'about' => ((array_key_exists('about', $arr)) ? $arr['about'] : ''), + 'summary' => ((array_key_exists('summary', $arr)) ? $arr['summary'] : ''), + 'music' => ((array_key_exists('music', $arr)) ? $arr['music'] : ''), + 'book' => ((array_key_exists('book', $arr)) ? $arr['book'] : ''), + 'tv' => ((array_key_exists('tv', $arr)) ? $arr['tv'] : ''), + 'film' => ((array_key_exists('film', $arr)) ? $arr['film'] : ''), + 'interest' => ((array_key_exists('interest', $arr)) ? $arr['interest'] : ''), + 'romance' => ((array_key_exists('romance', $arr)) ? $arr['romance'] : ''), + 'employment' => ((array_key_exists('employment', $arr)) ? $arr['employment'] : ''), + 'education' => ((array_key_exists('education', $arr)) ? $arr['education'] : ''), + 'contact' => ((array_key_exists('contact', $arr)) ? $arr['contact'] : ''), + 'channels' => ((array_key_exists('channels', $arr)) ? $arr['channels'] : ''), + 'homepage' => ((array_key_exists('homepage', $arr)) ? $arr['homepage'] : ''), + 'photo' => ((array_key_exists('photo', $arr)) ? $arr['photo'] : ''), + 'thumb' => ((array_key_exists('thumb', $arr)) ? $arr['thumb'] : ''), + 'publish' => ((array_key_exists('publish', $arr)) ? $arr['publish'] : 0), + 'profile_vcard' => ((array_key_exists('profile_vcard', $arr)) ? $arr['profile_vcard'] : '') + ]; - return create_table_from_array('profile',$store); + return create_table_from_array('profile', $store); } @@ -2124,229 +2250,246 @@ function profile_store_lowlevel($arr) { * authorisation to do this. * * @param int $account_id - * @param boolean $local (optional) default true - * @param boolean $unset_session (optional) default true - * @return boolean|array + * @param bool $local (optional) default true + * @param bool $unset_session (optional) default true + * @return bool|array */ -function account_remove($account_id, $local = true, $unset_session = true) { +function account_remove($account_id, $local = true, $unset_session = true) +{ - logger('account_remove: ' . $account_id); + logger('account_remove: ' . $account_id); - // Global removal (all clones) not currently supported - $local = true; + // Global removal (all clones) not currently supported + $local = true; - if (! intval($account_id)) { - logger('No account.'); - return false; - } + if (! intval($account_id)) { + logger('No account.'); + return false; + } - // Don't let anybody nuke the only admin account. + // Don't let anybody nuke the only admin account. - $r = q("select account_id from account where (account_roles & %d) > 0", - intval(ACCOUNT_ROLE_ADMIN) - ); + $r = q( + "select account_id from account where (account_roles & %d) > 0", + intval(ACCOUNT_ROLE_ADMIN) + ); - if ($r !== false && count($r) == 1 && $r[0]['account_id'] == $account_id) { - logger("Unable to remove the only remaining admin account"); - return false; - } + if ($r !== false && count($r) == 1 && $r[0]['account_id'] == $account_id) { + logger("Unable to remove the only remaining admin account"); + return false; + } - $r = q("select * from account where account_id = %d limit 1", - intval($account_id) - ); - - if (! $r) { - logger('No account with id: ' . $account_id); - return false; - } + $r = q( + "select * from account where account_id = %d limit 1", + intval($account_id) + ); - $account_email = $r[0]['account_email']; + if (! $r) { + logger('No account with id: ' . $account_id); + return false; + } - $x = q("select channel_id from channel where channel_account_id = %d", - intval($account_id) - ); - if ($x) { - foreach ($x as $xx) { - channel_remove($xx['channel_id'],$local,false); - } - } + $account_email = $r[0]['account_email']; - $r = q("delete from account where account_id = %d", - intval($account_id) - ); + $x = q( + "select channel_id from channel where channel_account_id = %d", + intval($account_id) + ); + if ($x) { + foreach ($x as $xx) { + channel_remove($xx['channel_id'], $local, false); + } + } - if ($unset_session) { - App::$session->nuke(); - notice( sprintf(t('Account \'%s\' deleted'),$account_email) . EOL); - goaway(z_root()); - } + $r = q( + "delete from account where account_id = %d", + intval($account_id) + ); - return $r; + if ($unset_session) { + App::$session->nuke(); + notice(sprintf(t('Account \'%s\' deleted'), $account_email) . EOL); + goaway(z_root()); + } + + return $r; } /** * @brief Removes a channel. * * @param int $channel_id - * @param boolean $local default true - * @param boolean $unset_session default false + * @param bool $local default true + * @param bool $unset_session default false */ -function channel_remove($channel_id, $local = true, $unset_session = false) { +function channel_remove($channel_id, $local = true, $unset_session = false) +{ - if (! $channel_id) { - return; - } + if (! $channel_id) { + return; + } - // global removal (all clones) not currently supported - // hence this operation _may_ leave orphan data on remote servers - - $local = true; + // global removal (all clones) not currently supported + // hence this operation _may_ leave orphan data on remote servers - logger('Removing channel: ' . $channel_id); - logger('local only: ' . intval($local)); + $local = true; - - $r = q("select * from channel where channel_id = %d limit 1", intval($channel_id)); - if (! $r) { - logger('channel not found: ' . $channel_id); - return; - } + logger('Removing channel: ' . $channel_id); + logger('local only: ' . intval($local)); - $channel = $r[0]; - /** - * @hooks channel_remove - * Called when removing a channel. - * * \e array with entry from channel tabel for $channel_id - */ + $r = q("select * from channel where channel_id = %d limit 1", intval($channel_id)); + if (! $r) { + logger('channel not found: ' . $channel_id); + return; + } - call_hooks('channel_remove', $channel); + $channel = $r[0]; - $r = q("select iid from iconfig left join item on item.id = iconfig.iid + /** + * @hooks channel_remove + * Called when removing a channel. + * * \e array with entry from channel tabel for $channel_id + */ + + call_hooks('channel_remove', $channel); + + $r = q( + "select iid from iconfig left join item on item.id = iconfig.iid where item.uid = %d", - intval($channel_id) - ); - if ($r) { - foreach ($r as $rr) { - q("delete from iconfig where iid = %d", - intval($rr['iid']) - ); - } - } + intval($channel_id) + ); + if ($r) { + foreach ($r as $rr) { + q( + "delete from iconfig where iid = %d", + intval($rr['iid']) + ); + } + } - q("DELETE FROM app WHERE app_channel = %d", intval($channel_id)); - q("DELETE FROM atoken WHERE atoken_uid = %d", intval($channel_id)); - q("DELETE FROM chatroom WHERE cr_uid = %d", intval($channel_id)); - q("DELETE FROM conv WHERE uid = %d", intval($channel_id)); + q("DELETE FROM app WHERE app_channel = %d", intval($channel_id)); + q("DELETE FROM atoken WHERE atoken_uid = %d", intval($channel_id)); + q("DELETE FROM chatroom WHERE cr_uid = %d", intval($channel_id)); + q("DELETE FROM conv WHERE uid = %d", intval($channel_id)); - q("DELETE FROM pgrp WHERE uid = %d", intval($channel_id)); - q("DELETE FROM pgrp_member WHERE uid = %d", intval($channel_id)); - q("DELETE FROM event WHERE uid = %d", intval($channel_id)); - q("DELETE FROM menu WHERE menu_channel_id = %d", intval($channel_id)); - q("DELETE FROM menu_item WHERE mitem_channel_id = %d", intval($channel_id)); + q("DELETE FROM pgrp WHERE uid = %d", intval($channel_id)); + q("DELETE FROM pgrp_member WHERE uid = %d", intval($channel_id)); + q("DELETE FROM event WHERE uid = %d", intval($channel_id)); + q("DELETE FROM menu WHERE menu_channel_id = %d", intval($channel_id)); + q("DELETE FROM menu_item WHERE mitem_channel_id = %d", intval($channel_id)); - q("DELETE FROM notify WHERE uid = %d", intval($channel_id)); - q("DELETE FROM obj WHERE obj_channel = %d", intval($channel_id)); + q("DELETE FROM notify WHERE uid = %d", intval($channel_id)); + q("DELETE FROM obj WHERE obj_channel = %d", intval($channel_id)); - q("DELETE FROM photo WHERE uid = %d", intval($channel_id)); - q("DELETE FROM attach WHERE uid = %d", intval($channel_id)); - q("DELETE FROM profile WHERE uid = %d", intval($channel_id)); - q("DELETE FROM source WHERE src_channel_id = %d", intval($channel_id)); + q("DELETE FROM photo WHERE uid = %d", intval($channel_id)); + q("DELETE FROM attach WHERE uid = %d", intval($channel_id)); + q("DELETE FROM profile WHERE uid = %d", intval($channel_id)); + q("DELETE FROM source WHERE src_channel_id = %d", intval($channel_id)); - $r = q("select hash FROM attach WHERE uid = %d", intval($channel_id)); - if ($r) { - foreach ($r as $rv) { - attach_delete($channel_id,$rv['hash']); - } - } + $r = q("select hash FROM attach WHERE uid = %d", intval($channel_id)); + if ($r) { + foreach ($r as $rv) { + attach_delete($channel_id, $rv['hash']); + } + } - - q("delete from abook where abook_xchan = '%s' and abook_self = 1 ", - dbesc($channel['channel_hash']) - ); - $r = q("update channel set channel_deleted = '%s', channel_removed = 1 where channel_id = %d", - dbesc(datetime_convert()), - intval($channel_id) - ); + q( + "delete from abook where abook_xchan = '%s' and abook_self = 1 ", + dbesc($channel['channel_hash']) + ); - // remove items - - Run::Summon( [ 'Channel_purge', $channel_id ] ); + $r = q( + "update channel set channel_deleted = '%s', channel_removed = 1 where channel_id = %d", + dbesc(datetime_convert()), + intval($channel_id) + ); - // if this was the default channel, set another one as default + // remove items - if (App::$account['account_default_channel'] == $channel_id) { - $r = q("select channel_id from channel where channel_account_id = %d and channel_removed = 0 limit 1", - intval(App::$account['account_id']), - intval(PAGE_REMOVED)); - if ($r) { - $rr = q("update account set account_default_channel = %d where account_id = %d", - intval($r[0]['channel_id']), - intval(App::$account['account_id']) - ); - logger("Default channel deleted, changing default to channel_id " . $r[0]['channel_id']); - } - else { - $rr = q("update account set account_default_channel = 0 where account_id = %d", - intval(App::$account['account_id']) - ); - } - } + Run::Summon([ 'Channel_purge', $channel_id ]); - logger('deleting hublocs',LOGGER_DEBUG); + // if this was the default channel, set another one as default - $r = q("update hubloc set hubloc_deleted = 1 where hubloc_hash = '%s' and hubloc_url = '%s' ", - dbesc($channel['channel_hash']), - dbesc(z_root()) - ); + if (App::$account['account_default_channel'] == $channel_id) { + $r = q( + "select channel_id from channel where channel_account_id = %d and channel_removed = 0 limit 1", + intval(App::$account['account_id']), + intval(PAGE_REMOVED) + ); + if ($r) { + $rr = q( + "update account set account_default_channel = %d where account_id = %d", + intval($r[0]['channel_id']), + intval(App::$account['account_id']) + ); + logger("Default channel deleted, changing default to channel_id " . $r[0]['channel_id']); + } else { + $rr = q( + "update account set account_default_channel = 0 where account_id = %d", + intval(App::$account['account_id']) + ); + } + } - // Do we have any valid hublocs remaining? + logger('deleting hublocs', LOGGER_DEBUG); - $hublocs = 0; + $r = q( + "update hubloc set hubloc_deleted = 1 where hubloc_hash = '%s' and hubloc_url = '%s' ", + dbesc($channel['channel_hash']), + dbesc(z_root()) + ); - $r = q("select hubloc_id from hubloc where hubloc_hash = '%s' and hubloc_deleted = 0", - dbesc($channel['channel_hash']) - ); - if ($r) { - $hublocs = count($r); - } - - if (! $hublocs) { - $r = q("update xchan set xchan_deleted = 1 where xchan_hash = '%s' ", - dbesc($channel['channel_hash']) - ); - // send a cleanup message to other servers - Run::Summon( [ 'Notifier', 'purge_all', $channel_id ] ); - } + // Do we have any valid hublocs remaining? - //remove from file system + $hublocs = 0; - $f = 'store/' . $channel['channel_address']; - // This shouldn't happen but make sure the address isn't empty because that could do bad things - if (is_dir($f) && $channel['channel_address']) { - @rrmdir($f); - } + $r = q( + "select hubloc_id from hubloc where hubloc_hash = '%s' and hubloc_deleted = 0", + dbesc($channel['channel_hash']) + ); + if ($r) { + $hublocs = count($r); + } - Run::Summon([ 'Directory', $channel_id ]); + if (! $hublocs) { + $r = q( + "update xchan set xchan_deleted = 1 where xchan_hash = '%s' ", + dbesc($channel['channel_hash']) + ); + // send a cleanup message to other servers + Run::Summon([ 'Notifier', 'purge_all', $channel_id ]); + } - if ($channel_id == local_channel() && $unset_session) { - App::$session->nuke(); - goaway(z_root()); - } + //remove from file system + + $f = 'store/' . $channel['channel_address']; + // This shouldn't happen but make sure the address isn't empty because that could do bad things + if (is_dir($f) && $channel['channel_address']) { + @rrmdir($f); + } + + Run::Summon([ 'Directory', $channel_id ]); + + if ($channel_id == local_channel() && $unset_session) { + App::$session->nuke(); + goaway(z_root()); + } } // execute this at least a week after removing a channel -function channel_remove_final($channel_id) { +function channel_remove_final($channel_id) +{ - q("delete from abook where abook_channel = %d", intval($channel_id)); - q("delete from abconfig where chan = %d", intval($channel_id)); - q("delete from pconfig where uid = %d", intval($channel_id)); - q("delete from channel where channel_id = %d", intval($channel_id)); + q("delete from abook where abook_channel = %d", intval($channel_id)); + q("delete from abconfig where chan = %d", intval($channel_id)); + q("delete from pconfig where uid = %d", intval($channel_id)); + q("delete from channel where channel_id = %d", intval($channel_id)); } @@ -2357,119 +2500,131 @@ function channel_remove_final($channel_id) { * is in fact the resource owner whose channel_id is being checked. * * @param int $channel_id - * @return boolean + * @return bool */ -function channel_codeallowed($channel_id) { - if(! intval($channel_id)) - return false; +function channel_codeallowed($channel_id) +{ + if (! intval($channel_id)) { + return false; + } - $x = channelx_by_n($channel_id); - if(($x) && ($x['channel_pageflags'] & PAGE_ALLOWCODE)) - return true; + $x = channelx_by_n($channel_id); + if (($x) && ($x['channel_pageflags'] & PAGE_ALLOWCODE)) { + return true; + } - return false; + return false; } -function anon_identity_init($reqvars) { +function anon_identity_init($reqvars) +{ - $x = [ - 'request_vars' => $reqvars, - 'xchan' => null, - 'success' => 'unset' - ]; - /** - * @hooks anon_identity_init - * * \e array \b request_vars - * * \e string \b xchan - return value - * * \e string|int \b success - Must be a number, so xchan return value gets used - */ - call_hooks('anon_identity_init', $x); + $x = [ + 'request_vars' => $reqvars, + 'xchan' => null, + 'success' => 'unset' + ]; + /** + * @hooks anon_identity_init + * * \e array \b request_vars + * * \e string \b xchan - return value + * * \e string|int \b success - Must be a number, so xchan return value gets used + */ + call_hooks('anon_identity_init', $x); - if($x['success'] !== 'unset' && intval($x['success']) && $x['xchan']) - return $x['xchan']; + if ($x['success'] !== 'unset' && intval($x['success']) && $x['xchan']) { + return $x['xchan']; + } - // allow a captcha handler to over-ride - if($x['success'] !== 'unset' && (intval($x['success']) === 0)) - return false; + // allow a captcha handler to over-ride + if ($x['success'] !== 'unset' && (intval($x['success']) === 0)) { + return false; + } - $anon_name = strip_tags(trim($reqvars['anonname'])); - $anon_email = strip_tags(trim($reqvars['anonmail'])); - $anon_url = strip_tags(trim($reqvars['anonurl'])); + $anon_name = strip_tags(trim($reqvars['anonname'])); + $anon_email = strip_tags(trim($reqvars['anonmail'])); + $anon_url = strip_tags(trim($reqvars['anonurl'])); - if(! ($anon_name && $anon_email)) { - logger('anonymous commenter did not complete form'); - return false; - } + if (! ($anon_name && $anon_email)) { + logger('anonymous commenter did not complete form'); + return false; + } - if(! validate_email($anon_email)) { - logger('enonymous email not valid'); - return false; - } + if (! validate_email($anon_email)) { + logger('enonymous email not valid'); + return false; + } - if(! $anon_url) - $anon_url = z_root(); + if (! $anon_url) { + $anon_url = z_root(); + } - $hash = hash('md5',$anon_email); + $hash = hash('md5', $anon_email); - $x = q("select * from xchan where xchan_guid = '%s' and xchan_hash = '%s' and xchan_network = 'anon' limit 1", - dbesc($anon_email), - dbesc($hash) - ); + $x = q( + "select * from xchan where xchan_guid = '%s' and xchan_hash = '%s' and xchan_network = 'anon' limit 1", + dbesc($anon_email), + dbesc($hash) + ); - if(! $x) { - xchan_store_lowlevel([ - 'xchan_guid' => $anon_email, - 'xchan_hash' => $hash, - 'xchan_name' => $anon_name, - 'xchan_url' => $anon_url, - 'xchan_network' => 'anon', - 'xchan_updated' => datetime_convert(), - 'xchan_name_date' => datetime_convert() - ]); + if (! $x) { + xchan_store_lowlevel([ + 'xchan_guid' => $anon_email, + 'xchan_hash' => $hash, + 'xchan_name' => $anon_name, + 'xchan_url' => $anon_url, + 'xchan_network' => 'anon', + 'xchan_updated' => datetime_convert(), + 'xchan_name_date' => datetime_convert() + ]); - $x = q("select * from xchan where xchan_guid = '%s' and xchan_hash = '%s' and xchan_network = 'anon' limit 1", - dbesc($anon_email), - dbesc($hash) - ); + $x = q( + "select * from xchan where xchan_guid = '%s' and xchan_hash = '%s' and xchan_network = 'anon' limit 1", + dbesc($anon_email), + dbesc($hash) + ); - $photo = z_root() . '/' . get_default_profile_photo(300); - $photos = import_remote_xchan_photo($photo,$hash); - if ($photos) { - $r = q("update xchan set xchan_updated = '%s', xchan_photo_date = '%s', xchan_photo_l = '%s', xchan_photo_m = '%s', xchan_photo_s = '%s', xchan_photo_mimetype = '%s' where xchan_guid = '%s' and xchan_hash = '%s' and xchan_network = 'anon' ", - dbesc(datetime_convert()), - dbesc(datetime_convert()), - dbesc($photos[0]), - dbesc($photos[1]), - dbesc($photos[2]), - dbesc($photos[3]), - dbesc($anon_email), - dbesc($hash) - ); - } - } + $photo = z_root() . '/' . get_default_profile_photo(300); + $photos = import_remote_xchan_photo($photo, $hash); + if ($photos) { + $r = q( + "update xchan set xchan_updated = '%s', xchan_photo_date = '%s', xchan_photo_l = '%s', xchan_photo_m = '%s', xchan_photo_s = '%s', xchan_photo_mimetype = '%s' where xchan_guid = '%s' and xchan_hash = '%s' and xchan_network = 'anon' ", + dbesc(datetime_convert()), + dbesc(datetime_convert()), + dbesc($photos[0]), + dbesc($photos[1]), + dbesc($photos[2]), + dbesc($photos[3]), + dbesc($anon_email), + dbesc($hash) + ); + } + } - return $x[0]; + return $x[0]; } -function channel_url($channel) { +function channel_url($channel) +{ - // data validation - if this is wrong, log the call stack so we can find the issue + // data validation - if this is wrong, log the call stack so we can find the issue - if (! is_array($channel)) { - btlogger('not a channel array: ' . print_r($channel,true)); - } + if (! is_array($channel)) { + btlogger('not a channel array: ' . print_r($channel, true)); + } - if ($channel['channel_address'] === App::get_hostname() || intval($channel['channel_system'])) { - return z_root(); - } + if ($channel['channel_address'] === App::get_hostname() || intval($channel['channel_system'])) { + return z_root(); + } - return (($channel) ? z_root() . '/channel/' . $channel['channel_address'] : z_root()); + return (($channel) ? z_root() . '/channel/' . $channel['channel_address'] : z_root()); } -function is_group($uid) { - $role = get_pconfig($uid,'system','permissions_role'); - $rolesettings = PermissionRoles::role_perms($role); - return ((isset($rolesettings['channel_type']) && $rolesettings['channel_type'] === 'group') ? true : false); +function is_group($uid) +{ + $role = get_pconfig($uid, 'system', 'permissions_role'); + $rolesettings = PermissionRoles::role_perms($role); + return ((isset($rolesettings['channel_type']) && $rolesettings['channel_type'] === 'group') ? true : false); } diff --git a/include/cli_startup.php b/include/cli_startup.php index cf99836d6..faf4792b2 100644 --- a/include/cli_startup.php +++ b/include/cli_startup.php @@ -4,9 +4,9 @@ require_once('boot.php'); // Everything we need to boot standalone 'background' processes -function cli_startup() { +function cli_startup() +{ - sys_boot(); - App::set_baseurl(get_config('system','baseurl')); - -} \ No newline at end of file + sys_boot(); + App::set_baseurl(get_config('system', 'baseurl')); +} diff --git a/include/config.php b/include/config.php index 0be791715..214ed56d9 100644 --- a/include/config.php +++ b/include/config.php @@ -1,4 +1,5 @@ ((array_key_exists('abook_account',$arr)) ? $arr['abook_account'] : 0), - 'abook_channel' => ((array_key_exists('abook_channel',$arr)) ? $arr['abook_channel'] : 0), - 'abook_xchan' => ((array_key_exists('abook_xchan',$arr)) ? $arr['abook_xchan'] : ''), - 'abook_alias' => ((array_key_exists('abook_alias',$arr)) ? $arr['abook_alias'] : ''), - 'abook_my_perms' => ((array_key_exists('abook_my_perms',$arr)) ? $arr['abook_my_perms'] : 0), - 'abook_their_perms' => ((array_key_exists('abook_their_perms',$arr)) ? $arr['abook_their_perms'] : 0), - 'abook_closeness' => ((array_key_exists('abook_closeness',$arr)) ? $arr['abook_closeness'] : 99), - 'abook_created' => ((array_key_exists('abook_created',$arr)) ? $arr['abook_created'] : NULL_DATE), - 'abook_updated' => ((array_key_exists('abook_updated',$arr)) ? $arr['abook_updated'] : NULL_DATE), - 'abook_connected' => ((array_key_exists('abook_connected',$arr)) ? $arr['abook_connected'] : NULL_DATE), - 'abook_dob' => ((array_key_exists('abook_dob',$arr)) ? $arr['abook_dob'] : NULL_DATE), - 'abook_flags' => ((array_key_exists('abook_flags',$arr)) ? $arr['abook_flags'] : 0), - 'abook_censor' => ((array_key_exists('abook_censor',$arr)) ? $arr['abook_censor'] : 0), - 'abook_blocked' => ((array_key_exists('abook_blocked',$arr)) ? $arr['abook_blocked'] : 0), - 'abook_ignored' => ((array_key_exists('abook_ignored',$arr)) ? $arr['abook_ignored'] : 0), - 'abook_hidden' => ((array_key_exists('abook_hidden',$arr)) ? $arr['abook_hidden'] : 0), - 'abook_archived' => ((array_key_exists('abook_archived',$arr)) ? $arr['abook_archived'] : 0), - 'abook_pending' => ((array_key_exists('abook_pending',$arr)) ? $arr['abook_pending'] : 0), - 'abook_unconnected' => ((array_key_exists('abook_unconnected',$arr)) ? $arr['abook_unconnected'] : 0), - 'abook_self' => ((array_key_exists('abook_self',$arr)) ? $arr['abook_self'] : 0), - 'abook_feed' => ((array_key_exists('abook_feed',$arr)) ? $arr['abook_feed'] : 0), - 'abook_not_here' => ((array_key_exists('abook_not_here',$arr)) ? $arr['abook_not_here'] : 0), - 'abook_profile' => ((array_key_exists('abook_profile',$arr)) ? $arr['abook_profile'] : ''), - 'abook_incl' => ((array_key_exists('abook_incl',$arr)) ? $arr['abook_incl'] : ''), - 'abook_excl' => ((array_key_exists('abook_excl',$arr)) ? $arr['abook_excl'] : ''), - 'abook_instance' => ((array_key_exists('abook_instance',$arr)) ? $arr['abook_instance'] : '') - ]; - - return create_table_from_array('abook',$store); + $store = [ + 'abook_account' => ((array_key_exists('abook_account', $arr)) ? $arr['abook_account'] : 0), + 'abook_channel' => ((array_key_exists('abook_channel', $arr)) ? $arr['abook_channel'] : 0), + 'abook_xchan' => ((array_key_exists('abook_xchan', $arr)) ? $arr['abook_xchan'] : ''), + 'abook_alias' => ((array_key_exists('abook_alias', $arr)) ? $arr['abook_alias'] : ''), + 'abook_my_perms' => ((array_key_exists('abook_my_perms', $arr)) ? $arr['abook_my_perms'] : 0), + 'abook_their_perms' => ((array_key_exists('abook_their_perms', $arr)) ? $arr['abook_their_perms'] : 0), + 'abook_closeness' => ((array_key_exists('abook_closeness', $arr)) ? $arr['abook_closeness'] : 99), + 'abook_created' => ((array_key_exists('abook_created', $arr)) ? $arr['abook_created'] : NULL_DATE), + 'abook_updated' => ((array_key_exists('abook_updated', $arr)) ? $arr['abook_updated'] : NULL_DATE), + 'abook_connected' => ((array_key_exists('abook_connected', $arr)) ? $arr['abook_connected'] : NULL_DATE), + 'abook_dob' => ((array_key_exists('abook_dob', $arr)) ? $arr['abook_dob'] : NULL_DATE), + 'abook_flags' => ((array_key_exists('abook_flags', $arr)) ? $arr['abook_flags'] : 0), + 'abook_censor' => ((array_key_exists('abook_censor', $arr)) ? $arr['abook_censor'] : 0), + 'abook_blocked' => ((array_key_exists('abook_blocked', $arr)) ? $arr['abook_blocked'] : 0), + 'abook_ignored' => ((array_key_exists('abook_ignored', $arr)) ? $arr['abook_ignored'] : 0), + 'abook_hidden' => ((array_key_exists('abook_hidden', $arr)) ? $arr['abook_hidden'] : 0), + 'abook_archived' => ((array_key_exists('abook_archived', $arr)) ? $arr['abook_archived'] : 0), + 'abook_pending' => ((array_key_exists('abook_pending', $arr)) ? $arr['abook_pending'] : 0), + 'abook_unconnected' => ((array_key_exists('abook_unconnected', $arr)) ? $arr['abook_unconnected'] : 0), + 'abook_self' => ((array_key_exists('abook_self', $arr)) ? $arr['abook_self'] : 0), + 'abook_feed' => ((array_key_exists('abook_feed', $arr)) ? $arr['abook_feed'] : 0), + 'abook_not_here' => ((array_key_exists('abook_not_here', $arr)) ? $arr['abook_not_here'] : 0), + 'abook_profile' => ((array_key_exists('abook_profile', $arr)) ? $arr['abook_profile'] : ''), + 'abook_incl' => ((array_key_exists('abook_incl', $arr)) ? $arr['abook_incl'] : ''), + 'abook_excl' => ((array_key_exists('abook_excl', $arr)) ? $arr['abook_excl'] : ''), + 'abook_instance' => ((array_key_exists('abook_instance', $arr)) ? $arr['abook_instance'] : '') + ]; + return create_table_from_array('abook', $store); } -function rconnect_url($channel_id,$xchan) { +function rconnect_url($channel_id, $xchan) +{ - if (! $xchan) { - return z_root() . '/fedi_id/' . $channel_id; - } + if (! $xchan) { + return z_root() . '/fedi_id/' . $channel_id; + } - $r = q("select abook_id from abook where abook_channel = %d and abook_xchan = '%s' limit 1", - intval($channel_id), - dbesc($xchan) - ); - - // Already connected - if ($r) { - return EMPTY_STR; - } - - $r = q("select * from xchan where xchan_hash = '%s' limit 1", - dbesc($xchan) - ); + $r = q( + "select abook_id from abook where abook_channel = %d and abook_xchan = '%s' limit 1", + intval($channel_id), + dbesc($xchan) + ); - if (($r) && ($r[0]['xchan_follow'])) { - return $r[0]['xchan_follow']; - } + // Already connected + if ($r) { + return EMPTY_STR; + } - $r = q("select hubloc_url from hubloc where hubloc_hash = '%s' and hubloc_primary = 1 limit 1", - dbesc($xchan) - ); + $r = q( + "select * from xchan where xchan_hash = '%s' limit 1", + dbesc($xchan) + ); - if ($r) { - return $r[0]['hubloc_url'] . '/follow?f=&url=%s'; - } - - return EMPTY_STR; + if (($r) && ($r[0]['xchan_follow'])) { + return $r[0]['xchan_follow']; + } + $r = q( + "select hubloc_url from hubloc where hubloc_hash = '%s' and hubloc_primary = 1 limit 1", + dbesc($xchan) + ); + + if ($r) { + return $r[0]['hubloc_url'] . '/follow?f=&url=%s'; + } + + return EMPTY_STR; } -function abook_connections($channel_id, $sql_conditions = '') { - $r = q("select * from abook left join xchan on abook_xchan = xchan_hash where abook_channel = %d +function abook_connections($channel_id, $sql_conditions = '') +{ + $r = q( + "select * from abook left join xchan on abook_xchan = xchan_hash where abook_channel = %d and abook_self = 0 $sql_conditions", - intval($channel_id) - ); - return(($r) ? $r : []); -} - - -function abook_by_hash($channel_id, $hash) { - $r = q("select * from abook left join xchan on abook_xchan = xchan_hash where abook_channel = %d - and abook_self = 0 and abook_xchan = '%s'", - intval($channel_id), - dbesc($hash) - ); - return(($r) ? array_shift($r) : false); -} - -function abook_self($channel_id) { - $r = q("select * from abook left join xchan on abook_xchan = xchan_hash where abook_channel = %d - and abook_self = 1 limit 1", - intval($channel_id) - ); - return(($r) ? $r[0] : []); -} - - -function vcard_from_xchan($xchan, $observer = null, $mode = '') { - - if (! $xchan) { - if (App::$poi) { - $xchan = App::$poi; - } - elseif (is_array(App::$profile) && App::$profile['channel_hash']) { - $r = q("select * from xchan where xchan_hash = '%s' limit 1", - dbesc(App::$profile['channel_hash']) - ); - if($r) { - $xchan = $r[0]; - } - } - } - - if (! $xchan) { - return; - } - - $connect = false; - if (local_channel()) { - $r = q("select * from abook where abook_xchan = '%s' and abook_channel = %d limit 1", - dbesc($xchan['xchan_hash']), - intval(local_channel()) - ); - if (! $r) { - $connect = t('Connect'); - } - } - - // don't provide a connect button for transient or one-way identities - - if (in_array($xchan['xchan_network'],[ 'rss','anon','token','unknown' ])) { - $connect = false; - } - - if (array_key_exists('channel_id',$xchan)) { - App::$profile_uid = $xchan['channel_id']; - } - - $url = (($observer) - ? z_root() . '/magic?f=&owa=1&bdest=' . bin2hex($xchan['xchan_url']) . '&addr=' . $xchan['xchan_addr'] - : $xchan['xchan_url'] - ); - - return replace_macros(get_markup_template('xchan_vcard.tpl'), [ - '$name' => $xchan['xchan_name'], - '$photo' => ((is_array(App::$profile) && array_key_exists('photo',App::$profile)) ? App::$profile['photo'] : $xchan['xchan_photo_l']), - '$follow' => urlencode(($xchan['xchan_addr']) ? $xchan['xchan_addr'] : $xchan['xchan_url']), - '$link' => zid($xchan['xchan_url']), - '$connect' => $connect, - '$newwin' => (($mode === 'chanview') ? t('New window') : EMPTY_STR), - '$newtit' => t('Open the selected location in a different window or browser tab'), - '$url' => $url, - ]); + intval($channel_id) + ); + return(($r) ? $r : []); } -function abook_toggle_flag($abook,$flag) { - $field = EMPTY_STR; +function abook_by_hash($channel_id, $hash) +{ + $r = q( + "select * from abook left join xchan on abook_xchan = xchan_hash where abook_channel = %d + and abook_self = 0 and abook_xchan = '%s'", + intval($channel_id), + dbesc($hash) + ); + return(($r) ? array_shift($r) : false); +} - switch ($flag) { - case ABOOK_FLAG_BLOCKED: - $field = 'abook_blocked'; - break; - case ABOOK_FLAG_IGNORED: - $field = 'abook_ignored'; - break; - case ABOOK_FLAG_CENSORED: - $field = 'abook_censor'; - break; - case ABOOK_FLAG_HIDDEN: - $field = 'abook_hidden'; - break; - case ABOOK_FLAG_ARCHIVED: - $field = 'abook_archived'; - break; - case ABOOK_FLAG_PENDING: - $field = 'abook_pending'; - break; - case ABOOK_FLAG_UNCONNECTED: - $field = 'abook_unconnected'; - break; - case ABOOK_FLAG_SELF: - $field = 'abook_self'; - break; - case ABOOK_FLAG_FEED: - $field = 'abook_feed'; - break; - default: - break; - } - if (! $field) { - return; - } +function abook_self($channel_id) +{ + $r = q( + "select * from abook left join xchan on abook_xchan = xchan_hash where abook_channel = %d + and abook_self = 1 limit 1", + intval($channel_id) + ); + return(($r) ? $r[0] : []); +} - $r = q("UPDATE abook set $field = (1 - $field) where abook_id = %d and abook_channel = %d", - intval($abook['abook_id']), - intval($abook['abook_channel']) - ); - // if unsetting the archive bit, update the timestamps so we'll try to connect for an additional 30 days. +function vcard_from_xchan($xchan, $observer = null, $mode = '') +{ - if(($flag === ABOOK_FLAG_ARCHIVED) && (intval($abook['abook_archived']))) { - $r = q("update abook set abook_connected = '%s', abook_updated = '%s' + if (! $xchan) { + if (App::$poi) { + $xchan = App::$poi; + } elseif (is_array(App::$profile) && App::$profile['channel_hash']) { + $r = q( + "select * from xchan where xchan_hash = '%s' limit 1", + dbesc(App::$profile['channel_hash']) + ); + if ($r) { + $xchan = $r[0]; + } + } + } + + if (! $xchan) { + return; + } + + $connect = false; + if (local_channel()) { + $r = q( + "select * from abook where abook_xchan = '%s' and abook_channel = %d limit 1", + dbesc($xchan['xchan_hash']), + intval(local_channel()) + ); + if (! $r) { + $connect = t('Connect'); + } + } + + // don't provide a connect button for transient or one-way identities + + if (in_array($xchan['xchan_network'], [ 'rss','anon','token','unknown' ])) { + $connect = false; + } + + if (array_key_exists('channel_id', $xchan)) { + App::$profile_uid = $xchan['channel_id']; + } + + $url = (($observer) + ? z_root() . '/magic?f=&owa=1&bdest=' . bin2hex($xchan['xchan_url']) . '&addr=' . $xchan['xchan_addr'] + : $xchan['xchan_url'] + ); + + return replace_macros(get_markup_template('xchan_vcard.tpl'), [ + '$name' => $xchan['xchan_name'], + '$photo' => ((is_array(App::$profile) && array_key_exists('photo', App::$profile)) ? App::$profile['photo'] : $xchan['xchan_photo_l']), + '$follow' => urlencode(($xchan['xchan_addr']) ? $xchan['xchan_addr'] : $xchan['xchan_url']), + '$link' => zid($xchan['xchan_url']), + '$connect' => $connect, + '$newwin' => (($mode === 'chanview') ? t('New window') : EMPTY_STR), + '$newtit' => t('Open the selected location in a different window or browser tab'), + '$url' => $url, + ]); +} + +function abook_toggle_flag($abook, $flag) +{ + + $field = EMPTY_STR; + + switch ($flag) { + case ABOOK_FLAG_BLOCKED: + $field = 'abook_blocked'; + break; + case ABOOK_FLAG_IGNORED: + $field = 'abook_ignored'; + break; + case ABOOK_FLAG_CENSORED: + $field = 'abook_censor'; + break; + case ABOOK_FLAG_HIDDEN: + $field = 'abook_hidden'; + break; + case ABOOK_FLAG_ARCHIVED: + $field = 'abook_archived'; + break; + case ABOOK_FLAG_PENDING: + $field = 'abook_pending'; + break; + case ABOOK_FLAG_UNCONNECTED: + $field = 'abook_unconnected'; + break; + case ABOOK_FLAG_SELF: + $field = 'abook_self'; + break; + case ABOOK_FLAG_FEED: + $field = 'abook_feed'; + break; + default: + break; + } + if (! $field) { + return; + } + + $r = q( + "UPDATE abook set $field = (1 - $field) where abook_id = %d and abook_channel = %d", + intval($abook['abook_id']), + intval($abook['abook_channel']) + ); + + // if unsetting the archive bit, update the timestamps so we'll try to connect for an additional 30 days. + + if (($flag === ABOOK_FLAG_ARCHIVED) && (intval($abook['abook_archived']))) { + $r = q( + "update abook set abook_connected = '%s', abook_updated = '%s' where abook_id = %d and abook_channel = %d", - dbesc(datetime_convert()), - dbesc(datetime_convert()), - intval($abook['abook_id']), - intval($abook['abook_channel']) - ); - } - - return $r; + dbesc(datetime_convert()), + dbesc(datetime_convert()), + intval($abook['abook_id']), + intval($abook['abook_channel']) + ); + } + return $r; } @@ -232,731 +248,762 @@ function abook_toggle_flag($abook,$flag) { */ -function mark_orphan_hubsxchans() { +function mark_orphan_hubsxchans() +{ - return; + return; - $dirmode = intval(get_config('system','directory_mode')); - if ($dirmode == DIRECTORY_MODE_NORMAL) { - return; - } + $dirmode = intval(get_config('system', 'directory_mode')); + if ($dirmode == DIRECTORY_MODE_NORMAL) { + return; + } $r = q("update hubloc set hubloc_deleted = 1 where hubloc_deleted = 0 and hubloc_network in ('nomad','zot6') and hubloc_connected < %s - interval %s", db_utcnow(), db_quoteinterval('36 day') ); - $r = q("select hubloc_id, hubloc_hash from hubloc where hubloc_deleted = 1 and hubloc_orphancheck = 0"); + $r = q("select hubloc_id, hubloc_hash from hubloc where hubloc_deleted = 1 and hubloc_orphancheck = 0"); - if ($r) { - foreach($r as $rr) { + if ($r) { + foreach ($r as $rr) { + // see if any other hublocs are still alive for this channel - // see if any other hublocs are still alive for this channel + $x = q( + "select * from hubloc where hubloc_hash = '%s' and hubloc_deleted = 0", + dbesc($rr['hubloc_hash']) + ); + if ($x) { + // yes - if the xchan was marked as an orphan, undo it - $x = q("select * from hubloc where hubloc_hash = '%s' and hubloc_deleted = 0", - dbesc($rr['hubloc_hash']) - ); - if ($x) { + $y = q( + "update xchan set xchan_orphan = 0 where xchan_orphan = 1 and xchan_hash = '%s'", + dbesc($rr['hubloc_hash']) + ); + } else { + // nope - mark the xchan as an orphan - // yes - if the xchan was marked as an orphan, undo it + $y = q( + "update xchan set xchan_orphan = 1 where xchan_hash = '%s'", + dbesc($rr['hubloc_hash']) + ); + } - $y = q("update xchan set xchan_orphan = 0 where xchan_orphan = 1 and xchan_hash = '%s'", - dbesc($rr['hubloc_hash']) - ); - } - else { - - // nope - mark the xchan as an orphan - - $y = q("update xchan set xchan_orphan = 1 where xchan_hash = '%s'", - dbesc($rr['hubloc_hash']) - ); - } - - // mark that we've checked this entry so we don't need to do it again - - $y = q("update hubloc set hubloc_orphancheck = 1 where hubloc_id = %d", - dbesc($rr['hubloc_id']) - ); - } - } + // mark that we've checked this entry so we don't need to do it again + $y = q( + "update hubloc set hubloc_orphancheck = 1 where hubloc_id = %d", + dbesc($rr['hubloc_id']) + ); + } + } } -function remove_all_xchan_resources($xchan, $channel_id = 0) { +function remove_all_xchan_resources($xchan, $channel_id = 0) +{ - // $channel_id is reserved for future use. + // $channel_id is reserved for future use. - if (intval($channel_id) === 0) { + if (intval($channel_id) === 0) { + if (! $xchan) { + return; + } - if (! $xchan) { - return; - } + // this function is only to be executed on remote servers where only the xchan exists and there is no associated channel. - // this function is only to be executed on remote servers where only the xchan exists and there is no associated channel. - - $c = q("select channel_id from channel where channel_hash = '%s'", - dbesc($xchan) - ); + $c = q( + "select channel_id from channel where channel_hash = '%s'", + dbesc($xchan) + ); - if ($c) { - return; - } + if ($c) { + return; + } - $dirmode = intval(get_config('system','directory_mode')); + $dirmode = intval(get_config('system', 'directory_mode')); - // note: we will not remove "guest" submitted files/photos this xchan created in the file spaces of others. - // We will however remove all their posts and comments. - - $r = q("select id from item where ( author_xchan = '%s' or owner_xchan = '%s' ) ", - dbesc($xchan), - dbesc($xchan) - ); - if ($r) { - foreach ($r as $rr) { - drop_item($rr['id'],false); - } - } - $r = q("delete from event where event_xchan = '%s'", - dbesc($xchan) - ); - $r = q("delete from pgrp_member where xchan = '%s'", - dbesc($xchan) - ); + // note: we will not remove "guest" submitted files/photos this xchan created in the file spaces of others. + // We will however remove all their posts and comments. - $r = q("delete from xlink where ( xlink_xchan = '%s' or xlink_link = '%s' )", - dbesc($xchan), - dbesc($xchan) - ); + $r = q( + "select id from item where ( author_xchan = '%s' or owner_xchan = '%s' ) ", + dbesc($xchan), + dbesc($xchan) + ); + if ($r) { + foreach ($r as $rr) { + drop_item($rr['id'], false); + } + } + $r = q( + "delete from event where event_xchan = '%s'", + dbesc($xchan) + ); + $r = q( + "delete from pgrp_member where xchan = '%s'", + dbesc($xchan) + ); - $r = q("delete from abook where abook_xchan = '%s'", - dbesc($xchan) - ); + $r = q( + "delete from xlink where ( xlink_xchan = '%s' or xlink_link = '%s' )", + dbesc($xchan), + dbesc($xchan) + ); - $r = q("delete from abconfig where xchan = '%s'", - dbesc($xchan) - ); + $r = q( + "delete from abook where abook_xchan = '%s'", + dbesc($xchan) + ); - if ($dirmode === false || $dirmode == DIRECTORY_MODE_NORMAL) { + $r = q( + "delete from abconfig where xchan = '%s'", + dbesc($xchan) + ); - $r = q("delete from xchan where xchan_hash = '%s'", - dbesc($xchan) - ); - $r = q("delete from hubloc where hubloc_hash = '%s'", - dbesc($xchan) - ); - $r = q("delete from xprof where xprof_hash = '%s'", - dbesc($xchan) - ); + if ($dirmode === false || $dirmode == DIRECTORY_MODE_NORMAL) { + $r = q( + "delete from xchan where xchan_hash = '%s'", + dbesc($xchan) + ); + $r = q( + "delete from hubloc where hubloc_hash = '%s'", + dbesc($xchan) + ); + $r = q( + "delete from xprof where xprof_hash = '%s'", + dbesc($xchan) + ); + } else { + // directory servers need to keep the record around for sync purposes - mark it deleted + $r = q( + "update hubloc set hubloc_deleted = 1 where hubloc_hash = '%s'", + dbesc($xchan) + ); - } - else { - - // directory servers need to keep the record around for sync purposes - mark it deleted - - $r = q("update hubloc set hubloc_deleted = 1 where hubloc_hash = '%s'", - dbesc($xchan) - ); - - $r = q("update xchan set xchan_deleted = 1 where xchan_hash = '%s'", - dbesc($xchan) - ); - } - } + $r = q( + "update xchan set xchan_deleted = 1 where xchan_hash = '%s'", + dbesc($xchan) + ); + } + } } -function contact_remove($channel_id, $abook_id, $atoken_sync = false) { +function contact_remove($channel_id, $abook_id, $atoken_sync = false) +{ - if ((! $channel_id) || (! $abook_id)) { - return false; - } + if ((! $channel_id) || (! $abook_id)) { + return false; + } - logger('removing connection ' . $abook_id . ' for channel ' . $channel_id,LOGGER_DEBUG); + logger('removing connection ' . $abook_id . ' for channel ' . $channel_id, LOGGER_DEBUG); - $x = [ - 'channel_id' => $channel_id, - 'abook_id' => $abook_id - ]; - - call_hooks('connection_remove',$x); + $x = [ + 'channel_id' => $channel_id, + 'abook_id' => $abook_id + ]; - $archive = get_pconfig($channel_id, 'system','archive_removed_contacts'); - if ($archive) { - q("update abook set abook_archived = 1 where abook_id = %d and abook_channel = %d", - intval($abook_id), - intval($channel_id) - ); - return true; - } + call_hooks('connection_remove', $x); - $r = q("select * from abook where abook_id = %d and abook_channel = %d limit 1", - intval($abook_id), - intval($channel_id) - ); + $archive = get_pconfig($channel_id, 'system', 'archive_removed_contacts'); + if ($archive) { + q( + "update abook set abook_archived = 1 where abook_id = %d and abook_channel = %d", + intval($abook_id), + intval($channel_id) + ); + return true; + } - if (! $r) { - return false; - } - - $abook = $r[0]; + $r = q( + "select * from abook where abook_id = %d and abook_channel = %d limit 1", + intval($abook_id), + intval($channel_id) + ); - if (intval($abook['abook_self'])) { - return false; - } + if (! $r) { + return false; + } - // if this is an atoken, delete the atoken record + $abook = $r[0]; - if ($atoken_sync) { - $xchan = q("select * from xchan where xchan_hash = '%s'", - dbesc($abook['abook_xchan']) - ); - if ($xchan && strpos($xchan[0]['xchan_addr'],'guest:') === 0 && strpos($abook['abook_xchan'],'.')){ - $atoken_guid = substr($abook['abook_xchan'],strrpos($abook['abook_xchan'],'.') + 1); - if ($atoken_guid) { - atoken_delete_and_sync($channel_id,$atoken_guid); - } - } - } + if (intval($abook['abook_self'])) { + return false; + } - // remove items in the background as this can take some time + // if this is an atoken, delete the atoken record - Run::Summon( [ 'Delxitems', $channel_id, $abook['abook_xchan'] ] ); + if ($atoken_sync) { + $xchan = q( + "select * from xchan where xchan_hash = '%s'", + dbesc($abook['abook_xchan']) + ); + if ($xchan && strpos($xchan[0]['xchan_addr'], 'guest:') === 0 && strpos($abook['abook_xchan'], '.')) { + $atoken_guid = substr($abook['abook_xchan'], strrpos($abook['abook_xchan'], '.') + 1); + if ($atoken_guid) { + atoken_delete_and_sync($channel_id, $atoken_guid); + } + } + } - $r = q("delete from abook where abook_id = %d and abook_channel = %d", - intval($abook['abook_id']), - intval($channel_id) - ); + // remove items in the background as this can take some time - $r = q("delete from event where event_xchan = '%s' and uid = %d", - dbesc($abook['abook_xchan']), - intval($channel_id) - ); + Run::Summon([ 'Delxitems', $channel_id, $abook['abook_xchan'] ]); - $r = q("delete from pgrp_member where xchan = '%s' and uid = %d", - dbesc($abook['abook_xchan']), - intval($channel_id) - ); + $r = q( + "delete from abook where abook_id = %d and abook_channel = %d", + intval($abook['abook_id']), + intval($channel_id) + ); - $r = q("delete from mail where ( from_xchan = '%s' or to_xchan = '%s' ) and channel_id = %d ", - dbesc($abook['abook_xchan']), - dbesc($abook['abook_xchan']), - intval($channel_id) - ); + $r = q( + "delete from event where event_xchan = '%s' and uid = %d", + dbesc($abook['abook_xchan']), + intval($channel_id) + ); - $r = q("delete from abconfig where chan = %d and xchan = '%s'", - intval($channel_id), - dbesc($abook['abook_xchan']) - ); + $r = q( + "delete from pgrp_member where xchan = '%s' and uid = %d", + dbesc($abook['abook_xchan']), + intval($channel_id) + ); - return true; + $r = q( + "delete from mail where ( from_xchan = '%s' or to_xchan = '%s' ) and channel_id = %d ", + dbesc($abook['abook_xchan']), + dbesc($abook['abook_xchan']), + intval($channel_id) + ); + + $r = q( + "delete from abconfig where chan = %d and xchan = '%s'", + intval($channel_id), + dbesc($abook['abook_xchan']) + ); + + return true; } -function remove_abook_items($channel_id,$xchan_hash) { +function remove_abook_items($channel_id, $xchan_hash) +{ - $r = q("select id, parent from item where (owner_xchan = '%s' or author_xchan = '%s') and uid = %d and item_retained = 0 and item_starred = 0", - dbesc($xchan_hash), - dbesc($xchan_hash), - intval($channel_id) - ); - if (! $r) { - return; - } + $r = q( + "select id, parent from item where (owner_xchan = '%s' or author_xchan = '%s') and uid = %d and item_retained = 0 and item_starred = 0", + dbesc($xchan_hash), + dbesc($xchan_hash), + intval($channel_id) + ); + if (! $r) { + return; + } - $already_saved = []; - foreach ($r as $rr) { - $w = $x = $y = null; - - // optimise so we only process newly seen parent items - if (in_array($rr['parent'],$already_saved)) { - continue; - } - // if this isn't the parent, fetch the parent's item_retained and item_starred to see if the conversation - // should be retained - if ($rr['id'] != $rr['parent']) { - $w = q("select id, item_retained, item_starred from item where id = %d", - intval($rr['parent']) - ); - if ($w) { - // see if the conversation was filed - $x = q("select uid from term where otype = %d and oid = %d and ttype = %d limit 1", - intval(TERM_OBJ_POST), - intval($w[0]['id']), - intval(TERM_FILE) - ); - if (intval($w[0]['item_retained']) || intval($w[0]['item_starred']) || $x) { - $already_saved[] = $rr['parent']; - continue; - } - } - } - // see if this item was filed - $y = q("select uid from term where otype = %d and oid = %d and ttype = %d limit 1", - intval(TERM_OBJ_POST), - intval($rr['id']), - intval(TERM_FILE) - ); - if ($y) { - continue; - } - drop_item($rr['id'],false); - } + $already_saved = []; + foreach ($r as $rr) { + $w = $x = $y = null; + + // optimise so we only process newly seen parent items + if (in_array($rr['parent'], $already_saved)) { + continue; + } + // if this isn't the parent, fetch the parent's item_retained and item_starred to see if the conversation + // should be retained + if ($rr['id'] != $rr['parent']) { + $w = q( + "select id, item_retained, item_starred from item where id = %d", + intval($rr['parent']) + ); + if ($w) { + // see if the conversation was filed + $x = q( + "select uid from term where otype = %d and oid = %d and ttype = %d limit 1", + intval(TERM_OBJ_POST), + intval($w[0]['id']), + intval(TERM_FILE) + ); + if (intval($w[0]['item_retained']) || intval($w[0]['item_starred']) || $x) { + $already_saved[] = $rr['parent']; + continue; + } + } + } + // see if this item was filed + $y = q( + "select uid from term where otype = %d and oid = %d and ttype = %d limit 1", + intval(TERM_OBJ_POST), + intval($rr['id']), + intval(TERM_FILE) + ); + if ($y) { + continue; + } + drop_item($rr['id'], false); + } } -function random_profile() { - $randfunc = db_getfunc('rand'); +function random_profile() +{ + $randfunc = db_getfunc('rand'); - $checkrandom = get_config('randprofile','check'); // False by default - $retryrandom = intval(get_config('randprofile','retry')); - if ($retryrandom == 0) { - $retryrandom = 5; - } - - for ($i = 0; $i < $retryrandom; $i++) { + $checkrandom = get_config('randprofile', 'check'); // False by default + $retryrandom = intval(get_config('randprofile', 'retry')); + if ($retryrandom == 0) { + $retryrandom = 5; + } + for ($i = 0; $i < $retryrandom; $i++) { $r = q("select xchan_url, xchan_hash from xchan left join hubloc on hubloc_hash = xchan_hash where xchan_hidden = 0 and xchan_network in ('nomad','zot6') and xchan_deleted = 0 and hubloc_connected > %s - interval %s order by $randfunc limit 1", - db_utcnow(), - db_quoteinterval('30 day') - ); + db_utcnow(), + db_quoteinterval('30 day') + ); - if (!$r) { - return EMPTY_STR; // Couldn't get a random channel - } - if ($checkrandom) { - $x = z_fetch_url($r[0]['xchan_url']); - if ($x['success']) { - return $r[0]['xchan_hash']; - } - else { - logger('Random channel turned out to be bad.'); - } - } - else { - return $r[0]['xchan_hash']; - } - - } - return EMPTY_STR; + if (!$r) { + return EMPTY_STR; // Couldn't get a random channel + } + if ($checkrandom) { + $x = z_fetch_url($r[0]['xchan_url']); + if ($x['success']) { + return $r[0]['xchan_hash']; + } else { + logger('Random channel turned out to be bad.'); + } + } else { + return $r[0]['xchan_hash']; + } + } + return EMPTY_STR; } -function update_vcard($arr,$vcard = null) { +function update_vcard($arr, $vcard = null) +{ - // logger('update_vcard: ' . print_r($arr,true)); + // logger('update_vcard: ' . print_r($arr,true)); - $fn = $arr['fn']; - - - // This isn't strictly correct and could be a cause for concern. - // 'N' => array_reverse(explode(' ', $fn)) + $fn = $arr['fn']; - // What we really want is - // 'N' => Adams;John;Quincy;Reverend,Dr.;III - // which is a very difficult parsing problem especially if you allow - // the surname to contain spaces. The only way to be sure to get it - // right is to provide a form to input all the various fields and not - // try to extract it from the FN. + // This isn't strictly correct and could be a cause for concern. + // 'N' => array_reverse(explode(' ', $fn)) - if (! $vcard) { - $vcard = new \Sabre\VObject\Component\VCard([ - 'FN' => $fn, - 'N' => array_reverse(explode(' ', $fn)) - ]); - } - else { - $vcard->FN = $fn; - $vcard->N = array_reverse(explode(' ', $fn)); - } - $org = $arr['org']; - if($org) { - $vcard->ORG = $org; - } + // What we really want is + // 'N' => Adams;John;Quincy;Reverend,Dr.;III + // which is a very difficult parsing problem especially if you allow + // the surname to contain spaces. The only way to be sure to get it + // right is to provide a form to input all the various fields and not + // try to extract it from the FN. - $title = $arr['title']; - if($title) { - $vcard->TITLE = $title; - } + if (! $vcard) { + $vcard = new VCard([ + 'FN' => $fn, + 'N' => array_reverse(explode(' ', $fn)) + ]); + } else { + $vcard->FN = $fn; + $vcard->N = array_reverse(explode(' ', $fn)); + } - $tel = $arr['tel']; - $tel_type = $arr['tel_type']; - if($tel) { - $i = 0; - foreach($tel as $item) { - if($item) { - $vcard->add('TEL', $item, ['type' => $tel_type[$i]]); - } - $i++; - } - } + $org = $arr['org']; + if ($org) { + $vcard->ORG = $org; + } - $email = $arr['email']; - $email_type = $arr['email_type']; - if($email) { - $i = 0; - foreach($email as $item) { - if($item) { - $vcard->add('EMAIL', $item, ['type' => $email_type[$i]]); - } - $i++; - } - } + $title = $arr['title']; + if ($title) { + $vcard->TITLE = $title; + } - $impp = $arr['impp']; - $impp_type = $arr['impp_type']; - if($impp) { - $i = 0; - foreach($impp as $item) { - if($item) { - $vcard->add('IMPP', $item, ['type' => $impp_type[$i]]); - } - $i++; - } - } + $tel = $arr['tel']; + $tel_type = $arr['tel_type']; + if ($tel) { + $i = 0; + foreach ($tel as $item) { + if ($item) { + $vcard->add('TEL', $item, ['type' => $tel_type[$i]]); + } + $i++; + } + } - $url = $arr['url']; - $url_type = $arr['url_type']; - if($url) { - $i = 0; - foreach($url as $item) { - if($item) { - $vcard->add('URL', $item, ['type' => $url_type[$i]]); - } - $i++; - } - } + $email = $arr['email']; + $email_type = $arr['email_type']; + if ($email) { + $i = 0; + foreach ($email as $item) { + if ($item) { + $vcard->add('EMAIL', $item, ['type' => $email_type[$i]]); + } + $i++; + } + } - $adr = $arr['adr']; - $adr_type = $arr['adr_type']; + $impp = $arr['impp']; + $impp_type = $arr['impp_type']; + if ($impp) { + $i = 0; + foreach ($impp as $item) { + if ($item) { + $vcard->add('IMPP', $item, ['type' => $impp_type[$i]]); + } + $i++; + } + } - if($adr) { - $i = 0; - foreach($adr as $item) { - if($item) { - $vcard->add('ADR', $item, ['type' => $adr_type[$i]]); - } - $i++; - } - } + $url = $arr['url']; + $url_type = $arr['url_type']; + if ($url) { + $i = 0; + foreach ($url as $item) { + if ($item) { + $vcard->add('URL', $item, ['type' => $url_type[$i]]); + } + $i++; + } + } - $note = $arr['note']; - if($note) { - $vcard->NOTE = $note; - } + $adr = $arr['adr']; + $adr_type = $arr['adr_type']; - return $vcard->serialize(); + if ($adr) { + $i = 0; + foreach ($adr as $item) { + if ($item) { + $vcard->add('ADR', $item, ['type' => $adr_type[$i]]); + } + $i++; + } + } + $note = $arr['note']; + if ($note) { + $vcard->NOTE = $note; + } + + return $vcard->serialize(); } -function get_vcard_array($vc,$id) { +function get_vcard_array($vc, $id) +{ - $photo = ''; - if($vc->PHOTO) { - $photo_value = strtolower($vc->PHOTO->getValueType()); // binary or uri - if($photo_value === 'binary') { - $photo_type = strtolower($vc->PHOTO['TYPE']); // mime jpeg, png or gif - $photo = 'data:image/' . $photo_type . ';base64,' . base64_encode((string)$vc->PHOTO); - } - else { - $url = parse_url((string)$vc->PHOTO); - $photo = 'data:' . $url['path']; - } - } + $photo = ''; + if ($vc->PHOTO) { + $photo_value = strtolower($vc->PHOTO->getValueType()); // binary or uri + if ($photo_value === 'binary') { + $photo_type = strtolower($vc->PHOTO['TYPE']); // mime jpeg, png or gif + $photo = 'data:image/' . $photo_type . ';base64,' . base64_encode((string)$vc->PHOTO); + } else { + $url = parse_url((string)$vc->PHOTO); + $photo = 'data:' . $url['path']; + } + } - $fn = ''; - if($vc->FN) { - $fn = (string) escape_tags($vc->FN); - } + $fn = ''; + if ($vc->FN) { + $fn = (string) escape_tags($vc->FN); + } - $org = ''; - if($vc->ORG) { - $org = (string) escape_tags($vc->ORG); - } + $org = ''; + if ($vc->ORG) { + $org = (string) escape_tags($vc->ORG); + } - $title = ''; - if($vc->TITLE) { - $title = (string) escape_tags($vc->TITLE); - } + $title = ''; + if ($vc->TITLE) { + $title = (string) escape_tags($vc->TITLE); + } - $tels = []; - if($vc->TEL) { - foreach($vc->TEL as $tel) { - $type = (($tel['TYPE']) ? vcard_translate_type((string)$tel['TYPE']) : ''); - $tels[] = [ - 'type' => $type, - 'nr' => (string) escape_tags($tel) - ]; - } - } - $emails = []; - if($vc->EMAIL) { - foreach($vc->EMAIL as $email) { - $type = (($email['TYPE']) ? vcard_translate_type((string)$email['TYPE']) : ''); - $emails[] = [ - 'type' => $type, - 'address' => (string) escape_tags($email) - ]; - } - } + $tels = []; + if ($vc->TEL) { + foreach ($vc->TEL as $tel) { + $type = (($tel['TYPE']) ? vcard_translate_type((string)$tel['TYPE']) : ''); + $tels[] = [ + 'type' => $type, + 'nr' => (string) escape_tags($tel) + ]; + } + } + $emails = []; + if ($vc->EMAIL) { + foreach ($vc->EMAIL as $email) { + $type = (($email['TYPE']) ? vcard_translate_type((string)$email['TYPE']) : ''); + $emails[] = [ + 'type' => $type, + 'address' => (string) escape_tags($email) + ]; + } + } - $impps = []; - if($vc->IMPP) { - foreach($vc->IMPP as $impp) { - $type = (($impp['TYPE']) ? vcard_translate_type((string)$impp['TYPE']) : ''); - $impps[] = [ - 'type' => $type, - 'address' => (string) escape_tags($impp) - ]; - } - } + $impps = []; + if ($vc->IMPP) { + foreach ($vc->IMPP as $impp) { + $type = (($impp['TYPE']) ? vcard_translate_type((string)$impp['TYPE']) : ''); + $impps[] = [ + 'type' => $type, + 'address' => (string) escape_tags($impp) + ]; + } + } - $urls = []; - if($vc->URL) { - foreach($vc->URL as $url) { - $type = (($url['TYPE']) ? vcard_translate_type((string)$url['TYPE']) : ''); - $urls[] = [ - 'type' => $type, - 'address' => (string) escape_tags($url) - ]; - } - } + $urls = []; + if ($vc->URL) { + foreach ($vc->URL as $url) { + $type = (($url['TYPE']) ? vcard_translate_type((string)$url['TYPE']) : ''); + $urls[] = [ + 'type' => $type, + 'address' => (string) escape_tags($url) + ]; + } + } - $adrs = []; - if($vc->ADR) { - foreach($vc->ADR as $adr) { - $type = (($adr['TYPE']) ? vcard_translate_type((string)$adr['TYPE']) : ''); - $entry = [ - 'type' => $type, - 'address' => $adr->getParts() - ]; + $adrs = []; + if ($vc->ADR) { + foreach ($vc->ADR as $adr) { + $type = (($adr['TYPE']) ? vcard_translate_type((string)$adr['TYPE']) : ''); + $entry = [ + 'type' => $type, + 'address' => $adr->getParts() + ]; - if(is_array($entry['address'])) { - array_walk($entry['address'],'array_escape_tags'); - } - else { - $entry['address'] = (string) escape_tags($entry['address']); - } + if (is_array($entry['address'])) { + array_walk($entry['address'], 'array_escape_tags'); + } else { + $entry['address'] = (string) escape_tags($entry['address']); + } - $adrs[] = $entry; - - } - } + $adrs[] = $entry; + } + } - $note = ''; - if($vc->NOTE) { - $note = (string) escape_tags($vc->NOTE); - } + $note = ''; + if ($vc->NOTE) { + $note = (string) escape_tags($vc->NOTE); + } - $card = [ - 'id' => $id, - 'photo' => $photo, - 'fn' => $fn, - 'org' => $org, - 'title' => $title, - 'tels' => $tels, - 'emails' => $emails, - 'impps' => $impps, - 'urls' => $urls, - 'adrs' => $adrs, - 'note' => $note - ]; - - return $card; + $card = [ + 'id' => $id, + 'photo' => $photo, + 'fn' => $fn, + 'org' => $org, + 'title' => $title, + 'tels' => $tels, + 'emails' => $emails, + 'impps' => $impps, + 'urls' => $urls, + 'adrs' => $adrs, + 'note' => $note + ]; + return $card; } -function vcard_translate_type($type) { +function vcard_translate_type($type) +{ - if(!$type) - return; + if (!$type) { + return; + } - $type = strtoupper($type); + $type = strtoupper($type); - $map = [ - 'CELL' => t('Mobile'), - 'HOME' => t('Home'), - 'HOME,VOICE' => t('Home, Voice'), - 'HOME,FAX' => t('Home, Fax'), - 'WORK' => t('Work'), - 'WORK,VOICE' => t('Work, Voice'), - 'WORK,FAX' => t('Work, Fax'), - 'OTHER' => t('Other') - ]; + $map = [ + 'CELL' => t('Mobile'), + 'HOME' => t('Home'), + 'HOME,VOICE' => t('Home, Voice'), + 'HOME,FAX' => t('Home, Fax'), + 'WORK' => t('Work'), + 'WORK,VOICE' => t('Work, Voice'), + 'WORK,FAX' => t('Work, Fax'), + 'OTHER' => t('Other') + ]; - if (array_key_exists($type, $map)) { - return [$type, $map[$type]]; - } - else { - return [$type, t('Other') . ' (' . $type . ')']; - } + if (array_key_exists($type, $map)) { + return [$type, $map[$type]]; + } else { + return [$type, t('Other') . ' (' . $type . ')']; + } } -function vcard_query(&$r) { +function vcard_query(&$r) +{ - $arr = []; + $arr = []; - if($r && is_array($r) && count($r)) { - $uid = $r[0]['abook_channel']; - foreach($r as $rv) { - if($rv['abook_xchan'] && (! in_array("'" . dbesc($rv['abook_xchan']) . "'",$arr))) - $arr[] = "'" . dbesc($rv['abook_xchan']) . "'"; - } - } + if ($r && is_array($r) && count($r)) { + $uid = $r[0]['abook_channel']; + foreach ($r as $rv) { + if ($rv['abook_xchan'] && (! in_array("'" . dbesc($rv['abook_xchan']) . "'", $arr))) { + $arr[] = "'" . dbesc($rv['abook_xchan']) . "'"; + } + } + } - if($arr) { - $a = q("select * from abconfig where chan = %d and xchan in (" . protect_sprintf(implode(',', $arr)) . ") and cat = 'system' and k = 'vcard'", - intval($uid) - ); - if($a) { - foreach($a as $av) { - for($x = 0; $x < count($r); $x ++) { - if($r[$x]['abook_xchan'] == $av['xchan']) { - $vctmp = \Sabre\VObject\Reader::read($av['v']); - $r[$x]['vcard'] = (($vctmp) ? get_vcard_array($vctmp,$r[$x]['abook_id']) : [] ); - } - } - } - } - } + if ($arr) { + $a = q( + "select * from abconfig where chan = %d and xchan in (" . protect_sprintf(implode(',', $arr)) . ") and cat = 'system' and k = 'vcard'", + intval($uid) + ); + if ($a) { + foreach ($a as $av) { + for ($x = 0; $x < count($r); $x++) { + if ($r[$x]['abook_xchan'] == $av['xchan']) { + $vctmp = Reader::read($av['v']); + $r[$x]['vcard'] = (($vctmp) ? get_vcard_array($vctmp, $r[$x]['abook_id']) : [] ); + } + } + } + } + } } -function contact_profile_assign($current) { +function contact_profile_assign($current) +{ - $o = ''; + $o = ''; - $o .= "\r\n"; - $r = q("SELECT profile_guid, profile_name FROM profile WHERE uid = %d", - intval($_SESSION['uid'])); + $r = q( + "SELECT profile_guid, profile_name FROM profile WHERE uid = %d", + intval($_SESSION['uid']) + ); - if($r) { - foreach($r as $rr) { - $selected = (($rr['profile_guid'] == $current) ? " selected=\"selected\" " : ""); - $o .= "\r\n"; - } - } - $o .= "\r\n"; - return $o; + if ($r) { + foreach ($r as $rr) { + $selected = (($rr['profile_guid'] == $current) ? " selected=\"selected\" " : ""); + $o .= "\r\n"; + } + } + $o .= "\r\n"; + return $o; } -function contact_block() { - $o = ''; +function contact_block() +{ + $o = ''; - if(! App::$profile['uid']) - return; + if (! App::$profile['uid']) { + return; + } - if(! perm_is_allowed(App::$profile['uid'],get_observer_hash(),'view_contacts')) - return; + if (! perm_is_allowed(App::$profile['uid'], get_observer_hash(), 'view_contacts')) { + return; + } - $shown = get_pconfig(App::$profile['uid'],'system','display_friend_count'); + $shown = get_pconfig(App::$profile['uid'], 'system', 'display_friend_count'); - if($shown === false) - $shown = 25; - if($shown == 0) - return; + if ($shown === false) { + $shown = 25; + } + if ($shown == 0) { + return; + } - $is_owner = ((local_channel() && local_channel() == App::$profile['uid']) ? true : false); - $sql_extra = ''; + $is_owner = ((local_channel() && local_channel() == App::$profile['uid']) ? true : false); + $sql_extra = ''; - $abook_flags = " and abook_pending = 0 and abook_self = 0 "; + $abook_flags = " and abook_pending = 0 and abook_self = 0 "; - if(! $is_owner) { - $abook_flags .= " and abook_hidden = 0 "; - $sql_extra = " and xchan_hidden = 0 "; - } + if (! $is_owner) { + $abook_flags .= " and abook_hidden = 0 "; + $sql_extra = " and xchan_hidden = 0 "; + } - if((! is_array(App::$profile)) || (App::$profile['hide_friends'])) - return $o; + if ((! is_array(App::$profile)) || (App::$profile['hide_friends'])) { + return $o; + } - $r = q("SELECT COUNT(abook_id) AS total FROM abook left join xchan on abook_xchan = xchan_hash WHERE abook_channel = %d + $r = q( + "SELECT COUNT(abook_id) AS total FROM abook left join xchan on abook_xchan = xchan_hash WHERE abook_channel = %d $abook_flags and xchan_orphan = 0 and xchan_deleted = 0 $sql_extra", - intval(App::$profile['uid']) - ); - if(count($r)) { - $total = intval($r[0]['total']); - } - if(! $total) { - $contacts = t('No connections'); - $micropro = null; - } else { + intval(App::$profile['uid']) + ); + if (count($r)) { + $total = intval($r[0]['total']); + } + if (! $total) { + $contacts = t('No connections'); + $micropro = null; + } else { + $randfunc = db_getfunc('RAND'); - $randfunc = db_getfunc('RAND'); + $r = q( + "SELECT abook.*, xchan.* FROM abook left join xchan on abook.abook_xchan = xchan.xchan_hash WHERE abook_channel = %d $abook_flags and abook_archived = 0 and xchan_orphan = 0 and xchan_deleted = 0 $sql_extra ORDER BY $randfunc LIMIT %d", + intval(App::$profile['uid']), + intval($shown) + ); - $r = q("SELECT abook.*, xchan.* FROM abook left join xchan on abook.abook_xchan = xchan.xchan_hash WHERE abook_channel = %d $abook_flags and abook_archived = 0 and xchan_orphan = 0 and xchan_deleted = 0 $sql_extra ORDER BY $randfunc LIMIT %d", - intval(App::$profile['uid']), - intval($shown) - ); + if (count($r)) { + $contacts = t('Connections'); + $micropro = array(); + foreach ($r as $rr) { + // There is no setting to discover if you are bi-directionally connected + // Use the ability to post comments as an indication that this relationship is more + // than wishful thinking; even though soapbox channels and feeds will disable it. - if(count($r)) { - $contacts = t('Connections'); - $micropro = Array(); - foreach($r as $rr) { + if (! their_perms_contains(App::$profile['uid'], $rr['xchan_hash'], 'post_comments')) { + $rr['oneway'] = true; + } + $micropro[] = micropro($rr, true, 'mpfriend'); + } + } + } - // There is no setting to discover if you are bi-directionally connected - // Use the ability to post comments as an indication that this relationship is more - // than wishful thinking; even though soapbox channels and feeds will disable it. + $tpl = get_markup_template('contact_block.tpl'); + $o = replace_macros($tpl, array( + '$contacts' => $contacts, + '$nickname' => App::$profile['channel_address'], + '$viewconnections' => (($total > $shown) ? sprintf(t('View all %s connections'), $total) : ''), + '$micropro' => $micropro, + )); - if(! their_perms_contains(App::$profile['uid'],$rr['xchan_hash'],'post_comments')) { - $rr['oneway'] = true; - } - $micropro[] = micropro($rr,true,'mpfriend'); - } - } - } + $arr = array('contacts' => $r, 'output' => $o); - $tpl = get_markup_template('contact_block.tpl'); - $o = replace_macros($tpl, array( - '$contacts' => $contacts, - '$nickname' => App::$profile['channel_address'], - '$viewconnections' => (($total > $shown) ? sprintf(t('View all %s connections'),$total) : ''), - '$micropro' => $micropro, - )); - - $arr = array('contacts' => $r, 'output' => $o); - - call_hooks('contact_block_end', $arr); - return $o; + call_hooks('contact_block_end', $arr); + return $o; } -function micropro($contact, $redirect = false, $class = '', $mode = false) { +function micropro($contact, $redirect = false, $class = '', $mode = false) +{ - if($contact['click']) - $url = '#'; - else - $url = chanlink_hash($contact['xchan_hash']); + if ($contact['click']) { + $url = '#'; + } else { + $url = chanlink_hash($contact['xchan_hash']); + } - $tpl = 'micropro_img.tpl'; - if($mode === true) - $tpl = 'micropro_txt.tpl'; - if($mode === 'card') - $tpl = 'micropro_card.tpl'; + $tpl = 'micropro_img.tpl'; + if ($mode === true) { + $tpl = 'micropro_txt.tpl'; + } + if ($mode === 'card') { + $tpl = 'micropro_card.tpl'; + } - return replace_macros(get_markup_template($tpl), array( - '$click' => (($contact['click']) ? $contact['click'] : ''), - '$class' => $class . (($contact['archived']) ? ' archived' : ''), - '$oneway' => (($contact['oneway']) ? true : false), - '$url' => $url, - '$photo' => $contact['xchan_photo_s'], - '$name' => $contact['xchan_name'], - '$addr' => $contact['xchan_addr'], - '$title' => $contact['xchan_name'] . ' [' . $contact['xchan_addr'] . ']', - '$network' => sprintf(t('Network: %s'), $contact['xchan_network']) - )); + return replace_macros(get_markup_template($tpl), array( + '$click' => (($contact['click']) ? $contact['click'] : ''), + '$class' => $class . (($contact['archived']) ? ' archived' : ''), + '$oneway' => (($contact['oneway']) ? true : false), + '$url' => $url, + '$photo' => $contact['xchan_photo_s'], + '$name' => $contact['xchan_name'], + '$addr' => $contact['xchan_addr'], + '$title' => $contact['xchan_name'] . ' [' . $contact['xchan_addr'] . ']', + '$network' => sprintf(t('Network: %s'), network_to_name($contact['xchan_network'])) + )); } diff --git a/include/conversation.php b/include/conversation.php index 1ab60758e..3e8682fd4 100644 --- a/include/conversation.php +++ b/include/conversation.php @@ -1,4 +1,6 @@ - $new_body, 'images' => $saved_image); + return array('body' => $new_body, 'images' => $saved_image); } -function item_redir_and_replace_images($body, $images, $cid) { +function item_redir_and_replace_images($body, $images, $cid) +{ - $origbody = $body; - $newbody = ''; + $origbody = $body; + $newbody = ''; - $observer = App::get_observer(); - $obhash = (($observer) ? $observer['xchan_hash'] : ''); - $obaddr = (($observer) ? $observer['xchan_addr'] : ''); + $observer = App::get_observer(); + $obhash = (($observer) ? $observer['xchan_hash'] : ''); + $obaddr = (($observer) ? $observer['xchan_addr'] : ''); - for($i = 0; $i < count($images); $i++) { - $search = '/\[url\=(.*?)\]\[!#saved_image' . $i . '#!\]\[\/url\]' . '/is'; - $replace = '[url=' . magiclink_url($obhash,$obaddr,'$1') . '][!#saved_image' . $i . '#!][/url]' ; + for ($i = 0; $i < count($images); $i++) { + $search = '/\[url\=(.*?)\]\[!#saved_image' . $i . '#!\]\[\/url\]' . '/is'; + $replace = '[url=' . magiclink_url($obhash, $obaddr, '$1') . '][!#saved_image' . $i . '#!][/url]' ; - $img_end = strpos($origbody, '[!#saved_image' . $i . '#!][/url]') + strlen('[!#saved_image' . $i . '#!][/url]'); - $process_part = substr($origbody, 0, $img_end); - $origbody = substr($origbody, $img_end); + $img_end = strpos($origbody, '[!#saved_image' . $i . '#!][/url]') + strlen('[!#saved_image' . $i . '#!][/url]'); + $process_part = substr($origbody, 0, $img_end); + $origbody = substr($origbody, $img_end); - $process_part = preg_replace($search, $replace, $process_part); - $newbody = $newbody . $process_part; - } - $newbody = $newbody . $origbody; + $process_part = preg_replace($search, $replace, $process_part); + $newbody = $newbody . $process_part; + } + $newbody = $newbody . $origbody; - $cnt = 0; - foreach($images as $image) { - // We're depending on the property of 'foreach' (specified on the PHP website) that - // it loops over the array starting from the first element and going sequentially - // to the last element - $newbody = str_replace('[!#saved_image' . $cnt . '#!]', '[img]' . $image . '[/img]', $newbody); - $cnt++; - } + $cnt = 0; + foreach ($images as $image) { + // We're depending on the property of 'foreach' (specified on the PHP website) that + // it loops over the array starting from the first element and going sequentially + // to the last element + $newbody = str_replace('[!#saved_image' . $cnt . '#!]', '[img]' . $image . '[/img]', $newbody); + $cnt++; + } - return $newbody; + return $newbody; } @@ -91,207 +94,209 @@ function item_redir_and_replace_images($body, $images, $cid) { * Render actions localized */ -function localize_item(&$item){ +function localize_item(&$item) +{ - if (activity_match($item['verb'],ACTIVITY_LIKE) || activity_match($item['verb'],ACTIVITY_DISLIKE) || $item['verb'] === 'Announce') { - - if (! $item['obj']) { - return; - } + if (activity_match($item['verb'], ACTIVITY_LIKE) || activity_match($item['verb'], ACTIVITY_DISLIKE) || $item['verb'] === 'Announce') { + if (! $item['obj']) { + return; + } - if (intval($item['item_thread_top'])) { - return; - } + if (intval($item['item_thread_top'])) { + return; + } - $obj = ((is_array($item['obj'])) ? $item['obj'] : json_decode($item['obj'],true)); - if (! is_array($obj)) { - logger('localize_item: failed to decode object: ' . print_r($item['obj'],true)); - return; - } - - if (isset($obj['author']) && $obj['author'] && $obj['author']['link']) { - $author_link = get_rel_link($obj['author']['link'],'alternate'); - } - else { - $author_link = ''; - } + $obj = ((is_array($item['obj'])) ? $item['obj'] : json_decode($item['obj'], true)); + if (! is_array($obj)) { + logger('localize_item: failed to decode object: ' . print_r($item['obj'], true)); + return; + } - $author_name = (($obj['author'] && $obj['author']['name']) ? $obj['author']['name'] : ''); + if (isset($obj['author']) && $obj['author'] && $obj['author']['link']) { + $author_link = get_rel_link($obj['author']['link'], 'alternate'); + } else { + $author_link = ''; + } - $item_url = get_rel_link($obj['link'],'alternate'); + $author_name = (($obj['author'] && $obj['author']['name']) ? $obj['author']['name'] : ''); - $Bphoto = ''; + $item_url = get_rel_link($obj['link'], 'alternate'); - switch ($obj['type']) { - case ACTIVITY_OBJ_PHOTO: - $post_type = t('photo'); - break; - case ACTIVITY_OBJ_EVENT: - $post_type = t('event'); - break; - case ACTIVITY_OBJ_PERSON: - $post_type = t('channel'); - $author_name = $obj['title']; - if ($obj['link']) { - $author_link = get_rel_link($obj['link'],'alternate'); - $Bphoto = get_rel_link($obj['link'],'photo'); - } - if ($obj['url']) { + $Bphoto = ''; - } - break; - case ACTIVITY_OBJ_THING: - $post_type = $obj['title']; - if($obj['owner']) { - if(array_key_exists('name',$obj['owner'])) - $obj['owner']['name']; - if(array_key_exists('link',$obj['owner'])) - $author_link = get_rel_link($obj['owner']['link'],'alternate'); - } - if($obj['link']) { - $Bphoto = get_rel_link($obj['link'],'photo'); - } - break; + switch ($obj['type']) { + case ACTIVITY_OBJ_PHOTO: + $post_type = t('photo'); + break; + case ACTIVITY_OBJ_EVENT: + $post_type = t('event'); + break; + case ACTIVITY_OBJ_PERSON: + $post_type = t('channel'); + $author_name = $obj['title']; + if ($obj['link']) { + $author_link = get_rel_link($obj['link'], 'alternate'); + $Bphoto = get_rel_link($obj['link'], 'photo'); + } + if ($obj['url']) { + } + break; + case ACTIVITY_OBJ_THING: + $post_type = $obj['title']; + if ($obj['owner']) { + if (array_key_exists('name', $obj['owner'])) { + $obj['owner']['name']; + } + if (array_key_exists('link', $obj['owner'])) { + $author_link = get_rel_link($obj['owner']['link'], 'alternate'); + } + } + if ($obj['link']) { + $Bphoto = get_rel_link($obj['link'], 'photo'); + } + break; - case ACTIVITY_OBJ_NOTE: - default: - $post_type = t('post'); - if($obj['id'] != $obj['parent']) - $post_type = t('comment'); - break; - } + case ACTIVITY_OBJ_NOTE: + default: + $post_type = t('post'); + if ($obj['id'] != $obj['parent']) { + $post_type = t('comment'); + } + break; + } - // If we couldn't parse something useful, don't bother translating. - // We need something better than zid here, probably magic_link(), but it needs writing + // If we couldn't parse something useful, don't bother translating. + // We need something better than zid here, probably magic_link(), but it needs writing - if($author_link && $author_name && $item_url) { - $author = '[zrl=' . chanlink_url($item['author']['xchan_url']) . ']' . $item['author']['xchan_name'] . '[/zrl]'; - $objauthor = '[zrl=' . chanlink_url($author_link) . ']' . $author_name . '[/zrl]'; - - $plink = '[zrl=' . zid($item_url) . ']' . $post_type . '[/zrl]'; + if ($author_link && $author_name && $item_url) { + $author = '[zrl=' . chanlink_url($item['author']['xchan_url']) . ']' . $item['author']['xchan_name'] . '[/zrl]'; + $objauthor = '[zrl=' . chanlink_url($author_link) . ']' . $author_name . '[/zrl]'; - if(activity_match($item['verb'],ACTIVITY_LIKE)) { - $bodyverb = t('%1$s likes %2$s\'s %3$s'); - } - elseif(activity_match($item['verb'],ACTIVITY_DISLIKE)) { - $bodyverb = t('%1$s doesn\'t like %2$s\'s %3$s'); - } - elseif ($item['verb'] === 'Announce') { - $bodyverb = t('%1$s repeated %2$s\'s %3$s'); - } + $plink = '[zrl=' . zid($item_url) . ']' . $post_type . '[/zrl]'; - // short version, in notification strings the author will be displayed separately + if (activity_match($item['verb'], ACTIVITY_LIKE)) { + $bodyverb = t('%1$s likes %2$s\'s %3$s'); + } elseif (activity_match($item['verb'], ACTIVITY_DISLIKE)) { + $bodyverb = t('%1$s doesn\'t like %2$s\'s %3$s'); + } elseif ($item['verb'] === 'Announce') { + $bodyverb = t('%1$s repeated %2$s\'s %3$s'); + } - if(activity_match($item['verb'],ACTIVITY_LIKE)) { - $shortbodyverb = t('likes %1$s\'s %2$s'); - } - elseif(activity_match($item['verb'],ACTIVITY_DISLIKE)) { - $shortbodyverb = t('doesn\'t like %1$s\'s %2$s'); - } - elseif ($item['verb'] === 'Announce') { - $shortbodyverb = t('repeated %1$s\'s %2$s'); - } + // short version, in notification strings the author will be displayed separately - if ($shortbodyverb) { - $item['shortlocalize'] = sprintf($shortbodyverb, $objauthor, $plink); - } - - $item['body'] = $item['localize'] = sprintf($bodyverb, $author, $objauthor, $plink); - if ($Bphoto != "") { - $item['body'] .= "\n\n\n" . '[zrl=' . chanlink_url($author_link) . '][zmg width="80" height="80"]' . $Bphoto . '[/zmg][/zrl]'; - } + if (activity_match($item['verb'], ACTIVITY_LIKE)) { + $shortbodyverb = t('likes %1$s\'s %2$s'); + } elseif (activity_match($item['verb'], ACTIVITY_DISLIKE)) { + $shortbodyverb = t('doesn\'t like %1$s\'s %2$s'); + } elseif ($item['verb'] === 'Announce') { + $shortbodyverb = t('repeated %1$s\'s %2$s'); + } - } - else { -// logger('localize_item like failed: link ' . $author_link . ' name ' . $author_name . ' url ' . $item_url); - } + if ($shortbodyverb) { + $item['shortlocalize'] = sprintf($shortbodyverb, $objauthor, $plink); + } - } + $item['body'] = $item['localize'] = sprintf($bodyverb, $author, $objauthor, $plink); + if ($Bphoto != "") { + $item['body'] .= "\n\n\n" . '[zrl=' . chanlink_url($author_link) . '][zmg width="80" height="80"]' . $Bphoto . '[/zmg][/zrl]'; + } + } else { +// logger('localize_item like failed: link ' . $author_link . ' name ' . $author_name . ' url ' . $item_url); + } + } - if (activity_match($item['verb'],ACTIVITY_FRIEND)) { + if (activity_match($item['verb'], ACTIVITY_FRIEND)) { + if ($item['obj_type'] == "" || $item['obj_type'] !== ACTIVITY_OBJ_PERSON) { + return; + } - if ($item['obj_type'] == "" || $item['obj_type'] !== ACTIVITY_OBJ_PERSON) - return; - - $Aname = $item['author']['xchan_name']; - $Alink = $item['author']['xchan_url']; + $Aname = $item['author']['xchan_name']; + $Alink = $item['author']['xchan_url']; - $obj= json_decode($item['obj'],true); - - $Blink = $Bphoto = ''; + $obj = json_decode($item['obj'], true); - if($obj['link']) { - $Blink = get_rel_link($obj['link'],'alternate'); - $Bphoto = get_rel_link($obj['link'],'photo'); - } - $Bname = $obj['title']; + $Blink = $Bphoto = ''; + + if ($obj['link']) { + $Blink = get_rel_link($obj['link'], 'alternate'); + $Bphoto = get_rel_link($obj['link'], 'photo'); + } + $Bname = $obj['title']; - $A = '[zrl=' . chanlink_url($Alink) . ']' . $Aname . '[/zrl]'; - $B = '[zrl=' . chanlink_url($Blink) . ']' . $Bname . '[/zrl]'; - if ($Bphoto!="") $Bphoto = '[zrl=' . chanlink_url($Blink) . '][zmg=80x80]' . $Bphoto . '[/zmg][/zrl]'; + $A = '[zrl=' . chanlink_url($Alink) . ']' . $Aname . '[/zrl]'; + $B = '[zrl=' . chanlink_url($Blink) . ']' . $Bname . '[/zrl]'; + if ($Bphoto != "") { + $Bphoto = '[zrl=' . chanlink_url($Blink) . '][zmg=80x80]' . $Bphoto . '[/zmg][/zrl]'; + } - $item['body'] = $item['localize'] = sprintf( t('%1$s is now connected with %2$s'), $A, $B); - $item['body'] .= "\n\n\n" . $Bphoto; - } + $item['body'] = $item['localize'] = sprintf(t('%1$s is now connected with %2$s'), $A, $B); + $item['body'] .= "\n\n\n" . $Bphoto; + } - if (stristr($item['verb'], ACTIVITY_POKE)) { + if (stristr($item['verb'], ACTIVITY_POKE)) { - /** @FIXME for obscured private posts, until then leave untranslated */ - return; + /** @FIXME for obscured private posts, until then leave untranslated */ + return; - $verb = urldecode(substr($item['verb'],strpos($item['verb'],'#')+1)); - if(! $verb) - return; + $verb = urldecode(substr($item['verb'], strpos($item['verb'], '#') + 1)); + if (! $verb) { + return; + } - if ($item['obj_type']=="" || $item['obj_type']!== ACTIVITY_OBJ_PERSON) return; + if ($item['obj_type'] == "" || $item['obj_type'] !== ACTIVITY_OBJ_PERSON) { + return; + } - $Aname = $item['author']['xchan_name']; - $Alink = $item['author']['xchan_url']; + $Aname = $item['author']['xchan_name']; + $Alink = $item['author']['xchan_url']; - $obj= json_decode($item['obj'],true); + $obj = json_decode($item['obj'], true); - $Blink = $Bphoto = ''; + $Blink = $Bphoto = ''; - if($obj['link']) { - $Blink = get_rel_link($obj['link'],'alternate'); - $Bphoto = get_rel_link($obj['link'],'photo'); - } - $Bname = $obj['title']; + if ($obj['link']) { + $Blink = get_rel_link($obj['link'], 'alternate'); + $Bphoto = get_rel_link($obj['link'], 'photo'); + } + $Bname = $obj['title']; - $A = '[zrl=' . chanlink_url($Alink) . ']' . $Aname . '[/zrl]'; - $B = '[zrl=' . chanlink_url($Blink) . ']' . $Bname . '[/zrl]'; - if ($Bphoto!="") $Bphoto = '[zrl=' . chanlink_url($Blink) . '][zmg=80x80]' . $Bphoto . '[/zmg][/zrl]'; + $A = '[zrl=' . chanlink_url($Alink) . ']' . $Aname . '[/zrl]'; + $B = '[zrl=' . chanlink_url($Blink) . ']' . $Bname . '[/zrl]'; + if ($Bphoto != "") { + $Bphoto = '[zrl=' . chanlink_url($Blink) . '][zmg=80x80]' . $Bphoto . '[/zmg][/zrl]'; + } - // we can't have a translation string with three positions but no distinguishable text - // So here is the translate string. + // we can't have a translation string with three positions but no distinguishable text + // So here is the translate string. - $txt = t('%1$s poked %2$s'); + $txt = t('%1$s poked %2$s'); - // now translate the verb + // now translate the verb - $txt = str_replace( t('poked'), t($verb), $txt); + $txt = str_replace(t('poked'), t($verb), $txt); - // then do the sprintf on the translation string + // then do the sprintf on the translation string - $item['body'] = $item['localize'] = sprintf($txt, $A, $B); - $item['body'] .= "\n\n\n" . $Bphoto; - } - if (stristr($item['verb'],ACTIVITY_MOOD)) { - $verb = urldecode(substr($item['verb'],strpos($item['verb'],'#')+1)); - if(! $verb) - return; + $item['body'] = $item['localize'] = sprintf($txt, $A, $B); + $item['body'] .= "\n\n\n" . $Bphoto; + } + if (stristr($item['verb'], ACTIVITY_MOOD)) { + $verb = urldecode(substr($item['verb'], strpos($item['verb'], '#') + 1)); + if (! $verb) { + return; + } - $Aname = $item['author']['xchan_name']; - $Alink = $item['author']['xchan_url']; + $Aname = $item['author']['xchan_name']; + $Alink = $item['author']['xchan_url']; - $A = '[zrl=' . chanlink_url($Alink) . ']' . $Aname . '[/zrl]'; - - $txt = t('%1$s is %2$s','mood'); + $A = '[zrl=' . chanlink_url($Alink) . ']' . $Aname . '[/zrl]'; - $item['body'] = sprintf($txt, $A, t($verb)); - } + $txt = t('%1$s is %2$s', 'mood'); + + $item['body'] = sprintf($txt, $A, t($verb)); + } } /** @@ -302,20 +307,22 @@ function localize_item(&$item){ * @return number */ -function count_descendants($item) { +function count_descendants($item) +{ - $total = count($item['children']); + $total = count($item['children']); - if($total > 0) { - foreach($item['children'] as $child) { - if(! visible_activity($child)) - $total --; + if ($total > 0) { + foreach ($item['children'] as $child) { + if (! visible_activity($child)) { + $total--; + } - $total += count_descendants($child); - } - } + $total += count_descendants($child); + } + } - return $total; + return $total; } /** @@ -324,41 +331,43 @@ function count_descendants($item) { * likes (etc.) can apply to other things besides posts. Check if they are post * children, in which case we handle them specially. Activities which are unrecognised * as having special meaning and hidden will be treated as posts or comments and visible - * in the stream. + * in the stream. * * @param array $item - * @return boolean + * @return bool */ -function visible_activity($item) { - $hidden_activities = [ ACTIVITY_LIKE, ACTIVITY_DISLIKE, 'Undo' ]; +function visible_activity($item) +{ + $hidden_activities = [ ACTIVITY_LIKE, ACTIVITY_DISLIKE, 'Undo' ]; - if(intval($item['item_notshown'])) - return false; + if (intval($item['item_notshown'])) { + return false; + } - if ($item['obj_type'] === 'Answer') { - return false; - } + if ($item['obj_type'] === 'Answer') { + return false; + } - // This is an experiment at group federation with microblog platforms. - // We need the Announce or "boost" for group replies by non-connections to end up in the personal timeline - // of those patforms. Hide them on our own platform because they make the conversation look like dung. - // Performance wise this is a mess because we need to send two activities for every group comment. - - if ($item['verb'] === 'Announce' && $item['author_xchan'] === $item['owner_xchan']) { - return false; - } + // This is an experiment at group federation with microblog platforms. + // We need the Announce or "boost" for group replies by non-connections to end up in the personal timeline + // of those patforms. Hide them on our own platform because they make the conversation look like dung. + // Performance wise this is a mess because we need to send two activities for every group comment. - foreach($hidden_activities as $act) { - if((activity_match($item['verb'], $act)) && ($item['mid'] != $item['parent_mid'])) { - return false; - } - } + if ($item['verb'] === 'Announce' && $item['author_xchan'] === $item['owner_xchan']) { + return false; + } - if(in_array($item['obj_type'],[ 'Event', 'Invite' ]) && in_array($item['verb'], [ 'Accept', 'Reject', 'TentativeAccept', 'TentativeReject', 'Ignore' ])) { - return false; - } + foreach ($hidden_activities as $act) { + if ((activity_match($item['verb'], $act)) && ($item['mid'] != $item['parent_mid'])) { + return false; + } + } - return true; + if (in_array($item['obj_type'], [ 'Event', 'Invite' ]) && in_array($item['verb'], [ 'Accept', 'Reject', 'TentativeAccept', 'TentativeReject', 'Ignore' ])) { + return false; + } + + return true; } /** @@ -374,665 +383,652 @@ function visible_activity($item) { * * @param array $items * @param string $mode - * @param boolean $update + * @param bool $update * @param string $page_mode default traditional * @param string $prepared_item * @return string */ -function conversation($items, $mode, $update, $page_mode = 'traditional', $prepared_item = '') { +function conversation($items, $mode, $update, $page_mode = 'traditional', $prepared_item = '') +{ - $content_html = ''; - $o = ''; + $content_html = ''; + $o = ''; - require_once('bbcode.php'); + require_once('bbcode.php'); - $ssl_state = ((local_channel()) ? true : false); + $ssl_state = ((local_channel()) ? true : false); - if (local_channel()) - load_pconfig(local_channel(),''); + if (local_channel()) { + load_pconfig(local_channel(), ''); + } - $profile_owner = 0; - $page_writeable = false; - $live_update_div = ''; - $jsreload = ''; + $profile_owner = 0; + $page_writeable = false; + $live_update_div = ''; + $jsreload = ''; - $preview = (($page_mode === 'preview') ? true : false); - $previewing = (($preview) ? ' preview ' : ''); - $preview_lbl = t('This is an unsaved preview'); + $preview = (($page_mode === 'preview') ? true : false); + $previewing = (($preview) ? ' preview ' : ''); + $preview_lbl = t('This is an unsaved preview'); - if (in_array($mode, [ 'stream', 'pubstream'])) { + if (in_array($mode, [ 'stream', 'pubstream'])) { + $profile_owner = local_channel(); + $page_writeable = ((local_channel()) ? true : false); - $profile_owner = local_channel(); - $page_writeable = ((local_channel()) ? true : false); + if (!$update) { + // The special div is needed for liveUpdate to kick in for this page. + // We only launch liveUpdate if you aren't filtering in some incompatible + // way and also you aren't writing a comment (discovered in javascript). - if (!$update) { - // The special div is needed for liveUpdate to kick in for this page. - // We only launch liveUpdate if you aren't filtering in some incompatible - // way and also you aren't writing a comment (discovered in javascript). + $live_update_div = '
                    ' . "\r\n" + . "\r\n"; + } + } elseif ($mode === 'hq') { + $profile_owner = local_channel(); + $page_writeable = true; + $live_update_div = '
                    ' . "\r\n"; + } elseif ($mode === 'channel') { + $profile_owner = App::$profile['profile_uid']; + $page_writeable = ($profile_owner == local_channel()); - $live_update_div = '
                    ' . "\r\n" - . "\r\n"; - } - } + if (!$update) { + $tab = notags(trim($_GET['tab'])); + if ($tab === 'posts') { + // This is ugly, but we can't pass the profile_uid through the session to the ajax updater, + // because browser prefetching might change it on us. We have to deliver it with the page. - elseif ($mode === 'hq') { - $profile_owner = local_channel(); - $page_writeable = true; - $live_update_div = '
                    ' . "\r\n"; - } + $live_update_div = '
                    ' . "\r\n" + . "\r\n"; + } + } + } elseif ($mode === 'cards') { + $profile_owner = App::$profile['profile_uid']; + $page_writeable = ($profile_owner == local_channel()); + $live_update_div = '
                    ' . "\r\n" + . "\r\n"; + $jsreload = $_SESSION['return_url']; + } elseif ($mode === 'articles') { + $profile_owner = App::$profile['profile_uid']; + $page_writeable = ($profile_owner == local_channel()); + $live_update_div = '
                    ' . "\r\n" + . "\r\n"; + $jsreload = $_SESSION['return_url']; + } elseif ($mode === 'display') { + $profile_owner = local_channel(); + $page_writeable = false; + $live_update_div = '
                    ' . "\r\n"; + } elseif ($mode === 'page') { + $profile_owner = App::$profile['uid']; + $page_writeable = ($profile_owner == local_channel()); + $live_update_div = '
                    ' . "\r\n"; + } elseif ($mode === 'search') { + $live_update_div = '' . "\r\n"; + } elseif ($mode === 'moderate') { + $profile_owner = local_channel(); + } elseif ($mode === 'photos') { + $profile_owner = App::$profile['profile_uid']; + $page_writeable = ($profile_owner == local_channel()); + $live_update_div = '
                    ' . "\r\n"; + // for photos we've already formatted the top-level item (the photo) + $content_html = App::$data['photo_html']; + } - elseif ($mode === 'channel') { - $profile_owner = App::$profile['profile_uid']; - $page_writeable = ($profile_owner == local_channel()); + $page_dropping = ((local_channel() && local_channel() == $profile_owner) ? true : false); - if (!$update) { - $tab = notags(trim($_GET['tab'])); - if ($tab === 'posts') { - // This is ugly, but we can't pass the profile_uid through the session to the ajax updater, - // because browser prefetching might change it on us. We have to deliver it with the page. + if (! feature_enabled($profile_owner, 'multi_delete')) { + $page_dropping = false; + } - $live_update_div = '
                    ' . "\r\n" - . "\r\n"; - } - } - } + $uploading = ((local_channel()) ? true : false); - elseif ($mode === 'cards') { - $profile_owner = App::$profile['profile_uid']; - $page_writeable = ($profile_owner == local_channel()); - $live_update_div = '
                    ' . "\r\n" - . "\r\n"; - $jsreload = $_SESSION['return_url']; - } + $channel = App::get_channel(); + $observer = App::get_observer(); - elseif ($mode === 'articles') { - $profile_owner = App::$profile['profile_uid']; - $page_writeable = ($profile_owner == local_channel()); - $live_update_div = '
                    ' . "\r\n" - . "\r\n"; - $jsreload = $_SESSION['return_url']; - } + if ($update && isset($_SESSION['return_url'])) { + $return_url = $_SESSION['return_url']; + } else { + $return_url = $_SESSION['return_url'] = App::$query_string; + } + + load_contact_links(local_channel()); + + $cb = array('items' => $items, 'mode' => $mode, 'update' => $update, 'preview' => $preview); + call_hooks('conversation_start', $cb); + + $items = $cb['items']; + + $conv_responses = [ + 'like' => [ 'title' => t('Likes', 'title') ], + 'dislike' => [ 'title' => t('Dislikes', 'title') ], + 'attendyes' => [ 'title' => t('Attending', 'title') ], + 'attendno' => [ 'title' => t('Not attending', 'title') ], + 'attendmaybe' => [ 'title' => t('Might attend', 'title') ] + ]; - elseif ($mode === 'display') { - $profile_owner = local_channel(); - $page_writeable = false; - $live_update_div = '
                    ' . "\r\n"; - } + // array with html for each thread (parent+comments) + $threads = []; + $threadsid = -1; - elseif ($mode === 'page') { - $profile_owner = App::$profile['uid']; - $page_writeable = ($profile_owner == local_channel()); - $live_update_div = '
                    ' . "\r\n"; - } + $page_template = get_markup_template("conversation.tpl"); - elseif ($mode === 'search') { - $live_update_div = '' . "\r\n"; - } + if ($items) { + if (in_array($mode, [ 'stream-new', 'search', 'community', 'moderate' ])) { + // "New Item View" on stream page or search page results + // - just loop through the items and format them minimally for display - elseif ($mode === 'moderate') { - $profile_owner = local_channel(); - } + $tpl = 'search_item.tpl'; - elseif ($mode === 'photos') { - $profile_owner = App::$profile['profile_uid']; - $page_writeable = ($profile_owner == local_channel()); - $live_update_div = '
                    ' . "\r\n"; - // for photos we've already formatted the top-level item (the photo) - $content_html = App::$data['photo_html']; - } + foreach ($items as $item) { + $x = [ + 'mode' => $mode, + 'item' => $item + ]; + call_hooks('stream_item', $x); - $page_dropping = ((local_channel() && local_channel() == $profile_owner) ? true : false); + $item = $x['item']; - if (! feature_enabled($profile_owner,'multi_delete')) - $page_dropping = false; + $threadsid++; - $uploading = ((local_channel()) ? true : false); + $comment = ''; + $owner_url = ''; + $owner_photo = ''; + $owner_name = ''; + $sparkle = ''; + $is_new = false; - $channel = App::get_channel(); - $observer = App::get_observer(); + if ($mode === 'search' || $mode === 'community') { + if ( + ((activity_match($item['verb'], ACTIVITY_LIKE)) || (activity_match($item['verb'], ACTIVITY_DISLIKE))) + && ($item['id'] != $item['parent']) + ) { + continue; + } + } - if($update && isset($_SESSION['return_url'])) { - $return_url = $_SESSION['return_url']; - } - else { - $return_url = $_SESSION['return_url'] = App::$query_string; - } - - load_contact_links(local_channel()); + $sp = false; +// $profile_link = best_link_url($item,$sp); +// if($sp) +// $sparkle = ' sparkle'; +// else +// $profile_link = zid($profile_link); - $cb = array('items' => $items, 'mode' => $mode, 'update' => $update, 'preview' => $preview); - call_hooks('conversation_start',$cb); + $profile_name = $item['author']['xchan_name']; + $profile_link = $item['author']['xchan_url']; + $profile_avatar = $item['author']['xchan_photo_m']; - $items = $cb['items']; - - $conv_responses = [ - 'like' => [ 'title' => t('Likes','title') ], - 'dislike' => [ 'title' => t('Dislikes','title') ], - 'attendyes' => [ 'title' => t('Attending','title') ], - 'attendno' => [ 'title' => t('Not attending','title') ], - 'attendmaybe' => [ 'title' => t('Might attend' ,'title') ] - ]; + if ($item['mid'] === $item['parent_mid'] && $item['author_xchan'] !== $item['owner_xchan']) { + $owner_name = $item['owner']['xchan_name']; + $owner_url = $item['owner']['xchan_url']; + $owner_photo = $item['owner']['xchan_photo']; + } - // array with html for each thread (parent+comments) - $threads = []; - $threadsid = -1; + $location = format_location($item); - $page_template = get_markup_template("conversation.tpl"); + localize_item($item); + if ($mode === 'stream-new') { + $dropping = true; + } else { + $dropping = false; + } - if($items) { + $drop = array( + 'pagedropping' => $page_dropping, + 'dropping' => $dropping, + 'select' => t('Select'), + 'delete' => t('Delete'), + ); - if(in_array($mode, [ 'stream-new', 'search', 'community', 'moderate' ])) { + $star = array( + 'toggle' => t("Toggle Star Status"), + 'isstarred' => ((intval($item['item_starred'])) ? true : false), + ); - // "New Item View" on stream page or search page results - // - just loop through the items and format them minimally for display - - $tpl = 'search_item.tpl'; - - foreach($items as $item) { - - $x = [ - 'mode' => $mode, - 'item' => $item - ]; - call_hooks('stream_item',$x); - - $item = $x['item']; - - $threadsid++; - - $comment = ''; - $owner_url = ''; - $owner_photo = ''; - $owner_name = ''; - $sparkle = ''; - $is_new = false; - - if($mode === 'search' || $mode === 'community') { - if(((activity_match($item['verb'],ACTIVITY_LIKE)) || (activity_match($item['verb'],ACTIVITY_DISLIKE))) - && ($item['id'] != $item['parent'])) - continue; + $lock = t('Public visibility'); + if (intval($item['item_private']) === 2) { + $lock = t('Direct message (private mail)'); + } + if (intval($item['item_private']) === 1) { + $lock = t('Restricted visibility'); } - $sp = false; -// $profile_link = best_link_url($item,$sp); -// if($sp) -// $sparkle = ' sparkle'; -// else -// $profile_link = zid($profile_link); + $locktype = intval($item['item_private']); - $profile_name = $item['author']['xchan_name']; - $profile_link = $item['author']['xchan_url']; - $profile_avatar = $item['author']['xchan_photo_m']; + $likebuttons = false; + $shareable = false; - if($item['mid'] === $item['parent_mid'] && $item['author_xchan'] !== $item['owner_xchan']) { - $owner_name = $item['owner']['xchan_name']; - $owner_url = $item['owner']['xchan_url']; - $owner_photo = $item['owner']['xchan_photo']; - } + $verified = (intval($item['item_verified']) ? t('Message signature validated') : ''); + $forged = ((($item['sig']) && (! intval($item['item_verified']))) ? t('Message signature incorrect') : ''); + + $unverified = ''; + +// $tags=[]; +// $terms = get_terms_oftype($item['term'],array(TERM_HASHTAG,TERM_MENTION,TERM_UNKNOWN,TERM_COMMUNITYTAG)); +// if(count($terms)) +// foreach($terms as $tag) +// $tags[] = format_term_for_display($tag); + + $body = prepare_body($item, true); + + $has_tags = (($body['tags'] || $body['categories'] || $body['mentions'] || $body['attachments'] || $body['folders']) ? true : false); + + if (strcmp(datetime_convert('UTC', 'UTC', $item['created']), datetime_convert('UTC', 'UTC', 'now - 12 hours')) > 0) { + $is_new = true; + } + + $conv_link_mid = (($mode == 'moderate') ? $item['parent_mid'] : $item['mid']); + + $conv_link = ((in_array($item['item_type'], [ ITEM_TYPE_CARD, ITEM_TYPE_ARTICLE])) ? $item['plink'] : z_root() . '/display/' . gen_link_id($conv_link_mid)); + + $allowed_type = (in_array($item['item_type'], get_config('system', 'pin_types', [ ITEM_TYPE_POST ])) ? true : false); + $pinned_items = ($allowed_type ? get_pconfig($item['uid'], 'pinned', $item['item_type'], []) : []); + $pinned = ((! empty($pinned_items) && in_array($item['mid'], $pinned_items)) ? true : false); + + $tmp_item = array( + 'template' => $tpl, + 'toplevel' => 'toplevel_item', + 'item_type' => intval($item['item_type']), + 'mode' => $mode, + 'approve' => t('Approve'), + 'delete' => t('Delete'), + 'preview_lbl' => $preview_lbl, + 'id' => (($preview) ? 'P0' : $item['item_id']), + 'linktitle' => sprintf(t('View %s\'s profile @ %s'), $profile_name, $profile_url), + 'profile_url' => $profile_link, + 'thread_action_menu' => thread_action_menu($item, $mode), + 'thread_author_menu' => thread_author_menu($item, $mode), + 'name' => $profile_name, + 'sparkle' => $sparkle, + 'lock' => $lock, + 'locktype' => $locktype, + 'thumb' => $profile_avatar, + 'title' => $item['title'], + 'body' => $body['html'], + 'event' => $body['event'], + 'photo' => $body['photo'], + 'tags' => $body['tags'], + 'categories' => $body['categories'], + 'mentions' => $body['mentions'], + 'attachments' => $body['attachments'], + 'folders' => $body['folders'], + 'verified' => $verified, + 'unverified' => $unverified, + 'forged' => $forged, + 'repeated' => ($item['verb'] === 'Announce'), + 'txt_cats' => t('Categories:'), + 'txt_folders' => t('Filed under:'), + 'has_cats' => (($body['categories']) ? 'true' : ''), + 'has_folders' => (($body['folders']) ? 'true' : ''), + 'text' => strip_tags($body['html']), + 'via' => t('via'), + 'ago' => relative_date($item['created']), + 'app' => $item['app'], + 'str_app' => sprintf(t('from %s'), $item['app']), + 'isotime' => datetime_convert('UTC', date_default_timezone_get(), $item['created'], 'c'), + 'localtime' => datetime_convert('UTC', date_default_timezone_get(), $item['created'], 'r'), + 'editedtime' => (($item['edited'] != $item['created']) ? sprintf(t('last edited: %s'), datetime_convert('UTC', date_default_timezone_get(), $item['edited'], 'r')) : ''), + 'expiretime' => (($item['expires'] > NULL_DATE) ? sprintf(t('Expires: %s'), datetime_convert('UTC', date_default_timezone_get(), $item['expires'], 'r')) : ''), + 'location' => $location, + 'divider' => false, + 'indent' => '', + 'owner_name' => $owner_name, + 'owner_url' => $owner_url, + 'owner_photo' => $owner_photo, + 'plink' => get_plink($item, false), + 'edpost' => false, + 'star' => ((feature_enabled(local_channel(), 'star_posts')) ? $star : ''), + 'drop' => $drop, + 'vote' => $likebuttons, + 'like' => '', + 'dislike' => '', + 'comment' => '', + 'pinned' => ($pinned ? t('Pinned post') : ''), + 'pinnable' => (($item['mid'] === $item['parent_mid'] && local_channel() && $item['owner_xchan'] == $observer['xchan_hash'] && $allowed_type && $item['item_private'] == 0) ? '1' : ''), + 'pinme' => ($pinned ? t('Unpin this post') : t('Pin this post')), + 'conv' => (($preview) ? '' : array('href' => $conv_link, 'title' => t('View Conversation'))), + 'previewing' => $previewing, + 'wait' => t('Please wait'), + 'thread_level' => 1, + 'has_tags' => $has_tags, + 'is_new' => $is_new + ); + + $arr = array('item' => $item, 'output' => $tmp_item); + call_hooks('display_item', $arr); + +// $threads[$threadsid]['id'] = $item['item_id']; + $threads[] = $arr['output']; + } + } else { + // Normal View +// logger('conv: items: ' . print_r($items,true)); + + $conv = new ThreadStream($mode, $preview, $uploading, $prepared_item); + + // In the display mode we don't have a profile owner. + + if ($mode === 'display' && $items) { + $conv->set_profile_owner($items[0]['uid']); + } + + // get all the topmost parents + // this shouldn't be needed, as we should have only them in our array + // But for now, this array respects the old style, just in case + + $threads = []; + foreach ($items as $item) { + $x = [ 'mode' => $mode, 'item' => $item ]; + call_hooks('stream_item', $x); + + $item = $x['item']; + + builtin_activity_puller($item, $conv_responses); + + if (! visible_activity($item)) { + continue; + } - $location = format_location($item); + $item['pagedrop'] = $page_dropping; - localize_item($item); - if($mode === 'stream-new') - $dropping = true; - else - $dropping = false; + if ($item['id'] == $item['parent']) { + $item_object = new ThreadItem($item); + $conv->add_thread($item_object); + if (($page_mode === 'list') || ($page_mode === 'pager_list')) { + $item_object->set_template('conv_list.tpl'); + $item_object->set_display_mode('list'); + } + if ($mode === 'cards' || $mode === 'articles') { + $item_object->set_reload($jsreload); + } + } + } - $drop = array( - 'pagedropping' => $page_dropping, - 'dropping' => $dropping, - 'select' => t('Select'), - 'delete' => t('Delete'), - ); + $threads = $conv->get_template_data($conv_responses); + if (!$threads) { + logger('[ERROR] conversation : Failed to get template data.', LOGGER_DEBUG); + $threads = []; + } + //logger('threads: ' . print_r($threads,true), LOGGER_DATA); + } + } - $star = array( - 'toggle' => t("Toggle Star Status"), - 'isstarred' => ((intval($item['item_starred'])) ? true : false), - ); + if (in_array($page_mode, [ 'traditional', 'preview', 'pager_list'])) { + $page_template = get_markup_template("threaded_conversation.tpl"); + } elseif ($update) { + $page_template = get_markup_template("convobj.tpl"); + } else { + $page_template = get_markup_template("conv_frame.tpl"); + $threads = null; + } - $lock = (($item['item_private'] || strlen($item['allow_cid']) || strlen($item['allow_gid']) || strlen($item['deny_cid']) || strlen($item['deny_gid'])) - ? t('Private Message') - : false - ); - - $likebuttons = false; - $shareable = false; - - $verified = (intval($item['item_verified']) ? t('Message signature validated') : ''); - $forged = ((($item['sig']) && (! intval($item['item_verified']))) ? t('Message signature incorrect') : ''); - - $unverified = ''; - -// $tags=[]; -// $terms = get_terms_oftype($item['term'],array(TERM_HASHTAG,TERM_MENTION,TERM_UNKNOWN,TERM_COMMUNITYTAG)); -// if(count($terms)) -// foreach($terms as $tag) -// $tags[] = format_term_for_display($tag); - - $body = prepare_body($item,true); - - $has_tags = (($body['tags'] || $body['categories'] || $body['mentions'] || $body['attachments'] || $body['folders']) ? true : false); - - if(strcmp(datetime_convert('UTC','UTC',$item['created']),datetime_convert('UTC','UTC','now - 12 hours')) > 0) - $is_new = true; - - $conv_link_mid = (($mode == 'moderate') ? $item['parent_mid'] : $item['mid']); - - $conv_link = ((in_array($item['item_type'],[ ITEM_TYPE_CARD, ITEM_TYPE_ARTICLE] )) ? $item['plink'] : z_root() . '/display/' . gen_link_id($conv_link_mid)); - - $allowed_type = (in_array($item['item_type'], get_config('system', 'pin_types', [ ITEM_TYPE_POST ])) ? true : false); - $pinned_items = ($allowed_type ? get_pconfig($item['uid'], 'pinned', $item['item_type'], []) : []); - $pinned = ((! empty($pinned_items) && in_array($item['mid'], $pinned_items)) ? true : false); - - $tmp_item = array( - 'template' => $tpl, - 'toplevel' => 'toplevel_item', - 'item_type' => intval($item['item_type']), - 'mode' => $mode, - 'approve' => t('Approve'), - 'delete' => t('Delete'), - 'preview_lbl' => $preview_lbl, - 'id' => (($preview) ? 'P0' : $item['item_id']), - 'linktitle' => sprintf( t('View %s\'s profile @ %s'), $profile_name, $profile_url), - 'profile_url' => $profile_link, - 'thread_action_menu' => thread_action_menu($item,$mode), - 'thread_author_menu' => thread_author_menu($item,$mode), - 'name' => $profile_name, - 'sparkle' => $sparkle, - 'lock' => $lock, - 'thumb' => $profile_avatar, - 'title' => $item['title'], - 'body' => $body['html'], - 'event' => $body['event'], - 'photo' => $body['photo'], - 'tags' => $body['tags'], - 'categories' => $body['categories'], - 'mentions' => $body['mentions'], - 'attachments' => $body['attachments'], - 'folders' => $body['folders'], - 'verified' => $verified, - 'unverified' => $unverified, - 'forged' => $forged, - 'repeated' => ($item['verb'] === 'Announce'), - 'txt_cats' => t('Categories:'), - 'txt_folders' => t('Filed under:'), - 'has_cats' => (($body['categories']) ? 'true' : ''), - 'has_folders' => (($body['folders']) ? 'true' : ''), - 'text' => strip_tags($body['html']), - 'via' => t('via'), - 'ago' => relative_date($item['created']), - 'app' => $item['app'], - 'str_app' => sprintf( t('from %s'), $item['app']), - 'isotime' => datetime_convert('UTC', date_default_timezone_get(), $item['created'], 'c'), - 'localtime' => datetime_convert('UTC', date_default_timezone_get(), $item['created'], 'r'), - 'editedtime' => (($item['edited'] != $item['created']) ? sprintf( t('last edited: %s'), datetime_convert('UTC', date_default_timezone_get(), $item['edited'], 'r')) : ''), - 'expiretime' => (($item['expires'] > NULL_DATE) ? sprintf( t('Expires: %s'), datetime_convert('UTC', date_default_timezone_get(), $item['expires'], 'r')):''), - 'location' => $location, - 'divider' => false, - 'indent' => '', - 'owner_name' => $owner_name, - 'owner_url' => $owner_url, - 'owner_photo' => $owner_photo, - 'plink' => get_plink($item,false), - 'edpost' => false, - 'star' => ((feature_enabled(local_channel(),'star_posts')) ? $star : ''), - 'drop' => $drop, - 'vote' => $likebuttons, - 'like' => '', - 'dislike' => '', - 'comment' => '', - 'pinned' => ($pinned ? t('Pinned post') : ''), - 'pinnable' => (($item['mid'] === $item['parent_mid'] && local_channel() && $item['owner_xchan'] == $observer['xchan_hash'] && $allowed_type && $item['item_private'] == 0) ? '1' : ''), - 'pinme' => ($pinned ? t('Unpin this post') : t('Pin this post')), - 'conv' => (($preview) ? '' : array('href'=> $conv_link, 'title'=> t('View Conversation'))), - 'previewing' => $previewing, - 'wait' => t('Please wait'), - 'thread_level' => 1, - 'has_tags' => $has_tags, - 'is_new' => $is_new - ); - - $arr = array('item' => $item, 'output' => $tmp_item); - call_hooks('display_item', $arr); - -// $threads[$threadsid]['id'] = $item['item_id']; - $threads[] = $arr['output']; - } - } - else { - - // Normal View -// logger('conv: items: ' . print_r($items,true)); - - $conv = new ThreadStream($mode, $preview, $uploading, $prepared_item); - - // In the display mode we don't have a profile owner. - - if($mode === 'display' && $items) - $conv->set_profile_owner($items[0]['uid']); - - // get all the topmost parents - // this shouldn't be needed, as we should have only them in our array - // But for now, this array respects the old style, just in case - - $threads = []; - foreach($items as $item) { - - - $x = [ 'mode' => $mode, 'item' => $item ]; - call_hooks('stream_item',$x); - - $item = $x['item']; - - builtin_activity_puller($item, $conv_responses); - - if(! visible_activity($item)) { - continue; - } - - - $item['pagedrop'] = $page_dropping; - - if($item['id'] == $item['parent']) { - - $item_object = new ThreadItem($item); - $conv->add_thread($item_object); - if(($page_mode === 'list') || ($page_mode === 'pager_list')) { - $item_object->set_template('conv_list.tpl'); - $item_object->set_display_mode('list'); - } - if($mode === 'cards' || $mode === 'articles') { - $item_object->set_reload($jsreload); - } - - } - } - - $threads = $conv->get_template_data($conv_responses); - if(!$threads) { - logger('[ERROR] conversation : Failed to get template data.', LOGGER_DEBUG); - $threads = []; - } - //logger('threads: ' . print_r($threads,true), LOGGER_DATA); - } - } - - if(in_array($page_mode, [ 'traditional', 'preview', 'pager_list'] )) { - $page_template = get_markup_template("threaded_conversation.tpl"); - } - elseif($update) { - $page_template = get_markup_template("convobj.tpl"); - } - else { - $page_template = get_markup_template("conv_frame.tpl"); - $threads = null; - } - -// if($page_mode === 'preview') -// logger('preview: ' . print_r($threads,true)); +// if($page_mode === 'preview') +// logger('preview: ' . print_r($threads,true)); // Do not un-comment if smarty3 is in use -// logger('page_template: ' . $page_template); +// logger('page_template: ' . $page_template); -// logger('nouveau: ' . print_r($threads,true)); +// logger('nouveau: ' . print_r($threads,true)); // logger('page_template: ' . print_r($page_template,true)); - $o .= replace_macros($page_template, array( - '$baseurl' => z_root(), - '$photo_item' => $content_html, - '$live_update' => $live_update_div, - '$remove' => t('remove'), - '$mode' => $mode, - '$user' => App::$user, - '$threads' => $threads, - '$wait' => t('Loading...'), - '$dropping' => ($page_dropping?t('Delete Selected Items'):False), - )); + $o .= replace_macros($page_template, array( + '$baseurl' => z_root(), + '$photo_item' => $content_html, + '$live_update' => $live_update_div, + '$remove' => t('remove'), + '$mode' => $mode, + '$user' => App::$user, + '$threads' => $threads, + '$wait' => t('Loading...'), + '$dropping' => ($page_dropping ? t('Delete Selected Items') : false), + )); - return $o; + return $o; } -function best_link_url($item) { +function best_link_url($item) +{ - $best_url = ''; - $sparkle = false; + $best_url = ''; + $sparkle = false; - $clean_url = normalise_link($item['author-link']); + $clean_url = normalise_link($item['author-link']); - if((local_channel()) && (local_channel() == $item['uid'])) { - if(isset(App::$contacts) && x(App::$contacts,$clean_url)) { - if(App::$contacts[$clean_url]['network'] === NETWORK_DFRN) { - $best_url = z_root() . '/redir/' . App::$contacts[$clean_url]['id']; - $sparkle = true; - } - else - $best_url = App::$contacts[$clean_url]['url']; - } - } - if(! $best_url) { - if(strlen($item['author-link'])) - $best_url = $item['author-link']; - else - $best_url = $item['url']; - } + if ((local_channel()) && (local_channel() == $item['uid'])) { + if (isset(App::$contacts) && x(App::$contacts, $clean_url)) { + if (App::$contacts[$clean_url]['network'] === NETWORK_DFRN) { + $best_url = z_root() . '/redir/' . App::$contacts[$clean_url]['id']; + $sparkle = true; + } else { + $best_url = App::$contacts[$clean_url]['url']; + } + } + } + if (! $best_url) { + if (strlen($item['author-link'])) { + $best_url = $item['author-link']; + } else { + $best_url = $item['url']; + } + } - return $best_url; + return $best_url; } -function thread_action_menu($item,$mode = '') { +function thread_action_menu($item, $mode = '') +{ - $menu = []; - - if((local_channel()) && local_channel() == $item['uid']) { - $menu[] = [ - 'menu' => 'view_source', - 'title' => t('View Source'), - 'icon' => 'code', - 'action' => 'viewsrc(' . $item['id'] . '); return false;', - 'href' => '#' - ]; + $menu = []; - if(! in_array($mode, [ 'stream-new', 'search', 'community'])) { - if($item['parent'] == $item['id'] && (get_observer_hash() != $item['author_xchan'])) { - $menu[] = [ - 'menu' => 'follow_thread', - 'title' => t('Follow Thread'), - 'icon' => 'plus', - 'action' => 'dosubthread(' . $item['id'] . '); return false;', - 'href' => '#' - ]; - } + if ((local_channel()) && local_channel() == $item['uid']) { + $menu[] = [ + 'menu' => 'view_source', + 'title' => t('View Source'), + 'icon' => 'code', + 'action' => 'viewsrc(' . $item['id'] . '); return false;', + 'href' => '#' + ]; - $menu[] = [ - 'menu' => 'unfollow_thread', - 'title' => t('Unfollow Thread'), - 'icon' => 'minus', - 'action' => 'dounsubthread(' . $item['id'] . '); return false;', - 'href' => '#' - ]; - } + if (! in_array($mode, [ 'stream-new', 'search', 'community'])) { + if ($item['parent'] == $item['id'] && (get_observer_hash() != $item['author_xchan'])) { + $menu[] = [ + 'menu' => 'follow_thread', + 'title' => t('Follow Thread'), + 'icon' => 'plus', + 'action' => 'dosubthread(' . $item['id'] . '); return false;', + 'href' => '#' + ]; + } - } + $menu[] = [ + 'menu' => 'unfollow_thread', + 'title' => t('Unfollow Thread'), + 'icon' => 'minus', + 'action' => 'dounsubthread(' . $item['id'] . '); return false;', + 'href' => '#' + ]; + } + } - $args = [ 'item' => $item, 'mode' => $mode, 'menu' => $menu ]; - call_hooks('thread_action_menu', $args); + $args = [ 'item' => $item, 'mode' => $mode, 'menu' => $menu ]; + call_hooks('thread_action_menu', $args); - return $args['menu']; + return $args['menu']; } -function author_is_pmable($xchan, $abook) { +function author_is_pmable($xchan, $abook) +{ - $x = [ 'xchan' => $xchan, 'abook' => $abook, 'result' => 'unset' ]; - call_hooks('author_is_pmable',$x); - if ($x['result'] !== 'unset') { - return $x['result']; - } - + $x = [ 'xchan' => $xchan, 'abook' => $abook, 'result' => 'unset' ]; + call_hooks('author_is_pmable', $x); + if ($x['result'] !== 'unset') { + return $x['result']; + } if (in_array($xchan['xchan_network'],['nomad','zot6']) && get_observer_hash()) { return true; } - return false; - + return false; } -function thread_author_menu($item, $mode = '') { +function thread_author_menu($item, $mode = '') +{ - $menu = []; + $menu = []; - $local_channel = local_channel(); - - if($local_channel) { - if(! count(App::$contacts)) - load_contact_links($local_channel); - $channel = App::get_channel(); - $channel_hash = (($channel) ? $channel['channel_hash'] : ''); - } + $local_channel = local_channel(); - $profile_link = chanlink_hash($item['author_xchan']); - $contact = false; + if ($local_channel) { + if (! count(App::$contacts)) { + load_contact_links($local_channel); + } + $channel = App::get_channel(); + $channel_hash = (($channel) ? $channel['channel_hash'] : ''); + } - if($channel['channel_hash'] !== $item['author_xchan']) { - if(App::$contacts && array_key_exists($item['author_xchan'],App::$contacts)) { - $contact = App::$contacts[$item['author_xchan']]; - } - else { - if($local_channel && (! in_array($item['author']['xchan_network'],[ 'rss', 'anon','token','unknown' ]))) { - $follow_url = z_root() . '/follow/?f=&url=' . urlencode(($item['author']['xchan_addr']) ? $item['author']['xchan_addr'] : $item['author']['xchan_url']) . '&interactive=0'; - } - } - } + $profile_link = chanlink_hash($item['author_xchan']); + $contact = false; - $poke_label = ucfirst( t(get_pconfig($local_channel,'system','pokeverb','poke')) ); + if ($channel['channel_hash'] !== $item['author_xchan']) { + if (App::$contacts && array_key_exists($item['author_xchan'], App::$contacts)) { + $contact = App::$contacts[$item['author_xchan']]; + } else { + if ($local_channel && (! in_array($item['author']['xchan_network'], [ 'rss', 'anon','token','unknown' ]))) { + $follow_url = z_root() . '/follow/?f=&url=' . urlencode(($item['author']['xchan_addr']) ? $item['author']['xchan_addr'] : $item['author']['xchan_url']) . '&interactive=0'; + } + } + } - if($contact) { - if (! (isset($contact['abook_self']) && intval($contact['abook_self']))) { - $contact_url = z_root() . '/connedit/' . $contact['abook_id']; - } - $posts_link = z_root() . '/stream/?cid=' . $contact['abook_id']; - $clean_url = $item['author']['xchan_url']; - } + $poke_label = ucfirst(t(get_pconfig($local_channel, 'system', 'pokeverb', 'poke'))); - $can_dm = false; + if ($contact) { + if (! (isset($contact['abook_self']) && intval($contact['abook_self']))) { + $contact_url = z_root() . '/connedit/' . $contact['abook_id']; + } + $posts_link = z_root() . '/stream/?cid=' . $contact['abook_id']; + $clean_url = $item['author']['xchan_url']; + } - if ($local_channel && $contact) { - $can_dm = perm_is_allowed($local_channel,$item['author_xchan'],'send_stream'); - } - elseif ($item['author']['xchan_network'] === 'activitypub') { - $can_dm = true; - } -// if ($can_dm) { -// $pm_url = z_root() . '/rpost?to=' . urlencode($item['author_xchan']); -// } - - if($profile_link) { - $menu[] = [ - 'menu' => 'view_profile', - 'title' => t('Visit'), - 'icon' => 'fw', - 'action' => '', - 'href' => $profile_link - ]; - } + $can_dm = false; - if(isset($posts_link) && $posts_link) { - $menu[] = [ - 'menu' => 'view_posts', - 'title' => t('Recent Activity'), - 'icon' => 'fw', - 'action' => '', - 'href' => $posts_link - ]; - } + if ($local_channel && $contact) { + $can_dm = perm_is_allowed($local_channel, $item['author_xchan'], 'post_mail') && intval($contact['xchan_type']) !== XCHAN_TYPE_GROUP ; + } elseif ($item['author']['xchan_network'] === 'activitypub') { + $can_dm = true; + } + if ($can_dm) { + $pm_url = z_root() + . '/rpost?to=' + . urlencode($item['author_xchan']) + . '&body=' + . urlencode('@!{' . $contact['xchan_addr'] ? $contact['xchan_addr'] : $contact['xchan_url'] . '}'); + } - if(isset($follow_url) && $follow_url) { - $menu[] = [ - 'menu' => 'follow', - 'title' => t('Connect'), - 'icon' => 'fw', - 'action' => 'doFollowAuthor(\'' . $follow_url . '\'); return false;', - 'href' => '#', - ]; - } + if ($profile_link) { + $menu[] = [ + 'menu' => 'view_profile', + 'title' => t('Visit'), + 'icon' => 'fw', + 'action' => '', + 'href' => $profile_link + ]; + } - if(isset($contact_url) && $contact_url) { - $menu[] = [ - 'menu' => 'connedit', - 'title' => t('Edit Connection'), - 'icon' => 'fw', - 'action' => '', - 'href' => $contact_url - ]; - } + if (isset($posts_link) && $posts_link) { + $menu[] = [ + 'menu' => 'view_posts', + 'title' => t('Recent Activity'), + 'icon' => 'fw', + 'action' => '', + 'href' => $posts_link + ]; + } - if(isset($pm_url) && $pm_url) { - $menu[] = [ - 'menu' => 'prv_message', - 'title' => t('Direct Message'), - 'icon' => 'fw', - 'action' => '', - 'href' => $pm_url - ]; - } + if (isset($follow_url) && $follow_url) { + $menu[] = [ + 'menu' => 'follow', + 'title' => t('Connect'), + 'icon' => 'fw', + 'action' => 'doFollowAuthor(\'' . $follow_url . '\'); return false;', + 'href' => '#', + ]; + } - if (Apps::system_app_installed($local_channel,'Poke')) { - $menu[] = [ - 'menu' => 'poke', - 'title' => $poke_label, - 'icon' => 'fw', - 'action' => 'doPoke(\'' . urlencode($item['author_xchan']) . '\'); return false;', - 'href' => '#' - ]; - } + if (isset($contact_url) && $contact_url) { + $menu[] = [ + 'menu' => 'connedit', + 'title' => t('Edit Connection'), + 'icon' => 'fw', + 'action' => '', + 'href' => $contact_url + ]; + } - if (local_channel()) { - $menu[] = [ - 'menu' => 'superblocksite', - 'title' => t('Block author\'s site'), - 'icon' => 'fw', - 'action' => 'blocksite(\'' . urlencode($item['author_xchan']) . '\',' . $item['id'] . '); return false;', - 'href' => '#' - ]; - $menu[] = [ - 'menu' => 'superblock', - 'title' => t('Block author'), - 'icon' => 'fw', - 'action' => 'superblock(\'' . urlencode($item['author_xchan']) . '\',' . $item['id'] . '); return false;', - 'href' => '#' - ]; - } + if (isset($pm_url) && $pm_url) { + $menu[] = [ + 'menu' => 'prv_message', + 'title' => t('Direct Message'), + 'icon' => 'fw', + 'action' => '', + 'href' => $pm_url + ]; + } - $args = [ 'item' => $item, 'mode' => $mode, 'menu' => $menu ]; - call_hooks('thread_author_menu', $args); + if (Apps::system_app_installed($local_channel, 'Poke')) { + $menu[] = [ + 'menu' => 'poke', + 'title' => $poke_label, + 'icon' => 'fw', + 'action' => 'doPoke(\'' . urlencode($item['author_xchan']) . '\'); return false;', + 'href' => '#' + ]; + } - return $args['menu']; + if (local_channel()) { + $menu[] = [ + 'menu' => 'superblocksite', + 'title' => t('Block author\'s site'), + 'icon' => 'fw', + 'action' => 'blocksite(\'' . urlencode($item['author_xchan']) . '\',' . $item['id'] . '); return false;', + 'href' => '#' + ]; + $menu[] = [ + 'menu' => 'superblock', + 'title' => t('Block author'), + 'icon' => 'fw', + 'action' => 'superblock(\'' . urlencode($item['author_xchan']) . '\',' . $item['id'] . '); return false;', + 'href' => '#' + ]; + } + $args = [ 'item' => $item, 'mode' => $mode, 'menu' => $menu ]; + call_hooks('thread_author_menu', $args); + + return $args['menu']; } @@ -1047,72 +1043,78 @@ function thread_author_menu($item, $mode = '') { * @param array $item * @param array &$conv_responses (already created with builtin activity structure) */ -function builtin_activity_puller($item, &$conv_responses) { +function builtin_activity_puller($item, &$conv_responses) +{ - // if this item is a post or comment there's nothing for us to do here, just return. + // if this item is a post or comment there's nothing for us to do here, just return. - if(in_array($item['verb'],['Create'])) - return; + if (in_array($item['verb'], ['Create'])) { + return; + } - foreach($conv_responses as $mode => $v) { + foreach ($conv_responses as $mode => $v) { + $url = ''; - $url = ''; + switch ($mode) { + case 'like': + $verb = ACTIVITY_LIKE; + break; + case 'dislike': + $verb = ACTIVITY_DISLIKE; + break; + case 'attendyes': + $verb = 'Accept'; + break; + case 'attendno': + $verb = 'Reject'; + break; + case 'attendmaybe': + $verb = 'TentativeAccept'; + break; + default: + return; + break; + } - switch($mode) { - case 'like': - $verb = ACTIVITY_LIKE; - break; - case 'dislike': - $verb = ACTIVITY_DISLIKE; - break; - case 'attendyes': - $verb = 'Accept'; - break; - case 'attendno': - $verb = 'Reject'; - break; - case 'attendmaybe': - $verb = 'TentativeAccept'; - break; - default: - return; - break; - } + if ((activity_match($item['verb'], $verb)) && ($item['id'] != $item['parent'])) { + $name = (($item['author']['xchan_name']) ? $item['author']['xchan_name'] : t('Unknown')); + $url = (($item['author_xchan'] && $item['author']['xchan_photo_s']) + ? '' . '' . urlencode($name) . ' ' . $name . '' + : '' . $name . '' + ); - if((activity_match($item['verb'], $verb)) && ($item['id'] != $item['parent'])) { + if (! $item['thr_parent']) { + $item['thr_parent'] = $item['parent_mid']; + } - $name = (($item['author']['xchan_name']) ? $item['author']['xchan_name'] : t('Unknown')); - $url = (($item['author_xchan'] && $item['author']['xchan_photo_s']) - ? '' . '' . urlencode($name) . ' ' . $name . '' - : '' . $name . '' - ); + if ( + ! ((isset($conv_responses[$mode][$item['thr_parent'] . '-l'])) + && (is_array($conv_responses[$mode][$item['thr_parent'] . '-l']))) + ) { + $conv_responses[$mode][$item['thr_parent'] . '-l'] = []; + } - if(! $item['thr_parent']) - $item['thr_parent'] = $item['parent_mid']; + // only list each unique author once + if (in_array($url, $conv_responses[$mode][$item['thr_parent'] . '-l'])) { + continue; + } - if(! ((isset($conv_responses[$mode][$item['thr_parent'] . '-l'])) - && (is_array($conv_responses[$mode][$item['thr_parent'] . '-l'])))) - $conv_responses[$mode][$item['thr_parent'] . '-l'] = []; + if (! isset($conv_responses[$mode][$item['thr_parent']])) { + $conv_responses[$mode][$item['thr_parent']] = 1; + } else { + $conv_responses[$mode][$item['thr_parent']] ++; + } - // only list each unique author once - if(in_array($url,$conv_responses[$mode][$item['thr_parent'] . '-l'])) - continue; + $conv_responses[$mode][$item['thr_parent'] . '-l'][] = $url; + if (get_observer_hash() && get_observer_hash() === $item['author_xchan']) { + $conv_responses[$mode][$item['thr_parent'] . '-m'] = true; + } - if(! isset($conv_responses[$mode][$item['thr_parent']])) - $conv_responses[$mode][$item['thr_parent']] = 1; - else - $conv_responses[$mode][$item['thr_parent']] ++; - - $conv_responses[$mode][$item['thr_parent'] . '-l'][] = $url; - if(get_observer_hash() && get_observer_hash() === $item['author_xchan']) { - $conv_responses[$mode][$item['thr_parent'] . '-m'] = true; - } - - // there can only be one activity verb per item so if we found anything, we can stop looking - return; - } - } + // there can only be one activity verb per item so if we found anything, we can stop looking + return; + } + } } @@ -1125,632 +1127,648 @@ function builtin_activity_puller($item, &$conv_responses) { * @param int $id item id * @return string formatted text */ -function format_like($cnt, $arr, $type, $id) { - $o = ''; - if ($cnt == 1) { - $o .= (($type === 'like') ? sprintf( t('%s likes this.'), $arr[0]) : sprintf( t('%s doesn\'t like this.'), $arr[0])) . EOL ; - } else { - $spanatts = 'class="fakelink" onclick="openClose(\'' . $type . 'list-' . $id . '\');"'; - $o .= (($type === 'like') ? - sprintf( tt('%2$d people like this.','%2$d people like this.',$cnt), $spanatts, $cnt) - : - sprintf( tt('%2$d people don\'t like this.','%2$d people don\'t like this.',$cnt), $spanatts, $cnt) ); - $o .= EOL; - $total = count($arr); - if($total >= MAX_LIKERS) - $arr = array_slice($arr, 0, MAX_LIKERS - 1); - if($total < MAX_LIKERS) - $arr[count($arr)-1] = t('and') . ' ' . $arr[count($arr)-1]; - $str = implode(', ', $arr); - if($total >= MAX_LIKERS) - $str .= sprintf( tt(', and %d other people',', and %d other people',$total - MAX_LIKERS), $total - MAX_LIKERS ); - $str = (($type === 'like') ? sprintf( t('%s like this.'), $str) : sprintf( t('%s don\'t like this.'), $str)); - $o .= "\t" . ''; - } +function format_like($cnt, $arr, $type, $id) +{ + $o = ''; + if ($cnt == 1) { + $o .= (($type === 'like') ? sprintf(t('%s likes this.'), $arr[0]) : sprintf(t('%s doesn\'t like this.'), $arr[0])) . EOL ; + } else { + $spanatts = 'class="fakelink" onclick="openClose(\'' . $type . 'list-' . $id . '\');"'; + $o .= (($type === 'like') ? + sprintf(tt('%2$d people like this.', '%2$d people like this.', $cnt), $spanatts, $cnt) + : + sprintf(tt('%2$d people don\'t like this.', '%2$d people don\'t like this.', $cnt), $spanatts, $cnt) ); + $o .= EOL; + $total = count($arr); + if ($total >= MAX_LIKERS) { + $arr = array_slice($arr, 0, MAX_LIKERS - 1); + } + if ($total < MAX_LIKERS) { + $arr[count($arr) - 1] = t('and') . ' ' . $arr[count($arr) - 1]; + } + $str = implode(', ', $arr); + if ($total >= MAX_LIKERS) { + $str .= sprintf(tt(', and %d other people', ', and %d other people', $total - MAX_LIKERS), $total - MAX_LIKERS); + } + $str = (($type === 'like') ? sprintf(t('%s like this.'), $str) : sprintf(t('%s don\'t like this.'), $str)); + $o .= "\t" . ''; + } - return $o; + return $o; } /** * Wrapper to allow addons to replace the status editor if desired. */ -function status_editor($x, $popup = false, $module='') { +function status_editor($x, $popup = false, $module = '') +{ $hook_info = ['editor_html' => '', 'x' => $x, 'popup' => $popup, 'module' => $module]; - call_hooks('status_editor',$hook_info); + call_hooks('status_editor', $hook_info); if ($hook_info['editor_html'] == '') { - return z_status_editor($x, $popup); - } - else { - return $hook_info['editor_html']; + return z_status_editor($x, $popup); + } else { + return $hook_info['editor_html']; } } /** - * This is our general purpose content editor. + * This is our general purpose content editor. * It was once nicknamed "jot" and you may see references to "jot" littered throughout the code. - * They are referring to the content editor or components thereof. + * They are referring to the content editor or components thereof. */ +function z_status_editor($x, $popup = false) +{ -function z_status_editor($x, $popup = false) { + $o = ''; - $o = ''; + $c = channelx_by_n($x['profile_uid']); + if ($c && $c['channel_moved']) { + return $o; + } - $c = channelx_by_n($x['profile_uid']); - if($c && $c['channel_moved']) - return $o; + $plaintext = true; + $webpage = false; + $feature_voting = false; - $plaintext = true; - $webpage = false; - $feature_voting = false; - - $feature_comment_control = Apps::system_app_installed($x['profile_uid'], 'Comment Control'); - if(x($x, 'disable_comment_control')) - $feature_comment_control = false; + $feature_comment_control = Apps::system_app_installed($x['profile_uid'], 'Comment Control'); + if (x($x, 'disable_comment_control')) { + $feature_comment_control = false; + } - $feature_expire = ((Apps::system_app_installed($x['profile_uid'], 'Expire Posts') && (! $webpage)) ? true : false); - if(x($x, 'hide_expire')) - $feature_expire = false; + $feature_expire = ((Apps::system_app_installed($x['profile_uid'], 'Expire Posts') && (! $webpage)) ? true : false); + if (x($x, 'hide_expire')) { + $feature_expire = false; + } - $feature_future = ((Apps::system_app_installed($x['profile_uid'], 'Future Posting') && (! $webpage)) ? true : false); - if(x($x, 'hide_future')) - $feature_future = false; + $feature_future = ((Apps::system_app_installed($x['profile_uid'], 'Future Posting') && (! $webpage)) ? true : false); + if (x($x, 'hide_future')) { + $feature_future = false; + } - $feature_markup = ((Apps::system_app_installed($x['profile_uid'], 'Markup') && (! $webpage)) ? true : false); - if(x($x, 'hide_markup')) - $feature_markup = false; + $feature_markup = ((Apps::system_app_installed($x['profile_uid'], 'Markup') && (! $webpage)) ? true : false); + if (x($x, 'hide_markup')) { + $feature_markup = false; + } - $geotag = (($x['allow_location']) ? replace_macros(get_markup_template('jot_geotag.tpl'), []) : ''); - $setloc = t('Set your location'); - $clearloc = ((get_pconfig($x['profile_uid'], 'system', 'use_browser_location')) ? t('Clear browser location') : ''); - if(x($x, 'hide_location')) - $geotag = $setloc = $clearloc = ''; + $geotag = (($x['allow_location']) ? replace_macros(get_markup_template('jot_geotag.tpl'), []) : ''); + $setloc = t('Set your location'); + $clearloc = ((get_pconfig($x['profile_uid'], 'system', 'use_browser_location')) ? t('Clear browser location') : ''); + if (x($x, 'hide_location')) { + $geotag = $setloc = $clearloc = ''; + } - $summaryenabled = ((array_key_exists('allow_summary',$x)) ? intval($x['allow_summary']) : false); + $summaryenabled = ((array_key_exists('allow_summary', $x)) ? intval($x['allow_summary']) : false); - $mimetype = ((x($x,'mimetype')) ? $x['mimetype'] : 'text/x-multicode'); + $mimetype = ((x($x, 'mimetype')) ? $x['mimetype'] : 'text/x-multicode'); - $mimeselect = ((x($x,'mimeselect')) ? $x['mimeselect'] : false); - if($mimeselect) - $mimeselect = mimetype_select($x['profile_uid'], $mimetype); - else - $mimeselect = ''; + $mimeselect = ((x($x, 'mimeselect')) ? $x['mimeselect'] : false); + if ($mimeselect) { + $mimeselect = mimetype_select($x['profile_uid'], $mimetype); + } else { + $mimeselect = ''; + } - $weblink = ((in_array($mimetype, [ 'text/bbcode', 'text/x-multicode' ])) ? t('Insert web link') : false); - if(x($x, 'hide_weblink')) - $weblink = false; - - $embedPhotos = t('Embed (existing) photo from your photo albums'); + $weblink = (($mimetype === 'text/x-multicode') ? t('Insert web link') : false); + if (x($x, 'hide_weblink')) { + $weblink = false; + } - $writefiles = ((in_array($mimetype, ['text/bbcode', 'text/x-multicode'])) ? perm_is_allowed($x['profile_uid'], get_observer_hash(), 'write_storage') : false); - if(x($x, 'hide_attach')) - $writefiles = false; - if(perm_is_allowed($x['profile_uid'],get_observer_hash(),'moderated')) { - $writefiles = false; - } + $embedPhotos = t('Embed (existing) photo from your photo albums'); - $layout = ((x($x,'layout')) ? $x['layout'] : ''); + $writefiles = (($mimetype === 'text/x-multicode') ? perm_is_allowed($x['profile_uid'], get_observer_hash(), 'write_storage') : false); + if (x($x, 'hide_attach')) { + $writefiles = false; + } + if (perm_is_allowed($x['profile_uid'], get_observer_hash(), 'moderated')) { + $writefiles = false; + } - $layoutselect = ((x($x,'layoutselect')) ? $x['layoutselect'] : false); - if($layoutselect) - $layoutselect = layout_select($x['profile_uid'], $layout); - else - $layoutselect = ''; + $layout = ((x($x, 'layout')) ? $x['layout'] : ''); - if(array_key_exists('channel_select',$x) && $x['channel_select']) { - require_once('include/channel.php'); - $id_select = identity_selector(); - } - else - $id_select = ''; + $layoutselect = ((x($x, 'layoutselect')) ? $x['layoutselect'] : false); + if ($layoutselect) { + $layoutselect = layout_select($x['profile_uid'], $layout); + } else { + $layoutselect = ''; + } - $webpage = ((x($x,'webpage')) ? $x['webpage'] : ''); + if (array_key_exists('channel_select', $x) && $x['channel_select']) { + require_once('include/channel.php'); + $id_select = identity_selector(); + } else { + $id_select = ''; + } - $reset = ((x($x,'reset')) ? $x['reset'] : ''); - - $feature_auto_save_draft = ((feature_enabled($x['profile_uid'], 'auto_save_draft')) ? "true" : "false"); - - $tpl = get_markup_template('jot-header.tpl'); + $webpage = ((x($x, 'webpage')) ? $x['webpage'] : ''); - if (! isset(App::$page['htmlhead'])) { - App::$page['htmlhead'] = EMPTY_STR; - } + $reset = ((x($x, 'reset')) ? $x['reset'] : ''); - App::$page['htmlhead'] .= replace_macros($tpl, array( - '$baseurl' => z_root(), - '$webpage' => $webpage, - '$editselect' => (($plaintext) ? 'none' : '/(profile-jot-text|prvmail-text)/'), - '$pretext' => ((x($x,'pretext')) ? $x['pretext'] : ''), - '$geotag' => $geotag, - '$nickname' => $x['nickname'], - '$linkurl' => t('Please enter a link URL:'), - '$term' => t('Tag term:'), - '$whereareu' => t('Where are you right now?'), - '$editor_autocomplete'=> ((x($x,'editor_autocomplete')) ? $x['editor_autocomplete'] : ''), - '$bbco_autocomplete'=> ((x($x,'bbco_autocomplete')) ? $x['bbco_autocomplete'] : ''), - '$modalchooseimages' => t('Choose images to embed'), - '$modalchoosealbum' => t('Choose an album'), - '$modaldiffalbum' => t('Choose a different album...'), - '$modalerrorlist' => t('Error getting album list'), - '$modalerrorlink' => t('Error getting photo link'), - '$modalerroralbum' => t('Error getting album'), - '$auto_save_draft' => $feature_auto_save_draft, - '$confirmdelete' => t('Delete this item?'), - '$reset' => $reset - )); + $feature_auto_save_draft = ((feature_enabled($x['profile_uid'], 'auto_save_draft')) ? "true" : "false"); - $tpl = get_markup_template('jot.tpl'); + $tpl = get_markup_template('jot-header.tpl'); - $preview = t('Preview'); - if(x($x, 'hide_preview')) - $preview = ''; + if (! isset(App::$page['htmlhead'])) { + App::$page['htmlhead'] = EMPTY_STR; + } - $defexpire = ((($z = get_pconfig($x['profile_uid'], 'system', 'default_post_expire')) && (! $webpage)) ? $z : ''); - if($defexpire) - $defexpire = datetime_convert('UTC',date_default_timezone_get(),$defexpire,'Y-m-d H:i'); - else { - $defexpire = ((($z = intval(get_pconfig($x['profile_uid'], 'system', 'selfexpiredays'))) && (! $webpage)) ? $z : ''); - if($defexpire) - $defexpire = datetime_convert('UTC',date_default_timezone_get(),"now + $defexpire days",'Y-m-d H:i'); - } + App::$page['htmlhead'] .= replace_macros($tpl, array( + '$baseurl' => z_root(), + '$webpage' => $webpage, + '$editselect' => (($plaintext) ? 'none' : '/(profile-jot-text|prvmail-text)/'), + '$pretext' => ((x($x, 'pretext')) ? $x['pretext'] : ''), + '$geotag' => $geotag, + '$nickname' => $x['nickname'], + '$linkurl' => t('Please enter a link URL:'), + '$term' => t('Tag term:'), + '$whereareu' => t('Where are you right now?'), + '$editor_autocomplete' => ((x($x, 'editor_autocomplete')) ? $x['editor_autocomplete'] : ''), + '$bbco_autocomplete' => ((x($x, 'bbco_autocomplete')) ? $x['bbco_autocomplete'] : ''), + '$modalchooseimages' => t('Choose images to embed'), + '$modalchoosealbum' => t('Choose an album'), + '$modaldiffalbum' => t('Choose a different album...'), + '$modalerrorlist' => t('Error getting album list'), + '$modalerrorlink' => t('Error getting photo link'), + '$modalerroralbum' => t('Error getting album'), + '$auto_save_draft' => $feature_auto_save_draft, + '$confirmdelete' => t('Delete this item?'), + '$reset' => $reset + )); + + $tpl = get_markup_template('jot.tpl'); + + $preview = t('Preview'); + if (x($x, 'hide_preview')) { + $preview = ''; + } + + $defexpire = ((($z = get_pconfig($x['profile_uid'], 'system', 'default_post_expire')) && (! $webpage)) ? $z : ''); + if ($defexpire) { + $defexpire = datetime_convert('UTC', date_default_timezone_get(), $defexpire, 'Y-m-d H:i'); + } else { + $defexpire = ((($z = intval(get_pconfig($x['profile_uid'], 'system', 'selfexpiredays'))) && (! $webpage)) ? $z : ''); + if ($defexpire) { + $defexpire = datetime_convert('UTC', date_default_timezone_get(), "now + $defexpire days", 'Y-m-d H:i'); + } + } - $defclosecomm = ((($z = get_pconfig($x['profile_uid'], 'system', 'close_comments',0)) && (! $webpage)) ? intval($z) : ''); - if($defclosecomm) { - $closecommdays = intval($defclosecomm); - } - else { - $closecommdays = EMPTY_STR; - } + $defclosecomm = ((($z = get_pconfig($x['profile_uid'], 'system', 'close_comments', 0)) && (! $webpage)) ? intval($z) : ''); + if ($defclosecomm) { + $closecommdays = intval($defclosecomm); + } else { + $closecommdays = EMPTY_STR; + } - $defcommuntil = (($closecommdays) ? datetime_convert('UTC', date_default_timezone_get(), 'now + ' . $closecommdays . ' days') : EMPTY_STR); + $defcommuntil = (($closecommdays) ? datetime_convert('UTC', date_default_timezone_get(), 'now + ' . $closecommdays . ' days') : EMPTY_STR); - $defpublish = ((($z = get_pconfig($x['profile_uid'], 'system', 'default_post_publish')) && (! $webpage)) ? $z : ''); - if($defpublish) - $defpublish = datetime_convert('UTC',date_default_timezone_get(),$defpublish,'Y-m-d H:i'); + $defpublish = ((($z = get_pconfig($x['profile_uid'], 'system', 'default_post_publish')) && (! $webpage)) ? $z : ''); + if ($defpublish) { + $defpublish = datetime_convert('UTC', date_default_timezone_get(), $defpublish, 'Y-m-d H:i'); + } - $cipher = get_pconfig($x['profile_uid'], 'system', 'default_cipher'); - if(! $cipher) - $cipher = 'AES-128-CCM'; + $cipher = get_pconfig($x['profile_uid'], 'system', 'default_cipher'); + if (! $cipher) { + $cipher = 'AES-128-CCM'; + } - if(array_key_exists('catsenabled',$x)) - $catsenabled = $x['catsenabled']; - else - $catsenabled = ((Apps::system_app_installed($x['profile_uid'], 'Categories') && (! $webpage)) ? 'categories' : ''); + if (array_key_exists('catsenabled', $x)) { + $catsenabled = $x['catsenabled']; + } else { + $catsenabled = ((Apps::system_app_installed($x['profile_uid'], 'Categories') && (! $webpage)) ? 'categories' : ''); + } - // we only need the comment_perms for the editor, but this logic is complicated enough (from Settings/Channel) - // that we will just duplicate most of that code block + // we only need the comment_perms for the editor, but this logic is complicated enough (from Settings/Channel) + // that we will just duplicate most of that code block - $global_perms = Permissions::Perms(); + $global_perms = Permissions::Perms(); - $permiss = []; - - $perm_opts = [ - [ t('Restricted - from connections only'), PERMS_SPECIFIC ], - [ t('Semi-public - from anybody that can be identified'), PERMS_AUTHED ], - [ t('Public - from anybody on the internet'), PERMS_PUBLIC ] - ]; - - $limits = PermissionLimits::Get(local_channel()); - $anon_comments = get_config('system','anonymous_comments'); - - foreach($global_perms as $k => $perm) { - $options = []; - $can_be_public = ((strstr($k,'view') || ($k === 'post_comments' && $anon_comments)) ? true : false); - foreach($perm_opts as $opt) { - if($opt[1] == PERMS_PUBLIC && (! $can_be_public)) - continue; - $options[$opt[1]] = $opt[0]; - } - if($k === 'post_comments') { - $comment_perms = [ $k, t('Accept delivery of comments and likes on this post from'), $limits[$k],'',$options ]; - } - else { - $permiss[] = array($k,$perm,$limits[$k],'',$options); - } - } + $permiss = []; - $defcommpolicy = $limits['post_comments']; - - // avoid illegal offset errors - if(! array_key_exists('permissions',$x)) - $x['permissions'] = [ 'allow_cid' => '', 'allow_gid' => '', 'deny_cid' => '', 'deny_gid' => '' ]; + $perm_opts = [ + [ t('Restricted - from connections only'), PERMS_SPECIFIC ], + [ t('Semi-public - from anybody that can be identified'), PERMS_AUTHED ], + [ t('Public - from anybody on the internet'), PERMS_PUBLIC ] + ]; - $jotplugins = ''; - call_hooks('jot_tool', $jotplugins); + $limits = PermissionLimits::Get(local_channel()); + $anon_comments = get_config('system', 'anonymous_comments'); - $jotcoll = jot_collections($c,((array_key_exists('collections',$x)) ? $x['collections'] : [])); - if(! $jotcoll) { - $jotcoll = EMPTY_STR; - } + foreach ($global_perms as $k => $perm) { + $options = []; + $can_be_public = ((strstr($k, 'view') || ($k === 'post_comments' && $anon_comments)) ? true : false); + foreach ($perm_opts as $opt) { + if ($opt[1] == PERMS_PUBLIC && (! $can_be_public)) { + continue; + } + $options[$opt[1]] = $opt[0]; + } + if ($k === 'post_comments') { + $comment_perms = [ $k, t('Accept delivery of comments and likes on this post from'), $limits[$k],'',$options ]; + } else { + $permiss[] = array($k,$perm,$limits[$k],'',$options); + } + } - $jotnets = EMPTY_STR; - if(x($x,'jotnets')) { - call_hooks('jot_networks', $jotnets); - } + $defcommpolicy = $limits['post_comments']; - $permanent_draft = ((intval($x['profile_uid']) && intval($x['profile_uid']) === local_channel() && Apps::system_app_installed($x['profile_uid'],'Drafts')) ? ('Save draft') : EMPTY_STR); + // avoid illegal offset errors + if (! array_key_exists('permissions', $x)) { + $x['permissions'] = [ 'allow_cid' => '', 'allow_gid' => '', 'deny_cid' => '', 'deny_gid' => '' ]; + } - $sharebutton = (x($x,'button') ? $x['button'] : t('Share')); - $placeholdtext = (x($x,'content_label') ? $x['content_label'] : $sharebutton); + $jotplugins = ''; + call_hooks('jot_tool', $jotplugins); - $o .= replace_macros($tpl, array( - '$return_path' => ((x($x, 'return_path')) ? $x['return_path'] : App::$query_string), - '$action' => z_root() . '/item', - '$share' => $sharebutton, - '$placeholdtext' => $placeholdtext, - '$webpage' => $webpage, - '$placeholdpagetitle' => ((x($x,'ptlabel')) ? $x['ptlabel'] : t('Page link name')), - '$pagetitle' => (x($x,'pagetitle') ? $x['pagetitle'] : ''), - '$id_select' => $id_select, - '$id_seltext' => t('Post as'), - '$writefiles' => $writefiles, - '$text_style' => t('Text styles'), - '$bold' => t('Bold'), - '$italic' => t('Italic'), - '$underline' => t('Underline'), - '$quote' => t('Quote'), - '$code' => t('Code'), - '$attach' => t('Attach/Upload file'), - '$weblink' => $weblink, - '$linkurl' => t('Please enter a link location (URL)'), - '$weblink_style' => [ t('Insert link only'), t('Embed content if possible') ], - '$embedPhotos' => $embedPhotos, - '$embedPhotosModalTitle' => t('Embed an image from your albums'), - '$embedPhotosModalCancel' => t('Cancel'), - '$embedPhotosModalOK' => t('OK'), - '$setloc' => $setloc, - '$poll' => t('Toggle poll'), - '$poll_option_label' => t('Option'), - '$poll_add_option_label' => t('Add option'), - '$poll_expire_unit_label' => [t('Minutes'), t('Hours'), t('Days')], - '$multiple_answers' => ['poll_multiple_answers', t("Allow multiple answers"), '', '', [t('No'), t('Yes')]], - '$feature_voting' => $feature_voting, - '$consensus' => ((array_key_exists('item',$x)) ? $x['item']['item_consensus'] : 0), - '$nocommenttitle' => t('Disable comments'), - '$nocommenttitlesub' => t('Toggle comments'), - '$comments_allowed' => [ 'comments_allowed', t('Allow comments on this post'), ((array_key_exists('item',$x)) ? 1 - $x['item']['item_nocomment'] : 1), '', [ t('No'), t('Yes')]], - - '$commentstate' => ((array_key_exists('item',$x)) ? 1 - $x['item']['item_nocomment'] : 1), - '$feature_comment_control' => $feature_comment_control, - '$commctrl' => t('Comment Control'), - '$comments_closed' => ((isset($x['item']) && isset($x['item']['comments_closed']) && $x['item']['comments_closed']) ? $x['item']['comments_closed'] : ''), - '$commclosedate' => t('Optional: disable comments after (date)'), - '$comment_perms' => $comment_perms, - '$defcommpolicy' => $defcommpolicy, - '$defcommuntil' => $defcommuntil, - '$clearloc' => $clearloc, - '$title' => ((x($x, 'title')) ? htmlspecialchars($x['title'], ENT_COMPAT,'UTF-8') : ''), - '$placeholdertitle' => ((x($x, 'placeholdertitle')) ? $x['placeholdertitle'] : t('Title (optional)')), - '$catsenabled' => $catsenabled, - '$category' => ((x($x, 'category')) ? $x['category'] : ''), - '$placeholdercategory' => t('Categories (optional, comma-separated list)'), - '$permset' => t('Permission settings'), - '$ptyp' => ((x($x, 'ptyp')) ? $x['ptyp'] : ''), - '$content' => ((x($x,'body')) ? htmlspecialchars($x['body'], ENT_COMPAT,'UTF-8') : ''), - '$attachment' => ((x($x, 'attachment')) ? $x['attachment'] : ''), - '$post_id' => ((x($x, 'post_id')) ? $x['post_id'] : ''), - '$defloc' => $x['default_location'], - '$visitor' => $x['visitor'], - '$lockstate' => $x['lockstate'], - '$acl' => $x['acl'], - '$allow_cid' => acl2json($x['permissions']['allow_cid']), - '$allow_gid' => acl2json($x['permissions']['allow_gid']), - '$deny_cid' => acl2json($x['permissions']['deny_cid']), - '$deny_gid' => acl2json($x['permissions']['deny_gid']), - '$mimeselect' => $mimeselect, - '$layoutselect' => $layoutselect, - '$showacl' => ((array_key_exists('showacl', $x)) ? $x['showacl'] : true), - '$bang' => $x['bang'], - '$profile_uid' => $x['profile_uid'], - '$preview' => $preview, - '$source' => ((x($x, 'source')) ? $x['source'] : ''), - '$jotplugins' => $jotplugins, - '$jotcoll' => $jotcoll, - '$jotnets' => $jotnets, - '$jotnets_label' => t('Other networks and post services'), - '$jotcoll_label' => t('Collections'), - '$defexpire' => $defexpire, - '$feature_expire' => $feature_expire, - '$expires' => t('Set expiration date'), - '$save' => $permanent_draft, - '$is_draft' => ((array_key_exists('is_draft',$x) && intval($x['is_draft'])) ? true : false), - '$defpublish' => $defpublish, - '$feature_future' => $feature_future, - '$future_txt' => t('Set publish date'), - '$feature_markup' => $feature_markup, - '$feature_encrypt' => ((Apps::system_app_installed($x['profile_uid'],'Secrets')) ? true : false), - '$encrypt' => t('Encrypt text'), - '$cipher' => $cipher, - '$expiryModalOK' => t('OK'), - '$expiryModalCANCEL' => t('Cancel'), - '$commModalOK' => t('OK'), - '$commModalCANCEL' => t('Cancel'), - '$linkModalOK' => t('OK'), - '$linkModalCANCEL' => t('Cancel'), - '$close' => t('Close'), - '$expanded' => ((x($x, 'expanded')) ? $x['expanded'] : false), - '$bbcode' => ((x($x, 'bbcode')) ? $x['bbcode'] : false), - '$parent' => ((array_key_exists('parent',$x) && $x['parent']) ? $x['parent'] : 0), - '$summaryenabled' => $summaryenabled, - '$summary' => ((x($x, 'summary')) ? htmlspecialchars($x['summary'], ENT_COMPAT,'UTF-8') : ''), - '$placeholdsummary' => t('Summary'), - '$discombed' => t('Load remote media players'), - '$discombed2' => t('This may subject viewers of this post to behaviour tracking'), - '$embedchecked' => ((get_pconfig($x['profile_uid'],'system','linkinfo_embed',true)) ? ' checked ' : ''), - '$disczot' => t('Find shareable objects (Zot)'), - '$reset' => $reset - )); + $jotcoll = jot_collections($c, ((array_key_exists('collections', $x)) ? $x['collections'] : [])); + if (! $jotcoll) { + $jotcoll = EMPTY_STR; + } - if ($popup === true) { - $o = ''; - } + $jotnets = EMPTY_STR; + if (x($x, 'jotnets')) { + call_hooks('jot_networks', $jotnets); + } - return $o; + $permanent_draft = ((intval($x['profile_uid']) && intval($x['profile_uid']) === local_channel() && Apps::system_app_installed($x['profile_uid'], 'Drafts')) ? ('Save draft') : EMPTY_STR); + + $sharebutton = (x($x, 'button') ? $x['button'] : t('Share')); + $placeholdtext = (x($x, 'content_label') ? $x['content_label'] : $sharebutton); + + $o .= replace_macros($tpl, array( + '$return_path' => ((x($x, 'return_path')) ? $x['return_path'] : App::$query_string), + '$action' => z_root() . '/item', + '$share' => $sharebutton, + '$placeholdtext' => $placeholdtext, + '$webpage' => $webpage, + '$placeholdpagetitle' => ((x($x, 'ptlabel')) ? $x['ptlabel'] : t('Page link name')), + '$pagetitle' => (x($x, 'pagetitle') ? $x['pagetitle'] : ''), + '$id_select' => $id_select, + '$id_seltext' => t('Post as'), + '$writefiles' => $writefiles, + '$text_style' => t('Text styles'), + '$bold' => t('Bold'), + '$italic' => t('Italic'), + '$underline' => t('Underline'), + '$quote' => t('Quote'), + '$code' => t('Code'), + '$attach' => t('Attach/Upload file'), + '$weblink' => $weblink, + '$linkurl' => t('Please enter a link location (URL)'), + '$weblink_style' => [ t('Insert link only'), t('Embed content if possible') ], + '$embedPhotos' => $embedPhotos, + '$embedPhotosModalTitle' => t('Embed an image from your albums'), + '$embedPhotosModalCancel' => t('Cancel'), + '$embedPhotosModalOK' => t('OK'), + '$setloc' => $setloc, + '$poll' => t('Toggle poll'), + '$poll_option_label' => t('Option'), + '$poll_add_option_label' => t('Add option'), + '$poll_expire_unit_label' => [t('Minutes'), t('Hours'), t('Days')], + '$multiple_answers' => ['poll_multiple_answers', t("Allow multiple answers"), '', '', [t('No'), t('Yes')]], + '$feature_voting' => $feature_voting, + '$consensus' => ((array_key_exists('item', $x)) ? $x['item']['item_consensus'] : 0), + '$nocommenttitle' => t('Disable comments'), + '$nocommenttitlesub' => t('Toggle comments'), + '$comments_allowed' => [ 'comments_allowed', t('Allow comments on this post'), ((array_key_exists('item', $x)) ? 1 - $x['item']['item_nocomment'] : 1), '', [ t('No'), t('Yes')]], + + '$commentstate' => ((array_key_exists('item', $x)) ? 1 - $x['item']['item_nocomment'] : 1), + '$feature_comment_control' => $feature_comment_control, + '$commctrl' => t('Comment Control'), + '$comments_closed' => ((isset($x['item']) && isset($x['item']['comments_closed']) && $x['item']['comments_closed']) ? $x['item']['comments_closed'] : ''), + '$commclosedate' => t('Optional: disable comments after (date)'), + '$comment_perms' => $comment_perms, + '$defcommpolicy' => $defcommpolicy, + '$defcommuntil' => $defcommuntil, + '$clearloc' => $clearloc, + '$title' => ((x($x, 'title')) ? htmlspecialchars($x['title'], ENT_COMPAT, 'UTF-8') : ''), + '$placeholdertitle' => ((x($x, 'placeholdertitle')) ? $x['placeholdertitle'] : t('Title (optional)')), + '$catsenabled' => $catsenabled, + '$category' => ((x($x, 'category')) ? $x['category'] : ''), + '$placeholdercategory' => t('Categories (optional, comma-separated list)'), + '$permset' => t('Permission settings'), + '$ptyp' => ((x($x, 'ptyp')) ? $x['ptyp'] : ''), + '$content' => ((x($x, 'body')) ? htmlspecialchars($x['body'], ENT_COMPAT, 'UTF-8') : ''), + '$attachment' => ((x($x, 'attachment')) ? $x['attachment'] : ''), + '$post_id' => ((x($x, 'post_id')) ? $x['post_id'] : ''), + '$defloc' => $x['default_location'], + '$visitor' => $x['visitor'], + '$lockstate' => $x['lockstate'], + '$acl' => $x['acl'], + '$allow_cid' => acl2json($x['permissions']['allow_cid']), + '$allow_gid' => acl2json($x['permissions']['allow_gid']), + '$deny_cid' => acl2json($x['permissions']['deny_cid']), + '$deny_gid' => acl2json($x['permissions']['deny_gid']), + '$mimeselect' => $mimeselect, + '$layoutselect' => $layoutselect, + '$showacl' => ((array_key_exists('showacl', $x)) ? $x['showacl'] : true), + '$bang' => $x['bang'], + '$profile_uid' => $x['profile_uid'], + '$preview' => $preview, + '$source' => ((x($x, 'source')) ? $x['source'] : ''), + '$jotplugins' => $jotplugins, + '$jotcoll' => $jotcoll, + '$jotnets' => $jotnets, + '$jotnets_label' => t('Other networks and post services'), + '$jotcoll_label' => t('Collections'), + '$defexpire' => $defexpire, + '$feature_expire' => $feature_expire, + '$expires' => t('Set expiration date'), + '$save' => $permanent_draft, + '$is_draft' => ((array_key_exists('is_draft', $x) && intval($x['is_draft'])) ? true : false), + '$defpublish' => $defpublish, + '$feature_future' => $feature_future, + '$future_txt' => t('Set publish date'), + '$feature_markup' => $feature_markup, + '$feature_encrypt' => ((Apps::system_app_installed($x['profile_uid'], 'Secrets')) ? true : false), + '$encrypt' => t('Encrypt text'), + '$cipher' => $cipher, + '$expiryModalOK' => t('OK'), + '$expiryModalCANCEL' => t('Cancel'), + '$commModalOK' => t('OK'), + '$commModalCANCEL' => t('Cancel'), + '$linkModalOK' => t('OK'), + '$linkModalCANCEL' => t('Cancel'), + '$close' => t('Close'), + '$expanded' => ((x($x, 'expanded')) ? $x['expanded'] : false), + '$bbcode' => ((x($x, 'bbcode')) ? $x['bbcode'] : false), + '$parent' => ((array_key_exists('parent', $x) && $x['parent']) ? $x['parent'] : 0), + '$summaryenabled' => $summaryenabled, + '$summary' => ((x($x, 'summary')) ? htmlspecialchars($x['summary'], ENT_COMPAT, 'UTF-8') : ''), + '$placeholdsummary' => t('Summary'), + '$discombed' => t('Load remote media players'), + '$discombed2' => t('This may subject viewers of this post to behaviour tracking'), + '$embedchecked' => ((get_pconfig($x['profile_uid'], 'system', 'linkinfo_embed', true)) ? ' checked ' : ''), + '$disczot' => t('Find shareable objects (Zot)'), + '$reset' => $reset + )); + + if ($popup === true) { + $o = ''; + } + + return $o; } -function jot_collections($channel,$collections) { +function jot_collections($channel, $collections) +{ - $output = EMPTY_STR; + $output = EMPTY_STR; - $r = q("select channel_address, channel_name from channel where channel_parent = '%s' and channel_removed = 0 order by channel_name asc", - dbesc($channel['channel_hash']) - ); - if(! $r) { - return $output; - } + $r = q( + "select channel_address, channel_name from channel where channel_parent = '%s' and channel_removed = 0 order by channel_name asc", + dbesc($channel['channel_hash']) + ); + if (! $r) { + return $output; + } - $size = ((count($r) < 4) ? count($r) : 4); + $size = ((count($r) < 4) ? count($r) : 4); - $output .= t('Post to Collections'); - $output .= ''; - - return $output; + $output .= t('Post to Collections'); + $output .= ''; + return $output; } -function get_item_children($arr, $parent) { +function get_item_children($arr, $parent) +{ - $children = []; - if (! $arr) { - return $children; - } + $children = []; + if (! $arr) { + return $children; + } - $thread_allow = get_config('system','thread_allow',true); - $thread_max = intval(get_config('system','thread_maxlevel',20)); - - foreach ($arr as $item) { - if (intval($item['id']) !== intval($item['parent'])) { - if ($thread_allow) { + $thread_allow = get_config('system', 'thread_allow', true); + $thread_max = intval(get_config('system', 'thread_maxlevel', 20)); - $thr_parent = $item['thr_parent']; + foreach ($arr as $item) { + if (intval($item['id']) !== intval($item['parent'])) { + if ($thread_allow) { + $thr_parent = $item['thr_parent']; - // Fallback to parent_mid if thr_parent is not set - if ($thr_parent === EMPTY_STR) { - $thr_parent = $item['parent_mid']; - } - - if ($thr_parent === $parent['mid']) { - $my_children = get_item_children($arr, $item); - if ($item['item_level'] > $thread_max) { - // Like and Dislike activities are allowed as children of the last supported level. - // After that they are ignored. - // Any other children deeper than $thread_max are flattened. - if(in_array($item['verb'], [ 'Like','Dislike' ])) { - if ($item['item_level'] > ($thread_max + 1)) { - continue; - } - } - $children = (($my_children) ? array_merge($children,$my_children) : $children); - } - else { - $item['children'] = $my_children; - } - $children[] = $item; - } - } - elseif (intval($item['parent']) === intval($parent['id'])) { - // threads are disabled. Anything that is in this conversation gets added to children. - $children[] = $item; - } - } - } - return $children; + // Fallback to parent_mid if thr_parent is not set + if ($thr_parent === EMPTY_STR) { + $thr_parent = $item['parent_mid']; + } + + if ($thr_parent === $parent['mid']) { + $my_children = get_item_children($arr, $item); + if ($item['item_level'] > $thread_max) { + // Like and Dislike activities are allowed as children of the last supported level. + // After that they are ignored. + // Any other children deeper than $thread_max are flattened. + if (in_array($item['verb'], [ 'Like','Dislike' ])) { + if ($item['item_level'] > ($thread_max + 1)) { + continue; + } + } + $children = (($my_children) ? array_merge($children, $my_children) : $children); + } else { + $item['children'] = $my_children; + } + $children[] = $item; + } + } elseif (intval($item['parent']) === intval($parent['id'])) { + // threads are disabled. Anything that is in this conversation gets added to children. + $children[] = $item; + } + } + } + return $children; } -function sort_item_children($items) { - $result = $items; - usort($result,'sort_thr_created_rev'); - foreach($result as $k => $i) { - if($result[$k]['children']) { - $result[$k]['children'] = sort_item_children($result[$k]['children']); - } - } - return $result; +function sort_item_children($items) +{ + $result = $items; + usort($result, 'sort_thr_created_rev'); + foreach ($result as $k => $i) { + if ($result[$k]['children']) { + $result[$k]['children'] = sort_item_children($result[$k]['children']); + } + } + return $result; } -function add_children_to_list($children, &$arr) { - foreach ($children as $y) { - $arr[] = $y; - if ($y['children']) { - add_children_to_list($y['children'], $arr); - } - } +function add_children_to_list($children, &$arr) +{ + foreach ($children as $y) { + $arr[] = $y; + if ($y['children']) { + add_children_to_list($y['children'], $arr); + } + } } /* * separate the incoming array into conversations, with the original post at index 0, * and the comments following in reverse date order (newest first). Likes and other hidden activities go to the end. * This lets us choose the most recent comments in each conversation (regardless of thread depth) - * to open by default - while collapsing everything else. + * to open by default - while collapsing everything else. */ -function flatten_and_order($arr) { - $narr = []; - $ret = []; - - foreach ($arr as $a) { - $narr[$a['parent']][] = $a; - } - - foreach ($narr as $n) { - usort($n,'sort_flatten'); - for($x = 0; $x < count($n); $x ++) { - $n[$x]['comment_order'] = $x; - $ret[] = $n[$x]; - } - } +function flatten_and_order($arr) +{ + $narr = []; + $ret = []; - return $ret; + foreach ($arr as $a) { + $narr[$a['parent']][] = $a; + } + + foreach ($narr as $n) { + usort($n, 'sort_flatten'); + for ($x = 0; $x < count($n); $x++) { + $n[$x]['comment_order'] = $x; + $ret[] = $n[$x]; + } + } + + return $ret; } -function conv_sort($arr, $order) { +function conv_sort($arr, $order) +{ - $parents = []; - $ret = []; - - if (! (is_array($arr) && count($arr))) { - return $ret; - } + $parents = []; + $ret = []; - $narr = []; - - foreach ($arr as $item) { + if (! (is_array($arr) && count($arr))) { + return $ret; + } - // perform view filtering if viewer is logged in locally - // This allows blocking and message filters to work on public stream items - // or other channel streams on this site which are not owned by the viewer - - if (local_channel()) { - - if (LibBlock::fetch_by_entity(local_channel(),$item['author_xchan']) || LibBlock::fetch_by_entity(local_channel(),$item['owner_xchan'])) { - continue; - } + $narr = []; - $message_filter_abook = []; - if (App::$contacts && array_key_exists($item['author_xchan'], App::$contacts)) { - $message_filter_abook[] = App::$contacts[$item['author_xchan']]; - } - if (App::$contacts && array_key_exists($item['owner_xchan'], App::$contacts)) { - $message_filter_abook[] = App::$contacts[$item['owner_xchan']]; - } + foreach ($arr as $item) { + // perform view filtering if viewer is logged in locally + // This allows blocking and message filters to work on public stream items + // or other channel streams on this site which are not owned by the viewer - if (! post_is_importable(local_channel(), $item, $message_filter_abook ? $message_filter_abook : false)) { - continue; - } + if (local_channel()) { + if (LibBlock::fetch_by_entity(local_channel(), $item['author_xchan']) || LibBlock::fetch_by_entity(local_channel(), $item['owner_xchan'])) { + continue; + } + + $message_filter_abook = []; + if (App::$contacts && array_key_exists($item['author_xchan'], App::$contacts)) { + $message_filter_abook[] = App::$contacts[$item['author_xchan']]; + } + if (App::$contacts && array_key_exists($item['owner_xchan'], App::$contacts)) { + $message_filter_abook[] = App::$contacts[$item['owner_xchan']]; + } + + if (! post_is_importable(local_channel(), $item, $message_filter_abook ? $message_filter_abook : false)) { + continue; + } - $matches = null; - $found = false; - - $cnt = preg_match_all("/\[share(.*?)portable_id='(.*?)'(.*?)\]/ism", $item['body'], $matches, PREG_SET_ORDER); - if ($cnt) { - foreach ($matches as $match) { - if (LibBlock::fetch_by_entity(local_channel(),$match[2])) { - $found = true; - } - } - } + $matches = null; + $found = false; - if ($found) { - continue; - } + $cnt = preg_match_all("/\[share(.*?)portable_id='(.*?)'(.*?)\]/ism", $item['body'], $matches, PREG_SET_ORDER); + if ($cnt) { + foreach ($matches as $match) { + if (LibBlock::fetch_by_entity(local_channel(), $match[2])) { + $found = true; + } + } + } + + if ($found) { + continue; + } - $matches = null; - $found = false; - $cnt = preg_match_all("/\[share(.*?)profile='(.*?)'(.*?)\]/ism", $item['body'], $matches, PREG_SET_ORDER); - if ($cnt) { - foreach ($matches as $match) { - $r = q("select hubloc_hash from hubloc where hubloc_id_url = '%s'", - dbesc($match[2]) - ); - if ($r) { - if (LibBlock::fetch_by_entity(local_channel(),$r[0]['hubloc_hash'])) { - $found = true; - } - } - } - } + $matches = null; + $found = false; + $cnt = preg_match_all("/\[share(.*?)profile='(.*?)'(.*?)\]/ism", $item['body'], $matches, PREG_SET_ORDER); + if ($cnt) { + foreach ($matches as $match) { + $r = q( + "select hubloc_hash from hubloc where hubloc_id_url = '%s'", + dbesc($match[2]) + ); + if ($r) { + if (LibBlock::fetch_by_entity(local_channel(), $r[0]['hubloc_hash'])) { + $found = true; + } + } + } + } - if ($found) { - continue; - } + if ($found) { + continue; + } + } - } - - $narr[] = $item; - } + $narr[] = $item; + } - $data = [ 'items' => $narr, 'order' => $order ]; + $data = [ 'items' => $narr, 'order' => $order ]; - call_hooks('conv_sort', $data); + call_hooks('conv_sort', $data); - $arr = $data['items']; + $arr = $data['items']; - if (! (is_array($arr) && count($arr))) { - return $ret; - } + if (! (is_array($arr) && count($arr))) { + return $ret; + } - $arr = flatten_and_order($arr); + $arr = flatten_and_order($arr); - foreach ($arr as $x) { - if (intval($x['id']) === intval($x['parent'])) { - $parents[] = $x; - } - } + foreach ($arr as $x) { + if (intval($x['id']) === intval($x['parent'])) { + $parents[] = $x; + } + } - if (stristr($order,'created')) { - usort($parents,'sort_thr_created'); - } - elseif (stristr($order,'commented')) { - usort($parents,'sort_thr_commented'); - } - elseif (stristr($order,'updated')) { - usort($parents,'sort_thr_updated'); - } - elseif (stristr($order,'ascending')) { - usort($parents,'sort_thr_created_rev'); - } + if (stristr($order, 'created')) { + usort($parents, 'sort_thr_created'); + } elseif (stristr($order, 'commented')) { + usort($parents, 'sort_thr_commented'); + } elseif (stristr($order, 'updated')) { + usort($parents, 'sort_thr_updated'); + } elseif (stristr($order, 'ascending')) { + usort($parents, 'sort_thr_created_rev'); + } - if ($parents) { - foreach ($parents as $i => $_x) { - $parents[$i]['children'] = get_item_children($arr, $_x); - } - - foreach ($parents as $k => $v) { - if ($parents[$k]['children']) { - $parents[$k]['children'] = sort_item_children($parents[$k]['children']); - } - } + if ($parents) { + foreach ($parents as $i => $_x) { + $parents[$i]['children'] = get_item_children($arr, $_x); + } - } + foreach ($parents as $k => $v) { + if ($parents[$k]['children']) { + $parents[$k]['children'] = sort_item_children($parents[$k]['children']); + } + } + } - if ($parents) { - foreach ($parents as $x) { - $ret[] = $x; - if ($x['children']) { - add_children_to_list($x['children'], $ret); - } - } - } + if ($parents) { + foreach ($parents as $x) { + $ret[] = $x; + if ($x['children']) { + add_children_to_list($x['children'], $ret); + } + } + } - return $ret; + return $ret; } @@ -1758,122 +1776,134 @@ function conv_sort($arr, $order) { // We want the original post at index 0 and all the comments (regardless of thread depth) ordered newest to oldest. // likes and other invisible activities go to the end of the array beyond the oldest comment. -function sort_flatten($a,$b) { +function sort_flatten($a, $b) +{ - if ($a['parent'] === $a['id']) { - return -1; - } - if ($b['parent'] === $b['id']) { - return 1; - } + if ($a['parent'] === $a['id']) { + return -1; + } + if ($b['parent'] === $b['id']) { + return 1; + } - if (! visible_activity($a)) { - return 1; - } - if (! visible_activity($b)) { - return -1; - } + if (! visible_activity($a)) { + return 1; + } + if (! visible_activity($b)) { + return -1; + } - return strcmp($b['created'],$a['created']); + return strcmp($b['created'], $a['created']); } -function sort_thr_created($a,$b) { - return strcmp($b['created'],$a['created']); +function sort_thr_created($a, $b) +{ + return strcmp($b['created'], $a['created']); } -function sort_thr_created_rev($a,$b) { - return strcmp($a['created'],$b['created']); +function sort_thr_created_rev($a, $b) +{ + return strcmp($a['created'], $b['created']); } -function sort_thr_commented($a,$b) { - return strcmp($b['commented'],$a['commented']); +function sort_thr_commented($a, $b) +{ + return strcmp($b['commented'], $a['commented']); } -function sort_thr_updated($a,$b) { - $indexa = (($a['changed'] > $a['edited']) ? $a['changed'] : $a['edited']); - $indexb = (($b['changed'] > $b['edited']) ? $b['changed'] : $b['edited']); - return strcmp($indexb,$indexa); +function sort_thr_updated($a, $b) +{ + $indexa = (($a['changed'] > $a['edited']) ? $a['changed'] : $a['edited']); + $indexb = (($b['changed'] > $b['edited']) ? $b['changed'] : $b['edited']); + return strcmp($indexb, $indexa); } -function find_thread_parent_index($arr,$x) { - foreach($arr as $k => $v) - if($v['id'] == $x['parent']) - return $k; +function find_thread_parent_index($arr, $x) +{ + foreach ($arr as $k => $v) { + if ($v['id'] == $x['parent']) { + return $k; + } + } - return false; + return false; } -function format_location($item) { +function format_location($item) +{ - if(strpos($item['location'],'#') === 0) { - $location = substr($item['location'],1); - $location = ((strpos($location,'[') !== false) ? zidify_links(bbcode($location)) : $location); - } - else { - $locate = array('location' => $item['location'], 'coord' => $item['coord'], 'html' => ''); - call_hooks('render_location',$locate); - $location = ((strlen($locate['html'])) ? $locate['html'] : render_location_default($locate)); - } - return $location; + if (strpos($item['location'], '#') === 0) { + $location = substr($item['location'], 1); + $location = ((strpos($location, '[') !== false) ? zidify_links(bbcode($location)) : $location); + } else { + $locate = array('location' => $item['location'], 'coord' => $item['coord'], 'html' => ''); + call_hooks('render_location', $locate); + $location = ((strlen($locate['html'])) ? $locate['html'] : render_location_default($locate)); + } + return $location; } -function render_location_default($item) { +function render_location_default($item) +{ - $location = $item['location']; - $coord = $item['coord']; + $location = $item['location']; + $coord = $item['coord']; - if($coord) { - if($location) - $location .= ' (' . $coord . ')'; - else - $location = '' . $coord . ''; - } + if ($coord) { + if ($location) { + $location .= ' (' . $coord . ')'; + } else { + $location = '' . $coord . ''; + } + } - return $location; + return $location; } -function prepare_page($item) { +function prepare_page($item) +{ - $naked = 1; -// $naked = ((get_pconfig($item['uid'],'system','nakedpage')) ? 1 : 0); - $observer = App::get_observer(); - //240 chars is the longest we can have before we start hitting problems with suhosin sites - $preview = substr(urlencode($item['body']), 0, 240); - $link = z_root() . '/' . App::$cmd; - if(array_key_exists('webpage',App::$layout) && array_key_exists('authored',App::$layout['webpage'])) { - if(App::$layout['webpage']['authored'] === 'none') - $naked = 1; - // ... other possible options - } + $naked = 1; +// $naked = ((get_pconfig($item['uid'],'system','nakedpage')) ? 1 : 0); + $observer = App::get_observer(); + //240 chars is the longest we can have before we start hitting problems with suhosin sites + $preview = substr(urlencode($item['body']), 0, 240); + $link = z_root() . '/' . App::$cmd; + if (array_key_exists('webpage', App::$layout) && array_key_exists('authored', App::$layout['webpage'])) { + if (App::$layout['webpage']['authored'] === 'none') { + $naked = 1; + } + // ... other possible options + } - // prepare_body calls unobscure() as a side effect. Do it here so that - // the template will get passed an unobscured title. + // prepare_body calls unobscure() as a side effect. Do it here so that + // the template will get passed an unobscured title. - $body = prepare_body($item, true, [ 'newwin' => false ]); - if(App::$page['template'] == 'none') { - $tpl = 'page_display_empty.tpl'; + $body = prepare_body($item, true, [ 'newwin' => false ]); + if (App::$page['template'] == 'none') { + $tpl = 'page_display_empty.tpl'; - return replace_macros(get_markup_template($tpl), array( - '$body' => $body['html'] - )); - - } - - $tpl = get_pconfig($item['uid'], 'system', 'pagetemplate'); - if (! $tpl) - $tpl = 'page_display.tpl'; + return replace_macros(get_markup_template($tpl), array( + '$body' => $body['html'] + )); + } - return replace_macros(get_markup_template($tpl), array( - '$author' => (($naked) ? '' : $item['author']['xchan_name']), - '$auth_url' => (($naked) ? '' : zid($item['author']['xchan_url'])), - '$date' => (($naked) ? '' : datetime_convert('UTC', date_default_timezone_get(), $item['created'], 'Y-m-d H:i')), - '$title' => zidify_links(smilies(bbcode($item['title']))), - '$body' => $body['html'], - '$preview' => $preview, - '$link' => $link, - )); + $tpl = get_pconfig($item['uid'], 'system', 'pagetemplate'); + if (! $tpl) { + $tpl = 'page_display.tpl'; + } + + return replace_macros(get_markup_template($tpl), array( + '$author' => (($naked) ? '' : $item['author']['xchan_name']), + '$auth_url' => (($naked) ? '' : zid($item['author']['xchan_url'])), + '$date' => (($naked) ? '' : datetime_convert('UTC', date_default_timezone_get(), $item['created'], 'Y-m-d H:i')), + '$title' => zidify_links(smilies(bbcode($item['title']))), + '$body' => $body['html'], + '$preview' => $preview, + '$link' => $link, + )); } @@ -1881,267 +1911,274 @@ function prepare_page($item) { * @brief * * @param App $a - * @param boolean $is_owner default false + * @param bool $is_owner default false * @param string $nickname default null * @return void|string */ -function profile_tabs($a, $is_owner = false, $nickname = null){ +function profile_tabs($a, $is_owner = false, $nickname = null) +{ - // Don't provide any profile tabs if we're running as the sys channel + // Don't provide any profile tabs if we're running as the sys channel - if (App::$is_sys) - return; + if (App::$is_sys) { + return; + } - if (get_pconfig($uid, 'system', 'noprofiletabs')) - return; + if (get_pconfig($uid, 'system', 'noprofiletabs')) { + return; + } - $channel = App::get_channel(); + $channel = App::get_channel(); - if (is_null($nickname)) - $nickname = $channel['channel_address']; + if (is_null($nickname)) { + $nickname = $channel['channel_address']; + } - $uid = ((App::$profile['profile_uid']) ? App::$profile['profile_uid'] : local_channel()); - $account_id = ((App::$profile['profile_uid']) ? App::$profile['channel_account_id'] : App::$channel['channel_account_id']); + $uid = ((App::$profile['profile_uid']) ? App::$profile['profile_uid'] : local_channel()); + $account_id = ((App::$profile['profile_uid']) ? App::$profile['channel_account_id'] : App::$channel['channel_account_id']); - if ($uid == local_channel()) - return; + if ($uid == local_channel()) { + return; + } - if($uid == local_channel()) { - $cal_link = ''; - } - else { - $cal_link = '/cal/' . $nickname; - } + if ($uid == local_channel()) { + $cal_link = ''; + } else { + $cal_link = '/cal/' . $nickname; + } - require_once('include/security.php'); - $sql_options = item_permissions_sql($uid); + require_once('include/security.php'); + $sql_options = item_permissions_sql($uid); - $r = q("select item.* from item left join iconfig on item.id = iconfig.iid + $r = q( + "select item.* from item left join iconfig on item.id = iconfig.iid where item.uid = %d and iconfig.cat = 'system' and iconfig.v = '%s' and item.item_delayed = 0 and item.item_deleted = 0 and ( iconfig.k = 'WEBPAGE' and item_type = %d ) $sql_options limit 1", - intval($uid), - dbesc('home'), - intval(ITEM_TYPE_WEBPAGE) - ); + intval($uid), + dbesc('home'), + intval(ITEM_TYPE_WEBPAGE) + ); - $has_webpages = (($r) ? true : false); + $has_webpages = (($r) ? true : false); - if (x($_GET, 'tab')) - $tab = notags(trim($_GET['tab'])); + if (x($_GET, 'tab')) { + $tab = notags(trim($_GET['tab'])); + } - $url = z_root() . '/channel/' . $nickname; - $pr = z_root() . '/profile/' . $nickname; + $url = z_root() . '/channel/' . $nickname; + $pr = z_root() . '/profile/' . $nickname; - $tabs = array( - array( - 'label' => t('Channel'), - 'url' => $url, - 'sel' => ((argv(0) == 'channel') ? 'active' : ''), - 'title' => t('Status Messages and Posts'), - 'id' => 'status-tab', - 'icon' => 'home' - ), - ); + $tabs = array( + array( + 'label' => t('Channel'), + 'url' => $url, + 'sel' => ((argv(0) == 'channel') ? 'active' : ''), + 'title' => t('Status Messages and Posts'), + 'id' => 'status-tab', + 'icon' => 'home' + ), + ); - $p = get_all_perms($uid,get_observer_hash()); + $p = get_all_perms($uid, get_observer_hash()); - if ($p['view_profile']) { - $tabs[] = array( - 'label' => t('About'), - 'url' => $pr, - 'sel' => ((argv(0) == 'profile') ? 'active' : ''), - 'title' => t('Profile Details'), - 'id' => 'profile-tab', - 'icon' => 'user' - ); - } - if ($p['view_storage']) { - $tabs[] = array( - 'label' => t('Photos'), - 'url' => z_root() . '/photos/' . $nickname, - 'sel' => ((argv(0) == 'photos') ? 'active' : ''), - 'title' => t('Photo Albums'), - 'id' => 'photo-tab', - 'icon' => 'photo' - ); - $tabs[] = array( - 'label' => t('Files'), - 'url' => z_root() . '/cloud/' . $nickname, - 'sel' => ((argv(0) == 'cloud' || argv(0) == 'sharedwithme') ? 'active' : ''), - 'title' => t('Files and Storage'), - 'id' => 'files-tab', - 'icon' => 'folder-open' - ); - } + if ($p['view_profile']) { + $tabs[] = array( + 'label' => t('About'), + 'url' => $pr, + 'sel' => ((argv(0) == 'profile') ? 'active' : ''), + 'title' => t('Profile Details'), + 'id' => 'profile-tab', + 'icon' => 'user' + ); + } + if ($p['view_storage']) { + $tabs[] = array( + 'label' => t('Photos'), + 'url' => z_root() . '/photos/' . $nickname, + 'sel' => ((argv(0) == 'photos') ? 'active' : ''), + 'title' => t('Photo Albums'), + 'id' => 'photo-tab', + 'icon' => 'photo' + ); + $tabs[] = array( + 'label' => t('Files'), + 'url' => z_root() . '/cloud/' . $nickname, + 'sel' => ((argv(0) == 'cloud' || argv(0) == 'sharedwithme') ? 'active' : ''), + 'title' => t('Files and Storage'), + 'id' => 'files-tab', + 'icon' => 'folder-open' + ); + } - if($p['view_stream'] && $cal_link) { - $tabs[] = array( - 'label' => t('Events'), - 'url' => z_root() . $cal_link, - 'sel' => ((argv(0) == 'cal' || argv(0) == 'events') ? 'active' : ''), - 'title' => t('Events'), - 'id' => 'event-tab', - 'icon' => 'calendar' - ); - } + if ($p['view_stream'] && $cal_link) { + $tabs[] = array( + 'label' => t('Events'), + 'url' => z_root() . $cal_link, + 'sel' => ((argv(0) == 'cal' || argv(0) == 'events') ? 'active' : ''), + 'title' => t('Events'), + 'id' => 'event-tab', + 'icon' => 'calendar' + ); + } - if ($p['chat'] && feature_enabled($uid,'ajaxchat')) { - $has_chats = Chatroom::list_count($uid); - if ($has_chats) { - $tabs[] = array( - 'label' => t('Chatrooms'), - 'url' => z_root() . '/chat/' . $nickname, - 'sel' => ((argv(0) == 'chat') ? 'active' : '' ), - 'title' => t('Chatrooms'), - 'id' => 'chat-tab', - 'icon' => 'comments-o' - ); - } - } + if ($p['chat'] && feature_enabled($uid, 'ajaxchat')) { + $has_chats = Chatroom::list_count($uid); + if ($has_chats) { + $tabs[] = array( + 'label' => t('Chatrooms'), + 'url' => z_root() . '/chat/' . $nickname, + 'sel' => ((argv(0) == 'chat') ? 'active' : '' ), + 'title' => t('Chatrooms'), + 'id' => 'chat-tab', + 'icon' => 'comments-o' + ); + } + } - require_once('include/menu.php'); - $has_bookmarks = menu_list_count(local_channel(),'',MENU_BOOKMARK) + menu_list_count(local_channel(),'',MENU_SYSTEM|MENU_BOOKMARK); + require_once('include/menu.php'); + $has_bookmarks = menu_list_count(local_channel(), '', MENU_BOOKMARK) + menu_list_count(local_channel(), '', MENU_SYSTEM | MENU_BOOKMARK); - if($is_owner && $has_bookmarks) { - $tabs[] = array( - 'label' => t('Bookmarks'), - 'url' => z_root() . '/bookmarks', - 'sel' => ((argv(0) == 'bookmarks') ? 'active' : ''), - 'title' => t('Saved Bookmarks'), - 'id' => 'bookmarks-tab', - 'icon' => 'bookmark' - ); - } + if ($is_owner && $has_bookmarks) { + $tabs[] = array( + 'label' => t('Bookmarks'), + 'url' => z_root() . '/bookmarks', + 'sel' => ((argv(0) == 'bookmarks') ? 'active' : ''), + 'title' => t('Saved Bookmarks'), + 'id' => 'bookmarks-tab', + 'icon' => 'bookmark' + ); + } - if(feature_enabled($uid,'cards')) { - $tabs[] = array( - 'label' => t('Cards'), - 'url' => z_root() . '/cards/' . $nickname, - 'sel' => ((argv(0) == 'cards') ? 'active' : ''), - 'title' => t('View Cards'), - 'id' => 'cards-tab', - 'icon' => 'list' - ); - } + if (feature_enabled($uid, 'cards')) { + $tabs[] = array( + 'label' => t('Cards'), + 'url' => z_root() . '/cards/' . $nickname, + 'sel' => ((argv(0) == 'cards') ? 'active' : ''), + 'title' => t('View Cards'), + 'id' => 'cards-tab', + 'icon' => 'list' + ); + } - if(feature_enabled($uid,'articles')) { - $tabs[] = array( - 'label' => t('articles'), - 'url' => z_root() . '/articles/' . $nickname, - 'sel' => ((argv(0) == 'articles') ? 'active' : ''), - 'title' => t('View Articles'), - 'id' => 'articles-tab', - 'icon' => 'file-text-o' - ); - } - - if($has_webpages && feature_enabled($uid,'webpages')) { - $tabs[] = array( - 'label' => t('Webpages'), - 'url' => z_root() . '/page/' . $nickname . '/home', - 'sel' => ((argv(0) == 'webpages') ? 'active' : ''), - 'title' => t('View Webpages'), - 'id' => 'webpages-tab', - 'icon' => 'newspaper-o' - ); - } + if (feature_enabled($uid, 'articles')) { + $tabs[] = array( + 'label' => t('articles'), + 'url' => z_root() . '/articles/' . $nickname, + 'sel' => ((argv(0) == 'articles') ? 'active' : ''), + 'title' => t('View Articles'), + 'id' => 'articles-tab', + 'icon' => 'file-text-o' + ); + } + + if ($has_webpages && feature_enabled($uid, 'webpages')) { + $tabs[] = array( + 'label' => t('Webpages'), + 'url' => z_root() . '/page/' . $nickname . '/home', + 'sel' => ((argv(0) == 'webpages') ? 'active' : ''), + 'title' => t('View Webpages'), + 'id' => 'webpages-tab', + 'icon' => 'newspaper-o' + ); + } - if ($p['view_wiki']) { - if(feature_enabled($uid,'wiki')) { - $tabs[] = array( - 'label' => t('Wikis'), - 'url' => z_root() . '/wiki/' . $nickname, - 'sel' => ((argv(0) == 'wiki') ? 'active' : ''), - 'title' => t('Wiki'), - 'id' => 'wiki-tab', - 'icon' => 'pencil-square-o' - ); - } - } + if ($p['view_wiki']) { + if (feature_enabled($uid, 'wiki')) { + $tabs[] = array( + 'label' => t('Wikis'), + 'url' => z_root() . '/wiki/' . $nickname, + 'sel' => ((argv(0) == 'wiki') ? 'active' : ''), + 'title' => t('Wiki'), + 'id' => 'wiki-tab', + 'icon' => 'pencil-square-o' + ); + } + } - $arr = array('is_owner' => $is_owner, 'nickname' => $nickname, 'tab' => (($tab) ? $tab : false), 'tabs' => $tabs); - call_hooks('profile_tabs', $arr); - - $tpl = get_markup_template('profile_tabs.tpl'); + $arr = array('is_owner' => $is_owner, 'nickname' => $nickname, 'tab' => (($tab) ? $tab : false), 'tabs' => $tabs); + call_hooks('profile_tabs', $arr); - return replace_macros($tpl, array( - '$tabs' => $arr['tabs'], - '$name' => App::$profile['channel_name'], - '$thumb' => App::$profile['thumb'] - )); + $tpl = get_markup_template('profile_tabs.tpl'); + + return replace_macros($tpl, array( + '$tabs' => $arr['tabs'], + '$name' => App::$profile['channel_name'], + '$thumb' => App::$profile['thumb'] + )); } -function get_responses($conv_responses,$response_verbs,$ob,$item) { +function get_responses($conv_responses, $response_verbs, $ob, $item) +{ - $ret = []; - foreach($response_verbs as $v) { - $ret[$v] = []; - $ret[$v]['count'] = ((x($conv_responses[$v],$item['mid'])) ? $conv_responses[$v][$item['mid']] : ''); - $ret[$v]['list'] = ((x($conv_responses[$v],$item['mid'])) ? $conv_responses[$v][$item['mid'] . '-l'] : ''); - $ret[$v]['button'] = get_response_button_text($v,$ret[$v]['count']); - $ret[$v]['title'] = $conv_responses[$v]['title']; - if($ret[$v]['count'] > MAX_LIKERS) { - $ret[$v]['modal'] = true; - } - } + $ret = []; + foreach ($response_verbs as $v) { + $ret[$v] = []; + $ret[$v]['count'] = ((x($conv_responses[$v], $item['mid'])) ? $conv_responses[$v][$item['mid']] : ''); + $ret[$v]['list'] = ((x($conv_responses[$v], $item['mid'])) ? $conv_responses[$v][$item['mid'] . '-l'] : ''); + $ret[$v]['button'] = get_response_button_text($v, $ret[$v]['count']); + $ret[$v]['title'] = $conv_responses[$v]['title']; + if ($ret[$v]['count'] > MAX_LIKERS) { + $ret[$v]['modal'] = true; + } + } - $count = 0; - foreach ($ret as $key) { - if ($key['count'] == true) - $count++; - } + $count = 0; + foreach ($ret as $key) { + if ($key['count'] == true) { + $count++; + } + } - $ret['count'] = $count; + $ret['count'] = $count; //logger('ret: ' . print_r($ret,true)); - return $ret; + return $ret; } -function get_response_button_text($v,$count) { - switch($v) { - case 'like': - if(get_config('system','show_like_counts',true)) { - return $count . ' ' . tt('Like','Likes',$count,'noun'); - } - else { - return t('Likes', 'noun'); - } - break; - case 'dislike': - if(get_config('system','show_like_counts',true)) { - return $count . ' ' . tt('Dislike','Dislikes',$count,'noun'); - } - else { - return t('Dislikes', 'noun'); - } - break; - case 'attendyes': - return $count . ' ' . tt('Attending','Attending',$count,'noun'); - break; - case 'attendno': - return $count . ' ' . tt('Not Attending','Not Attending',$count,'noun'); - break; - case 'attendmaybe': - return $count . ' ' . tt('Undecided','Undecided',$count,'noun'); - break; - case 'agree': - return $count . ' ' . tt('Agree','Agrees',$count,'noun'); - break; - case 'disagree': - return $count . ' ' . tt('Disagree','Disagrees',$count,'noun'); - break; - case 'abstain': - return $count . ' ' . tt('Abstain','Abstains',$count,'noun'); - break; - default: - return ''; - break; - } +function get_response_button_text($v, $count) +{ + switch ($v) { + case 'like': + if (get_config('system', 'show_like_counts', true)) { + return $count . ' ' . tt('Like', 'Likes', $count, 'noun'); + } else { + return t('Likes', 'noun'); + } + break; + case 'dislike': + if (get_config('system', 'show_like_counts', true)) { + return $count . ' ' . tt('Dislike', 'Dislikes', $count, 'noun'); + } else { + return t('Dislikes', 'noun'); + } + break; + case 'attendyes': + return $count . ' ' . tt('Attending', 'Attending', $count, 'noun'); + break; + case 'attendno': + return $count . ' ' . tt('Not Attending', 'Not Attending', $count, 'noun'); + break; + case 'attendmaybe': + return $count . ' ' . tt('Undecided', 'Undecided', $count, 'noun'); + break; + case 'agree': + return $count . ' ' . tt('Agree', 'Agrees', $count, 'noun'); + break; + case 'disagree': + return $count . ' ' . tt('Disagree', 'Disagrees', $count, 'noun'); + break; + case 'abstain': + return $count . ' ' . tt('Abstain', 'Abstains', $count, 'noun'); + break; + default: + return ''; + break; + } } diff --git a/include/datetime.php b/include/datetime.php index a993d6bc1..91c9703dd 100644 --- a/include/datetime.php +++ b/include/datetime.php @@ -1,4 +1,5 @@ 1) { - $continent = t($ex[0]); - if (count($ex) > 2) - $city = substr($value, strpos($value, '/')+1); - else - $city = $ex[1]; - } else { - $city = $ex[0]; - $continent = t('Miscellaneous'); - } - $city = str_replace('_', ' ', t($city)); + usort($timezone_identifiers, 'timezone_cmp'); + $continent = ''; + $continents = []; + foreach ($timezone_identifiers as $value) { + $ex = explode("/", $value); + if (count($ex) > 1) { + $continent = t($ex[0]); + if (count($ex) > 2) { + $city = substr($value, strpos($value, '/') + 1); + } else { + $city = $ex[1]; + } + } else { + $city = $ex[0]; + $continent = t('Miscellaneous'); + } + $city = str_replace('_', ' ', t($city)); - if (!x($continents, $ex[0])) $continents[$ex[0]] = []; - $continents[$continent][$value] = $city; - } + if (!x($continents, $ex[0])) { + $continents[$ex[0]] = []; + } + $continents[$continent][$value] = $city; + } - return $continents; + return $continents; } /** @@ -76,45 +92,49 @@ function get_timezones( ){ * http://www.php.net/manual/en/datetime.format.php * @return string */ -function datetime_convert($from = 'UTC', $to = 'UTC', $s = 'now', $fmt = "Y-m-d H:i:s") { +function datetime_convert($from = 'UTC', $to = 'UTC', $s = 'now', $fmt = "Y-m-d H:i:s") +{ - // Defaults to UTC if nothing is set, but throws an exception if set to empty string. - // Provide some sane defaults regardless. + // Defaults to UTC if nothing is set, but throws an exception if set to empty string. + // Provide some sane defaults regardless. - if($from === '') - $from = 'UTC'; - if($to === '') - $to = 'UTC'; - if( ($s === '') || (! is_string($s)) ) - $s = 'now'; + if ($from === '') { + $from = 'UTC'; + } + if ($to === '') { + $to = 'UTC'; + } + if (($s === '') || (! is_string($s))) { + $s = 'now'; + } - if(is_null_date($s)) { - $d = new DateTime('0001-01-01 00:00:00', new DateTimeZone('UTC')); - return $d->format($fmt); - } + if (is_null_date($s)) { + $d = new DateTime('0001-01-01 00:00:00', new DateTimeZone('UTC')); + return $d->format($fmt); + } - try { - $from_obj = new DateTimeZone($from); - } catch(Exception $e) { - $from_obj = new DateTimeZone('UTC'); - } + try { + $from_obj = new DateTimeZone($from); + } catch (Exception $e) { + $from_obj = new DateTimeZone('UTC'); + } - try { - $d = new DateTime($s, $from_obj); - } catch(Exception $e) { - logger('exception: ' . $e->getMessage()); - $d = new DateTime('now', $from_obj); - } + try { + $d = new DateTime($s, $from_obj); + } catch (Exception $e) { + logger('exception: ' . $e->getMessage()); + $d = new DateTime('now', $from_obj); + } - try { - $to_obj = new DateTimeZone($to); - } catch(Exception $e) { - $to_obj = new DateTimeZone('UTC'); - } + try { + $to_obj = new DateTimeZone($to); + } catch (Exception $e) { + $to_obj = new DateTimeZone('UTC'); + } - $d->setTimeZone($to_obj); + $d->setTimeZone($to_obj); - return($d->format($fmt)); + return($d->format($fmt)); } /** @@ -123,27 +143,30 @@ function datetime_convert($from = 'UTC', $to = 'UTC', $s = 'now', $fmt = "Y-m-d * @param string $dob Date of Birth * @return string Parsed HTML with selector */ -function dob($dob) { +function dob($dob) +{ - $y = substr($dob,0,4); - if((! ctype_digit($y)) || ($y < 1900)) - $ignore_year = true; - else - $ignore_year = false; + $y = substr($dob, 0, 4); + if ((! ctype_digit($y)) || ($y < 1900)) { + $ignore_year = true; + } else { + $ignore_year = false; + } - if ($dob === '0000-00-00' || $dob === '') - $value = ''; - else - $value = (($ignore_year) ? datetime_convert('UTC','UTC',$dob,'m-d') : datetime_convert('UTC','UTC',$dob,'Y-m-d')); + if ($dob === '0000-00-00' || $dob === '') { + $value = ''; + } else { + $value = (($ignore_year) ? datetime_convert('UTC', 'UTC', $dob, 'm-d') : datetime_convert('UTC', 'UTC', $dob, 'Y-m-d')); + } - $age = age($value,App::$user['timezone'],App::$user['timezone']); + $age = age($value, App::$user['timezone'], App::$user['timezone']); - $o = replace_macros(get_markup_template("field_input.tpl"), [ - '$field' => [ 'dob', t('Birthday'), $value, ((intval($age)) ? t('Age: ') . $age : ''), '', 'placeholder="' . t('YYYY-MM-DD or MM-DD') .'"' ] - ]); + $o = replace_macros(get_markup_template("field_input.tpl"), [ + '$field' => [ 'dob', t('Birthday'), $value, ((intval($age)) ? t('Age: ') . $age : ''), '', 'placeholder="' . t('YYYY-MM-DD or MM-DD') . '"' ] + ]); - return $o; + return $o; } /** @@ -160,61 +183,74 @@ function dob($dob) { * @param string $label * @param string $id * id and name of datetimepicker (defaults to "datetimepicker") - * @param boolean $pickdate + * @param bool $pickdate * true to show date picker (default) - * @param boolean $picktime + * @param bool $picktime * true to show time picker (default) * @param DateTime $minfrom * set minimum date from picker with id $minfrom (none by default) * @param DateTime $maxfrom * set maximum date from picker with id $maxfrom (none by default) - * @param boolean $required default false + * @param bool $required default false * @param int $first_day (optional) default 0 * @return string Parsed HTML output. * * @todo Once browser support is better this could probably be replaced with * native HTML5 date picker. */ -function datetimesel($format, $min, $max, $default, $label, $id = 'datetimepicker', $pickdate = true, $picktime = true, $minfrom = '', $maxfrom = '', $required = false, $first_day = 0) { +function datetimesel($format, $min, $max, $default, $label, $id = 'datetimepicker', $pickdate = true, $picktime = true, $minfrom = '', $maxfrom = '', $required = false, $first_day = 0) +{ - $o = ''; - $dateformat = ''; + $o = ''; + $dateformat = ''; - if($pickdate) $dateformat .= 'Y-m-d'; - if($pickdate && $picktime) $dateformat .= ' '; - if($picktime) $dateformat .= 'H:i'; + if ($pickdate) { + $dateformat .= 'Y-m-d'; + } + if ($pickdate && $picktime) { + $dateformat .= ' '; + } + if ($picktime) { + $dateformat .= 'H:i'; + } - $minjs = $min->getTimestamp() ? ",minDate: new Date({$min->getTimestamp()}*1000), yearStart: " . $min->format('Y') : ''; - $maxjs = $max->getTimestamp() ? ",maxDate: new Date({$max->getTimestamp()}*1000), yearEnd: " . $max->format('Y') : ''; + $minjs = $min->getTimestamp() ? ",minDate: new Date({$min->getTimestamp()}*1000), yearStart: " . $min->format('Y') : ''; + $maxjs = $max->getTimestamp() ? ",maxDate: new Date({$max->getTimestamp()}*1000), yearEnd: " . $max->format('Y') : ''; - $input_text = $default->getTimestamp() ? date($dateformat, $default->getTimestamp()) : ''; - $defaultdatejs = $default->getTimestamp() ? ",defaultDate: new Date({$default->getTimestamp()}*1000)" : ''; + $input_text = $default->getTimestamp() ? date($dateformat, $default->getTimestamp()) : ''; + $defaultdatejs = $default->getTimestamp() ? ",defaultDate: new Date({$default->getTimestamp()}*1000)" : ''; - $pickers = ''; - if(!$pickdate) $pickers .= ',datepicker: false'; - if(!$picktime) $pickers .= ',timepicker: false, closeOnDateSelect:true'; + $pickers = ''; + if (!$pickdate) { + $pickers .= ',datepicker: false'; + } + if (!$picktime) { + $pickers .= ',timepicker: false, closeOnDateSelect:true'; + } - $extra_js = ''; - if($minfrom != '') - $extra_js .= "\$('#id_$minfrom').data('xdsoft_datetimepicker').setOptions({onChangeDateTime: function (currentDateTime) { \$('#id_$id').data('xdsoft_datetimepicker').setOptions({minDate: currentDateTime})}})"; + $extra_js = ''; + if ($minfrom != '') { + $extra_js .= "\$('#id_$minfrom').data('xdsoft_datetimepicker').setOptions({onChangeDateTime: function (currentDateTime) { \$('#id_$id').data('xdsoft_datetimepicker').setOptions({minDate: currentDateTime})}})"; + } - if($maxfrom != '') - $extra_js .= "\$('#id_$maxfrom').data('xdsoft_datetimepicker').setOptions({onChangeDateTime: function (currentDateTime) { \$('#id_$id').data('xdsoft_datetimepicker').setOptions({maxDate: currentDateTime})}})"; + if ($maxfrom != '') { + $extra_js .= "\$('#id_$maxfrom').data('xdsoft_datetimepicker').setOptions({onChangeDateTime: function (currentDateTime) { \$('#id_$id').data('xdsoft_datetimepicker').setOptions({maxDate: currentDateTime})}})"; + } - $readable_format = $dateformat; - $readable_format = str_replace('Y','yyyy',$readable_format); - $readable_format = str_replace('m','mm',$readable_format); - $readable_format = str_replace('d','dd',$readable_format); - $readable_format = str_replace('H','HH',$readable_format); - $readable_format = str_replace('i','MM',$readable_format); + $readable_format = $dateformat; + $readable_format = str_replace('Y', 'yyyy', $readable_format); + $readable_format = str_replace('m', 'mm', $readable_format); + $readable_format = str_replace('d', 'dd', $readable_format); + $readable_format = str_replace('H', 'HH', $readable_format); + $readable_format = str_replace('i', 'MM', $readable_format); - $tpl = get_markup_template('field_input.tpl'); - $o .= replace_macros($tpl,array( - '$field' => array($id, $label, $input_text, (($required) ? t('Required') : ''), (($required) ? '*' : ''), 'placeholder="' . $readable_format . '"'), - )); - $o .= ""; + $tpl = get_markup_template('field_input.tpl'); + $o .= replace_macros($tpl, array( + '$field' => array($id, $label, $input_text, (($required) ? t('Required') : ''), (($required) ? '*' : ''), 'placeholder="' . $readable_format . '"'), + )); + $o .= ""; - return $o; + return $o; } /** @@ -230,78 +266,79 @@ function datetimesel($format, $min, $max, $default, $label, $id = 'datetimepicke * %1$d %2$s ago, e.g. 22 hours ago, 1 minute ago * @return string with relative date */ -function relative_date($posted_date, $format = null) { +function relative_date($posted_date, $format = null) +{ - $localtime = datetime_convert('UTC', date_default_timezone_get(), $posted_date); + $localtime = datetime_convert('UTC', date_default_timezone_get(), $posted_date); - $abs = strtotime($localtime); + $abs = strtotime($localtime); - if (is_null($posted_date) || is_null_date($posted_date) || $abs === false) { - return t('never'); - } + if (is_null($posted_date) || is_null_date($posted_date) || $abs === false) { + return t('never'); + } - if ($abs > time()) { - $direction = t('from now'); - $etime = $abs - time(); - } - else { - $direction = t('ago'); - $etime = time() - $abs; - } - - if ($etime < 1) { - return sprintf( t('less than a second %s'), $direction); - } + if ($abs > time()) { + $direction = t('from now'); + $etime = $abs - time(); + } else { + $direction = t('ago'); + $etime = time() - $abs; + } - $a = array( 12 * 30 * 24 * 60 * 60 => 'y', - 30 * 24 * 60 * 60 => 'm', - 7 * 24 * 60 * 60 => 'w', - 24 * 60 * 60 => 'd', - 60 * 60 => 'h', - 60 => 'i', - 1 => 's' - ); + if ($etime < 1) { + return sprintf(t('less than a second %s'), $direction); + } + + $a = array( 12 * 30 * 24 * 60 * 60 => 'y', + 30 * 24 * 60 * 60 => 'm', + 7 * 24 * 60 * 60 => 'w', + 24 * 60 * 60 => 'd', + 60 * 60 => 'h', + 60 => 'i', + 1 => 's' + ); - foreach ($a as $secs => $str) { - $d = $etime / $secs; - if ($d >= 1) { - $r = round($d); - if (! $format) { - $format = t('%1$d %2$s %3$s', 'e.g. 22 hours ago, 1 minute ago'); - } - return sprintf($format, $r, plural_dates($str,$r), $direction); - } - } + foreach ($a as $secs => $str) { + $d = $etime / $secs; + if ($d >= 1) { + $r = round($d); + if (! $format) { + $format = t('%1$d %2$s %3$s', 'e.g. 22 hours ago, 1 minute ago'); + } + return sprintf($format, $r, plural_dates($str, $r), $direction); + } + } } -function plural_dates($k,$n) { +function plural_dates($k, $n) +{ - switch($k) { - case 'y': - return tt('year','years',$n,'relative_date'); - break; - case 'm': - return tt('month','months',$n,'relative_date'); - break; - case 'w': - return tt('week','weeks',$n,'relative_date'); - break; - case 'd': - return tt('day','days',$n,'relative_date'); - break; - case 'h': - return tt('hour','hours',$n,'relative_date'); - break; - case 'i': - return tt('minute','minutes',$n,'relative_date'); - break; - case 's': - return tt('second','seconds',$n,'relative_date'); - break; - default: - return; - } + switch ($k) { + case 'y': + return tt('year', 'years', $n, 'relative_date'); + break; + case 'm': + return tt('month', 'months', $n, 'relative_date'); + break; + case 'w': + return tt('week', 'weeks', $n, 'relative_date'); + break; + case 'd': + return tt('day', 'days', $n, 'relative_date'); + break; + case 'h': + return tt('hour', 'hours', $n, 'relative_date'); + break; + case 'i': + return tt('minute', 'minutes', $n, 'relative_date'); + break; + case 's': + return tt('second', 'seconds', $n, 'relative_date'); + break; + default: + return; + } } /** @@ -324,24 +361,29 @@ function plural_dates($k,$n) { * @param string $viewer_tz (optional) timezone of the person viewing * @return number */ -function age($dob, $owner_tz = '', $viewer_tz = '') { - if (! intval($dob)) - return 0; - if (! $owner_tz) - $owner_tz = date_default_timezone_get(); - if (! $viewer_tz) - $viewer_tz = date_default_timezone_get(); +function age($dob, $owner_tz = '', $viewer_tz = '') +{ + if (! intval($dob)) { + return 0; + } + if (! $owner_tz) { + $owner_tz = date_default_timezone_get(); + } + if (! $viewer_tz) { + $viewer_tz = date_default_timezone_get(); + } - $birthdate = datetime_convert('UTC', $owner_tz, $dob . ' 00:00:00+00:00','Y-m-d'); - list($year,$month,$day) = explode("-", $birthdate); - $year_diff = datetime_convert('UTC', $viewer_tz, 'now', 'Y') - $year; - $curr_month = datetime_convert('UTC', $viewer_tz, 'now', 'm'); - $curr_day = datetime_convert('UTC', $viewer_tz, 'now', 'd'); + $birthdate = datetime_convert('UTC', $owner_tz, $dob . ' 00:00:00+00:00', 'Y-m-d'); + list($year,$month,$day) = explode("-", $birthdate); + $year_diff = datetime_convert('UTC', $viewer_tz, 'now', 'Y') - $year; + $curr_month = datetime_convert('UTC', $viewer_tz, 'now', 'm'); + $curr_day = datetime_convert('UTC', $viewer_tz, 'now', 'd'); - if (($curr_month < $month) || (($curr_month == $month) && ($curr_day < $day))) - $year_diff--; + if (($curr_month < $month) || (($curr_month == $month) && ($curr_day < $day))) { + $year_diff--; + } - return $year_diff; + return $year_diff; } /** @@ -354,19 +396,22 @@ function age($dob, $owner_tz = '', $viewer_tz = '') { * @param int $m month (1=January, 12=December) * @return int number of days in the given month */ -function get_dim($y, $m) { - $dim = array( 0, - 31, 28, 31, 30, 31, 30, - 31, 31, 30, 31, 30, 31 - ); +function get_dim($y, $m) +{ + $dim = array( 0, + 31, 28, 31, 30, 31, 30, + 31, 31, 30, 31, 30, 31 + ); - if ($m != 2) - return $dim[$m]; + if ($m != 2) { + return $dim[$m]; + } - if (((($y % 4) == 0) && (($y % 100) != 0)) || (($y % 400) == 0)) - return 29; + if (((($y % 4) == 0) && (($y % 100) != 0)) || (($y % 400) == 0)) { + return 29; + } - return $dim[2]; + return $dim[2]; } /** @@ -378,10 +423,11 @@ function get_dim($y, $m) { * @param int $m Month (1=January, 12=December) * @return string day 0 = Sunday through 6 = Saturday */ -function get_first_dim($y, $m) { - $d = sprintf('%04d-%02d-01 00:00', intval($y), intval($m)); +function get_first_dim($y, $m) +{ + $d = sprintf('%04d-%02d-01 00:00', intval($y), intval($m)); - return datetime_convert('UTC', 'UTC', $d, 'w'); + return datetime_convert('UTC', 'UTC', $d, 'w'); } /** @@ -400,69 +446,78 @@ function get_first_dim($y, $m) { * * @todo provide (prev,next) links, define class variations for different size calendars */ -function cal($y = 0, $m = 0, $links = false, $class='') { +function cal($y = 0, $m = 0, $links = false, $class = '') +{ - // month table - start at 1 to match human usage. + // month table - start at 1 to match human usage. - $mtab = [ ' ', 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December' ]; + $mtab = [ ' ', 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December' ]; - $thisyear = datetime_convert('UTC',date_default_timezone_get(),'now','Y'); - $thismonth = datetime_convert('UTC',date_default_timezone_get(),'now','m'); - if (! $y) - $y = $thisyear; - if (! $m) - $m = intval($thismonth); + $thisyear = datetime_convert('UTC', date_default_timezone_get(), 'now', 'Y'); + $thismonth = datetime_convert('UTC', date_default_timezone_get(), 'now', 'm'); + if (! $y) { + $y = $thisyear; + } + if (! $m) { + $m = intval($thismonth); + } - $dn = [ 'Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday' ]; - $f = get_first_dim($y, $m); - $l = get_dim($y, $m); - $d = 1; - $dow = 0; - $started = false; + $dn = [ 'Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday' ]; + $f = get_first_dim($y, $m); + $l = get_dim($y, $m); + $d = 1; + $dow = 0; + $started = false; - if (($y == $thisyear) && ($m == $thismonth)) - $tddate = intval(datetime_convert('UTC',date_default_timezone_get(),'now','j')); + if (($y == $thisyear) && ($m == $thismonth)) { + $tddate = intval(datetime_convert('UTC', date_default_timezone_get(), 'now', 'j')); + } - $str_month = day_translate($mtab[$m]); - $o = ''; - $o .= ""; - for ($a = 0; $a < 7; $a ++) - $o .= ''; + $str_month = day_translate($mtab[$m]); + $o = '
                    $str_month $y
                    ' . mb_substr(day_translate($dn[$a]),0,3,'UTF-8') . '
                    '; + $o .= ""; + for ($a = 0; $a < 7; $a++) { + $o .= ''; + } - $o .= ''; + $o .= ''; - while ($d <= $l) { - if (($dow == $f) && (! $started)) - $started = true; + while ($d <= $l) { + if (($dow == $f) && (! $started)) { + $started = true; + } - $today = (((isset($tddate)) && ($tddate == $d)) ? "class=\"today\" " : ''); - $o .= "'; - $dow ++; - if (($dow == 7) && ($d <= $l)) { - $dow = 0; - $o .= ''; - } - } - if ($dow) - for ($a = $dow; $a < 7; $a ++) - $o .= ''; + $o .= ''; + $dow++; + if (($dow == 7) && ($d <= $l)) { + $dow = 0; + $o .= ''; + } + } + if ($dow) { + for ($a = $dow; $a < 7; $a++) { + $o .= ''; + } + } - $o .= '
                    $str_month $y
                    ' . mb_substr(day_translate($dn[$a]), 0, 3, 'UTF-8') . '
                    "; - $day = str_replace(' ',' ',sprintf('%2.2d', $d)); - if ($started) { - if (is_array($links) && isset($links[$d])) - $o .= "$day"; - else - $o .= $day; + $today = (((isset($tddate)) && ($tddate == $d)) ? "class=\"today\" " : ''); + $o .= ""; + $day = str_replace(' ', ' ', sprintf('%2.2d', $d)); + if ($started) { + if (is_array($links) && isset($links[$d])) { + $o .= "$day"; + } else { + $o .= $day; + } - $d ++; - } else { - $o .= ' '; - } + $d++; + } else { + $o .= ' '; + } - $o .= '
                     
                     
                    '."\r\n"; + $o .= '' . "\r\n"; - return $o; + return $o; } /** @@ -477,26 +532,29 @@ function cal($y = 0, $m = 0, $links = false, $class='') { * @param string $format * @return string */ -function z_birthday($dob, $tz, $format="Y-m-d H:i:s") { +function z_birthday($dob, $tz, $format = "Y-m-d H:i:s") +{ - if (! strlen($tz)) - $tz = 'UTC'; + if (! strlen($tz)) { + $tz = 'UTC'; + } - $birthday = ''; - $tmp_dob = substr($dob,5); - $tmp_d = substr($dob,8); - if (intval($tmp_dob) && intval($tmp_d)) { - $y = datetime_convert($tz,$tz,'now','Y'); - $bd = $y . '-' . $tmp_dob . ' 00:00'; - $t_dob = strtotime($bd); - $now = strtotime(datetime_convert($tz,$tz,'now')); - if ($t_dob < $now) - $bd = $y + 1 . '-' . $tmp_dob . ' 00:00'; + $birthday = ''; + $tmp_dob = substr($dob, 5); + $tmp_d = substr($dob, 8); + if (intval($tmp_dob) && intval($tmp_d)) { + $y = datetime_convert($tz, $tz, 'now', 'Y'); + $bd = $y . '-' . $tmp_dob . ' 00:00'; + $t_dob = strtotime($bd); + $now = strtotime(datetime_convert($tz, $tz, 'now')); + if ($t_dob < $now) { + $bd = $y + 1 . '-' . $tmp_dob . ' 00:00'; + } - $birthday = datetime_convert($tz,'UTC',$bd,$format); - } + $birthday = datetime_convert($tz, 'UTC', $bd, $format); + } - return $birthday; + return $birthday; } /** @@ -504,41 +562,47 @@ function z_birthday($dob, $tz, $format="Y-m-d H:i:s") { * * Update the year so that we don't create another event until next year. */ -function update_birthdays() { +function update_birthdays() +{ - require_once('include/event.php'); - require_once('include/permissions.php'); + require_once('include/event.php'); + require_once('include/permissions.php'); - $r = q("SELECT * FROM abook left join xchan on abook_xchan = xchan_hash + $r = q( + "SELECT * FROM abook left join xchan on abook_xchan = xchan_hash WHERE abook_dob > %s + interval %s and abook_dob < %s + interval %s", - db_utcnow(), db_quoteinterval('7 day'), - db_utcnow(), db_quoteinterval('14 day') - ); - if ($r) { - foreach ($r as $rr) { - if (! perm_is_allowed($rr['abook_channel'], $rr['xchan_hash'], 'send_stream')) - continue; + db_utcnow(), + db_quoteinterval('7 day'), + db_utcnow(), + db_quoteinterval('14 day') + ); + if ($r) { + foreach ($r as $rr) { + if (! perm_is_allowed($rr['abook_channel'], $rr['xchan_hash'], 'send_stream')) { + continue; + } - $ev = [ - 'uid' => $rr['abook_channel'], - 'account' => $rr['abook_account'], - 'event_xchan' => $rr['xchan_hash'], - 'dtstart' => datetime_convert('UTC', 'UTC', $rr['abook_dob']), - 'dtend' => datetime_convert('UTC', 'UTC', $rr['abook_dob'] . ' + 1 day '), - 'adjust' => intval(feature_enabled($rr['abook_channel'],'smart_birthdays')), - 'summary' => sprintf( t('%1$s\'s birthday'), $rr['xchan_name']), - 'description' => sprintf( t('Happy Birthday %1$s'), '[zrl=' . $rr['xchan_url'] . ']' . $rr['xchan_name'] . '[/zrl]'), - 'etype' => 'birthday', - ]; + $ev = [ + 'uid' => $rr['abook_channel'], + 'account' => $rr['abook_account'], + 'event_xchan' => $rr['xchan_hash'], + 'dtstart' => datetime_convert('UTC', 'UTC', $rr['abook_dob']), + 'dtend' => datetime_convert('UTC', 'UTC', $rr['abook_dob'] . ' + 1 day '), + 'adjust' => intval(feature_enabled($rr['abook_channel'], 'smart_birthdays')), + 'summary' => sprintf(t('%1$s\'s birthday'), $rr['xchan_name']), + 'description' => sprintf(t('Happy Birthday %1$s'), '[zrl=' . $rr['xchan_url'] . ']' . $rr['xchan_name'] . '[/zrl]'), + 'etype' => 'birthday', + ]; - $z = event_store_event($ev); - if ($z) { - $item_id = event_store_item($ev, $z); - q("update abook set abook_dob = '%s' where abook_id = %d", - dbesc(intval($rr['abook_dob']) + 1 . substr($rr['abook_dob'], 4)), - intval($rr['abook_id']) - ); - } - } - } + $z = event_store_event($ev); + if ($z) { + $item_id = event_store_item($ev, $z); + q( + "update abook set abook_dob = '%s' where abook_id = %d", + dbesc(intval($rr['abook_dob']) + 1 . substr($rr['abook_dob'], 4)), + intval($rr['abook_id']) + ); + } + } + } } diff --git a/include/dba/dba_driver.php b/include/dba/dba_driver.php index fce48bce0..32506293d 100755 --- a/include/dba/dba_driver.php +++ b/include/dba/dba_driver.php @@ -7,6 +7,8 @@ * database class DBA and some functions for working with databases. */ +use Zotlabs\Lib\System; + /** * @brief Database classs with database factory method. * @@ -14,67 +16,68 @@ * abstract dba_driver class. */ -class DBA { +class DBA +{ - static public $dba = null; - static public $dbtype = null; - static public $scheme = 'mysql'; - static public $logging = false; + public static $dba = null; + public static $dbtype = null; + public static $scheme = 'mysql'; + public static $logging = false; - static public $install_script = 'schema_mysql.sql'; - static public $null_date = '0001-01-01 00:00:00'; - static public $utc_now = 'UTC_TIMESTAMP()'; - static public $tquot = "`"; + public static $install_script = 'schema_mysql.sql'; + public static $null_date = '0001-01-01 00:00:00'; + public static $utc_now = 'UTC_TIMESTAMP()'; + public static $tquot = "`"; - /** - * @brief Returns the database driver object. - * - * @param string $server DB server name (or PDO dsn - e.g. mysqli:foobar.com;) - * @param string $port DB port - * @param string $user DB username - * @param string $pass DB password - * @param string $db database name - * @param string $dbtype 0 for mysql, 1 for postgres - * @param bool $install Defaults to false - * @return null|dba_driver A database driver object (dba_pdo) or null if no driver found. - */ - static public function dba_factory($server,$port,$user,$pass,$db,$dbtype,$install = false) { + /** + * @brief Returns the database driver object. + * + * @param string $server DB server name (or PDO dsn - e.g. mysqli:foobar.com;) + * @param string $port DB port + * @param string $user DB username + * @param string $pass DB password + * @param string $db database name + * @param string $dbtype 0 for mysql, 1 for postgres + * @param bool $install Defaults to false + * @return null|dba_driver A database driver object (dba_pdo) or null if no driver found. + */ + public static function dba_factory($server, $port, $user, $pass, $db, $dbtype, $install = false) + { - self::$dba = null; - self::$dbtype = intval($dbtype); + self::$dba = null; + self::$dbtype = intval($dbtype); - if(self::$dbtype == DBTYPE_POSTGRES) { - if(!($port)) - $port = 5432; + if (self::$dbtype == DBTYPE_POSTGRES) { + if (!($port)) { + $port = 5432; + } - self::$install_script = 'schema_postgres.sql'; - self::$utc_now = "now() at time zone 'UTC'"; - self::$tquot = '"'; - self::$scheme = 'pgsql'; - } - else { + self::$install_script = 'schema_postgres.sql'; + self::$utc_now = "now() at time zone 'UTC'"; + self::$tquot = '"'; + self::$scheme = 'pgsql'; + } else { + // attempt to use the pdo driver compiled-in mysqli socket + // if using 'localhost' with no port configured. + // If this is wrong you'll need to set the socket path specifically + // using a server name of 'mysql:unix_socket=/socket/path', setting /socket/path + // as needed for your platform - // attempt to use the pdo driver compiled-in mysqli socket - // if using 'localhost' with no port configured. - // If this is wrong you'll need to set the socket path specifically - // using a server name of 'mysql:unix_socket=/socket/path', setting /socket/path - // as needed for your platform + if ((!($port)) && ($server !== 'localhost')) { + $port = 3306; + } + } - if((!($port)) && ($server !== 'localhost')) - $port = 3306; - } + require_once('include/dba/dba_pdo.php'); + self::$dba = new dba_pdo($server, self::$scheme, $port, $user, $pass, $db, $install); - require_once('include/dba/dba_pdo.php'); - self::$dba = new dba_pdo($server,self::$scheme,$port,$user,$pass,$db,$install); - - define('NULL_DATE', self::$null_date); - define('ACTIVE_DBTYPE', self::$dbtype); - define('TQUOT', self::$tquot); - - return self::$dba; - } + define('NULL_DATE', self::$null_date); + define('ACTIVE_DBTYPE', self::$dbtype); + define('TQUOT', self::$tquot); + return self::$dba; + } } /** @@ -84,143 +87,157 @@ class DBA { * dba_mysql, dba_mysqli or dba_postgres, but we moved to PDO and the only * implemented driver is dba_pdo. */ -abstract class dba_driver { - // legacy behavior +abstract class dba_driver +{ + // legacy behavior - public $db; + public $db; - public $debug = 0; - public $connected = false; - public $error = false; + public $debug = 0; + public $connected = false; + public $error = false; - /** - * @brief Connect to the database. - * - * This abstract function needs to be implemented in the real driver. - * - * @param string $server DB server name - * @param string $scheme DB scheme - * @param string $port DB port - * @param string $user DB username - * @param string $pass DB password - * @param string $db database name - * @return bool - */ - abstract function connect($server, $scheme, $port, $user, $pass, $db); + /** + * @brief Connect to the database. + * + * This abstract function needs to be implemented in the real driver. + * + * @param string $server DB server name + * @param string $scheme DB scheme + * @param string $port DB port + * @param string $user DB username + * @param string $pass DB password + * @param string $db database name + * @return bool + */ + abstract public function connect($server, $scheme, $port, $user, $pass, $db); - /** - * @brief Perform a DB query with the SQL statement $sql. - * - * This abstract function needs to be implemented in the real driver. - * - * @param string $sql The SQL query to execute - */ - abstract function q($sql); + /** + * @brief Perform a DB query with the SQL statement $sql. + * + * This abstract function needs to be implemented in the real driver. + * + * @param string $sql The SQL query to execute + */ + abstract public function q($sql); - /** - * @brief Escape a string before being passed to a DB query. - * - * This abstract function needs to be implemented in the real driver. - * - * @param string $str The string to escape. - */ - abstract function escape($str); + /** + * @brief Escape a string before being passed to a DB query. + * + * This abstract function needs to be implemented in the real driver. + * + * @param string $str The string to escape. + */ + abstract public function escape($str); - /** - * @brief Close the database connection. - * - * This abstract function needs to be implemented in the real driver. - */ - abstract function close(); + /** + * @brief Close the database connection. + * + * This abstract function needs to be implemented in the real driver. + */ + abstract public function close(); - /** - * @brief Return text name for db driver - * - * This abstract function needs to be implemented in the real driver. - */ - abstract function getdriver(); + /** + * @brief Return text name for db driver + * + * This abstract function needs to be implemented in the real driver. + */ + abstract public function getdriver(); - function __construct($server, $scheme, $port, $user,$pass,$db,$install = false) { - if(($install) && (! $this->install($server, $scheme, $port, $user, $pass, $db))) { - return; - } - $this->connect($server, $scheme, $port, $user, $pass, $db); - } + public function __construct($server, $scheme, $port, $user, $pass, $db, $install = false) + { + if (($install) && (!$this->install($server, $scheme, $port, $user, $pass, $db))) { + return; + } + $this->connect($server, $scheme, $port, $user, $pass, $db); + } - function get_null_date() { - return \DBA::$null_date; - } + public function get_null_date() + { + return DBA::$null_date; + } - function get_install_script() { - $platform_name = \Zotlabs\Lib\System::get_platform_name(); - if(file_exists('install/' . $platform_name . '/' . \DBA::$install_script)) - return 'install/' . $platform_name . '/' . \DBA::$install_script; + public function get_install_script() + { + $platform_name = System::get_platform_name(); + if (file_exists('install/' . $platform_name . '/' . DBA::$install_script)) { + return 'install/' . $platform_name . '/' . DBA::$install_script; + } - return 'install/' . \DBA::$install_script; - } + return 'install/' . DBA::$install_script; + } - function get_table_quote() { - return \DBA::$tquot; - } + public function get_table_quote() + { + return DBA::$tquot; + } - function utcnow() { - return \DBA::$utc_now; - } + public function utcnow() + { + return DBA::$utc_now; + } - function install($server,$scheme,$port,$user,$pass,$db) { - if (!(strlen($server) && strlen($user))){ - $this->connected = false; - $this->db = null; - return false; - } + public function install($server, $scheme, $port, $user, $pass, $db) + { + if (!(strlen($server) && strlen($user))) { + $this->connected = false; + $this->db = null; + return false; + } - if(strlen($server) && ($server !== 'localhost') && ($server !== '127.0.0.1') && (! strpbrk($server,':;'))) { - if(! z_dns_check($server)) { - $this->error = sprintf( t('Cannot locate DNS info for database server \'%s\''), $server); - $this->connected = false; - $this->db = null; - return false; - } - } + if (strlen($server) && ($server !== 'localhost') && ($server !== '127.0.0.1') && (!strpbrk($server, ':;'))) { + if (!z_dns_check($server)) { + $this->error = sprintf(t('Cannot locate DNS info for database server \'%s\''), $server); + $this->connected = false; + $this->db = null; + return false; + } + } - return true; - } + return true; + } - /** - * @brief Sets the database driver's debugging state. - * - * @param int $dbg 0 to disable debugging - */ - function dbg($dbg) { - $this->debug = $dbg; - } + /** + * @brief Sets the database driver's debugging state. + * + * @param int $dbg 0 to disable debugging + */ + public function dbg($dbg) + { + $this->debug = $dbg; + } - function __destruct() { - if($this->db && $this->connected) { - $this->close(); - } - } + public function __destruct() + { + if ($this->db && $this->connected) { + $this->close(); + } + } - function quote_interval($txt) { - return $txt; - } + public function quote_interval($txt) + { + return $txt; + } - function optimize_table($table) { - q('OPTIMIZE TABLE '.$table); - } + public function optimize_table($table) + { + q('OPTIMIZE TABLE ' . $table); + } - function concat($fld, $sep) { - return 'GROUP_CONCAT(DISTINCT '.$fld.' SEPARATOR \''.$sep.'\')'; - } + public function concat($fld, $sep) + { + return 'GROUP_CONCAT(DISTINCT ' . $fld . ' SEPARATOR \'' . $sep . '\')'; + } - function escapebin($str) { - return $this->escape($str); - } - - function unescapebin($str) { - return $str; - } + public function escapebin($str) + { + return $this->escape($str); + } + public function unescapebin($str) + { + return $str; + } } // end abstract dba_driver class @@ -228,14 +245,15 @@ abstract class dba_driver { // Procedural functions // -function printable($s, $escape = true) { - $s = preg_replace("~([\x01-\x08\x0E-\x0F\x10-\x1F\x7F-\xFF])~",".", $s); - $s = str_replace("\x00",'.',$s); - if ($escape) { - $s = escape_tags($s); - } +function printable($s, $escape = true) +{ + $s = preg_replace("~([\x01-\x08\x0E-\x0F\x10-\x1F\x7F-\xFF])~", ".", $s); + $s = str_replace("\x00", '.', $s); + if ($escape) { + $s = escape_tags($s); + } - return $s; + return $s; } /** @@ -243,11 +261,13 @@ function printable($s, $escape = true) { * * @param int $state 0 to disable debugging */ -function dbg($state) { - global $db; +function dbg($state) +{ + global $db; - if(\DBA::$dba) - \DBA::$dba->dbg($state); + if (DBA::$dba) { + DBA::$dba->dbg($state); + } } /** @@ -260,53 +280,66 @@ function dbg($state) { * @param string $str A string to pass to a DB query * @return string Return an escaped string of the value to pass to a DB query. */ -function dbesc($str) { +function dbesc($str) +{ - if(is_null_date($str)) - $str = NULL_DATE; + if (is_null_date($str)) { + $str = NULL_DATE; + } - if(\DBA::$dba && \DBA::$dba->connected) - return(\DBA::$dba->escape($str)); - else - return(str_replace("'", "\\'", $str)); + if (DBA::$dba && DBA::$dba->connected) { + return(DBA::$dba->escape($str)); + } else { + return(str_replace("'", "\\'", $str)); + } } -function dbescbin($str) { - return \DBA::$dba->escapebin($str); +function dbescbin($str) +{ + return DBA::$dba->escapebin($str); } -function dbunescbin($str) { - return \DBA::$dba->unescapebin($str); +function dbunescbin($str) +{ + return DBA::$dba->unescapebin($str); } -function dbescdate($date) { - if(is_null_date($date)) - return \DBA::$dba->escape(NULL_DATE); +function dbescdate($date) +{ + if (is_null_date($date)) { + return DBA::$dba->escape(NULL_DATE); + } - return \DBA::$dba->escape($date); + return DBA::$dba->escape($date); } -function db_quoteinterval($txt) { - return \DBA::$dba->quote_interval($txt); +function db_quoteinterval($txt) +{ + return DBA::$dba->quote_interval($txt); } -function dbesc_identifier($str) { - return \DBA::$dba->escape_identifier($str); +function dbesc_identifier($str) +{ + return DBA::$dba->escape_identifier($str); } -function db_utcnow() { - return \DBA::$dba->utcnow(); +function db_utcnow() +{ + return DBA::$dba->utcnow(); } -function db_optimizetable($table) { - \DBA::$dba->optimize_table($table); +function db_optimizetable($table) +{ + DBA::$dba->optimize_table($table); } -function db_concat($fld, $sep) { - return \DBA::$dba->concat($fld, $sep); +function db_concat($fld, $sep) +{ + return DBA::$dba->concat($fld, $sep); } -function db_use_index($str) { - return \DBA::$dba->use_index($str); +function db_use_index($str) +{ + return DBA::$dba->use_index($str); } /** @@ -325,31 +358,33 @@ function db_use_index($str) { * @param string $sql The SQL query to execute * @return bool|array */ -function q($sql) { +function q($sql) +{ - $args = func_get_args(); - array_shift($args); + $args = func_get_args(); + array_shift($args); - if(\DBA::$dba && \DBA::$dba->connected) { - $stmt = vsprintf($sql, $args); - if($stmt === false) { - db_logger('dba: vsprintf error: ' . - print_r(debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT, 1), true),LOGGER_NORMAL,LOG_CRIT); - } - if(\DBA::$dba->debug) - db_logger('Sql: ' . $stmt, LOGGER_DEBUG, LOG_INFO); + if (DBA::$dba && DBA::$dba->connected) { + $stmt = vsprintf($sql, $args); + if ($stmt === false) { + db_logger('dba: vsprintf error: ' . + print_r(debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT, 1), true), LOGGER_NORMAL, LOG_CRIT); + } + if (DBA::$dba->debug) { + db_logger('Sql: ' . $stmt, LOGGER_DEBUG, LOG_INFO); + } - return \DBA::$dba->q($stmt); - } + return DBA::$dba->q($stmt); + } - /* - * This will happen occasionally trying to store the - * session data after abnormal program termination - */ + /* + * This will happen occasionally trying to store the + * session data after abnormal program termination + */ - db_logger('dba: no database: ' . print_r($args,true),LOGGER_NORMAL,LOG_CRIT); + db_logger('dba: no database: ' . print_r($args, true), LOGGER_NORMAL, LOG_CRIT); - return false; + return false; } /** @@ -359,14 +394,16 @@ function q($sql) { * * @param string $sql The SQL query to execute */ -function dbq($sql) { +function dbq($sql) +{ - if(\DBA::$dba && \DBA::$dba->connected) - $ret = \DBA::$dba->q($sql); - else - $ret = false; + if (DBA::$dba && DBA::$dba->connected) { + $ret = DBA::$dba->q($sql); + } else { + $ret = false; + } - return $ret; + return $ret; } @@ -376,72 +413,78 @@ function dbq($sql) { // SQL injection vectors. All integer array elements should be specifically // cast to int to avoid trouble. -function dbesc_array_cb(&$item, $key) { - if(is_string($item)) { - if(is_null_date($item)) - $item = NULL_DATE; - $item = dbesc($item); - } +function dbesc_array_cb(&$item, $key) +{ + if (is_string($item)) { + if (is_null_date($item)) { + $item = NULL_DATE; + } + $item = dbesc($item); + } } -function dbesc_array(&$arr) { - $bogus_key = false; - if(is_array($arr) && count($arr)) { - $matches = false; - foreach($arr as $k => $v) { - if(preg_match('/([^a-zA-Z0-9\-\_\.])/',$k,$matches)) { - logger('bogus key: ' . $k); - $bogus_key = true; - } - } - array_walk($arr,'dbesc_array_cb'); - if($bogus_key) { - $arr['BOGUS.KEY'] = 1; - return false; - } - } - return true; +function dbesc_array(&$arr) +{ + $bogus_key = false; + if (is_array($arr) && count($arr)) { + $matches = false; + foreach ($arr as $k => $v) { + if (preg_match('/([^a-zA-Z0-9\-\_\.])/', $k, $matches)) { + logger('bogus key: ' . $k); + $bogus_key = true; + } + } + array_walk($arr, 'dbesc_array_cb'); + if ($bogus_key) { + $arr['BOGUS.KEY'] = 1; + return false; + } + } + return true; } -function db_getfunc($f) { - $lookup = array( - 'rand'=>array( - DBTYPE_MYSQL=>'RAND()', - DBTYPE_POSTGRES=>'RANDOM()' - ), - 'utc_timestamp'=>array( - DBTYPE_MYSQL=>'UTC_TIMESTAMP()', - DBTYPE_POSTGRES=>"now() at time zone 'UTC'" - ), - 'regexp'=>array( - DBTYPE_MYSQL=>'REGEXP', - DBTYPE_POSTGRES=>'~' - ), - '^'=>array( - DBTYPE_MYSQL=>'^', - DBTYPE_POSTGRES=>'#' - ) - ); - $f = strtolower($f); - if(isset($lookup[$f]) && isset($lookup[$f][ACTIVE_DBTYPE])) - return $lookup[$f][ACTIVE_DBTYPE]; +function db_getfunc($f) +{ + $lookup = array( + 'rand'=>array( + DBTYPE_MYSQL=>'RAND()', + DBTYPE_POSTGRES=>'RANDOM()' + ), + 'utc_timestamp'=>array( + DBTYPE_MYSQL=>'UTC_TIMESTAMP()', + DBTYPE_POSTGRES=>"now() at time zone 'UTC'" + ), + 'regexp'=>array( + DBTYPE_MYSQL=>'REGEXP', + DBTYPE_POSTGRES=>'~' + ), + '^'=>array( + DBTYPE_MYSQL=>'^', + DBTYPE_POSTGRES=>'#' + ) + ); + $f = strtolower($f); + if (isset($lookup[$f]) && isset($lookup[$f][ACTIVE_DBTYPE])) { + return $lookup[$f][ACTIVE_DBTYPE]; + } - db_logger('Unable to abstract DB function "'. $f . '" for dbtype ' . ACTIVE_DBTYPE, LOGGER_DEBUG, LOG_ERR); - return $f; + db_logger('Unable to abstract DB function "'. $f . '" for dbtype ' . ACTIVE_DBTYPE, LOGGER_DEBUG, LOG_ERR); + return $f; } -function db_load_file($f) { - // db errors should get logged to the logfile - $str = @file_get_contents($f); - $arr = explode(';', $str); - if($arr) { - foreach($arr as $a) { - if(strlen(trim($a))) { - $r = dbq(trim($a)); - } - } - } +function db_load_file($f) +{ + // db errors should get logged to the logfile + $str = @file_get_contents($f); + $arr = explode(';', $str); + if ($arr) { + foreach ($arr as $a) { + if (strlen(trim($a))) { + $r = dbq(trim($a)); + } + } + } } @@ -450,65 +493,71 @@ function db_load_file($f) { // So this function preserves the current database debugging state and then turns it off // temporarily while doing the logger() call -function db_logger($s,$level = LOGGER_NORMAL,$syslog = LOG_INFO) { +function db_logger($s, $level = LOGGER_NORMAL, $syslog = LOG_INFO) +{ - if(\DBA::$logging || ! \DBA::$dba) - return; + if (DBA::$logging || ! DBA::$dba) { + return; + } - $saved = \DBA::$dba->debug; - \DBA::$dba->debug = false; - \DBA::$logging = true; - logger($s,$level,$syslog); - \DBA::$logging = false; - \DBA::$dba->debug = $saved; + $saved = DBA::$dba->debug; + DBA::$dba->debug = false; + DBA::$logging = true; + logger($s, $level, $syslog); + DBA::$logging = false; + DBA::$dba->debug = $saved; } -function db_columns($table) { +function db_columns($table) +{ - if($table) { - if(ACTIVE_DBTYPE === DBTYPE_POSTGRES) { - $r = q("SELECT column_name as field FROM information_schema.columns WHERE table_schema = 'public' AND table_name = '%s'", - dbesc($table) - ); - if($r) { - return ids_to_array($r,'field'); - } - } - else { - $r = q("show columns in %s", - dbesc($table) - ); - if($r) { - return ids_to_array($r,'Field'); - } - } - } + if ($table) { + if (ACTIVE_DBTYPE === DBTYPE_POSTGRES) { + $r = q( + "SELECT column_name as field FROM information_schema.columns WHERE table_schema = 'public' AND table_name = '%s'", + dbesc($table) + ); + if ($r) { + return ids_to_array($r, 'field'); + } + } else { + $r = q( + "show columns in %s", + dbesc($table) + ); + if ($r) { + return ids_to_array($r, 'Field'); + } + } + } - return []; + return []; } -function db_indexes($table) { +function db_indexes($table) +{ - if($table) { - if(ACTIVE_DBTYPE === DBTYPE_POSTGRES) { - $r = q("SELECT indexname from pg_indexes where tablename = '%s'", - dbesc($table) - ); - if($r) { - return ids_to_array($r,'indexname'); - } - } - else { - $r = q("show index from %s", - dbesc($table) - ); - if($r) { - return ids_to_array($r,'Key_name'); - } - } - } + if ($table) { + if (ACTIVE_DBTYPE === DBTYPE_POSTGRES) { + $r = q( + "SELECT indexname from pg_indexes where tablename = '%s'", + dbesc($table) + ); + if ($r) { + return ids_to_array($r, 'indexname'); + } + } else { + $r = q( + "show index from %s", + dbesc($table) + ); + if ($r) { + return ids_to_array($r, 'Key_name'); + } + } + } - return []; + return []; } diff --git a/include/dba/dba_pdo.php b/include/dba/dba_pdo.php index de301a800..9009c03d2 100755 --- a/include/dba/dba_pdo.php +++ b/include/dba/dba_pdo.php @@ -6,180 +6,183 @@ require_once 'include/dba/dba_driver.php'; * @brief PDO based database driver. * */ -class dba_pdo extends dba_driver { +class dba_pdo extends dba_driver +{ - public $driver_dbtype = null; + public $driver_dbtype = null; - /** - * {@inheritDoc} - * @see dba_driver::connect() - */ - function connect($server, $scheme, $port, $user, $pass, $db) { + /** + * {@inheritDoc} + * @see dba_driver::connect() + */ + public function connect($server, $scheme, $port, $user, $pass, $db) + { - $this->driver_dbtype = $scheme; + $this->driver_dbtype = $scheme; - if(strpbrk($server,':;')) { - $dsn = $server; - } - else { - $dsn = $this->driver_dbtype . ':host=' . $server . (intval($port) ? ';port=' . $port : ''); - } + if (strpbrk($server, ':;')) { + $dsn = $server; + } else { + $dsn = $this->driver_dbtype . ':host=' . $server . (intval($port) ? ';port=' . $port : ''); + } - $dsn .= ';dbname=' . $db; + $dsn .= ';dbname=' . $db; - try { - $this->db = new PDO($dsn,$user,$pass); - $this->db->setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_EXCEPTION); - } - catch(PDOException $e) { - if(file_exists('dbfail.out')) { - file_put_contents('dbfail.out', datetime_convert() . "\nConnect: " . $e->getMessage() . "\n", FILE_APPEND); - } + try { + $this->db = new PDO($dsn, $user, $pass); + $this->db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + } catch (PDOException $e) { + if (file_exists('dbfail.out')) { + file_put_contents('dbfail.out', datetime_convert() . "\nConnect: " . $e->getMessage() . "\n", FILE_APPEND); + } - return false; - } + return false; + } - if($this->driver_dbtype === 'pgsql') - $this->q("SET standard_conforming_strings = 'off'; SET backslash_quote = 'on';"); + if ($this->driver_dbtype === 'pgsql') { + $this->q("SET standard_conforming_strings = 'off'; SET backslash_quote = 'on';"); + } - $this->connected = true; + $this->connected = true; - return true; - } + return true; + } - /** - * {@inheritDoc} - * @see dba_driver::q() - * - * @return bool|array|PDOStatement - * - \b false if not connected or PDOException occured on query - * - \b array with results on a SELECT query - * - \b PDOStatement on a non SELECT SQL query - */ - function q($sql) { - if((! $this->db) || (! $this->connected)) - return false; + /** + * {@inheritDoc} + * @return bool|array|PDOStatement + * - \b false if not connected or PDOException occured on query + * - \b array with results on a SELECT query + * - \b PDOStatement on a non SELECT SQL query + * @see dba_driver::q() + * + */ + public function q($sql) + { + if ((!$this->db) || (!$this->connected)) { + return false; + } - if($this->driver_dbtype === 'pgsql') { - if(substr(rtrim($sql),-1,1) !== ';') { - $sql .= ';'; - } - } + if ($this->driver_dbtype === 'pgsql') { + if (substr(rtrim($sql), -1, 1) !== ';') { + $sql .= ';'; + } + } - $result = null; - $this->error = ''; - $select = ((stripos($sql, 'select') === 0) ? true : false); + $result = null; + $this->error = ''; + $select = ((stripos($sql, 'select') === 0) ? true : false); - try { - $result = $this->db->query($sql, PDO::FETCH_ASSOC); - } - catch(PDOException $e) { + try { + $result = $this->db->query($sql, PDO::FETCH_ASSOC); + } catch (PDOException $e) { + $this->error = $e->getMessage(); + if ($this->error) { + db_logger('dba_pdo: ERROR: ' . printable($sql) . "\n" . $this->error, LOGGER_NORMAL, LOG_ERR); + if (file_exists('dbfail.out')) { + file_put_contents('dbfail.out', datetime_convert() . "\n" . printable($sql) . "\n" . $this->error . "\n", FILE_APPEND); + } + } + } - $this->error = $e->getMessage(); - if($this->error) { - db_logger('dba_pdo: ERROR: ' . printable($sql) . "\n" . $this->error, LOGGER_NORMAL, LOG_ERR); - if(file_exists('dbfail.out')) { - file_put_contents('dbfail.out', datetime_convert() . "\n" . printable($sql) . "\n" . $this->error . "\n", FILE_APPEND); - } - } - } + if (!($select)) { + if ($this->debug) { + db_logger('dba_pdo: DEBUG: ' . printable($sql) . ' returns ' . (($result) ? 'true' : 'false'), LOGGER_NORMAL, (($result) ? LOG_INFO : LOG_ERR)); + } + return $result; + } - if(!($select)) { - if($this->debug) { - db_logger('dba_pdo: DEBUG: ' . printable($sql) . ' returns ' . (($result) ? 'true' : 'false'), LOGGER_NORMAL,(($result) ? LOG_INFO : LOG_ERR)); - } - return $result; - } + $r = []; + if ($result) { + foreach ($result as $x) { + $r[] = $x; + } + } - $r = []; - if($result) { - foreach($result as $x) { - $r[] = $x; - } - } + if ($this->debug) { + db_logger('dba_pdo: DEBUG: ' . printable($sql) . ' returned ' . count($r) . ' results.', LOGGER_NORMAL, LOG_INFO); + if (intval($this->debug) > 1) { + db_logger('dba_pdo: ' . printable(print_r($r, true)), LOGGER_NORMAL, LOG_INFO); + } + } - if($this->debug) { - db_logger('dba_pdo: DEBUG: ' . printable($sql) . ' returned ' . count($r) . ' results.', LOGGER_NORMAL, LOG_INFO); - if(intval($this->debug) > 1) { - db_logger('dba_pdo: ' . printable(print_r($r,true)), LOGGER_NORMAL, LOG_INFO); - } - } + return (($this->error) ? false : $r); + } - return (($this->error) ? false : $r); - } + public function escape($str) + { + if ($this->db && $this->connected) { + return substr(substr(@$this->db->quote($str), 1), 0, -1); + } + } - function escape($str) { - if($this->db && $this->connected) { - return substr(substr(@$this->db->quote($str),1),0,-1); - } - } + public function close() + { + if ($this->db) { + $this->db = null; + } - function close() { - if($this->db) - $this->db = null; + $this->connected = false; + } - $this->connected = false; - } + public function concat($fld, $sep) + { + if ($this->driver_dbtype === 'pgsql') { + return 'string_agg(' . $fld . ',\'' . $sep . '\')'; + } else { + return 'GROUP_CONCAT(DISTINCT ' . $fld . ' SEPARATOR \'' . $sep . '\')'; + } + } - function concat($fld,$sep) { - if($this->driver_dbtype === 'pgsql') { - return 'string_agg(' . $fld . ',\'' . $sep . '\')'; - } - else { - return 'GROUP_CONCAT(DISTINCT ' . $fld . ' SEPARATOR \'' . $sep . '\')'; - } - } + public function use_index($str) + { + if ($this->driver_dbtype === 'pgsql') { + return ''; + } else { + return 'USE INDEX( ' . $str . ')'; + } + } - function use_index($str) { - if($this->driver_dbtype === 'pgsql') { - return ''; - } - else { - return 'USE INDEX( ' . $str . ')'; - } - } + public function quote_interval($txt) + { + if ($this->driver_dbtype === 'pgsql') { + return "'$txt'"; + } else { + return $txt; + } + } - function quote_interval($txt) { - if($this->driver_dbtype === 'pgsql') { - return "'$txt'"; - } - else { - return $txt; - } - } + // These two functions assume that postgres standard_conforming_strings is set to off; + // which we perform during DB open. - // These two functions assume that postgres standard_conforming_strings is set to off; - // which we perform during DB open. + public function escapebin($str) + { + if ($this->driver_dbtype === 'pgsql') { + return "\\\\x" . bin2hex($str); + } else { + return $this->escape($str); + } + } - function escapebin($str) { - if($this->driver_dbtype === 'pgsql') { - return "\\\\x" . bin2hex($str); - } - else { - return $this->escape($str); - } - } - - function unescapebin($str) { - if(gettype($str) === 'resource') { - $x = ''; - while(! feof($str)) { - $x .= fread($str,8192); - } - if(substr($x,0,2) === '\\x') { - $x = hex2bin(substr($x,2)); - } - return $x; - - } - else { - return $str; - } - } + public function unescapebin($str) + { + if (gettype($str) === 'resource') { + $x = ''; + while (!feof($str)) { + $x .= fread($str, 8192); + } + if (substr($x, 0, 2) === '\\x') { + $x = hex2bin(substr($x, 2)); + } + return $x; + } else { + return $str; + } + } - function getdriver() { - return 'pdo'; - } - + public function getdriver() + { + return 'pdo'; + } } diff --git a/include/event.php b/include/event.php index 4136abcd5..1a6d5784e 100644 --- a/include/event.php +++ b/include/event.php @@ -1,4 +1,5 @@ ' . "\r\n"; + $o = '
                    ' . "\r\n"; - $o .= '

                     ' . zidify_links(smilies(bbcode($ev['summary']))) . '

                    ' . "\r\n"; + $o .= '

                     ' . zidify_links(smilies(bbcode($ev['summary']))) . '

                    ' . "\r\n"; - $o .= '
                    ' . t('Starts:') . ' ' - . (($ev['adjust']) ? day_translate(datetime_convert('UTC', date_default_timezone_get(), - $ev['dtstart'] , $bd_format )) - : day_translate(datetime_convert('UTC', 'UTC', - $ev['dtstart'] , $bd_format))) - . '
                    ' . "\r\n"; + $o .= '
                    ' . t('Starts:') . ' ' + . (($ev['adjust']) ? day_translate(datetime_convert( + 'UTC', + date_default_timezone_get(), + $ev['dtstart'], + $bd_format + )) + : day_translate(datetime_convert( + 'UTC', + 'UTC', + $ev['dtstart'], + $bd_format + ))) + . '
                    ' . "\r\n"; - if(! $ev['nofinish']) - $o .= '
                    ' . t('Finishes:') . ' ' - . (($ev['adjust']) ? day_translate(datetime_convert('UTC', date_default_timezone_get(), - $ev['dtend'] , $bd_format )) - : day_translate(datetime_convert('UTC', 'UTC', - $ev['dtend'] , $bd_format ))) - . '
                    ' . "\r\n"; + if (! $ev['nofinish']) { + $o .= '
                    ' . t('Finishes:') . ' ' + . (($ev['adjust']) ? day_translate(datetime_convert( + 'UTC', + date_default_timezone_get(), + $ev['dtend'], + $bd_format + )) + : day_translate(datetime_convert( + 'UTC', + 'UTC', + $ev['dtend'], + $bd_format + ))) + . '
                    ' . "\r\n"; + } - $o .= '
                    ' . zidify_links(smilies(bbcode($ev['description']))) . '
                    ' . "\r\n"; + $o .= '
                    ' . zidify_links(smilies(bbcode($ev['description']))) . '
                    ' . "\r\n"; - if(isset($ev['location']) && strlen($ev['location'])) - $o .= '
                    ' . t('Location:') . ' ' - . zidify_links(smilies(bbcode($ev['location']))) - . '
                    ' . "\r\n"; + if (isset($ev['location']) && strlen($ev['location'])) { + $o .= '
                    ' . t('Location:') . ' ' + . zidify_links(smilies(bbcode($ev['location']))) + . '
                    ' . "\r\n"; + } - $o .= '
                    ' . "\r\n"; + $o .= '' . "\r\n"; - return $o; + return $o; } -function format_event_obj($jobject) { +function format_event_obj($jobject) +{ - $event = []; + $event = []; - $object = json_decode($jobject,true); + $object = json_decode($jobject, true); /******* - This is our encoded format - - $x = [ - 'type' => 'Event', - 'id' => z_root() . '/event/' . $r[0]['resource_id'], - 'summary' => bbcode($arr['summary']), - // RFC3339 Section 4.3 - 'startTime' => (($arr['adjust']) ? datetime_convert('UTC','UTC',$arr['dtstart'], ATOM_TIME) : datetime_convert('UTC','UTC',$arr['dtstart'],'Y-m-d\\TH:i:s-00:00')), - 'content' => bbcode($arr['description']), - 'location' => [ 'type' => 'Place', 'content' => $arr['location'] ], - 'source' => [ 'content' => format_event_bbcode($arr), 'mediaType' => 'text/x-multicode' ], - 'url' => [ [ 'mediaType' => 'text/calendar', 'href' => z_root() . '/events/ical/' . $event['event_hash'] ] ], - 'actor' => Activity::encode_person($r[0],false), - ]; - if(! $arr['nofinish']) { - $x['endTime'] = (($arr['adjust']) ? datetime_convert('UTC','UTC',$arr['dtend'], ATOM_TIME) : datetime_convert('UTC','UTC',$arr['dtend'],'Y-m-d\\TH:i:s-00:00')); - } + This is our encoded format + $x = [ + 'type' => 'Event', + 'id' => z_root() . '/event/' . $r[0]['resource_id'], + 'summary' => bbcode($arr['summary']), + // RFC3339 Section 4.3 + 'startTime' => (($arr['adjust']) ? datetime_convert('UTC','UTC',$arr['dtstart'], ATOM_TIME) : datetime_convert('UTC','UTC',$arr['dtstart'],'Y-m-d\\TH:i:s-00:00')), + 'content' => bbcode($arr['description']), + 'location' => [ 'type' => 'Place', 'content' => $arr['location'] ], + 'source' => [ 'content' => format_event_bbcode($arr), 'mediaType' => 'text/x-multicode' ], + 'url' => [ [ 'mediaType' => 'text/calendar', 'href' => z_root() . '/events/ical/' . $event['event_hash'] ] ], + 'actor' => Activity::encode_person($r[0],false), + ]; + if(! $arr['nofinish']) { + $x['endTime'] = (($arr['adjust']) ? datetime_convert('UTC','UTC',$arr['dtend'], ATOM_TIME) : datetime_convert('UTC','UTC',$arr['dtend'],'Y-m-d\\TH:i:s-00:00')); + } ******/ - if(is_array($object) && (array_key_exists('summary', $object) || array_key_exists('name',$object))) { + if (is_array($object) && (array_key_exists('summary', $object) || array_key_exists('name', $object))) { + $bd_format = t('l F d, Y \@ g:i A'); // Friday January 18, 2011 @ 8:01 AM + $dtend = ((array_key_exists('endTime', $object)) ? $object['endTime'] : NULL_DATE); + $title = ((isset($object['summary']) && $object['summary']) ? zidify_links(smilies(bbcode($object['summary']))) : $object['name']); - $bd_format = t('l F d, Y \@ g:i A'); // Friday January 18, 2011 @ 8:01 AM - $dtend = ((array_key_exists('endTime',$object)) ? $object['endTime'] : NULL_DATE); - $title = ((isset($object['summary']) && $object['summary']) ? zidify_links(smilies(bbcode($object['summary']))) : $object['name']); + $event['header'] = replace_macros(get_markup_template('event_item_header.tpl'), array( + '$title' => $title, + '$dtstart_label' => t('Starts:'), + '$dtstart_title' => datetime_convert('UTC', 'UTC', $object['startTime'], ((strpos($object['startTime'], 'Z')) ? ATOM_TIME : 'Y-m-d\TH:i:s' )), + '$dtstart_dt' => ((strpos($object['startTime'], 'Z')) ? day_translate(datetime_convert('UTC', date_default_timezone_get(), $object['startTime'], $bd_format)) : day_translate(datetime_convert('UTC', 'UTC', $object['startTime'], $bd_format))), + '$finish' => ((array_key_exists('endTime', $object)) ? true : false), + '$dtend_label' => t('Finishes:'), + '$dtend_title' => datetime_convert('UTC', 'UTC', $dtend, ((strpos($object['startTime'], 'Z')) ? ATOM_TIME : 'Y-m-d\TH:i:s' )), + '$dtend_dt' => ((strpos($object['startTime'], 'Z')) ? day_translate(datetime_convert('UTC', date_default_timezone_get(), $dtend, $bd_format)) : day_translate(datetime_convert('UTC', 'UTC', $dtend, $bd_format))) - $event['header'] = replace_macros(get_markup_template('event_item_header.tpl'),array( - '$title' => $title, - '$dtstart_label' => t('Starts:'), - '$dtstart_title' => datetime_convert('UTC', 'UTC', $object['startTime'], ((strpos($object['startTime'],'Z')) ? ATOM_TIME : 'Y-m-d\TH:i:s' )), - '$dtstart_dt' => ((strpos($object['startTime'],'Z')) ? day_translate(datetime_convert('UTC', date_default_timezone_get(), $object['startTime'] , $bd_format )) : day_translate(datetime_convert('UTC', 'UTC', $object['startTime'] , $bd_format))), - '$finish' => ((array_key_exists('endTime',$object)) ? true : false), - '$dtend_label' => t('Finishes:'), - '$dtend_title' => datetime_convert('UTC','UTC',$dtend, ((strpos($object['startTime'],'Z')) ? ATOM_TIME : 'Y-m-d\TH:i:s' )), - '$dtend_dt' => ((strpos($object['startTime'],'Z')) ? day_translate(datetime_convert('UTC', date_default_timezone_get(), $dtend , $bd_format )) : day_translate(datetime_convert('UTC', 'UTC', $dtend , $bd_format ))) + )); - )); + $event['content'] = replace_macros(get_markup_template('event_item_content.tpl'), array( + '$description' => $object['content'], + '$location_label' => t('Location:'), + '$location' => ((array_path_exists('location/content', $object)) ? zidify_links(smilies(bbcode($object['location']['content']))) : EMPTY_STR) + )); + } - $event['content'] = replace_macros(get_markup_template('event_item_content.tpl'),array( - '$description' => $object['content'], - '$location_label' => t('Location:'), - '$location' => ((array_path_exists('location/content',$object)) ? zidify_links(smilies(bbcode($object['location']['content']))) : EMPTY_STR) - )); - - } - - return $event; + return $event; } -function ical_wrapper($ev) { +function ical_wrapper($ev) +{ - if(! ((is_array($ev)) && count($ev))) - return ''; + if (! ((is_array($ev)) && count($ev))) { + return ''; + } - $o .= "BEGIN:VCALENDAR"; - $o .= "\r\nVERSION:2.0"; - $o .= "\r\nMETHOD:PUBLISH"; - $o .= "\r\nPRODID:-//" . get_config('system','sitename') . "//" . Zotlabs\Lib\System::get_platform_name() . "//" . strtoupper(App::$language). "\r\n"; - if(array_key_exists('dtstart', $ev)) - $o .= format_event_ical($ev); - else { - foreach($ev as $e) { - $o .= format_event_ical($e); - } - } - $o .= "\r\nEND:VCALENDAR\r\n"; + $o .= "BEGIN:VCALENDAR"; + $o .= "\r\nVERSION:2.0"; + $o .= "\r\nMETHOD:PUBLISH"; + $o .= "\r\nPRODID:-//" . get_config('system', 'sitename') . "//" . Zotlabs\Lib\System::get_platform_name() . "//" . strtoupper(App::$language) . "\r\n"; + if (array_key_exists('dtstart', $ev)) { + $o .= format_event_ical($ev); + } else { + foreach ($ev as $e) { + $o .= format_event_ical($e); + } + } + $o .= "\r\nEND:VCALENDAR\r\n"; - return $o; + return $o; } -function format_event_ical($ev) { +function format_event_ical($ev) +{ - if($ev['etype'] === 'task') - return format_todo_ical($ev); + if ($ev['etype'] === 'task') { + return format_todo_ical($ev); + } - $o = ''; + $o = ''; - $o .= "\r\nBEGIN:VEVENT"; + $o .= "\r\nBEGIN:VEVENT"; - $o .= "\r\nCREATED:" . datetime_convert('UTC','UTC', $ev['created'],'Ymd\\THis\\Z'); - $o .= "\r\nLAST-MODIFIED:" . datetime_convert('UTC','UTC', $ev['edited'],'Ymd\\THis\\Z'); - $o .= "\r\nDTSTAMP:" . datetime_convert('UTC','UTC', $ev['edited'],'Ymd\\THis\\Z'); - if($ev['dtstart']) - $o .= "\r\nDTSTART:" . datetime_convert('UTC','UTC', $ev['dtstart'],'Ymd\\THis' . (($ev['adjust']) ? '\\Z' : '')); - if($ev['dtend'] && ! $ev['nofinish']) - $o .= "\r\nDTEND:" . datetime_convert('UTC','UTC', $ev['dtend'],'Ymd\\THis' . (($ev['adjust']) ? '\\Z' : '')); - if($ev['summary']) { - $o .= "\r\nSUMMARY:" . format_ical_text($ev['summary']); - $o .= "\r\nX-ZOT-SUMMARY:" . format_ical_sourcetext($ev['summary']); - } - if($ev['location']) { - $o .= "\r\nLOCATION:" . format_ical_text($ev['location']); - $o .= "\r\nX-ZOT-LOCATION:" . format_ical_sourcetext($ev['location']); - } - if($ev['description']) { - $o .= "\r\nDESCRIPTION:" . format_ical_text($ev['description']); - $o .= "\r\nX-ZOT-DESCRIPTION:" . format_ical_sourcetext($ev['description']); - } - if($ev['event_priority']) - $o .= "\r\nPRIORITY:" . intval($ev['event_priority']); - $o .= "\r\nUID:" . $ev['event_hash'] ; - $o .= "\r\nEND:VEVENT\r\n"; + $o .= "\r\nCREATED:" . datetime_convert('UTC', 'UTC', $ev['created'], 'Ymd\\THis\\Z'); + $o .= "\r\nLAST-MODIFIED:" . datetime_convert('UTC', 'UTC', $ev['edited'], 'Ymd\\THis\\Z'); + $o .= "\r\nDTSTAMP:" . datetime_convert('UTC', 'UTC', $ev['edited'], 'Ymd\\THis\\Z'); + if ($ev['dtstart']) { + $o .= "\r\nDTSTART:" . datetime_convert('UTC', 'UTC', $ev['dtstart'], 'Ymd\\THis' . (($ev['adjust']) ? '\\Z' : '')); + } + if ($ev['dtend'] && ! $ev['nofinish']) { + $o .= "\r\nDTEND:" . datetime_convert('UTC', 'UTC', $ev['dtend'], 'Ymd\\THis' . (($ev['adjust']) ? '\\Z' : '')); + } + if ($ev['summary']) { + $o .= "\r\nSUMMARY:" . format_ical_text($ev['summary']); + $o .= "\r\nX-ZOT-SUMMARY:" . format_ical_sourcetext($ev['summary']); + } + if ($ev['location']) { + $o .= "\r\nLOCATION:" . format_ical_text($ev['location']); + $o .= "\r\nX-ZOT-LOCATION:" . format_ical_sourcetext($ev['location']); + } + if ($ev['description']) { + $o .= "\r\nDESCRIPTION:" . format_ical_text($ev['description']); + $o .= "\r\nX-ZOT-DESCRIPTION:" . format_ical_sourcetext($ev['description']); + } + if ($ev['event_priority']) { + $o .= "\r\nPRIORITY:" . intval($ev['event_priority']); + } + $o .= "\r\nUID:" . $ev['event_hash'] ; + $o .= "\r\nEND:VEVENT\r\n"; - return $o; + return $o; } -function format_todo_ical($ev) { +function format_todo_ical($ev) +{ - $o = ''; + $o = ''; - $o .= "\r\nBEGIN:VTODO"; - $o .= "\r\nCREATED:" . datetime_convert('UTC','UTC', $ev['created'],'Ymd\\THis\\Z'); - $o .= "\r\nLAST-MODIFIED:" . datetime_convert('UTC','UTC', $ev['edited'],'Ymd\\THis\\Z'); - $o .= "\r\nDTSTAMP:" . datetime_convert('UTC','UTC', $ev['edited'],'Ymd\\THis\\Z'); - if($ev['dtstart']) - $o .= "\r\nDTSTART:" . datetime_convert('UTC','UTC', $ev['dtstart'],'Ymd\\THis' . (($ev['adjust']) ? '\\Z' : '')); - if($ev['dtend'] && ! $ev['nofinish']) - $o .= "\r\nDUE:" . datetime_convert('UTC','UTC', $ev['dtend'],'Ymd\\THis' . (($ev['adjust']) ? '\\Z' : '')); - if($ev['summary']) { - $o .= "\r\nSUMMARY:" . format_ical_text($ev['summary']); - $o .= "\r\nX-ZOT-SUMMARY:" . format_ical_sourcetext($ev['summary']); - } - if($ev['event_status']) { - $o .= "\r\nSTATUS:" . $ev['event_status']; - if($ev['event_status'] === 'COMPLETED') - $o .= "\r\nCOMPLETED:" . datetime_convert('UTC','UTC', $ev['event_status_date'],'Ymd\\THis\\Z'); - } - if(intval($ev['event_percent'])) - $o .= "\r\nPERCENT-COMPLETE:" . $ev['event_percent']; - if(intval($ev['event_sequence'])) - $o .= "\r\nSEQUENCE:" . $ev['event_sequence']; - if($ev['location']) { - $o .= "\r\nLOCATION:" . format_ical_text($ev['location']); - $o .= "\r\nX-ZOT-LOCATION:" . format_ical_sourcetext($ev['location']); - } - if($ev['description']) { - $o .= "\r\nDESCRIPTION:" . format_ical_text($ev['description']); - $o .= "\r\nX-ZOT-DESCRIPTION:" . format_ical_sourcetext($ev['description']); - } - $o .= "\r\nUID:" . $ev['event_hash'] ; - if($ev['event_priority']) - $o .= "\r\nPRIORITY:" . intval($ev['event_priority']); - $o .= "\r\nEND:VTODO\r\n"; + $o .= "\r\nBEGIN:VTODO"; + $o .= "\r\nCREATED:" . datetime_convert('UTC', 'UTC', $ev['created'], 'Ymd\\THis\\Z'); + $o .= "\r\nLAST-MODIFIED:" . datetime_convert('UTC', 'UTC', $ev['edited'], 'Ymd\\THis\\Z'); + $o .= "\r\nDTSTAMP:" . datetime_convert('UTC', 'UTC', $ev['edited'], 'Ymd\\THis\\Z'); + if ($ev['dtstart']) { + $o .= "\r\nDTSTART:" . datetime_convert('UTC', 'UTC', $ev['dtstart'], 'Ymd\\THis' . (($ev['adjust']) ? '\\Z' : '')); + } + if ($ev['dtend'] && ! $ev['nofinish']) { + $o .= "\r\nDUE:" . datetime_convert('UTC', 'UTC', $ev['dtend'], 'Ymd\\THis' . (($ev['adjust']) ? '\\Z' : '')); + } + if ($ev['summary']) { + $o .= "\r\nSUMMARY:" . format_ical_text($ev['summary']); + $o .= "\r\nX-ZOT-SUMMARY:" . format_ical_sourcetext($ev['summary']); + } + if ($ev['event_status']) { + $o .= "\r\nSTATUS:" . $ev['event_status']; + if ($ev['event_status'] === 'COMPLETED') { + $o .= "\r\nCOMPLETED:" . datetime_convert('UTC', 'UTC', $ev['event_status_date'], 'Ymd\\THis\\Z'); + } + } + if (intval($ev['event_percent'])) { + $o .= "\r\nPERCENT-COMPLETE:" . $ev['event_percent']; + } + if (intval($ev['event_sequence'])) { + $o .= "\r\nSEQUENCE:" . $ev['event_sequence']; + } + if ($ev['location']) { + $o .= "\r\nLOCATION:" . format_ical_text($ev['location']); + $o .= "\r\nX-ZOT-LOCATION:" . format_ical_sourcetext($ev['location']); + } + if ($ev['description']) { + $o .= "\r\nDESCRIPTION:" . format_ical_text($ev['description']); + $o .= "\r\nX-ZOT-DESCRIPTION:" . format_ical_sourcetext($ev['description']); + } + $o .= "\r\nUID:" . $ev['event_hash'] ; + if ($ev['event_priority']) { + $o .= "\r\nPRIORITY:" . intval($ev['event_priority']); + } + $o .= "\r\nEND:VTODO\r\n"; - return $o; + return $o; } -function format_ical_text($s) { +function format_ical_text($s) +{ - require_once('include/html2plain.php'); + require_once('include/html2plain.php'); - $s = html2plain(bbcode($s)); - $s = str_replace(["\r\n","\n"],["",""],$s); + $s = html2plain(bbcode($s)); + $s = str_replace(["\r\n","\n"], ["",""], $s); - return(wordwrap(str_replace(['\\',',',';'],['\\\\','\\,','\\;'],$s),72,"\r\n ",true)); + return(wordwrap(str_replace(['\\',',',';'], ['\\\\','\\,','\\;'], $s), 72, "\r\n ", true)); } -function format_ical_sourcetext($s) { - $s = base64_encode($s); +function format_ical_sourcetext($s) +{ + $s = base64_encode($s); - return(wordwrap(str_replace(['\\',',',';'],['\\\\','\\,','\\;'],$s),72,"\r\n ",true)); + return(wordwrap(str_replace(['\\',',',';'], ['\\\\','\\,','\\;'], $s), 72, "\r\n ", true)); } -function format_event_bbcode($ev) { +function format_event_bbcode($ev) +{ - $o = ''; + $o = ''; - if($ev['event_vdata']) { - $o .= '[event]' . $ev['event_vdata'] . '[/event]'; - } + if ($ev['event_vdata']) { + $o .= '[event]' . $ev['event_vdata'] . '[/event]'; + } - if($ev['summary']) - $o .= '[event-summary]' . $ev['summary'] . '[/event-summary]'; + if ($ev['summary']) { + $o .= '[event-summary]' . $ev['summary'] . '[/event-summary]'; + } - if($ev['description']) - $o .= '[event-description]' . $ev['description'] . '[/event-description]'; + if ($ev['description']) { + $o .= '[event-description]' . $ev['description'] . '[/event-description]'; + } - if($ev['dtstart']) - $o .= '[event-start]' . $ev['dtstart'] . '[/event-start]'; + if ($ev['dtstart']) { + $o .= '[event-start]' . $ev['dtstart'] . '[/event-start]'; + } - if(($ev['dtend']) && (! $ev['nofinish'])) - $o .= '[event-finish]' . $ev['dtend'] . '[/event-finish]'; + if (($ev['dtend']) && (! $ev['nofinish'])) { + $o .= '[event-finish]' . $ev['dtend'] . '[/event-finish]'; + } - if($ev['location']) - $o .= '[event-location]' . $ev['location'] . '[/event-location]'; + if ($ev['location']) { + $o .= '[event-location]' . $ev['location'] . '[/event-location]'; + } - if($ev['event_repeat']) - $o .= '[event-repeat]' . $ev['event_repeat'] . '[/event-repeat]'; + if ($ev['event_repeat']) { + $o .= '[event-repeat]' . $ev['event_repeat'] . '[/event-repeat]'; + } - if($ev['event_hash']) - $o .= '[event-id]' . $ev['event_hash'] . '[/event-id]'; + if ($ev['event_hash']) { + $o .= '[event-id]' . $ev['event_hash'] . '[/event-id]'; + } - if($ev['adjust']) - $o .= '[event-adjust]' . $ev['adjust'] . '[/event-adjust]'; + if ($ev['adjust']) { + $o .= '[event-adjust]' . $ev['adjust'] . '[/event-adjust]'; + } - // Hubzilla compatibility - $o .= '[event-timezone]UTC[/event-timezone]'; - - return $o; + // Hubzilla compatibility + $o .= '[event-timezone]UTC[/event-timezone]'; + + return $o; } -function bbtovcal($s) { - $o = ''; - $ev = bbtoevent($s); - if($ev['description']) - $o = format_event_html($ev); +function bbtovcal($s) +{ + $o = ''; + $ev = bbtoevent($s); + if ($ev['description']) { + $o = format_event_html($ev); + } - return $o; + return $o; } -function bbtoevent($s) { +function bbtoevent($s) +{ - $ev = []; + $ev = []; - $match = ''; - if(preg_match("/\[event\](.*?)\[\/event\]/is",$s,$match)) { - // only parse one object per event tag - // @fixme disabled for now - ical_to_event() expects a VCALENDAR wrapper - // and our VEVENT does not current include it. -// $x = ical_to_ev($match[1]); -// if($x) -// $ev = $x[0]; - } + $match = ''; + if (preg_match("/\[event\](.*?)\[\/event\]/is", $s, $match)) { + // only parse one object per event tag + // @fixme disabled for now - ical_to_event() expects a VCALENDAR wrapper + // and our VEVENT does not current include it. +// $x = ical_to_ev($match[1]); +// if($x) +// $ev = $x[0]; + } - $match = ''; - if(preg_match("/\[event\-summary\](.*?)\[\/event\-summary\]/is",$s,$match)) - $ev['summary'] = $match[1]; - $match = ''; - if(preg_match("/\[event\-description\](.*?)\[\/event\-description\]/is",$s,$match)) - $ev['description'] = $match[1]; - $match = ''; - if(preg_match("/\[event\-start\](.*?)\[\/event\-start\]/is",$s,$match)) - $ev['dtstart'] = $match[1]; - $match = ''; - if(preg_match("/\[event\-finish\](.*?)\[\/event\-finish\]/is",$s,$match)) - $ev['dtend'] = $match[1]; - $match = ''; - if(preg_match("/\[event\-location\](.*?)\[\/event\-location\]/is",$s,$match)) - $ev['location'] = $match[1]; - $match = ''; - if(preg_match("/\[event\-repeat\](.*?)\[\/event\-repeat\]/is",$s,$match)) - $ev['event_repeat'] = $match[1]; - $match = ''; - if(preg_match("/\[event\-id\](.*?)\[\/event\-id\]/is",$s,$match)) - $ev['event_hash'] = $match[1]; - $match = ''; - if(preg_match("/\[event\-adjust\](.*?)\[\/event\-adjust\]/is",$s,$match)) - $ev['adjust'] = $match[1]; - if(array_key_exists('dtstart',$ev)) { - if(array_key_exists('dtend',$ev)) { - if($ev['dtend'] === $ev['dtstart']) - $ev['nofinish'] = 1; - elseif($ev['dtend']) - $ev['nofinish'] = 0; - else - $ev['nofinish'] = 1; - } - else - $ev['nofinish'] = 1; - } + $match = ''; + if (preg_match("/\[event\-summary\](.*?)\[\/event\-summary\]/is", $s, $match)) { + $ev['summary'] = $match[1]; + } + $match = ''; + if (preg_match("/\[event\-description\](.*?)\[\/event\-description\]/is", $s, $match)) { + $ev['description'] = $match[1]; + } + $match = ''; + if (preg_match("/\[event\-start\](.*?)\[\/event\-start\]/is", $s, $match)) { + $ev['dtstart'] = $match[1]; + } + $match = ''; + if (preg_match("/\[event\-finish\](.*?)\[\/event\-finish\]/is", $s, $match)) { + $ev['dtend'] = $match[1]; + } + $match = ''; + if (preg_match("/\[event\-location\](.*?)\[\/event\-location\]/is", $s, $match)) { + $ev['location'] = $match[1]; + } + $match = ''; + if (preg_match("/\[event\-repeat\](.*?)\[\/event\-repeat\]/is", $s, $match)) { + $ev['event_repeat'] = $match[1]; + } + $match = ''; + if (preg_match("/\[event\-id\](.*?)\[\/event\-id\]/is", $s, $match)) { + $ev['event_hash'] = $match[1]; + } + $match = ''; + if (preg_match("/\[event\-adjust\](.*?)\[\/event\-adjust\]/is", $s, $match)) { + $ev['adjust'] = $match[1]; + } + if (array_key_exists('dtstart', $ev)) { + if (array_key_exists('dtend', $ev)) { + if ($ev['dtend'] === $ev['dtstart']) { + $ev['nofinish'] = 1; + } elseif ($ev['dtend']) { + $ev['nofinish'] = 0; + } else { + $ev['nofinish'] = 1; + } + } else { + $ev['nofinish'] = 1; + } + } - if(preg_match("/\[event\-timezone\](.*?)\[\/event\-timezone\]/is",$s,$match)) { - $tz = $match[1]; - if (array_key_exists('dtstart',$ev)) { - $ev['dtstart'] = datetime_convert($tz,'UTC',$ev['dtstart']); - } - if (array_key_exists('dtend',$ev)) { - $ev['dtend'] = datetime_convert($tz,'UTC',$ev['dtend']); - } - } + if (preg_match("/\[event\-timezone\](.*?)\[\/event\-timezone\]/is", $s, $match)) { + $tz = $match[1]; + if (array_key_exists('dtstart', $ev)) { + $ev['dtstart'] = datetime_convert($tz, 'UTC', $ev['dtstart']); + } + if (array_key_exists('dtend', $ev)) { + $ev['dtend'] = datetime_convert($tz, 'UTC', $ev['dtend']); + } + } -// logger('bbtoevent: ' . print_r($ev,true)); +// logger('bbtoevent: ' . print_r($ev,true)); - return $ev; + return $ev; } /** @@ -365,11 +419,13 @@ function bbtoevent($s) { * @param array $arr * @return array Date sorted array of events */ -function sort_by_date($arr) { - if (is_array($arr)) - usort($arr, 'ev_compare'); +function sort_by_date($arr) +{ + if (is_array($arr)) { + usort($arr, 'ev_compare'); + } - return $arr; + return $arr; } /** @@ -382,93 +438,99 @@ function sort_by_date($arr) { * @param array $b * @return number return values like strcmp() */ -function ev_compare($a, $b) { +function ev_compare($a, $b) +{ - $date_a = (($a['adjust']) ? datetime_convert('UTC',date_default_timezone_get(),$a['dtstart']) : $a['dtstart']); - $date_b = (($b['adjust']) ? datetime_convert('UTC',date_default_timezone_get(),$b['dtstart']) : $b['dtstart']); + $date_a = (($a['adjust']) ? datetime_convert('UTC', date_default_timezone_get(), $a['dtstart']) : $a['dtstart']); + $date_b = (($b['adjust']) ? datetime_convert('UTC', date_default_timezone_get(), $b['dtstart']) : $b['dtstart']); - if ($date_a === $date_b) - return strcasecmp($a['description'], $b['description']); + if ($date_a === $date_b) { + return strcasecmp($a['description'], $b['description']); + } - return strcmp($date_a, $date_b); + return strcmp($date_a, $date_b); } -function event_store_event($arr) { +function event_store_event($arr) +{ - $arr['created'] = (($arr['created']) ? $arr['created'] : datetime_convert()); - $arr['edited'] = (($arr['edited']) ? $arr['edited'] : datetime_convert()); - $arr['etype'] = (($arr['etype']) ? $arr['etype'] : 'event' ); - $arr['event_xchan'] = (($arr['event_xchan']) ? $arr['event_xchan'] : ''); - $arr['event_priority'] = (($arr['event_priority']) ? $arr['event_priority'] : 0); + $arr['created'] = (($arr['created']) ? $arr['created'] : datetime_convert()); + $arr['edited'] = (($arr['edited']) ? $arr['edited'] : datetime_convert()); + $arr['etype'] = (($arr['etype']) ? $arr['etype'] : 'event' ); + $arr['event_xchan'] = (($arr['event_xchan']) ? $arr['event_xchan'] : ''); + $arr['event_priority'] = (($arr['event_priority']) ? $arr['event_priority'] : 0); - if (! $arr['dtend']) { - $arr['dtend'] = NULL_DATE; - $arr['nofinish'] = 1; - } + if (! $arr['dtend']) { + $arr['dtend'] = NULL_DATE; + $arr['nofinish'] = 1; + } - if(array_key_exists('event_status_date',$arr)) - $arr['event_status_date'] = datetime_convert('UTC','UTC', $arr['event_status_date']); - else - $arr['event_status_date'] = NULL_DATE; + if (array_key_exists('event_status_date', $arr)) { + $arr['event_status_date'] = datetime_convert('UTC', 'UTC', $arr['event_status_date']); + } else { + $arr['event_status_date'] = NULL_DATE; + } - $existing_event = null; + $existing_event = null; - if($arr['event_hash']) { - $r = q("SELECT * FROM event WHERE event_hash = '%s' AND uid = %d LIMIT 1", - dbesc($arr['event_hash']), - intval($arr['uid']) - ); - if($r) { - $existing_event = $r[0]; - } - } + if ($arr['event_hash']) { + $r = q( + "SELECT * FROM event WHERE event_hash = '%s' AND uid = %d LIMIT 1", + dbesc($arr['event_hash']), + intval($arr['uid']) + ); + if ($r) { + $existing_event = $r[0]; + } + } - if($arr['id']) { - $r = q("SELECT * FROM event WHERE id = %d AND uid = %d LIMIT 1", - intval($arr['id']), - intval($arr['uid']) - ); - if($r) { - $existing_event = $r[0]; - } - else { - return false; - } - } + if ($arr['id']) { + $r = q( + "SELECT * FROM event WHERE id = %d AND uid = %d LIMIT 1", + intval($arr['id']), + intval($arr['uid']) + ); + if ($r) { + $existing_event = $r[0]; + } else { + return false; + } + } - $hook_info = [ - 'event' => $arr, - 'existing_event' => $existing_event, - 'cancel' => false - ]; - /** - * @hooks event_store_event - * Called when an event record is created or updated. - * * \e array \b event - * * \e array \b existing_event - * * \e boolean \b cancel - default false - */ - call_hooks('event_store_event', $hook_info); - if($hook_info['cancel']) - return false; + $hook_info = [ + 'event' => $arr, + 'existing_event' => $existing_event, + 'cancel' => false + ]; + /** + * @hooks event_store_event + * Called when an event record is created or updated. + * * \e array \b event + * * \e array \b existing_event + * * \e boolean \b cancel - default false + */ + call_hooks('event_store_event', $hook_info); + if ($hook_info['cancel']) { + return false; + } - $arr = $hook_info['event']; - $existing_event = $hook_info['existing_event']; + $arr = $hook_info['event']; + $existing_event = $hook_info['existing_event']; - if($existing_event) { + if ($existing_event) { + if ($existing_event['edited'] >= $arr['edited']) { + // Nothing has changed. + return $existing_event; + } - if($existing_event['edited'] >= $arr['edited']) { - // Nothing has changed. - return $existing_event; - } + $hash = $existing_event['event_hash']; - $hash = $existing_event['event_hash']; + // The event changed. Update it. - // The event changed. Update it. - - $r = q("UPDATE event SET + $r = q( + "UPDATE event SET edited = '%s', dtstart = '%s', dtend = '%s', @@ -490,926 +552,996 @@ function event_store_event($arr) { deny_cid = '%s', deny_gid = '%s' WHERE id = %d AND uid = %d", + dbesc(datetime_convert('UTC', 'UTC', $arr['edited'])), + dbesc(datetime_convert('UTC', 'UTC', $arr['dtstart'])), + dbesc(datetime_convert('UTC', 'UTC', $arr['dtend'])), + dbesc($arr['summary']), + dbesc($arr['description']), + dbesc($arr['location']), + dbesc($arr['etype']), + intval($arr['adjust']), + intval($arr['nofinish']), + dbesc($arr['event_status']), + dbesc(datetime_convert('UTC', 'UTC', $arr['event_status_date'])), + intval($arr['event_percent']), + dbesc($arr['event_repeat']), + intval($arr['event_sequence']), + intval($arr['event_priority']), + dbesc($arr['event_vdata']), + dbesc($arr['allow_cid']), + dbesc($arr['allow_gid']), + dbesc($arr['deny_cid']), + dbesc($arr['deny_gid']), + intval($existing_event['id']), + intval($arr['uid']) + ); + } else { + // New event. Store it. - dbesc(datetime_convert('UTC','UTC',$arr['edited'])), - dbesc(datetime_convert('UTC','UTC',$arr['dtstart'])), - dbesc(datetime_convert('UTC','UTC',$arr['dtend'])), - dbesc($arr['summary']), - dbesc($arr['description']), - dbesc($arr['location']), - dbesc($arr['etype']), - intval($arr['adjust']), - intval($arr['nofinish']), - dbesc($arr['event_status']), - dbesc(datetime_convert('UTC','UTC',$arr['event_status_date'])), - intval($arr['event_percent']), - dbesc($arr['event_repeat']), - intval($arr['event_sequence']), - intval($arr['event_priority']), - dbesc($arr['event_vdata']), - dbesc($arr['allow_cid']), - dbesc($arr['allow_gid']), - dbesc($arr['deny_cid']), - dbesc($arr['deny_gid']), - intval($existing_event['id']), - intval($arr['uid']) - ); - } else { + if (array_key_exists('external_id', $arr)) { + $hash = $arr['external_id']; + } elseif (array_key_exists('event_hash', $arr)) { + $hash = $arr['event_hash']; + } else { + try { + $hash = Uuid::uuid4()->toString(); + } catch (UnsatisfiedDependencyException $e) { + $hash = random_string(48); + } + } - // New event. Store it. - - if(array_key_exists('external_id',$arr)) - $hash = $arr['external_id']; - elseif(array_key_exists('event_hash',$arr)) - $hash = $arr['event_hash']; - else { - try { - $hash = Uuid::uuid4()->toString(); - } catch (UnsatisfiedDependencyException $e) { - $hash = random_string(48); - } - } - - $r = q("INSERT INTO event ( uid,aid,event_xchan,event_hash,created,edited,dtstart,dtend,summary,description,location,etype, + $r = q( + "INSERT INTO event ( uid,aid,event_xchan,event_hash,created,edited,dtstart,dtend,summary,description,location,etype, adjust,nofinish, event_status, event_status_date, event_percent, event_repeat, event_sequence, event_priority, event_vdata, allow_cid,allow_gid,deny_cid,deny_gid) VALUES ( %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, %d, '%s', '%s', %d, '%s', %d, %d, '%s', '%s', '%s', '%s', '%s' ) ", - intval($arr['uid']), - intval($arr['account']), - dbesc($arr['event_xchan']), - dbesc($hash), - dbesc(datetime_convert('UTC','UTC',$arr['created'])), - dbesc(datetime_convert('UTC','UTC',$arr['edited'])), - dbesc(datetime_convert('UTC','UTC',$arr['dtstart'])), - dbesc(datetime_convert('UTC','UTC',$arr['dtend'])), - dbesc($arr['summary']), - dbesc($arr['description']), - dbesc($arr['location']), - dbesc($arr['etype']), - intval($arr['adjust']), - intval($arr['nofinish']), - dbesc($arr['event_status']), - dbesc(datetime_convert('UTC','UTC',$arr['event_status_date'])), - intval($arr['event_percent']), - dbesc($arr['event_repeat']), - intval($arr['event_sequence']), - intval($arr['event_priority']), - dbesc($arr['event_vdata']), - dbesc($arr['allow_cid']), - dbesc($arr['allow_gid']), - dbesc($arr['deny_cid']), - dbesc($arr['deny_gid']) - ); - } + intval($arr['uid']), + intval($arr['account']), + dbesc($arr['event_xchan']), + dbesc($hash), + dbesc(datetime_convert('UTC', 'UTC', $arr['created'])), + dbesc(datetime_convert('UTC', 'UTC', $arr['edited'])), + dbesc(datetime_convert('UTC', 'UTC', $arr['dtstart'])), + dbesc(datetime_convert('UTC', 'UTC', $arr['dtend'])), + dbesc($arr['summary']), + dbesc($arr['description']), + dbesc($arr['location']), + dbesc($arr['etype']), + intval($arr['adjust']), + intval($arr['nofinish']), + dbesc($arr['event_status']), + dbesc(datetime_convert('UTC', 'UTC', $arr['event_status_date'])), + intval($arr['event_percent']), + dbesc($arr['event_repeat']), + intval($arr['event_sequence']), + intval($arr['event_priority']), + dbesc($arr['event_vdata']), + dbesc($arr['allow_cid']), + dbesc($arr['allow_gid']), + dbesc($arr['deny_cid']), + dbesc($arr['deny_gid']) + ); + } - $r = q("SELECT * FROM event WHERE event_hash = '%s' AND uid = %d LIMIT 1", - dbesc($hash), - intval($arr['uid']) - ); - if($r) - return $r[0]; + $r = q( + "SELECT * FROM event WHERE event_hash = '%s' AND uid = %d LIMIT 1", + dbesc($hash), + intval($arr['uid']) + ); + if ($r) { + return $r[0]; + } - return false; + return false; } -function event_addtocal($item_id, $uid) { +function event_addtocal($item_id, $uid) +{ - $c = q("select * from channel where channel_id = %d limit 1", - intval($uid) - ); + $c = q( + "select * from channel where channel_id = %d limit 1", + intval($uid) + ); - if(! $c) - return false; + if (! $c) { + return false; + } - $channel = $c[0]; + $channel = $c[0]; - $r = q("select * from item where id = %d and uid = %d limit 1", - intval($item_id), - intval($channel['channel_id']) - ); + $r = q( + "select * from item where id = %d and uid = %d limit 1", + intval($item_id), + intval($channel['channel_id']) + ); - if((! $r) || ($r[0]['obj_type'] !== ACTIVITY_OBJ_EVENT)) - return false; + if ((! $r) || ($r[0]['obj_type'] !== ACTIVITY_OBJ_EVENT)) { + return false; + } - $item = $r[0]; + $item = $r[0]; - $ev = bbtoevent($r[0]['body']); + $ev = bbtoevent($r[0]['body']); - if(x($ev,'summary') && x($ev,'dtstart')) { - $ev['event_xchan'] = $item['author_xchan']; - $ev['uid'] = $channel['channel_id']; - $ev['account'] = $channel['channel_account_id']; - $ev['edited'] = $item['edited']; - $ev['mid'] = $item['mid']; - $ev['private'] = $item['item_private']; + if (x($ev, 'summary') && x($ev, 'dtstart')) { + $ev['event_xchan'] = $item['author_xchan']; + $ev['uid'] = $channel['channel_id']; + $ev['account'] = $channel['channel_account_id']; + $ev['edited'] = $item['edited']; + $ev['mid'] = $item['mid']; + $ev['private'] = $item['item_private']; - // is this an edit? + // is this an edit? - if($item['resource_type'] === 'event' && (! $ev['event_hash'])) { - $ev['event_hash'] = $item['resource_id']; - } + if ($item['resource_type'] === 'event' && (! $ev['event_hash'])) { + $ev['event_hash'] = $item['resource_id']; + } - if($ev['private']) - $ev['allow_cid'] = '<' . $channel['channel_hash'] . '>'; - else { - $acl = new AccessControl($channel); - $x = $acl->get(); - $ev['allow_cid'] = $x['allow_cid']; - $ev['allow_gid'] = $x['allow_gid']; - $ev['deny_cid'] = $x['deny_cid']; - $ev['deny_gid'] = $x['deny_gid']; - } + if ($ev['private']) { + $ev['allow_cid'] = '<' . $channel['channel_hash'] . '>'; + } else { + $acl = new AccessControl($channel); + $x = $acl->get(); + $ev['allow_cid'] = $x['allow_cid']; + $ev['allow_gid'] = $x['allow_gid']; + $ev['deny_cid'] = $x['deny_cid']; + $ev['deny_gid'] = $x['deny_gid']; + } - $event = event_store_event($ev); - if($event) { - $r = q("update item set resource_id = '%s', resource_type = 'event' where id = %d and uid = %d", - dbesc($event['event_hash']), - intval($item['id']), - intval($channel['channel_id']) - ); + $event = event_store_event($ev); + if ($event) { + $r = q( + "update item set resource_id = '%s', resource_type = 'event' where id = %d and uid = %d", + dbesc($event['event_hash']), + intval($item['id']), + intval($channel['channel_id']) + ); - $item['resource_id'] = $event['event_hash']; - $item['resource_type'] = 'event'; + $item['resource_id'] = $event['event_hash']; + $item['resource_type'] = 'event'; - $i = array($item); - xchan_query($i); - $sync_item = fetch_post_tags($i); - $z = q("select * from event where event_hash = '%s' and uid = %d limit 1", - dbesc($event['event_hash']), - intval($channel['channel_id']) - ); - if($z) { - Libsync::build_sync_packet($channel['channel_id'],array('event_item' => array(encode_item($sync_item[0],true)),'event' => $z)); - } - return true; - } - } + $i = array($item); + xchan_query($i); + $sync_item = fetch_post_tags($i); + $z = q( + "select * from event where event_hash = '%s' and uid = %d limit 1", + dbesc($event['event_hash']), + intval($channel['channel_id']) + ); + if ($z) { + Libsync::build_sync_packet($channel['channel_id'], array('event_item' => array(encode_item($sync_item[0], true)),'event' => $z)); + } + return true; + } + } - return false; + return false; } -function ical_to_ev($s) { - require_once('vendor/autoload.php'); +function ical_to_ev($s) +{ + require_once('vendor/autoload.php'); - $saved_timezone = date_default_timezone_get(); - date_default_timezone_set('Australia/Sydney'); + $saved_timezone = date_default_timezone_get(); + date_default_timezone_set('Australia/Sydney'); - $ical = VObject\Reader::read($s); + $ical = VObject\Reader::read($s); - $ev = []; + $ev = []; - if($ical) { - if($ical->VEVENT) { - foreach($ical->VEVENT as $event) { - $ev[] = parse_vobject($event,'event'); - } - } - if($ical->VTODO) { - foreach($ical->VTODO as $event) { - $ev[] = parse_vobject($event,'task'); - } - } - } + if ($ical) { + if ($ical->VEVENT) { + foreach ($ical->VEVENT as $event) { + $ev[] = parse_vobject($event, 'event'); + } + } + if ($ical->VTODO) { + foreach ($ical->VTODO as $event) { + $ev[] = parse_vobject($event, 'task'); + } + } + } - date_default_timezone_set($saved_timezone); + date_default_timezone_set($saved_timezone); - return $ev; + return $ev; } -function parse_vobject($ical, $type) { +function parse_vobject($ical, $type) +{ - $ev = []; + $ev = []; - if(! isset($ical->DTSTART)) { - logger('no event start'); - return $ev; - } + if (! isset($ical->DTSTART)) { + logger('no event start'); + return $ev; + } - $ev['etype'] = $type; + $ev['etype'] = $type; - $dtstart = $ical->DTSTART->getDateTime(); - $ev['adjust'] = (($ical->DTSTART->isFloating()) ? 0 : 1); + $dtstart = $ical->DTSTART->getDateTime(); + $ev['adjust'] = (($ical->DTSTART->isFloating()) ? 0 : 1); - $ev['dtstart'] = datetime_convert((($ev['adjust']) ? 'UTC' : date_default_timezone_get()),'UTC', - $dtstart->format(\DateTime::W3C)); + $ev['dtstart'] = datetime_convert( + (($ev['adjust']) ? 'UTC' : date_default_timezone_get()), + 'UTC', + $dtstart->format(DateTime::W3C) + ); - if(isset($ical->DUE)) { - $dtend = $ical->DUE->getDateTime(); - $ev['dtend'] = datetime_convert((($ev['adjust']) ? 'UTC' : date_default_timezone_get()),'UTC', - $dtend->format(\DateTime::W3C)); - } - elseif(isset($ical->DTEND)) { - $dtend = $ical->DTEND->getDateTime(); - $ev['dtend'] = datetime_convert((($ev['adjust']) ? 'UTC' : date_default_timezone_get()),'UTC', - $dtend->format(\DateTime::W3C)); - } - else - $ev['nofinish'] = 1; + if (isset($ical->DUE)) { + $dtend = $ical->DUE->getDateTime(); + $ev['dtend'] = datetime_convert( + (($ev['adjust']) ? 'UTC' : date_default_timezone_get()), + 'UTC', + $dtend->format(DateTime::W3C) + ); + } elseif (isset($ical->DTEND)) { + $dtend = $ical->DTEND->getDateTime(); + $ev['dtend'] = datetime_convert( + (($ev['adjust']) ? 'UTC' : date_default_timezone_get()), + 'UTC', + $dtend->format(DateTime::W3C) + ); + } else { + $ev['nofinish'] = 1; + } - if($ev['dtstart'] === $ev['dtend']) - $ev['nofinish'] = 1; + if ($ev['dtstart'] === $ev['dtend']) { + $ev['nofinish'] = 1; + } - if(isset($ical->CREATED)) { - $created = $ical->CREATED->getDateTime(); - $ev['created'] = datetime_convert('UTC','UTC',$created->format(\DateTime::W3C)); - } + if (isset($ical->CREATED)) { + $created = $ical->CREATED->getDateTime(); + $ev['created'] = datetime_convert('UTC', 'UTC', $created->format(DateTime::W3C)); + } - if(isset($ical->{'DTSTAMP'})) { - $edited = $ical->{'DTSTAMP'}->getDateTime(); - $ev['edited'] = datetime_convert('UTC','UTC',$edited->format(\DateTime::W3C)); - } - if(isset($ical->{'LAST-MODIFIED'})) { - $edited = $ical->{'LAST-MODIFIED'}->getDateTime(); - $ev['edited'] = datetime_convert('UTC','UTC',$edited->format(\DateTime::W3C)); - } + if (isset($ical->{'DTSTAMP'})) { + $edited = $ical->{'DTSTAMP'}->getDateTime(); + $ev['edited'] = datetime_convert('UTC', 'UTC', $edited->format(DateTime::W3C)); + } + if (isset($ical->{'LAST-MODIFIED'})) { + $edited = $ical->{'LAST-MODIFIED'}->getDateTime(); + $ev['edited'] = datetime_convert('UTC', 'UTC', $edited->format(DateTime::W3C)); + } - if(isset($ical->{'X-ZOT-LOCATION'})) - $ev['location'] = event_ical_get_sourcetext( (string) $ical->{'X-ZOT-LOCATION'}); - elseif(isset($ical->LOCATION)) - $ev['location'] = (string) $ical->LOCATION; + if (isset($ical->{'X-ZOT-LOCATION'})) { + $ev['location'] = event_ical_get_sourcetext((string) $ical->{'X-ZOT-LOCATION'}); + } elseif (isset($ical->LOCATION)) { + $ev['location'] = (string) $ical->LOCATION; + } - if(isset($ical->{'X-ZOT-DESCRIPTION'})) - $ev['description'] = event_ical_get_sourcetext( (string) $ical->{'X-ZOT-DESCRIPTION'}); - elseif(isset($ical->DESCRIPTION)) - $ev['description'] = (string) $ical->DESCRIPTION; + if (isset($ical->{'X-ZOT-DESCRIPTION'})) { + $ev['description'] = event_ical_get_sourcetext((string) $ical->{'X-ZOT-DESCRIPTION'}); + } elseif (isset($ical->DESCRIPTION)) { + $ev['description'] = (string) $ical->DESCRIPTION; + } - if(isset($ical->{'X-ZOT-SUMMARY'})) - $ev['summary'] = event_ical_get_sourcetext( (string) $ical->{'X-ZOT-SUMMARY'}); - elseif(isset($ical->SUMMARY)) - $ev['summary'] = (string) $ical->SUMMARY; + if (isset($ical->{'X-ZOT-SUMMARY'})) { + $ev['summary'] = event_ical_get_sourcetext((string) $ical->{'X-ZOT-SUMMARY'}); + } elseif (isset($ical->SUMMARY)) { + $ev['summary'] = (string) $ical->SUMMARY; + } - if(isset($ical->PRIORITY)) - $ev['event_priority'] = intval((string) $ical->PRIORITY); + if (isset($ical->PRIORITY)) { + $ev['event_priority'] = intval((string) $ical->PRIORITY); + } - if(isset($ical->UID)) { - $evuid = (string) $ical->UID; - $ev['event_hash'] = $evuid; - } + if (isset($ical->UID)) { + $evuid = (string) $ical->UID; + $ev['event_hash'] = $evuid; + } - if(isset($ical->SEQUENCE)) { - $ev['event_sequence'] = (string) $ical->SEQUENCE; - } + if (isset($ical->SEQUENCE)) { + $ev['event_sequence'] = (string) $ical->SEQUENCE; + } - if(isset($ical->STATUS)) { - $ev['event_status'] = (string) $ical->STATUS; - } + if (isset($ical->STATUS)) { + $ev['event_status'] = (string) $ical->STATUS; + } - if(isset($ical->{'COMPLETED'})) { - $completed = $ical->{'COMPLETED'}->getDateTime(); - $ev['event_status_date'] = datetime_convert('UTC','UTC',$completed->format(\DateTime::W3C)); - } + if (isset($ical->{'COMPLETED'})) { + $completed = $ical->{'COMPLETED'}->getDateTime(); + $ev['event_status_date'] = datetime_convert('UTC', 'UTC', $completed->format(DateTime::W3C)); + } - if(isset($ical->{'PERCENT-COMPLETE'})) { - $ev['event_percent'] = (string) $ical->{'PERCENT-COMPLETE'} ; - } + if (isset($ical->{'PERCENT-COMPLETE'})) { + $ev['event_percent'] = (string) $ical->{'PERCENT-COMPLETE'} ; + } - $ev['event_vdata'] = $ical->serialize(); + $ev['event_vdata'] = $ical->serialize(); - return $ev; + return $ev; } -function parse_ical_file($f,$uid) { - require_once('vendor/autoload.php'); +function parse_ical_file($f, $uid) +{ + require_once('vendor/autoload.php'); - $s = @file_get_contents($f); + $s = @file_get_contents($f); - $ical = VObject\Reader::read($s); + $ical = VObject\Reader::read($s); - if($ical) { - if($ical->VEVENT) { - foreach($ical->VEVENT as $event) { - event_import_ical($event,$uid); - } - } - if($ical->VTODO) { - foreach($ical->VTODO as $event) { - event_import_ical_task($event,$uid); - } - } - } + if ($ical) { + if ($ical->VEVENT) { + foreach ($ical->VEVENT as $event) { + event_import_ical($event, $uid); + } + } + if ($ical->VTODO) { + foreach ($ical->VTODO as $event) { + event_import_ical_task($event, $uid); + } + } + } - if($ical) - return true; + if ($ical) { + return true; + } - return false; + return false; } -function event_import_ical($ical, $uid) { +function event_import_ical($ical, $uid) +{ - $c = q("select * from channel where channel_id = %d limit 1", - intval($uid) - ); + $c = q( + "select * from channel where channel_id = %d limit 1", + intval($uid) + ); - if(! $c) - return false; + if (! $c) { + return false; + } - $channel = $c[0]; - $ev = []; + $channel = $c[0]; + $ev = []; - if(! isset($ical->DTSTART)) { - logger('no event start'); - return false; - } + if (! isset($ical->DTSTART)) { + logger('no event start'); + return false; + } - $dtstart = $ical->DTSTART->getDateTime(); - $ev['adjust'] = (($ical->DTSTART->isFloating()) ? 0 : 1); + $dtstart = $ical->DTSTART->getDateTime(); + $ev['adjust'] = (($ical->DTSTART->isFloating()) ? 0 : 1); -// logger('dtstart: ' . var_export($dtstart,true)); +// logger('dtstart: ' . var_export($dtstart,true)); - $ev['dtstart'] = datetime_convert((($ev['adjust']) ? 'UTC' : date_default_timezone_get()),'UTC', - $dtstart->format(\DateTime::W3C)); + $ev['dtstart'] = datetime_convert( + (($ev['adjust']) ? 'UTC' : date_default_timezone_get()), + 'UTC', + $dtstart->format(DateTime::W3C) + ); - if(isset($ical->DTEND)) { - $dtend = $ical->DTEND->getDateTime(); - $ev['dtend'] = datetime_convert((($ev['adjust']) ? 'UTC' : date_default_timezone_get()),'UTC', - $dtend->format(\DateTime::W3C)); - } - else { - $ev['nofinish'] = 1; - } + if (isset($ical->DTEND)) { + $dtend = $ical->DTEND->getDateTime(); + $ev['dtend'] = datetime_convert( + (($ev['adjust']) ? 'UTC' : date_default_timezone_get()), + 'UTC', + $dtend->format(DateTime::W3C) + ); + } else { + $ev['nofinish'] = 1; + } - if($ev['dtstart'] === $ev['dtend']) - $ev['nofinish'] = 1; + if ($ev['dtstart'] === $ev['dtend']) { + $ev['nofinish'] = 1; + } - if(isset($ical->CREATED)) { - $created = $ical->CREATED->getDateTime(); - $ev['created'] = datetime_convert('UTC','UTC',$created->format(\DateTime::W3C)); - } + if (isset($ical->CREATED)) { + $created = $ical->CREATED->getDateTime(); + $ev['created'] = datetime_convert('UTC', 'UTC', $created->format(DateTime::W3C)); + } - if(isset($ical->{'LAST-MODIFIED'})) { - $edited = $ical->{'LAST-MODIFIED'}->getDateTime(); - $ev['edited'] = datetime_convert('UTC','UTC',$edited->format(\DateTime::W3C)); - } + if (isset($ical->{'LAST-MODIFIED'})) { + $edited = $ical->{'LAST-MODIFIED'}->getDateTime(); + $ev['edited'] = datetime_convert('UTC', 'UTC', $edited->format(DateTime::W3C)); + } - if(isset($ical->{'X-ZOT-LOCATION'})) - $ev['location'] = event_ical_get_sourcetext( (string) $ical->{'X-ZOT-LOCATION'}); - elseif(isset($ical->LOCATION)) - $ev['location'] = (string) $ical->LOCATION; + if (isset($ical->{'X-ZOT-LOCATION'})) { + $ev['location'] = event_ical_get_sourcetext((string) $ical->{'X-ZOT-LOCATION'}); + } elseif (isset($ical->LOCATION)) { + $ev['location'] = (string) $ical->LOCATION; + } - if(isset($ical->{'X-ZOT-DESCRIPTION'})) - $ev['description'] = event_ical_get_sourcetext( (string) $ical->{'X-ZOT-DESCRIPTION'}); - elseif(isset($ical->DESCRIPTION)) - $ev['description'] = (string) $ical->DESCRIPTION; + if (isset($ical->{'X-ZOT-DESCRIPTION'})) { + $ev['description'] = event_ical_get_sourcetext((string) $ical->{'X-ZOT-DESCRIPTION'}); + } elseif (isset($ical->DESCRIPTION)) { + $ev['description'] = (string) $ical->DESCRIPTION; + } - if(isset($ical->{'X-ZOT-SUMMARY'})) - $ev['summary'] = event_ical_get_sourcetext( (string) $ical->{'X-ZOT-SUMMARY'}); - elseif(isset($ical->SUMMARY)) - $ev['summary'] = (string) $ical->SUMMARY; + if (isset($ical->{'X-ZOT-SUMMARY'})) { + $ev['summary'] = event_ical_get_sourcetext((string) $ical->{'X-ZOT-SUMMARY'}); + } elseif (isset($ical->SUMMARY)) { + $ev['summary'] = (string) $ical->SUMMARY; + } - if(isset($ical->PRIORITY)) - $ev['event_priority'] = intval((string) $ical->PRIORITY); + if (isset($ical->PRIORITY)) { + $ev['event_priority'] = intval((string) $ical->PRIORITY); + } - if(isset($ical->UID)) { - $evuid = (string) $ical->UID; - $r = q("SELECT * FROM event WHERE event_hash = '%s' AND uid = %d LIMIT 1", - dbesc($evuid), - intval($uid) - ); - if($r) - $ev['event_hash'] = $evuid; - else - $ev['external_id'] = $evuid; - } + if (isset($ical->UID)) { + $evuid = (string) $ical->UID; + $r = q( + "SELECT * FROM event WHERE event_hash = '%s' AND uid = %d LIMIT 1", + dbesc($evuid), + intval($uid) + ); + if ($r) { + $ev['event_hash'] = $evuid; + } else { + $ev['external_id'] = $evuid; + } + } - if($ev['summary'] && $ev['dtstart']) { - $ev['event_xchan'] = $channel['channel_hash']; - $ev['uid'] = $channel['channel_id']; - $ev['account'] = $channel['channel_account_id']; - $ev['private'] = 1; - $ev['allow_cid'] = '<' . $channel['channel_hash'] . '>'; + if ($ev['summary'] && $ev['dtstart']) { + $ev['event_xchan'] = $channel['channel_hash']; + $ev['uid'] = $channel['channel_id']; + $ev['account'] = $channel['channel_account_id']; + $ev['private'] = 1; + $ev['allow_cid'] = '<' . $channel['channel_hash'] . '>'; - logger('storing event: ' . print_r($ev,true), LOGGER_ALL); - $event = event_store_event($ev); - if($event) { - $item_id = event_store_item($ev,$event); - return true; - } - } + logger('storing event: ' . print_r($ev, true), LOGGER_ALL); + $event = event_store_event($ev); + if ($event) { + $item_id = event_store_item($ev, $event); + return true; + } + } - return false; + return false; } -function event_ical_get_sourcetext($s) { - return base64_decode($s); +function event_ical_get_sourcetext($s) +{ + return base64_decode($s); } -function event_import_ical_task($ical, $uid) { +function event_import_ical_task($ical, $uid) +{ - $c = q("select * from channel where channel_id = %d limit 1", - intval($uid) - ); + $c = q( + "select * from channel where channel_id = %d limit 1", + intval($uid) + ); - if(! $c) - return false; + if (! $c) { + return false; + } - $channel = $c[0]; - $ev = []; + $channel = $c[0]; + $ev = []; - if(! isset($ical->DTSTART)) { - logger('no event start'); - return false; - } + if (! isset($ical->DTSTART)) { + logger('no event start'); + return false; + } - $dtstart = $ical->DTSTART->getDateTime(); + $dtstart = $ical->DTSTART->getDateTime(); - $ev['adjust'] = (($ical->DTSTART->isFloating()) ? 0 : 1); + $ev['adjust'] = (($ical->DTSTART->isFloating()) ? 0 : 1); -// logger('dtstart: ' . var_export($dtstart,true)); +// logger('dtstart: ' . var_export($dtstart,true)); - $ev['dtstart'] = datetime_convert((($ev['adjust']) ? 'UTC' : date_default_timezone_get()),'UTC', - $dtstart->format(\DateTime::W3C)); + $ev['dtstart'] = datetime_convert( + (($ev['adjust']) ? 'UTC' : date_default_timezone_get()), + 'UTC', + $dtstart->format(DateTime::W3C) + ); - if(isset($ical->DUE)) { - $dtend = $ical->DUE->getDateTime(); - $ev['dtend'] = datetime_convert((($ev['adjust']) ? 'UTC' : date_default_timezone_get()),'UTC', - $dtend->format(\DateTime::W3C)); - } - else - $ev['nofinish'] = 1; + if (isset($ical->DUE)) { + $dtend = $ical->DUE->getDateTime(); + $ev['dtend'] = datetime_convert( + (($ev['adjust']) ? 'UTC' : date_default_timezone_get()), + 'UTC', + $dtend->format(DateTime::W3C) + ); + } else { + $ev['nofinish'] = 1; + } - if($ev['dtstart'] === $ev['dtend']) - $ev['nofinish'] = 1; + if ($ev['dtstart'] === $ev['dtend']) { + $ev['nofinish'] = 1; + } - if(isset($ical->CREATED)) { - $created = $ical->CREATED->getDateTime(); - $ev['created'] = datetime_convert('UTC','UTC',$created->format(\DateTime::W3C)); - } + if (isset($ical->CREATED)) { + $created = $ical->CREATED->getDateTime(); + $ev['created'] = datetime_convert('UTC', 'UTC', $created->format(DateTime::W3C)); + } - if(isset($ical->{'DTSTAMP'})) { - $edited = $ical->{'DTSTAMP'}->getDateTime(); - $ev['edited'] = datetime_convert('UTC','UTC',$edited->format(\DateTime::W3C)); - } + if (isset($ical->{'DTSTAMP'})) { + $edited = $ical->{'DTSTAMP'}->getDateTime(); + $ev['edited'] = datetime_convert('UTC', 'UTC', $edited->format(DateTime::W3C)); + } - if(isset($ical->{'LAST-MODIFIED'})) { - $edited = $ical->{'LAST-MODIFIED'}->getDateTime(); - $ev['edited'] = datetime_convert('UTC','UTC',$edited->format(\DateTime::W3C)); - } + if (isset($ical->{'LAST-MODIFIED'})) { + $edited = $ical->{'LAST-MODIFIED'}->getDateTime(); + $ev['edited'] = datetime_convert('UTC', 'UTC', $edited->format(DateTime::W3C)); + } - if(isset($ical->{'X-ZOT-LOCATION'})) - $ev['location'] = event_ical_get_sourcetext( (string) $ical->{'X-ZOT-LOCATION'}); - elseif(isset($ical->LOCATION)) - $ev['location'] = (string) $ical->LOCATION; + if (isset($ical->{'X-ZOT-LOCATION'})) { + $ev['location'] = event_ical_get_sourcetext((string) $ical->{'X-ZOT-LOCATION'}); + } elseif (isset($ical->LOCATION)) { + $ev['location'] = (string) $ical->LOCATION; + } - if(isset($ical->{'X-ZOT-DESCRIPTION'})) - $ev['description'] = event_ical_get_sourcetext( (string) $ical->{'X-ZOT-DESCRIPTION'}); - elseif(isset($ical->DESCRIPTION)) - $ev['description'] = (string) $ical->DESCRIPTION; + if (isset($ical->{'X-ZOT-DESCRIPTION'})) { + $ev['description'] = event_ical_get_sourcetext((string) $ical->{'X-ZOT-DESCRIPTION'}); + } elseif (isset($ical->DESCRIPTION)) { + $ev['description'] = (string) $ical->DESCRIPTION; + } - if(isset($ical->{'X-ZOT-SUMMARY'})) - $ev['summary'] = event_ical_get_sourcetext( (string) $ical->{'X-ZOT-SUMMARY'}); - elseif(isset($ical->SUMMARY)) - $ev['summary'] = (string) $ical->SUMMARY; + if (isset($ical->{'X-ZOT-SUMMARY'})) { + $ev['summary'] = event_ical_get_sourcetext((string) $ical->{'X-ZOT-SUMMARY'}); + } elseif (isset($ical->SUMMARY)) { + $ev['summary'] = (string) $ical->SUMMARY; + } - if(isset($ical->PRIORITY)) - $ev['event_priority'] = intval((string) $ical->PRIORITY); + if (isset($ical->PRIORITY)) { + $ev['event_priority'] = intval((string) $ical->PRIORITY); + } - $stored_event = null; + $stored_event = null; - if(isset($ical->UID)) { - $evuid = (string) $ical->UID; - $r = q("SELECT * FROM event WHERE event_hash = '%s' AND uid = %d LIMIT 1", - dbesc($evuid), - intval($uid) - ); - if($r) { - $ev['event_hash'] = $evuid; - $stored_event = $r[0]; - } - else { - $ev['external_id'] = $evuid; - } - } + if (isset($ical->UID)) { + $evuid = (string) $ical->UID; + $r = q( + "SELECT * FROM event WHERE event_hash = '%s' AND uid = %d LIMIT 1", + dbesc($evuid), + intval($uid) + ); + if ($r) { + $ev['event_hash'] = $evuid; + $stored_event = $r[0]; + } else { + $ev['external_id'] = $evuid; + } + } - if(isset($ical->SEQUENCE)) { - $ev['event_sequence'] = (string) $ical->SEQUENCE; - // see if our stored event is more current than the one we're importing - if((intval($ev['event_sequence']) <= intval($stored_event['event_sequence'])) - && ($ev['edited'] <= $stored_event['edited'])) - return false; - } + if (isset($ical->SEQUENCE)) { + $ev['event_sequence'] = (string) $ical->SEQUENCE; + // see if our stored event is more current than the one we're importing + if ( + (intval($ev['event_sequence']) <= intval($stored_event['event_sequence'])) + && ($ev['edited'] <= $stored_event['edited']) + ) { + return false; + } + } - if(isset($ical->STATUS)) { - $ev['event_status'] = (string) $ical->STATUS; - } + if (isset($ical->STATUS)) { + $ev['event_status'] = (string) $ical->STATUS; + } - if(isset($ical->{'COMPLETED'})) { - $completed = $ical->{'COMPLETED'}->getDateTime(); - $ev['event_status_date'] = datetime_convert('UTC','UTC',$completed->format(\DateTime::W3C)); - } + if (isset($ical->{'COMPLETED'})) { + $completed = $ical->{'COMPLETED'}->getDateTime(); + $ev['event_status_date'] = datetime_convert('UTC', 'UTC', $completed->format(DateTime::W3C)); + } - if(isset($ical->{'PERCENT-COMPLETE'})) { - $ev['event_percent'] = (string) $ical->{'PERCENT-COMPLETE'} ; - } + if (isset($ical->{'PERCENT-COMPLETE'})) { + $ev['event_percent'] = (string) $ical->{'PERCENT-COMPLETE'} ; + } - $ev['etype'] = 'task'; + $ev['etype'] = 'task'; - if($ev['summary'] && $ev['dtstart']) { - $ev['event_xchan'] = $channel['channel_hash']; - $ev['uid'] = $channel['channel_id']; - $ev['account'] = $channel['channel_account_id']; - $ev['private'] = 1; - $ev['allow_cid'] = '<' . $channel['channel_hash'] . '>'; + if ($ev['summary'] && $ev['dtstart']) { + $ev['event_xchan'] = $channel['channel_hash']; + $ev['uid'] = $channel['channel_id']; + $ev['account'] = $channel['channel_account_id']; + $ev['private'] = 1; + $ev['allow_cid'] = '<' . $channel['channel_hash'] . '>'; - logger('storing event: ' . print_r($ev,true), LOGGER_ALL); - $event = event_store_event($ev); - if($event) { - $item_id = event_store_item($ev,$event); - return true; - } - } + logger('storing event: ' . print_r($ev, true), LOGGER_ALL); + $event = event_store_event($ev); + if ($event) { + $item_id = event_store_item($ev, $event); + return true; + } + } - return false; + return false; } +function event_store_item($arr, $event) +{ -function event_store_item($arr, $event) { + require_once('include/datetime.php'); + require_once('include/items.php'); - require_once('include/datetime.php'); - require_once('include/items.php'); + $item = null; - $item = null; - - if($arr['mid'] && $arr['uid']) { - $i = q("select * from item where mid = '%s' and uid = %d limit 1", - dbesc($arr['mid']), - intval($arr['uid']) - ); - if($i) { - xchan_query($i); - $item = fetch_post_tags($i,true); - } - } + if ($arr['mid'] && $arr['uid']) { + $i = q( + "select * from item where mid = '%s' and uid = %d limit 1", + dbesc($arr['mid']), + intval($arr['uid']) + ); + if ($i) { + xchan_query($i); + $item = fetch_post_tags($i, true); + } + } - $item_arr = []; - $prefix = ''; -// $birthday = false; + $item_arr = []; + $prefix = ''; +// $birthday = false; - if(($event) && array_key_exists('event_hash',$event) && (! array_key_exists('event_hash',$arr))) - $arr['event_hash'] = $event['event_hash']; + if (($event) && array_key_exists('event_hash', $event) && (! array_key_exists('event_hash', $arr))) { + $arr['event_hash'] = $event['event_hash']; + } - if($event['etype'] === 'birthday') { - if(! is_sys_channel($arr['uid'])) - $prefix = t('This event has been added to your calendar.'); -// $birthday = true; + if ($event['etype'] === 'birthday') { + if (! is_sys_channel($arr['uid'])) { + $prefix = t('This event has been added to your calendar.'); + } +// $birthday = true; - // The event is created on your own site by the system, but appears to belong - // to the birthday person. It also isn't propagated - so we need to prevent - // folks from trying to comment on it. If you're looking at this and trying to - // fix it, you'll need to completely change the way birthday events are created - // and send them out from the source. This has its own issues. + // The event is created on your own site by the system, but appears to belong + // to the birthday person. It also isn't propagated - so we need to prevent + // folks from trying to comment on it. If you're looking at this and trying to + // fix it, you'll need to completely change the way birthday events are created + // and send them out from the source. This has its own issues. - $item_arr['comment_policy'] = 'none'; - } + $item_arr['comment_policy'] = 'none'; + } - $r = q("SELECT * FROM item left join xchan on author_xchan = xchan_hash WHERE resource_id = '%s' AND resource_type = 'event' and uid = %d LIMIT 1", - dbesc($event['event_hash']), - intval($arr['uid']) - ); + $r = q( + "SELECT * FROM item left join xchan on author_xchan = xchan_hash WHERE resource_id = '%s' AND resource_type = 'event' and uid = %d LIMIT 1", + dbesc($event['event_hash']), + intval($arr['uid']) + ); - if($r) { - $x = [ - 'type' => 'Event', - 'id' => z_root() . '/event/' . $r[0]['resource_id'], - 'name' => $arr['summary'], -// 'summary' => bbcode($arr['summary']), - // RFC3339 Section 4.3 - 'startTime' => (($arr['adjust']) ? datetime_convert('UTC','UTC',$arr['dtstart'], ATOM_TIME) : datetime_convert('UTC','UTC',$arr['dtstart'],'Y-m-d\\TH:i:s-00:00')), - 'content' => bbcode($arr['description']), - 'location' => [ 'type' => 'Place', 'content' => $arr['location'] ], - 'source' => [ 'content' => format_event_bbcode($arr), 'mediaType' => 'text/x-multicode' ], - 'url' => [ [ 'mediaType' => 'text/calendar', 'href' => z_root() . '/events/ical/' . $event['event_hash'] ] ], - 'actor' => Activity::encode_person($r[0],false), - 'attachment' => Activity::encode_attachment($r[0]), - 'tag' => Activity::encode_taxonomy($r[0]) - ]; + if ($r) { + $x = [ + 'type' => 'Event', + 'id' => z_root() . '/event/' . $r[0]['resource_id'], + 'name' => $arr['summary'], +// 'summary' => bbcode($arr['summary']), + // RFC3339 Section 4.3 + 'startTime' => (($arr['adjust']) ? datetime_convert('UTC', 'UTC', $arr['dtstart'], ATOM_TIME) : datetime_convert('UTC', 'UTC', $arr['dtstart'], 'Y-m-d\\TH:i:s-00:00')), + 'content' => bbcode($arr['description']), + 'location' => [ 'type' => 'Place', 'content' => $arr['location'] ], + 'source' => [ 'content' => format_event_bbcode($arr), 'mediaType' => 'text/x-multicode' ], + 'url' => [ [ 'mediaType' => 'text/calendar', 'href' => z_root() . '/events/ical/' . $event['event_hash'] ] ], + 'actor' => Activity::encode_person($r[0], false), + 'attachment' => Activity::encode_attachment($r[0]), + 'tag' => Activity::encode_taxonomy($r[0]) + ]; - if(! $arr['nofinish']) { - $x['endTime'] = (($arr['adjust']) ? datetime_convert('UTC','UTC',$arr['dtend'], ATOM_TIME) : datetime_convert('UTC','UTC',$arr['dtend'],'Y-m-d\\TH:i:s-00:00')); - } - if($event['event_repeat']) { - $x['eventRepeat'] = $event['event_repeat']; - } - $object = json_encode($x); + if (! $arr['nofinish']) { + $x['endTime'] = (($arr['adjust']) ? datetime_convert('UTC', 'UTC', $arr['dtend'], ATOM_TIME) : datetime_convert('UTC', 'UTC', $arr['dtend'], 'Y-m-d\\TH:i:s-00:00')); + } + if ($event['event_repeat']) { + $x['eventRepeat'] = $event['event_repeat']; + } + $object = json_encode($x); - $private = (($arr['allow_cid'] || $arr['allow_gid'] || $arr['deny_cid'] || $arr['deny_gid']) ? 1 : 0); + $private = (($arr['allow_cid'] || $arr['allow_gid'] || $arr['deny_cid'] || $arr['deny_gid']) ? 1 : 0); - /** - * @FIXME can only update sig if we have the author's channel on this site - * Until fixed, set it to nothing so it won't give us signature errors. - */ - $sig = ''; + /** + * @FIXME can only update sig if we have the author's channel on this site + * Until fixed, set it to nothing so it won't give us signature errors. + */ + $sig = ''; - q("UPDATE item SET title = '%s', body = '%s', obj = '%s', allow_cid = '%s', allow_gid = '%s', deny_cid = '%s', deny_gid = '%s', edited = '%s', sig = '%s', item_flags = %d, item_private = %d, obj_type = '%s' WHERE id = %d AND uid = %d", - dbesc($arr['summary']), - dbesc($prefix . format_event_bbcode($arr)), - dbesc($object), - dbesc($arr['allow_cid']), - dbesc($arr['allow_gid']), - dbesc($arr['deny_cid']), - dbesc($arr['deny_gid']), - dbesc($arr['edited']), - dbesc($sig), - intval($r[0]['item_flags']), - intval($private), - dbesc(ACTIVITY_OBJ_EVENT), - intval($r[0]['id']), - intval($arr['uid']) - ); + q( + "UPDATE item SET title = '%s', body = '%s', obj = '%s', allow_cid = '%s', allow_gid = '%s', deny_cid = '%s', deny_gid = '%s', edited = '%s', sig = '%s', item_flags = %d, item_private = %d, obj_type = '%s' WHERE id = %d AND uid = %d", + dbesc($arr['summary']), + dbesc($prefix . format_event_bbcode($arr)), + dbesc($object), + dbesc($arr['allow_cid']), + dbesc($arr['allow_gid']), + dbesc($arr['deny_cid']), + dbesc($arr['deny_gid']), + dbesc($arr['edited']), + dbesc($sig), + intval($r[0]['item_flags']), + intval($private), + dbesc(ACTIVITY_OBJ_EVENT), + intval($r[0]['id']), + intval($arr['uid']) + ); - q("delete from term where oid = %d and otype = %d", - intval($r[0]['id']), - intval(TERM_OBJ_POST) - ); + q( + "delete from term where oid = %d and otype = %d", + intval($r[0]['id']), + intval(TERM_OBJ_POST) + ); - if(($arr['term']) && (is_array($arr['term']))) { - foreach($arr['term'] as $t) { - q("insert into term (uid,oid,otype,ttype,term,url) + if (($arr['term']) && (is_array($arr['term']))) { + foreach ($arr['term'] as $t) { + q( + "insert into term (uid,oid,otype,ttype,term,url) values(%d,%d,%d,%d,'%s','%s') ", - intval($arr['uid']), - intval($r[0]['id']), - intval(TERM_OBJ_POST), - intval($t['ttype']), - dbesc($t['term']), - dbesc($t['url']) - ); - } - } + intval($arr['uid']), + intval($r[0]['id']), + intval(TERM_OBJ_POST), + intval($t['ttype']), + dbesc($t['term']), + dbesc($t['url']) + ); + } + } - $item_id = $r[0]['id']; - /** - * @hooks event_updated - * Called when an event record is modified. - */ - call_hooks('event_updated', $event['id']); + $item_id = $r[0]['id']; + /** + * @hooks event_updated + * Called when an event record is modified. + */ + call_hooks('event_updated', $event['id']); - return $item_id; - } - else { + return $item_id; + } else { + $z = channelx_by_n($arr['uid']); - $z = channelx_by_n($arr['uid']); + $private = (($arr['allow_cid'] || $arr['allow_gid'] || $arr['deny_cid'] || $arr['deny_gid']) ? 1 : 0); - $private = (($arr['allow_cid'] || $arr['allow_gid'] || $arr['deny_cid'] || $arr['deny_gid']) ? 1 : 0); + $item_wall = 0; + $item_origin = 0; + $item_thread_top = 0; - $item_wall = 0; - $item_origin = 0; - $item_thread_top = 0; + if ($item) { + $item_arr['id'] = $item['id']; + } else { + $wall = (($z['channel_hash'] == $event['event_xchan']) ? true : false); + $item_thread_top = 1; + if ($wall) { + $item_wall = 1; + $item_origin = 1; + } + } - if($item) { - $item_arr['id'] = $item['id']; - } - else { - $wall = (($z['channel_hash'] == $event['event_xchan']) ? true : false); - $item_thread_top = 1; - if($wall) { - $item_wall = 1; - $item_origin = 1; - } - } + if (! $arr['mid']) { + $arr['mid'] = z_root() . '/activity/' . $event['event_hash']; + } - if (! $arr['mid']) { - $arr['mid'] = z_root() . '/activity/' . $event['event_hash']; - } - - $item_arr['aid'] = $z['channel_account_id']; - $item_arr['uid'] = $arr['uid']; - $item_arr['author_xchan'] = $arr['event_xchan']; - $item_arr['mid'] = $arr['mid']; - $item_arr['parent_mid'] = $arr['mid']; - $item_arr['uuid'] = $event['event_hash']; - $item_arr['owner_xchan'] = (($wall) ? $z['channel_hash'] : $arr['event_xchan']); - $item_arr['author_xchan'] = $arr['event_xchan']; - $item_arr['summary'] = $arr['summary']; - $item_arr['allow_cid'] = $arr['allow_cid']; - $item_arr['allow_gid'] = $arr['allow_gid']; - $item_arr['deny_cid'] = $arr['deny_cid']; - $item_arr['deny_gid'] = $arr['deny_gid']; - $item_arr['item_private'] = $private; - $item_arr['verb'] = 'Invite'; - $item_arr['item_wall'] = $item_wall; - $item_arr['item_origin'] = $item_origin; - $item_arr['item_thread_top'] = $item_thread_top; + $item_arr['aid'] = $z['channel_account_id']; + $item_arr['uid'] = $arr['uid']; + $item_arr['author_xchan'] = $arr['event_xchan']; + $item_arr['mid'] = $arr['mid']; + $item_arr['parent_mid'] = $arr['mid']; + $item_arr['uuid'] = $event['event_hash']; + $item_arr['owner_xchan'] = (($wall) ? $z['channel_hash'] : $arr['event_xchan']); + $item_arr['author_xchan'] = $arr['event_xchan']; + $item_arr['summary'] = $arr['summary']; + $item_arr['allow_cid'] = $arr['allow_cid']; + $item_arr['allow_gid'] = $arr['allow_gid']; + $item_arr['deny_cid'] = $arr['deny_cid']; + $item_arr['deny_gid'] = $arr['deny_gid']; + $item_arr['item_private'] = $private; + $item_arr['verb'] = 'Invite'; + $item_arr['item_wall'] = $item_wall; + $item_arr['item_origin'] = $item_origin; + $item_arr['item_thread_top'] = $item_thread_top; - $attach = [ - [ - 'href' => z_root() . '/calendar/ical/' . urlencode($event['event_hash']), - 'length' => 0, - 'type' => 'text/calendar', - 'title' => t('event') . '-' . $event['event_hash'], - 'revision' => '' - ] - ]; + $attach = [ + [ + 'href' => z_root() . '/calendar/ical/' . urlencode($event['event_hash']), + 'length' => 0, + 'type' => 'text/calendar', + 'title' => t('event') . '-' . $event['event_hash'], + 'revision' => '' + ] + ]; - $item_arr['attach'] = $attach; + $item_arr['attach'] = $attach; - if(array_key_exists('term', $arr)) - $item_arr['term'] = $arr['term']; + if (array_key_exists('term', $arr)) { + $item_arr['term'] = $arr['term']; + } - $item_arr['resource_type'] = 'event'; - $item_arr['resource_id'] = $event['event_hash']; - $item_arr['obj_type'] = ACTIVITY_OBJ_EVENT; - $item_arr['body'] = $prefix . format_event_bbcode($arr); + $item_arr['resource_type'] = 'event'; + $item_arr['resource_id'] = $event['event_hash']; + $item_arr['obj_type'] = ACTIVITY_OBJ_EVENT; + $item_arr['body'] = $prefix . format_event_bbcode($arr); - // if it's local send the permalink to the channel page. - // otherwise we'll fallback to /display/$message_id + // if it's local send the permalink to the channel page. + // otherwise we'll fallback to /display/$message_id - if($wall) - $item_arr['plink'] = z_root() . '/channel/' . $z['channel_address'] . '/?f=&mid=' . gen_link_id($item_arr['mid']); - else - $item_arr['plink'] = z_root() . '/display/' . gen_link_id($item_arr['mid']); + if ($wall) { + $item_arr['plink'] = z_root() . '/channel/' . $z['channel_address'] . '/?f=&mid=' . gen_link_id($item_arr['mid']); + } else { + $item_arr['plink'] = z_root() . '/display/' . gen_link_id($item_arr['mid']); + } - $x = q("select * from xchan where xchan_hash = '%s' limit 1", - dbesc($arr['event_xchan']) - ); - if($x) { - $y = [ - 'type' => 'Event', - 'id' => z_root() . '/event/' . $event['event_hash'], - 'name' => $arr['summary'], -// 'summary' => bbcode($arr['summary']), - // RFC3339 Section 4.3 - 'startTime' => (($arr['adjust']) ? datetime_convert('UTC','UTC',$arr['dtstart'], ATOM_TIME) : datetime_convert('UTC','UTC',$arr['dtstart'],'Y-m-d\\TH:i:s-00:00')), - 'content' => bbcode($arr['description']), - 'location' => [ 'type' => 'Place', 'content' => bbcode($arr['location']) ], - 'source' => [ 'content' => format_event_bbcode($arr), 'mediaType' => 'text/x-multicode' ], - 'url' => [ [ 'mediaType' => 'text/calendar', 'href' => z_root() . '/events/ical/' . $event['event_hash'] ] ], - 'actor' => Activity::encode_person($z,false), - 'attachment' => Activity::encode_attachment($item_arr), - 'tag' => Activity::encode_taxonomy($item_arr) - ]; + $x = q( + "select * from xchan where xchan_hash = '%s' limit 1", + dbesc($arr['event_xchan']) + ); + if ($x) { + $y = [ + 'type' => 'Event', + 'id' => z_root() . '/event/' . $event['event_hash'], + 'name' => $arr['summary'], +// 'summary' => bbcode($arr['summary']), + // RFC3339 Section 4.3 + 'startTime' => (($arr['adjust']) ? datetime_convert('UTC', 'UTC', $arr['dtstart'], ATOM_TIME) : datetime_convert('UTC', 'UTC', $arr['dtstart'], 'Y-m-d\\TH:i:s-00:00')), + 'content' => bbcode($arr['description']), + 'location' => [ 'type' => 'Place', 'content' => bbcode($arr['location']) ], + 'source' => [ 'content' => format_event_bbcode($arr), 'mediaType' => 'text/x-multicode' ], + 'url' => [ [ 'mediaType' => 'text/calendar', 'href' => z_root() . '/events/ical/' . $event['event_hash'] ] ], + 'actor' => Activity::encode_person($z, false), + 'attachment' => Activity::encode_attachment($item_arr), + 'tag' => Activity::encode_taxonomy($item_arr) + ]; - if(! $arr['nofinish']) { - $y['endTime'] = (($arr['adjust']) ? datetime_convert('UTC','UTC',$arr['dtend'], ATOM_TIME) : datetime_convert('UTC','UTC',$arr['dtend'],'Y-m-d\\TH:i:s-00:00')); - } - if($arr['event_repeat']) { - $y['eventRepeat'] = $arr['event_repeat']; - } + if (! $arr['nofinish']) { + $y['endTime'] = (($arr['adjust']) ? datetime_convert('UTC', 'UTC', $arr['dtend'], ATOM_TIME) : datetime_convert('UTC', 'UTC', $arr['dtend'], 'Y-m-d\\TH:i:s-00:00')); + } + if ($arr['event_repeat']) { + $y['eventRepeat'] = $arr['event_repeat']; + } - $item_arr['obj'] = json_encode($y); - } + $item_arr['obj'] = json_encode($y); + } - // propagate the event resource_id so that posts containing it are easily searchable in downstream copies - // of the item which have not stored the actual event. Required for Diaspora event federation as Diaspora - // event_participation messages refer to the event resource_id as a parent, while out own event attendance - // activities refer to the item message_id as the parent. + // propagate the event resource_id so that posts containing it are easily searchable in downstream copies + // of the item which have not stored the actual event. Required for Diaspora event federation as Diaspora + // event_participation messages refer to the event resource_id as a parent, while out own event attendance + // activities refer to the item message_id as the parent. - set_iconfig($item_arr, 'system','event_id',$event['event_hash'],true); + set_iconfig($item_arr, 'system', 'event_id', $event['event_hash'], true); - $res = item_store($item_arr); + $res = item_store($item_arr); - $item_id = $res['item_id']; + $item_id = $res['item_id']; - /** - * @hooks event_created - * Called when an event record is created. - */ - call_hooks('event_created', $event['id']); + /** + * @hooks event_created + * Called when an event record is created. + */ + call_hooks('event_created', $event['id']); - return $item_id; - } + return $item_id; + } } -function todo_stat() { - return array( - '' => t('Not specified'), - 'NEEDS-ACTION' => t('Needs Action'), - 'COMPLETED' => t('Completed'), - 'IN-PROCESS' => t('In Process'), - 'CANCELLED' => t('Cancelled') - ); +function todo_stat() +{ + return array( + '' => t('Not specified'), + 'NEEDS-ACTION' => t('Needs Action'), + 'COMPLETED' => t('Completed'), + 'IN-PROCESS' => t('In Process'), + 'CANCELLED' => t('Cancelled') + ); } -function tasks_fetch($arr) { +function tasks_fetch($arr) +{ - if(! local_channel()) - return; + if (! local_channel()) { + return; + } - $ret = []; - $sql_extra = " and event_status != 'COMPLETED' "; - if($arr && $arr['all'] == 1) - $sql_extra = ''; + $ret = []; + $sql_extra = " and event_status != 'COMPLETED' "; + if ($arr && $arr['all'] == 1) { + $sql_extra = ''; + } - $r = q("select * from event where etype = 'task' and uid = %d $sql_extra order by created desc", - intval(local_channel()) - ); + $r = q( + "select * from event where etype = 'task' and uid = %d $sql_extra order by created desc", + intval(local_channel()) + ); - $ret['success'] = (($r) ? true : false); - if($r) { - $ret['tasks'] = $r; - } + $ret['success'] = (($r) ? true : false); + if ($r) { + $ret['tasks'] = $r; + } - return $ret; + return $ret; } -function cdav_principal($uri) { - $r = q("SELECT uri FROM principals WHERE uri = '%s' LIMIT 1", - dbesc($uri) - ); +function cdav_principal($uri) +{ + $r = q( + "SELECT uri FROM principals WHERE uri = '%s' LIMIT 1", + dbesc($uri) + ); - if($r[0]['uri'] === $uri) - return true; - else - return false; + if ($r[0]['uri'] === $uri) { + return true; + } else { + return false; + } } -function cdav_perms($needle, $haystack, $check_rw = false) { +function cdav_perms($needle, $haystack, $check_rw = false) +{ - if ($needle === 'calendar') { - return true; - } - - foreach ($haystack as $item) { - if ($check_rw) { - if (is_array($item['id'])) { - if ($item['id'][0] == $needle && $item['share-access'] != 2) { - return $item['{DAV:}displayname']; - } - } - else { - if ($item['id'] == $needle && $item['share-access'] != 2) { - return $item['{DAV:}displayname']; - } - } - } - else { - if (is_array($item['id'])) { - if ($item['id'][0] == $needle) { - return $item['{DAV:}displayname']; - } - } - else { - if ($item['id'] == $needle) { - return $item['{DAV:}displayname']; - } - } - } - } - return false; + if ($needle === 'calendar') { + return true; + } + + foreach ($haystack as $item) { + if ($check_rw) { + if (is_array($item['id'])) { + if ($item['id'][0] == $needle && $item['share-access'] != 2) { + return $item['{DAV:}displayname']; + } + } else { + if ($item['id'] == $needle && $item['share-access'] != 2) { + return $item['{DAV:}displayname']; + } + } + } else { + if (is_array($item['id'])) { + if ($item['id'][0] == $needle) { + return $item['{DAV:}displayname']; + } + } else { + if ($item['id'] == $needle) { + return $item['{DAV:}displayname']; + } + } + } + } + return false; } -function translate_type($type) { +function translate_type($type) +{ - if(!$type) - return; + if (!$type) { + return; + } - $type = strtoupper($type); + $type = strtoupper($type); - $map = [ - 'CELL' => t('Mobile'), - 'HOME' => t('Home'), - 'HOME,VOICE' => t('Home, Voice'), - 'HOME,FAX' => t('Home, Fax'), - 'WORK' => t('Work'), - 'WORK,VOICE' => t('Work, Voice'), - 'WORK,FAX' => t('Work, Fax'), - 'OTHER' => t('Other') - ]; + $map = [ + 'CELL' => t('Mobile'), + 'HOME' => t('Home'), + 'HOME,VOICE' => t('Home, Voice'), + 'HOME,FAX' => t('Home, Fax'), + 'WORK' => t('Work'), + 'WORK,VOICE' => t('Work, Voice'), + 'WORK,FAX' => t('Work, Fax'), + 'OTHER' => t('Other') + ]; - if (array_key_exists($type, $map)) { - return [$type, $map[$type]]; - } - else { - return [$type, t('Other') . ' (' . $type . ')']; - } + if (array_key_exists($type, $map)) { + return [$type, $map[$type]]; + } else { + return [$type, t('Other') . ' (' . $type . ')']; + } } -function cal_store_lowlevel($arr) { +function cal_store_lowlevel($arr) +{ - $store = [ - 'cal_aid' => ((array_key_exists('cal_aid',$arr)) ? $arr['cal_aid'] : 0), - 'cal_uid' => ((array_key_exists('cal_uid',$arr)) ? $arr['cal_uid'] : 0), - 'cal_hash' => ((array_key_exists('cal_hash',$arr)) ? $arr['cal_hash'] : ''), - 'cal_name' => ((array_key_exists('cal_name',$arr)) ? $arr['cal_name'] : ''), - 'uri' => ((array_key_exists('uri',$arr)) ? $arr['uri'] : ''), - 'logname' => ((array_key_exists('logname',$arr)) ? $arr['logname'] : ''), - 'pass' => ((array_key_exists('pass',$arr)) ? $arr['pass'] : ''), - 'ctag' => ((array_key_exists('ctag',$arr)) ? $arr['ctag'] : ''), - 'synctoken' => ((array_key_exists('synctoken',$arr)) ? $arr['synctoken'] : ''), - 'cal_types' => ((array_key_exists('cal_types',$arr)) ? $arr['cal_types'] : ''), - ]; - - return create_table_from_array('cal', $store); + $store = [ + 'cal_aid' => ((array_key_exists('cal_aid', $arr)) ? $arr['cal_aid'] : 0), + 'cal_uid' => ((array_key_exists('cal_uid', $arr)) ? $arr['cal_uid'] : 0), + 'cal_hash' => ((array_key_exists('cal_hash', $arr)) ? $arr['cal_hash'] : ''), + 'cal_name' => ((array_key_exists('cal_name', $arr)) ? $arr['cal_name'] : ''), + 'uri' => ((array_key_exists('uri', $arr)) ? $arr['uri'] : ''), + 'logname' => ((array_key_exists('logname', $arr)) ? $arr['logname'] : ''), + 'pass' => ((array_key_exists('pass', $arr)) ? $arr['pass'] : ''), + 'ctag' => ((array_key_exists('ctag', $arr)) ? $arr['ctag'] : ''), + 'synctoken' => ((array_key_exists('synctoken', $arr)) ? $arr['synctoken'] : ''), + 'cal_types' => ((array_key_exists('cal_types', $arr)) ? $arr['cal_types'] : ''), + ]; + return create_table_from_array('cal', $store); } - - diff --git a/include/features.php b/include/features.php index 31ab84bf3..6514857d2 100644 --- a/include/features.php +++ b/include/features.php @@ -1,4 +1,6 @@ - $uid, 'feature' => $feature, 'enabled' => $x); - call_hooks('feature_enabled',$arr); - return($arr['enabled']); + $x = get_config('feature_lock', $feature); + if ($x === false) { + $x = get_pconfig($uid, 'feature', $feature); + if ($x === false) { + $x = get_config('feature', $feature); + if ($x === false) { + $x = get_feature_default($feature); + } + } + } + $arr = array('uid' => $uid, 'feature' => $feature, 'enabled' => $x); + call_hooks('feature_enabled', $arr); + return($arr['enabled']); } -function get_feature_default($feature) { - $f = get_features(false); - foreach($f as $cat) { - foreach($cat as $feat) { - if(is_array($feat) && $feat[0] === $feature) { - return $feat[3]; - } - } - } - return false; +function get_feature_default($feature) +{ + $f = get_features(false); + foreach ($f as $cat) { + foreach ($cat as $feat) { + if (is_array($feat) && $feat[0] === $feature) { + return $feat[3]; + } + } + } + return false; } -function feature_level($feature,$def) { - $x = get_config('feature_level',$feature); - if($x !== false) - return intval($x); - return $def; +function feature_level($feature, $def) +{ + $x = get_config('feature_level', $feature); + if ($x !== false) { + return intval($x); + } + return $def; } -function get_features($filtered = true, $level = (-1)) { +function get_features($filtered = true, $level = (-1)) +{ - $account = App::get_account(); + $account = App::get_account(); - $arr = [ + $arr = [ - // General - 'general' => [ + // General + 'general' => [ - t('General Features'), + t('General Features'), - [ - 'start_menu', - t('New Member Links'), - t('Display new member quick links menu'), - (($account && $account['account_created'] > datetime_convert('','','now - 30 days')) ? true : false), - get_config('feature_lock','start_menu'), - feature_level('start_menu',1), - ], + [ + 'start_menu', + t('New Member Links'), + t('Display new member quick links menu'), + (($account && $account['account_created'] > datetime_convert('', '', 'now - 30 days')) ? true : false), + get_config('feature_lock', 'start_menu'), + feature_level('start_menu', 1), + ], - [ - 'advanced_profiles', - t('Advanced Profiles'), - t('Additional profile sections and selections'), - false, - get_config('feature_lock','advanced_profiles'), - feature_level('advanced_profiles',1), - ], + [ + 'advanced_profiles', + t('Advanced Profiles'), + t('Additional profile sections and selections'), + false, + get_config('feature_lock', 'advanced_profiles'), + feature_level('advanced_profiles', 1), + ], -// [ -// 'profile_export', -// t('Profile Import/Export'), -// t('Save and load profile details across sites/channels'), -// false, -// get_config('feature_lock','profile_export'), -// feature_level('profile_export',3), -// ], +// [ +// 'profile_export', +// t('Profile Import/Export'), +// t('Save and load profile details across sites/channels'), +// false, +// get_config('feature_lock','profile_export'), +// feature_level('profile_export',3), +// ], -// [ -// 'webpages', -// t('Web Pages'), -// t('Provide managed web pages on your channel'), -// false, -// get_config('feature_lock','webpages'), -// feature_level('webpages',3), -// ], +// [ +// 'webpages', +// t('Web Pages'), +// t('Provide managed web pages on your channel'), +// false, +// get_config('feature_lock','webpages'), +// feature_level('webpages',3), +// ], -// [ -// 'wiki', -// t('Wiki'), -// t('Provide a wiki for your channel'), -// false, -// get_config('feature_lock','wiki'), -// feature_level('wiki',2), -// ], +// [ +// 'wiki', +// t('Wiki'), +// t('Provide a wiki for your channel'), +// false, +// get_config('feature_lock','wiki'), +// feature_level('wiki',2), +// ], /* - [ - 'hide_rating', - t('Hide Rating'), - t('Hide the rating buttons on your channel and profile pages. Note: People can still rate you somewhere else.'), - false, - get_config('feature_lock','hide_rating'), - feature_level('hide_rating',3), - ], -*/ - [ - 'private_notes', - t('Private Notes'), - t('Enables a tool to store notes and reminders (note: not encrypted)'), - false, - get_config('feature_lock','private_notes'), - feature_level('private_notes',1), - ], + [ + 'hide_rating', + t('Hide Rating'), + t('Hide the rating buttons on your channel and profile pages. Note: People can still rate you somewhere else.'), + false, + get_config('feature_lock','hide_rating'), + feature_level('hide_rating',3), + ], +*/ + [ + 'private_notes', + t('Private Notes'), + t('Enables a tool to store notes and reminders (note: not encrypted)'), + false, + get_config('feature_lock', 'private_notes'), + feature_level('private_notes', 1), + ], -// [ -// 'cards', -// t('Cards'), -// t('Create personal planning cards'), -// false, -// get_config('feature_lock','cards'), -// feature_level('cards',1), -// ], +// [ +// 'cards', +// t('Cards'), +// t('Create personal planning cards'), +// false, +// get_config('feature_lock','cards'), +// feature_level('cards',1), +// ], - [ - 'articles', - t('Articles'), - t('Create interactive articles'), - false, - get_config('feature_lock','articles'), - feature_level('articles',1), - ], + [ + 'articles', + t('Articles'), + t('Create interactive articles'), + false, + get_config('feature_lock', 'articles'), + feature_level('articles', 1), + ], -// [ -// 'nav_channel_select', -// t('Navigation Channel Select'), -// t('Change channels directly from within the navigation dropdown menu'), -// false, -// get_config('feature_lock','nav_channel_select'), -// feature_level('nav_channel_select',3), -// ], +// [ +// 'nav_channel_select', +// t('Navigation Channel Select'), +// t('Change channels directly from within the navigation dropdown menu'), +// false, +// get_config('feature_lock','nav_channel_select'), +// feature_level('nav_channel_select',3), +// ], - [ - 'photo_location', - t('Photo Location'), - t('If location data is available on uploaded photos, link this to a map.'), - false, - get_config('feature_lock','photo_location'), - feature_level('photo_location',2), - ], + [ + 'photo_location', + t('Photo Location'), + t('If location data is available on uploaded photos, link this to a map.'), + false, + get_config('feature_lock', 'photo_location'), + feature_level('photo_location', 2), + ], -// [ -// 'ajaxchat', -// t('Access Controlled Chatrooms'), -// t('Provide chatrooms and chat services with access control.'), -// true, -// get_config('feature_lock','ajaxchat'), -// feature_level('ajaxchat',1), -// ], +// [ +// 'ajaxchat', +// t('Access Controlled Chatrooms'), +// t('Provide chatrooms and chat services with access control.'), +// true, +// get_config('feature_lock','ajaxchat'), +// feature_level('ajaxchat',1), +// ], -// [ -// 'smart_birthdays', -// t('Smart Birthdays'), -// t('Make birthday events timezone aware in case your friends are scattered across the planet.'), -// true, -// get_config('feature_lock','smart_birthdays'), -// feature_level('smart_birthdays',2), -// ], +// [ +// 'smart_birthdays', +// t('Smart Birthdays'), +// t('Make birthday events timezone aware in case your friends are scattered across the planet.'), +// true, +// get_config('feature_lock','smart_birthdays'), +// feature_level('smart_birthdays',2), +// ], - [ - 'event_tz_select', - t('Event Timezone Selection'), - t('Allow event creation in timezones other than your own.'), - false, - get_config('feature_lock','event_tz_select'), - feature_level('event_tz_select',2), - ], + [ + 'event_tz_select', + t('Event Timezone Selection'), + t('Allow event creation in timezones other than your own.'), + false, + get_config('feature_lock', 'event_tz_select'), + feature_level('event_tz_select', 2), + ], -// [ -// 'premium_channel', -// t('Premium Channel'), -// t('Allows you to set restrictions and terms on those that connect with your channel'), -// false, -// get_config('feature_lock','premium_channel'), -// feature_level('premium_channel',4), -// ], +// [ +// 'premium_channel', +// t('Premium Channel'), +// t('Allows you to set restrictions and terms on those that connect with your channel'), +// false, +// get_config('feature_lock','premium_channel'), +// feature_level('premium_channel',4), +// ], - [ - 'advanced_dirsearch', - t('Advanced Directory Search'), - t('Allows creation of complex directory search queries'), - false, - get_config('feature_lock','advanced_dirsearch'), - feature_level('advanced_dirsearch',4), - ], + [ + 'advanced_dirsearch', + t('Advanced Directory Search'), + t('Allows creation of complex directory search queries'), + false, + get_config('feature_lock', 'advanced_dirsearch'), + feature_level('advanced_dirsearch', 4), + ], - [ - 'advanced_theming', - t('Advanced Theme and Layout Settings'), - t('Allows fine tuning of themes and page layouts'), - false, - get_config('feature_lock','advanced_theming'), - feature_level('advanced_theming',4), - ], - ], + [ + 'advanced_theming', + t('Advanced Theme and Layout Settings'), + t('Allows fine tuning of themes and page layouts'), + false, + get_config('feature_lock', 'advanced_theming'), + feature_level('advanced_theming', 4), + ], + ], - 'access_control' => [ - t('Access Control and Permissions'), + 'access_control' => [ + t('Access Control and Permissions'), - [ - 'groups', - t('Privacy Groups'), - t('Enable management and selection of privacy groups'), - false, - get_config('feature_lock','groups'), - feature_level('groups',0), - ], + [ + 'groups', + t('Privacy Groups'), + t('Enable management and selection of privacy groups'), + false, + get_config('feature_lock', 'groups'), + feature_level('groups', 0), + ], -// [ -// 'multi_profiles', -// t('Multiple Profiles'), -// t('Ability to create multiple profiles'), -// false, -// get_config('feature_lock','multi_profiles'), -// feature_level('multi_profiles',3), -// ], +// [ +// 'multi_profiles', +// t('Multiple Profiles'), +// t('Ability to create multiple profiles'), +// false, +// get_config('feature_lock','multi_profiles'), +// feature_level('multi_profiles',3), +// ], -// [ -// 'permcats', -// t('Permission Categories'), -// t('Create custom connection permission limits'), -// false, -// get_config('feature_lock','permcats'), -// feature_level('permcats',2), -// ], +// [ +// 'permcats', +// t('Permission Categories'), +// t('Create custom connection permission limits'), +// false, +// get_config('feature_lock','permcats'), +// feature_level('permcats',2), +// ], -// [ -// 'oauth_clients', -// t('OAuth1 Clients'), -// t('Manage OAuth1 authenticatication tokens for mobile and remote apps.'), -// false, -// get_config('feature_lock','oauth_clients'), -// feature_level('oauth_clients',1), -// ], +// [ +// 'oauth_clients', +// t('OAuth1 Clients'), +// t('Manage OAuth1 authenticatication tokens for mobile and remote apps.'), +// false, +// get_config('feature_lock','oauth_clients'), +// feature_level('oauth_clients',1), +// ], - [ - 'oauth2_clients', - t('OAuth2 Clients'), - t('Manage OAuth2 authenticatication tokens for mobile and remote apps.'), - false, - get_config('feature_lock','oauth2_clients'), - feature_level('oauth2_clients',1), - ], + [ + 'oauth2_clients', + t('OAuth2 Clients'), + t('Manage OAuth2 authenticatication tokens for mobile and remote apps.'), + false, + get_config('feature_lock', 'oauth2_clients'), + feature_level('oauth2_clients', 1), + ], -// [ -// 'access_tokens', -// t('Access Tokens'), -// t('Create access tokens so that non-members can access private content.'), -// false, -// get_config('feature_lock','access_tokens'), -// feature_level('access_tokens',2), -// ], +// [ +// 'access_tokens', +// t('Access Tokens'), +// t('Create access tokens so that non-members can access private content.'), +// false, +// get_config('feature_lock','access_tokens'), +// feature_level('access_tokens',2), +// ], - ], + ], - // Post composition - 'composition' => [ + // Post composition + 'composition' => [ - t('Post Composition Features'), + t('Post Composition Features'), -// [ -// 'large_photos', -// t('Large Photos'), -// t('Include large (1024px) photo thumbnails in posts. If not enabled, use small (640px) photo thumbnails'), -// false, -// get_config('feature_lock','large_photos'), -// feature_level('large_photos',1), -// ], +// [ +// 'large_photos', +// t('Large Photos'), +// t('Include large (1024px) photo thumbnails in posts. If not enabled, use small (640px) photo thumbnails'), +// false, +// get_config('feature_lock','large_photos'), +// feature_level('large_photos',1), +// ], -// [ -// 'channel_sources', -// t('Channel Sources'), -// t('Automatically import channel content from other channels or feeds'), -// false, -// get_config('feature_lock','channel_sources'), -// feature_level('channel_sources',3), -// ], - - [ - 'content_encrypt', - t('Browser Encryption'), - t('Provide optional browser-to-browser encryption of content with a shared secret key'), - true, - get_config('feature_lock','content_encrypt'), - feature_level('content_encrypt',3), - ], - -// [ -// 'consensus_tools', -// t('Enable Voting Tools'), -// t('Provide a class of post which others can vote on'), -// false, -// get_config('feature_lock','consensus_tools'), -// feature_level('consensus_tools',3), -// ], +// [ +// 'channel_sources', +// t('Channel Sources'), +// t('Automatically import channel content from other channels or feeds'), +// false, +// get_config('feature_lock','channel_sources'), +// feature_level('channel_sources',3), +// ], -// [ -// 'disable_comments', -// t('Disable Comments'), -// t('Provide the option to disable comments for a post'), -// false, -// get_config('feature_lock','disable_comments'), -// feature_level('disable_comments',2), -// ], + [ + 'content_encrypt', + t('Browser Encryption'), + t('Provide optional browser-to-browser encryption of content with a shared secret key'), + true, + get_config('feature_lock', 'content_encrypt'), + feature_level('content_encrypt', 3), + ], -// [ -// 'delayed_posting', -// t('Delayed Posting'), -// t('Allow posts to be published at a later date'), -// false, -// get_config('feature_lock','delayed_posting'), -// feature_level('delayed_posting',2), -// ], +// [ +// 'consensus_tools', +// t('Enable Voting Tools'), +// t('Provide a class of post which others can vote on'), +// false, +// get_config('feature_lock','consensus_tools'), +// feature_level('consensus_tools',3), +// ], -// [ -// 'content_expire', -// t('Content Expiration'), -// t('Remove posts/comments and/or private messages at a future time'), -// false, -// get_config('feature_lock','content_expire'), -// feature_level('content_expire',1), -// ], +// [ +// 'disable_comments', +// t('Disable Comments'), +// t('Provide the option to disable comments for a post'), +// false, +// get_config('feature_lock','disable_comments'), +// feature_level('disable_comments',2), +// ], - [ - 'suppress_duplicates', - t('Suppress Duplicate Posts/Comments'), - t('Prevent posts with identical content to be published with less than two minutes in between submissions.'), - true, - get_config('feature_lock','suppress_duplicates'), - feature_level('suppress_duplicates',1), - ], +// [ +// 'delayed_posting', +// t('Delayed Posting'), +// t('Allow posts to be published at a later date'), +// false, +// get_config('feature_lock','delayed_posting'), +// feature_level('delayed_posting',2), +// ], - [ - 'auto_save_draft', - t('Auto-save drafts of posts and comments'), - t('Automatically saves post and comment drafts in local browser storage to help prevent accidental loss of compositions'), - true, - get_config('feature_lock','auto_save_draft'), - feature_level('auto_save_draft',1), - ], +// [ +// 'content_expire', +// t('Content Expiration'), +// t('Remove posts/comments and/or private messages at a future time'), +// false, +// get_config('feature_lock','content_expire'), +// feature_level('content_expire',1), +// ], - ], + [ + 'suppress_duplicates', + t('Suppress Duplicate Posts/Comments'), + t('Prevent posts with identical content to be published with less than two minutes in between submissions.'), + true, + get_config('feature_lock', 'suppress_duplicates'), + feature_level('suppress_duplicates', 1), + ], - // Network Tools - 'net_module' => [ + [ + 'auto_save_draft', + t('Auto-save drafts of posts and comments'), + t('Automatically saves post and comment drafts in local browser storage to help prevent accidental loss of compositions'), + true, + get_config('feature_lock', 'auto_save_draft'), + feature_level('auto_save_draft', 1), + ], - t('Network and Stream Filtering'), + ], - [ - 'archives', - t('Search by Date'), - t('Ability to select posts by date ranges'), - false, - get_config('feature_lock','archives'), - feature_level('archives',1), - ], + // Network Tools + 'net_module' => [ + + t('Network and Stream Filtering'), + + [ + 'archives', + t('Search by Date'), + t('Ability to select posts by date ranges'), + false, + get_config('feature_lock', 'archives'), + feature_level('archives', 1), + ], - [ - 'savedsearch', - t('Saved Searches'), - t('Save search terms for re-use'), - false, - get_config('feature_lock','savedsearch'), - feature_level('savedsearch',2), - ], + [ + 'savedsearch', + t('Saved Searches'), + t('Save search terms for re-use'), + false, + get_config('feature_lock', 'savedsearch'), + feature_level('savedsearch', 2), + ], - [ - 'order_tab', - t('Alternate Stream Order'), - t('Ability to order the stream by last post date, last comment date or unthreaded activities'), - false, - get_config('feature_lock','order_tab'), - feature_level('order_tab',2), - ], + [ + 'order_tab', + t('Alternate Stream Order'), + t('Ability to order the stream by last post date, last comment date or unthreaded activities'), + false, + get_config('feature_lock', 'order_tab'), + feature_level('order_tab', 2), + ], - [ - 'name_tab', - t('Contact Filter'), - t('Ability to display only posts of a selected contact'), - false, - get_config('feature_lock','name_tab'), - feature_level('name_tab',1), - ], + [ + 'name_tab', + t('Contact Filter'), + t('Ability to display only posts of a selected contact'), + false, + get_config('feature_lock', 'name_tab'), + feature_level('name_tab', 1), + ], - [ - 'forums_tab', - t('Forum Filter'), - t('Ability to display only posts of a specific forum'), - false, - get_config('feature_lock','forums_tab'), - feature_level('forums_tab',1), - ], + [ + 'forums_tab', + t('Forum Filter'), + t('Ability to display only posts of a specific forum'), + false, + get_config('feature_lock', 'forums_tab'), + feature_level('forums_tab', 1), + ], - [ - 'personal_tab', - t('Personal Posts Filter'), - t('Ability to display only posts that you\'ve interacted on'), - false, - get_config('feature_lock','personal_tab'), - feature_level('personal_tab',1), - ], + [ + 'personal_tab', + t('Personal Posts Filter'), + t('Ability to display only posts that you\'ve interacted on'), + false, + get_config('feature_lock', 'personal_tab'), + feature_level('personal_tab', 1), + ], - [ - 'affinity', - t('Affinity Tool'), - t('Filter stream activity by depth of relationships'), - false, - get_config('feature_lock','affinity'), - feature_level('affinity',1), - ], + [ + 'affinity', + t('Affinity Tool'), + t('Filter stream activity by depth of relationships'), + false, + get_config('feature_lock', 'affinity'), + feature_level('affinity', 1), + ], - [ - 'suggest', - t('Suggest Channels'), - t('Show friend and connection suggestions'), - false, - get_config('feature_lock','suggest'), - feature_level('suggest',1), - ], + [ + 'suggest', + t('Suggest Channels'), + t('Show friend and connection suggestions'), + false, + get_config('feature_lock', 'suggest'), + feature_level('suggest', 1), + ], - [ - 'connfilter', - t('Connection Filtering'), - t('Filter incoming posts from connections based on keywords/content'), - false, - get_config('feature_lock','connfilter'), - feature_level('connfilter',3), - ], + [ + 'connfilter', + t('Connection Filtering'), + t('Filter incoming posts from connections based on keywords/content'), + false, + get_config('feature_lock', 'connfilter'), + feature_level('connfilter', 3), + ], - ], + ], - // Item tools - 'tools' => [ + // Item tools + 'tools' => [ - t('Post/Comment Tools'), + t('Post/Comment Tools'), - [ - 'commtag', - t('Community Tagging'), - t('Ability to tag existing posts'), - false, - get_config('feature_lock','commtag'), - feature_level('commtag',1), - ], + [ + 'commtag', + t('Community Tagging'), + t('Ability to tag existing posts'), + false, + get_config('feature_lock', 'commtag'), + feature_level('commtag', 1), + ], - [ - 'categories', - t('Post Categories'), - t('Add categories to your posts'), - false, - get_config('feature_lock','categories'), - feature_level('categories',1), - ], + [ + 'categories', + t('Post Categories'), + t('Add categories to your posts'), + false, + get_config('feature_lock', 'categories'), + feature_level('categories', 1), + ], - [ - 'emojis', - t('Emoji Reactions'), - t('Add emoji reaction ability to posts'), - true, - get_config('feature_lock','emojis'), - feature_level('emojis',1), - ], + [ + 'emojis', + t('Emoji Reactions'), + t('Add emoji reaction ability to posts'), + true, + get_config('feature_lock', 'emojis'), + feature_level('emojis', 1), + ], - [ - 'filing', - t('Saved Folders'), - t('Ability to file posts under folders'), - false, - get_config('feature_lock','filing'), - feature_level('filing',2), - ], + [ + 'filing', + t('Saved Folders'), + t('Ability to file posts under folders'), + false, + get_config('feature_lock', 'filing'), + feature_level('filing', 2), + ], - [ - 'dislike', - t('Dislike Posts'), - t('Ability to dislike posts/comments'), - false, - get_config('feature_lock','dislike'), - feature_level('dislike',1), - ], + [ + 'dislike', + t('Dislike Posts'), + t('Ability to dislike posts/comments'), + false, + get_config('feature_lock', 'dislike'), + feature_level('dislike', 1), + ], -// [ -// 'star_posts', -// t('Star Posts'), -// t('Ability to mark special posts with a star indicator'), -// false, -// get_config('feature_lock','star_posts'), -// feature_level('star_posts',1), -// ], +// [ +// 'star_posts', +// t('Star Posts'), +// t('Ability to mark special posts with a star indicator'), +// false, +// get_config('feature_lock','star_posts'), +// feature_level('star_posts',1), +// ], // - [ - 'tagadelic', - t('Tag Cloud'), - t('Provide a personal tag cloud on your channel page'), - false, - get_config('feature_lock','tagadelic'), - feature_level('tagadelic',2), - ], - ], - ]; + [ + 'tagadelic', + t('Tag Cloud'), + t('Provide a personal tag cloud on your channel page'), + false, + get_config('feature_lock', 'tagadelic'), + feature_level('tagadelic', 2), + ], + ], + ]; - $x = [ 'features' => $arr, ]; - call_hooks('get_features',$x); + $x = [ 'features' => $arr, ]; + call_hooks('get_features', $x); - $arr = $x['features']; + $arr = $x['features']; - // removed any locked features and remove the entire category if this makes it empty + // removed any locked features and remove the entire category if this makes it empty - if($filtered) { - $narr = []; - foreach($arr as $k => $x) { - $narr[$k] = [ $arr[$k][0] ]; - $has_items = false; - for($y = 0; $y < count($arr[$k]); $y ++) { - $disabled = false; - if(is_array($arr[$k][$y])) { - if($arr[$k][$y][4] !== false) { - $disabled = true; - } - if(! $disabled) { - $has_items = true; - $narr[$k][$y] = $arr[$k][$y]; - } - } - } - if(! $has_items) { - unset($narr[$k]); - } - } - } - else { - $narr = $arr; - } + if ($filtered) { + $narr = []; + foreach ($arr as $k => $x) { + $narr[$k] = [ $arr[$k][0] ]; + $has_items = false; + for ($y = 0; $y < count($arr[$k]); $y++) { + $disabled = false; + if (is_array($arr[$k][$y])) { + if ($arr[$k][$y][4] !== false) { + $disabled = true; + } + if (! $disabled) { + $has_items = true; + $narr[$k][$y] = $arr[$k][$y]; + } + } + } + if (! $has_items) { + unset($narr[$k]); + } + } + } else { + $narr = $arr; + } - return $narr; + return $narr; } diff --git a/include/feedutils.php b/include/feedutils.php index a00a72c31..99d2b2540 100644 --- a/include/feedutils.php +++ b/include/feedutils.php @@ -1,9 +1,12 @@ xmlify(Zotlabs\Lib\System::get_project_version()), - '$generator' => xmlify(Zotlabs\Lib\System::get_platform_name()), + $atom .= replace_macros($feed_template, array( + '$version' => xmlify(Zotlabs\Lib\System::get_project_version()), + '$generator' => xmlify(Zotlabs\Lib\System::get_platform_name()), '$generator_uri' => 'https://codeberg.org/' . ((PLATFORM_NAME === 'streams') ? 'streams' : 'zot') . '/' . PLATFORM_NAME, - '$feed_id' => xmlify($channel['xchan_url']), - '$feed_title' => xmlify($channel['channel_name']), - '$feed_updated' => xmlify(datetime_convert('UTC', 'UTC', 'now', ATOM_TIME)), - '$author' => $feed_author, - '$owner' => $owner, - '$profile_page' => xmlify($channel['xchan_url']), - )); + '$feed_id' => xmlify($channel['xchan_url']), + '$feed_title' => xmlify($channel['channel_name']), + '$feed_updated' => xmlify(datetime_convert('UTC', 'UTC', 'now', ATOM_TIME)), + '$author' => $feed_author, + '$owner' => $owner, + '$profile_page' => xmlify($channel['xchan_url']), + )); - $x = [ - 'xml' => $atom, - 'channel' => $channel, - 'observer_hash' => $observer_hash, - 'params' => $params - ]; - /** - * @hooks atom_feed_top - * * \e string \b xml - the generated feed and what will get returned from the hook - * * \e array \b channel - * * \e string \b observer_hash - * * \e array \b params - */ - call_hooks('atom_feed_top', $x); + $x = [ + 'xml' => $atom, + 'channel' => $channel, + 'observer_hash' => $observer_hash, + 'params' => $params + ]; + /** + * @hooks atom_feed_top + * * \e string \b xml - the generated feed and what will get returned from the hook + * * \e array \b channel + * * \e string \b observer_hash + * * \e array \b params + */ + call_hooks('atom_feed_top', $x); - $atom = $x['xml']; + $atom = $x['xml']; - /** - * @hooks atom_feed - * A much simpler interface than atom_feed_top. - * * \e string - the feed after atom_feed_top hook - */ - call_hooks('atom_feed', $atom); + /** + * @hooks atom_feed + * A much simpler interface than atom_feed_top. + * * \e string - the feed after atom_feed_top hook + */ + call_hooks('atom_feed', $atom); - $items = items_fetch( - [ - 'wall' => '1', - 'datequery' => $params['end'], - 'datequery2' => $params['begin'], - 'start' => intval($params['start']), - 'records' => intval($params['records']), - 'direction' => dbesc($params['direction']), - 'pages' => $params['pages'], - 'order' => dbesc('post'), - 'top' => $params['top'], - 'cat' => $params['cat'], - 'compat' => $params['compat'] - ], $channel, $observer_hash, CLIENT_MODE_NORMAL, App::$module - ); + $items = items_fetch( + [ + 'wall' => '1', + 'datequery' => $params['end'], + 'datequery2' => $params['begin'], + 'start' => intval($params['start']), + 'records' => intval($params['records']), + 'direction' => dbesc($params['direction']), + 'pages' => $params['pages'], + 'order' => dbesc('post'), + 'top' => $params['top'], + 'cat' => $params['cat'], + 'compat' => $params['compat'] + ], + $channel, + $observer_hash, + CLIENT_MODE_NORMAL, + App::$module + ); - if($items) { - $type = 'html'; - foreach($items as $item) { - if($item['item_private']) - continue; + if ($items) { + $type = 'html'; + foreach ($items as $item) { + if ($item['item_private']) { + continue; + } - $atom .= atom_entry($item, $type, null, $owner, true, '', $params['compat']); - } - } + $atom .= atom_entry($item, $type, null, $owner, true, '', $params['compat']); + } + } - /** - * @hooks atom_feed_end - * \e string - The created XML feed as a string without closing tag - */ - call_hooks('atom_feed_end', $atom); + /** + * @hooks atom_feed_end + * \e string - The created XML feed as a string without closing tag + */ + call_hooks('atom_feed_end', $atom); - $atom .= '' . "\r\n"; + $atom .= '' . "\r\n"; - return $atom; + return $atom; } /** @@ -178,34 +187,36 @@ function get_feed_for($channel, $observer_hash, $params) { * @param string $photo Fully qualified URL to a profile/avator photo * @return string XML tag */ -function atom_author($tag, $nick, $name, $uri, $h, $w, $type, $photo) { - $o = ''; - if(! $tag) - return $o; +function atom_author($tag, $nick, $name, $uri, $h, $w, $type, $photo) +{ + $o = ''; + if (! $tag) { + return $o; + } - $nick = xmlify($nick); - $name = xmlify($name); - $uri = xmlify($uri); - $h = intval($h); - $w = intval($w); - $photo = xmlify($photo); + $nick = xmlify($nick); + $name = xmlify($name); + $uri = xmlify($uri); + $h = intval($h); + $w = intval($w); + $photo = xmlify($photo); - $o .= "<$tag>\r\n"; - $o .= " $name\r\n"; - $o .= " $uri\r\n"; - $o .= ' ' . "\r\n"; - $o .= ' ' . "\r\n"; + $o .= "<$tag>\r\n"; + $o .= " $name\r\n"; + $o .= " $uri\r\n"; + $o .= ' ' . "\r\n"; + $o .= ' ' . "\r\n"; - /** - * @hooks atom_author - * Possibility to add further tags to returned XML string - * * \e string - The created XML tag as a string without closing tag - */ - call_hooks('atom_author', $o); + /** + * @hooks atom_author + * Possibility to add further tags to returned XML string + * * \e string - The created XML tag as a string without closing tag + */ + call_hooks('atom_author', $o); - $o .= "\r\n"; + $o .= "\r\n"; - return $o; + return $o; } @@ -216,56 +227,58 @@ function atom_author($tag, $nick, $name, $uri, $h, $w, $type, $photo) { * @param array $xchan * @return string */ -function atom_render_author($tag, $xchan) { +function atom_render_author($tag, $xchan) +{ - $nick = xmlify(substr($xchan['xchan_addr'], 0, strpos($xchan['xchan_addr'], '@'))); - $id = xmlify($xchan['xchan_url']); - $name = xmlify($xchan['xchan_name']); - $photo = xmlify($xchan['xchan_photo_l']); - $type = xmlify($xchan['xchan_photo_mimetype']); - $w = $h = 300; + $nick = xmlify(substr($xchan['xchan_addr'], 0, strpos($xchan['xchan_addr'], '@'))); + $id = xmlify($xchan['xchan_url']); + $name = xmlify($xchan['xchan_name']); + $photo = xmlify($xchan['xchan_photo_l']); + $type = xmlify($xchan['xchan_photo_mimetype']); + $w = $h = 300; - $o = "<$tag>\r\n"; - $o .= " $name\r\n"; - $o .= " $id\r\n"; - $o .= ' ' . "\r\n"; - $o .= ' ' . "\r\n"; + $o = "<$tag>\r\n"; + $o .= " $name\r\n"; + $o .= " $id\r\n"; + $o .= ' ' . "\r\n"; + $o .= ' ' . "\r\n"; - /** - * @hooks atom_render_author - * Possibility to add further tags to returned XML string. - * * \e string The created XML tag as a string without closing tag - */ - call_hooks('atom_render_author', $o); + /** + * @hooks atom_render_author + * Possibility to add further tags to returned XML string. + * * \e string The created XML tag as a string without closing tag + */ + call_hooks('atom_render_author', $o); - $o .= "\r\n"; + $o .= "\r\n"; - return $o; + return $o; } -function compat_photos_list($s) { +function compat_photos_list($s) +{ - $ret = []; + $ret = []; - $found = preg_match_all('/\[[zi]mg(.*?)\](.*?)\[/ism',$s,$matches,PREG_SET_ORDER); + $found = preg_match_all('/\[[zi]mg(.*?)\](.*?)\[/ism', $s, $matches, PREG_SET_ORDER); - if($found) { - foreach($matches as $match) { - $entry = [ - 'href' => $match[2], - 'type' => guess_image_type($match[2]) - ]; - $sizer = new \Zotlabs\Lib\Img_filesize($match[2]); - $size = $sizer->getSize(); - if(intval($size)) { - $entry['length'] = intval($size); - } + if ($found) { + foreach ($matches as $match) { + $entry = [ + 'href' => $match[2], + 'type' => guess_image_type($match[2]) + ]; + $sizer = new Img_filesize($match[2]); + $size = $sizer->getSize(); + if (intval($size)) { + $entry['length'] = intval($size); + } - $ret[] = $entry; - } - } + $ret[] = $entry; + } + } - return $ret; + return $ret; } @@ -280,131 +293,134 @@ function compat_photos_list($s) { * @param array $owner * @param string $comment default false * @param number $cid default 0 - * @param boolean $compat default false + * @param bool $compat default false * @return void|string */ -function atom_entry($item, $type, $author, $owner, $comment = false, $cid = 0, $compat = false) { +function atom_entry($item, $type, $author, $owner, $comment = false, $cid = 0, $compat = false) +{ - if(! $item['parent']) - return; + if (! $item['parent']) { + return; + } - if($item['deleted']) - return '' . "\r\n"; + if ($item['deleted']) { + return '' . "\r\n"; + } - create_export_photo_body($item); + create_export_photo_body($item); - // provide separate summary and content unless compat is true; as summary represents a content-warning on some networks + // provide separate summary and content unless compat is true; as summary represents a content-warning on some networks - $summary = $item['summary']; + $summary = $item['summary']; - $body = $item['body']; + $body = $item['body']; - $compat_photos = null; + $compat_photos = null; - $o = "\r\n\r\n\r\n"; + $o = "\r\n\r\n\r\n"; - if(is_array($author)) { - $o .= atom_render_author('author',$author); - } - else { - $o .= atom_render_author('author',$item['author']); - } + if (is_array($author)) { + $o .= atom_render_author('author', $author); + } else { + $o .= atom_render_author('author', $item['author']); + } - if(($item['parent'] != $item['id']) || ($item['parent_mid'] !== $item['mid']) || (($item['thr_parent'] !== '') && ($item['thr_parent'] !== $item['mid']))) { - $parent_item = (($item['thr_parent']) ? $item['thr_parent'] : $item['parent_mid']); + if (($item['parent'] != $item['id']) || ($item['parent_mid'] !== $item['mid']) || (($item['thr_parent'] !== '') && ($item['thr_parent'] !== $item['mid']))) { + $parent_item = (($item['thr_parent']) ? $item['thr_parent'] : $item['parent_mid']); - $o .= '' . "\r\n"; - } + $o .= '' . "\r\n"; + } else { + $o .= '' . xmlify($item['title']) . '' . "\r\n"; + if ($summary) { + $o .= '' . xmlify(prepare_text($summary, $item['mimetype'])) . '' . "\r\n"; + } + $o .= '' . xmlify(prepare_text($body, $item['mimetype'])) . '' . "\r\n"; + } + + $o .= '' . xmlify($item['mid']) . '' . "\r\n"; + $o .= '' . xmlify(datetime_convert('UTC', 'UTC', $item['created'] . '+00:00', ATOM_TIME)) . '' . "\r\n"; + $o .= '' . xmlify(datetime_convert('UTC', 'UTC', $item['edited'] . '+00:00', ATOM_TIME)) . '' . "\r\n"; + + $o .= '' . "\r\n"; - else { - $o .= '' . xmlify($item['title']) . '' . "\r\n"; - if($summary) - $o .= '' . xmlify(prepare_text($summary,$item['mimetype'])) . '' . "\r\n"; - $o .= '' . xmlify(prepare_text($body,$item['mimetype'])) . '' . "\r\n"; - } + if ($item['attach']) { + $enclosures = json_decode($item['attach'], true); + if ($enclosures) { + foreach ($enclosures as $enc) { + $o .= '' . "\r\n"; + } + } + } - $o .= '' . xmlify($item['mid']) . '' . "\r\n"; - $o .= '' . xmlify(datetime_convert('UTC','UTC',$item['created'] . '+00:00',ATOM_TIME)) . '' . "\r\n"; - $o .= '' . xmlify(datetime_convert('UTC','UTC',$item['edited'] . '+00:00',ATOM_TIME)) . '' . "\r\n"; + if ($item['term']) { + foreach ($item['term'] as $term) { + $scheme = ''; + $label = ''; + switch ($term['ttype']) { + case TERM_HASHTAG: + $scheme = NAMESPACE_ZOT . '/term/hashtag'; + $label = '#' . str_replace('"', '', $term['term']); + break; + case TERM_CATEGORY: + $scheme = NAMESPACE_ZOT . '/term/category'; + $label = str_replace('"', '', $term['term']); + break; + default: + break; + } + if (! $scheme) { + continue; + } - $o .= '' . "\r\n"; + $o .= '' . "\r\n"; + } + } + $o .= '' . "\r\n"; - if($item['attach']) { - $enclosures = json_decode($item['attach'], true); - if($enclosures) { - foreach($enclosures as $enc) { - $o .= '' . "\r\n"; - } - } - } + // build array to pass to hook + $x = [ + 'item' => $item, + 'type' => $type, + 'author' => $author, + 'owner' => $owner, + 'comment' => $comment, + 'abook_id' => $cid, + 'entry' => $o + ]; + /** + * @hooks atom_entry + * * \e array \b item + * * \e string \b type + * * \e array \b author + * * \e array \b owner + * * \e string \b comment + * * \e number \b abook_id + * * \e string \b entry - The generated entry and what will get returned + */ + call_hooks('atom_entry', $x); - if($item['term']) { - foreach($item['term'] as $term) { - $scheme = ''; - $label = ''; - switch($term['ttype']) { - case TERM_HASHTAG: - $scheme = NAMESPACE_ZOT . '/term/hashtag'; - $label = '#' . str_replace('"','',$term['term']); - break; - case TERM_CATEGORY: - $scheme = NAMESPACE_ZOT . '/term/category'; - $label = str_replace('"','',$term['term']); - break; - default: - break; - } - if(! $scheme) - continue; - - $o .= '' . "\r\n"; - } - } - - $o .= '' . "\r\n"; - - // build array to pass to hook - $x = [ - 'item' => $item, - 'type' => $type, - 'author' => $author, - 'owner' => $owner, - 'comment' => $comment, - 'abook_id' => $cid, - 'entry' => $o - ]; - /** - * @hooks atom_entry - * * \e array \b item - * * \e string \b type - * * \e array \b author - * * \e array \b owner - * * \e string \b comment - * * \e number \b abook_id - * * \e string \b entry - The generated entry and what will get returned - */ - call_hooks('atom_entry', $x); - - return $x['entry']; + return $x['entry']; } -function get_mentions($item,$tags) { - $o = ''; +function get_mentions($item, $tags) +{ + $o = ''; - if(! count($tags)) - return $o; + if (! count($tags)) { + return $o; + } - foreach($tags as $x) { - if($x['ttype'] == TERM_MENTION) { - $o .= "\t\t" . '' . "\r\n"; - } - } - return $o; + foreach ($tags as $x) { + if ($x['ttype'] == TERM_MENTION) { + $o .= "\t\t" . '' . "\r\n"; + } + } + return $o; } diff --git a/include/help.php b/include/help.php index 78ce709e2..4a67f2603 100644 --- a/include/help.php +++ b/include/help.php @@ -1,6 +1,5 @@ 1) { - for ($x = 1; $x < argc(); $x ++) { - if ($path) { - $path .= '/'; - } - $path .= App::$argv[$x]; - } - } + $path = ''; + if (argc() > 1) { + for ($x = 1; $x < argc(); $x++) { + if ($path) { + $path .= '/'; + } + $path .= App::$argv[$x]; + } + } - $fullpath = get_help_fullpath($path); + $fullpath = get_help_fullpath($path); - $text = load_doc_file($fullpath); + $text = load_doc_file($fullpath); - App::$page['title'] = t('Help'); + App::$page['title'] = t('Help'); - $content = bbcode($text); + $content = bbcode($text); - return translate_projectname($content); + return translate_projectname($content); } -function preg_callback_help_include($matches) { - - if($matches[1]) { - $include = str_replace($matches[0],load_doc_file($matches[1]),$matches[0]); - if(preg_match('/\.bb$/', $matches[1]) || preg_match('/\.txt$/', $matches[1])) { - require_once('include/bbcode.php'); - $include = zidify_links(bbcode($include)); - $include = str_replace(' target="_blank"','',$include); - } - elseif(preg_match('/\.md$/', $matches[1])) { - $include = MarkdownExtra::defaultTransform($include); - } - return $include; - } +function preg_callback_help_include($matches) +{ + if ($matches[1]) { + $include = str_replace($matches[0], load_doc_file($matches[1]), $matches[0]); + if (preg_match('/\.bb$/', $matches[1]) || preg_match('/\.txt$/', $matches[1])) { + require_once('include/bbcode.php'); + $include = zidify_links(bbcode($include)); + $include = str_replace(' target="_blank"', '', $include); + } elseif (preg_match('/\.md$/', $matches[1])) { + $include = MarkdownExtra::defaultTransform($include); + } + return $include; + } } /** * @brief * - * @return boolean|array + * @return bool|array */ -function determine_help_language() { +function determine_help_language() +{ - require_once('library/text_languagedetect/Text/LanguageDetect.php'); + require_once('library/text_languagedetect/Text/LanguageDetect.php'); - $lang_detect = new Text_LanguageDetect(); - // Set this mode to recognize language by the short code like "en", "ru", etc. - $lang_detect->setNameMode(2); - // If the language was specified in the URL, override the language preference - // of the browser. Default to English if both of these are absent. - if($lang_detect->languageExists(argv(1))) { - $lang = argv(1); - $from_url = true; - } else { - $lang = \App::$language; - if(! isset($lang)) - $lang = 'en'; + $lang_detect = new Text_LanguageDetect(); + // Set this mode to recognize language by the short code like "en", "ru", etc. + $lang_detect->setNameMode(2); + // If the language was specified in the URL, override the language preference + // of the browser. Default to English if both of these are absent. + if ($lang_detect->languageExists(argv(1))) { + $lang = argv(1); + $from_url = true; + } else { + $lang = App::$language; + if (! isset($lang)) { + $lang = 'en'; + } - $from_url = false; - } + $from_url = false; + } - return array('language' => $lang, 'from_url' => $from_url); + return array('language' => $lang, 'from_url' => $from_url); } -function load_doc_file($s) { +function load_doc_file($s) +{ - $c = find_doc_file($s); - if($c) - return $c; - return ''; + $c = find_doc_file($s); + if ($c) { + return $c; + } + return ''; } -function find_doc_file($s) { - if(file_exists($s)) { - return file_get_contents($s); - } - return ''; +function find_doc_file($s) +{ + if (file_exists($s)) { + return file_get_contents($s); + } + return ''; } /** * @brief * * @param string $s - * @return number|mixed|unknown|boolean + * @return number|mixed|unknown|bool */ -function search_doc_files($s) { +function search_doc_files($s) +{ - App::set_pager_itemspage(60); - $pager_sql = sprintf(" LIMIT %d OFFSET %d ", intval(\App::$pager['itemspage']), intval(\App::$pager['start'])); + App::set_pager_itemspage(60); + $pager_sql = sprintf(" LIMIT %d OFFSET %d ", intval(App::$pager['itemspage']), intval(App::$pager['start'])); - $regexop = db_getfunc('REGEXP'); + $regexop = db_getfunc('REGEXP'); - $r = q("select iconfig.v, item.* from item left join iconfig on item.id = iconfig.iid + $r = q( + "select iconfig.v, item.* from item left join iconfig on item.id = iconfig.iid where iconfig.cat = 'system' and iconfig.k = 'docfile' and body $regexop '%s' and item_type = %d $pager_sql", - dbesc($s), - intval(ITEM_TYPE_DOC) - ); + dbesc($s), + intval(ITEM_TYPE_DOC) + ); - $r = fetch_post_tags($r, true); + $r = fetch_post_tags($r, true); - for($x = 0; $x < count($r); $x ++) { - $position = stripos($r[$x]['body'], $s); - $dislen = 300; - $start = $position-floor($dislen/2); - if ( $start < 0) { - $start = 0; - } - $r[$x]['text'] = substr($r[$x]['body'], $start, $dislen); + for ($x = 0; $x < count($r); $x++) { + $position = stripos($r[$x]['body'], $s); + $dislen = 300; + $start = $position - floor($dislen / 2); + if ($start < 0) { + $start = 0; + } + $r[$x]['text'] = substr($r[$x]['body'], $start, $dislen); - $r[$x]['rank'] = 0; - if($r[$x]['term']) { - foreach($r[$x]['term'] as $t) { - if(stristr($t['term'],$s)) { - $r[$x]['rank'] ++; - } - } - } - if(stristr($r[$x]['v'], $s)) - $r[$x]['rank'] ++; - $r[$x]['rank'] += substr_count(strtolower($r[$x]['text']), strtolower($s)); - // bias the results to the observer's native language - if($r[$x]['lang'] === App::$language) - $r[$x]['rank'] = $r[$x]['rank'] + 10; + $r[$x]['rank'] = 0; + if ($r[$x]['term']) { + foreach ($r[$x]['term'] as $t) { + if (stristr($t['term'], $s)) { + $r[$x]['rank'] ++; + } + } + } + if (stristr($r[$x]['v'], $s)) { + $r[$x]['rank'] ++; + } + $r[$x]['rank'] += substr_count(strtolower($r[$x]['text']), strtolower($s)); + // bias the results to the observer's native language + if ($r[$x]['lang'] === App::$language) { + $r[$x]['rank'] = $r[$x]['rank'] + 10; + } + } + usort($r, 'doc_rank_sort'); - } - usort($r,'doc_rank_sort'); - - return $r; + return $r; } -function doc_rank_sort($s1, $s2) { - if($s1['rank'] == $s2['rank']) - return 0; +function doc_rank_sort($s1, $s2) +{ + if ($s1['rank'] == $s2['rank']) { + return 0; + } - return (($s1['rank'] < $s2['rank']) ? 1 : (-1)); + return (($s1['rank'] < $s2['rank']) ? 1 : (-1)); } /** @@ -198,78 +209,82 @@ function doc_rank_sort($s1, $s2) { * @return string */ -function load_context_help() { +function load_context_help() +{ - $path = App::$cmd; - $args = App::$argv; - $lang = App::$language; + $path = App::$cmd; + $args = App::$argv; + $lang = App::$language; - if(! isset($lang) || !is_dir('doc/context/' . $lang . '/')) { - $lang = 'en'; - } - while($path) { - $context_help = load_doc_file('doc/context/' . $lang . '/' . $path . '/help.html'); - if(!$context_help) { - // Fallback to English if the translation is absent - $context_help = load_doc_file('doc/context/en/' . $path . '/help.html'); - } - if($context_help) - break; + if (! isset($lang) || !is_dir('doc/context/' . $lang . '/')) { + $lang = 'en'; + } + while ($path) { + $context_help = load_doc_file('doc/context/' . $lang . '/' . $path . '/help.html'); + if (!$context_help) { + // Fallback to English if the translation is absent + $context_help = load_doc_file('doc/context/en/' . $path . '/help.html'); + } + if ($context_help) { + break; + } - array_pop($args); - $path = implode('/',$args); - } + array_pop($args); + $path = implode('/', $args); + } - return $context_help; + return $context_help; } /** * @brief * * @param string $s - * @return void|boolean[]|number[]|string[]|unknown[] + * @return void|bool|number[]|string[]|unknown[] */ -function store_doc_file($s) { +function store_doc_file($s) +{ - if(is_dir($s)) - return; + if (is_dir($s)) { + return; + } - $item = []; - $sys = get_sys_channel(); + $item = []; + $sys = get_sys_channel(); - $item['aid'] = 0; - $item['uid'] = $sys['channel_id']; + $item['aid'] = 0; + $item['uid'] = $sys['channel_id']; - $mimetype = 'text/x-multicode'; + $mimetype = 'text/x-multicode'; - require_once('include/html2plain.php'); + require_once('include/html2plain.php'); - $item['body'] = html2plain(prepare_text(file_get_contents($s),$mimetype, [ 'cache' => true ])); - $item['mimetype'] = 'text/plain'; + $item['body'] = html2plain(prepare_text(file_get_contents($s), $mimetype, [ 'cache' => true ])); + $item['mimetype'] = 'text/plain'; - $item['plink'] = z_root() . '/' . str_replace('doc','help',$s); - $item['owner_xchan'] = $item['author_xchan'] = $sys['channel_hash']; - $item['item_type'] = ITEM_TYPE_DOC; + $item['plink'] = z_root() . '/' . str_replace('doc', 'help', $s); + $item['owner_xchan'] = $item['author_xchan'] = $sys['channel_hash']; + $item['item_type'] = ITEM_TYPE_DOC; - $r = q("select item.* from item left join iconfig on item.id = iconfig.iid + $r = q( + "select item.* from item left join iconfig on item.id = iconfig.iid where iconfig.cat = 'system' and iconfig.k = 'docfile' and iconfig.v = '%s' and item_type = %d limit 1", - dbesc($s), - intval(ITEM_TYPE_DOC) - ); + dbesc($s), + intval(ITEM_TYPE_DOC) + ); - IConfig::Set($item,'system','docfile',$s); + IConfig::Set($item, 'system', 'docfile', $s); - if($r) { - $item['id'] = $r[0]['id']; - $item['mid'] = $item['parent_mid'] = $r[0]['mid']; - $x = item_store_update($item); - } - else { - $item['uuid'] = new_uuid(); - $item['mid'] = $item['parent_mid'] = z_root() . '/item/' . $item['uuid']; - $x = item_store($item); - } + if ($r) { + $item['id'] = $r[0]['id']; + $item['mid'] = $item['parent_mid'] = $r[0]['mid']; + $x = item_store_update($item); + } else { + $item['uuid'] = new_uuid(); + $item['mid'] = $item['parent_mid'] = z_root() . '/item/' . $item['uuid']; + $x = item_store($item); + } - return $x; + return $x; } diff --git a/include/html2bbcode.php b/include/html2bbcode.php index 1901d991c..e4533d2d7 100644 --- a/include/html2bbcode.php +++ b/include/html2bbcode.php @@ -1,320 +1,332 @@ -query("//".$oldnode); - foreach ($list as $oldNode) { + $list = $xpath->query("//" . $oldnode); + foreach ($list as $oldNode) { + $attr = []; + if ($oldNode->attributes->length) { + foreach ($oldNode->attributes as $attribute) { + $attr[$attribute->name] = $attribute->value; + } + } - $attr = []; - if ($oldNode->attributes->length) - foreach ($oldNode->attributes as $attribute) - $attr[$attribute->name] = $attribute->value; + $replace = true; - $replace = true; + $startbb = $savestart; - $startbb = $savestart; + $i = 0; - $i = 0; + foreach ($attributes as $attribute => $value) { + $startbb = str_replace('\x01' . ++$i, '$1', $startbb); - foreach ($attributes as $attribute => $value) { + if (strpos('*' . $startbb, '$1') > 0) { + if ($replace and (@$attr[$attribute] != '')) { + $startbb = preg_replace($value, $startbb, $attr[$attribute], -1, $count); - $startbb = str_replace('\x01'.++$i, '$1', $startbb); + // If nothing could be changed + if ($count == 0) { + $replace = false; + } + } else { + $replace = false; + } + } else { + if (@$attr[$attribute] != $value) { + $replace = false; + } + } + } - if (strpos('*'.$startbb, '$1') > 0) { + if ($replace) { + $StartCode = $oldNode->ownerDocument->createTextNode($startbb); + $EndCode = $oldNode->ownerDocument->createTextNode($endbb); - if ($replace and (@$attr[$attribute] != '')) { + $oldNode->parentNode->insertBefore($StartCode, $oldNode); - $startbb = preg_replace($value, $startbb, $attr[$attribute], -1, $count); + if ($oldNode->hasChildNodes()) { + foreach ($oldNode->childNodes as $child) { + $newNode = $child->cloneNode(true); + $oldNode->parentNode->insertBefore($newNode, $oldNode); + } + } - // If nothing could be changed - if ($count == 0) - $replace = false; - } else - $replace = false; - } else { - if (@$attr[$attribute] != $value) - $replace = false; - } - } - - if ($replace) { - $StartCode = $oldNode->ownerDocument->createTextNode($startbb); - $EndCode = $oldNode->ownerDocument->createTextNode($endbb); - - $oldNode->parentNode->insertBefore($StartCode, $oldNode); - - if ($oldNode->hasChildNodes()) { - foreach ($oldNode->childNodes as $child) { - $newNode = $child->cloneNode(true); - $oldNode->parentNode->insertBefore($newNode, $oldNode); - } - } - - $oldNode->parentNode->insertBefore($EndCode, $oldNode); - $oldNode->parentNode->removeChild($oldNode); - } - } - return($replace); + $oldNode->parentNode->insertBefore($EndCode, $oldNode); + $oldNode->parentNode->removeChild($oldNode); + } + } + return($replace); } function deletenode(&$doc, $node) { - $xpath = new DomXPath($doc); - $list = $xpath->query("//".$node); - foreach ($list as $child) - $child->parentNode->removeChild($child); + $xpath = new DomXPath($doc); + $list = $xpath->query("//" . $node); + foreach ($list as $child) { + $child->parentNode->removeChild($child); + } } -function svg2bbcode($match) { +function svg2bbcode($match) +{ - $params = $match[1]; - $s = $match[2]; - - $output = '' . $s . ''; + $params = $match[1]; + $s = $match[2]; - $purify = new SvgSanitizer(); - if ($purify->loadXML($s)) { - $purify->sanitize(); - $output = $purify->saveSVG(); - $output = preg_replace("/\<\?xml(.*?)\>/",'',$output); - $output = preg_replace("/\<\!\-\-(.*?)\-\-\>/",'',$output); - $output = str_replace(['<','>'],['[',']'],$output); - return $output; - } - return EMPTY_STR; + $output = '' . $s . ''; + $purify = new SvgSanitizer(); + if ($purify->loadXML($s)) { + $purify->sanitize(); + $output = $purify->saveSVG(); + $output = preg_replace("/\<\?xml(.*?)\>/", '', $output); + $output = preg_replace("/\<\!\-\-(.*?)\-\-\>/", '', $output); + $output = str_replace(['<','>'], ['[',']'], $output); + return $output; + } + return EMPTY_STR; } -function html2bbcode($message) { +function html2bbcode($message) +{ - if (! $message) { - return EMPTY_STR; - } + if (! $message) { + return EMPTY_STR; + } - $message = str_replace("\r", "", $message); + $message = str_replace("\r", "", $message); - $message = str_replace(array( - "
                  • ", - "

                  • "), - array( - "
                  • ", - "
                  • "), - $message); + $message = str_replace( + array( + "
                  • ", + "

                  • "), + array( + "
                  • ", + "
                  • "), + $message + ); - // remove namespaces - $message = preg_replace('=<(\w+):(.+?)>=', '', $message); - $message = preg_replace('==', '', $message); + // remove namespaces + $message = preg_replace('=<(\w+):(.+?)>=', '', $message); + $message = preg_replace('==', '', $message); - $doc = new DOMDocument(); - $doc->preserveWhiteSpace = false; + $doc = new DOMDocument(); + $doc->preserveWhiteSpace = false; - $tmp_message = mb_convert_encoding($message, 'HTML-ENTITIES', "UTF-8"); + $tmp_message = mb_convert_encoding($message, 'HTML-ENTITIES', "UTF-8"); - if (! $tmp_message) { - logger('mb_convert_encoding failed: ' . $message); - return EMPTY_STR; - } + if (! $tmp_message) { + logger('mb_convert_encoding failed: ' . $message); + return EMPTY_STR; + } - @$doc->loadHTML($tmp_message); + @$doc->loadHTML($tmp_message); - deletenode($doc, 'style'); - deletenode($doc, 'head'); - deletenode($doc, 'title'); - deletenode($doc, 'meta'); - deletenode($doc, 'xml'); - deletenode($doc, 'removeme'); + deletenode($doc, 'style'); + deletenode($doc, 'head'); + deletenode($doc, 'title'); + deletenode($doc, 'meta'); + deletenode($doc, 'xml'); + deletenode($doc, 'removeme'); - $xpath = new DomXPath($doc); - $list = $xpath->query("//pre"); - foreach ($list as $node) - $node->nodeValue = str_replace("\n", "\r", $node->nodeValue); + $xpath = new DomXPath($doc); + $list = $xpath->query("//pre"); + foreach ($list as $node) { + $node->nodeValue = str_replace("\n", "\r", $node->nodeValue); + } - $message = $doc->saveHTML(); - $message = str_replace(array("\n<", ">\n", "\r", "\n", "\xC3\x82\xC2\xA0"), array("<", ">", "
                    ", " ", ""), $message); - $message = preg_replace('= [\s]*=i', " ", $message); - @$doc->loadHTML($message); + $message = $doc->saveHTML(); + $message = str_replace(array("\n<", ">\n", "\r", "\n", "\xC3\x82\xC2\xA0"), array("<", ">", "
                    ", " ", ""), $message); + $message = preg_replace('= [\s]*=i', " ", $message); + @$doc->loadHTML($message); - node2bbcode($doc, 'html', [], "", ""); - node2bbcode($doc, 'body', [], "", ""); + node2bbcode($doc, 'html', [], "", ""); + node2bbcode($doc, 'body', [], "", ""); - // Outlook-Quote - Variant 1 - node2bbcode($doc, 'p', array('class'=>'MsoNormal', 'style'=>'margin-left:35.4pt'), '[quote]', '[/quote]'); + // Outlook-Quote - Variant 1 + node2bbcode($doc, 'p', array('class' => 'MsoNormal', 'style' => 'margin-left:35.4pt'), '[quote]', '[/quote]'); - // Outlook-Quote - Variant 2 - node2bbcode($doc, 'div', array('style'=>'border:none;border-left:solid blue 1.5pt;padding:0cm 0cm 0cm 4.0pt'), '[quote]', '[/quote]'); + // Outlook-Quote - Variant 2 + node2bbcode($doc, 'div', array('style' => 'border:none;border-left:solid blue 1.5pt;padding:0cm 0cm 0cm 4.0pt'), '[quote]', '[/quote]'); - // MyBB-Stuff - node2bbcode($doc, 'span', array('style'=>'text-decoration: underline;'), '[u]', '[/u]'); - node2bbcode($doc, 'span', array('style'=>'font-style: italic;'), '[i]', '[/i]'); - node2bbcode($doc, 'span', array('style'=>'font-weight: bold;'), '[b]', '[/b]'); + // MyBB-Stuff + node2bbcode($doc, 'span', array('style' => 'text-decoration: underline;'), '[u]', '[/u]'); + node2bbcode($doc, 'span', array('style' => 'font-style: italic;'), '[i]', '[/i]'); + node2bbcode($doc, 'span', array('style' => 'font-weight: bold;'), '[b]', '[/b]'); - /*node2bbcode($doc, 'font', array('face'=>'/([\w ]+)/', 'size'=>'/(\d+)/', 'color'=>'/(.+)/'), '[font=$1][size=$2][color=$3]', '[/color][/size][/font]'); - node2bbcode($doc, 'font', array('size'=>'/(\d+)/', 'color'=>'/(.+)/'), '[size=$1][color=$2]', '[/color][/size]'); - node2bbcode($doc, 'font', array('face'=>'/([\w ]+)/', 'size'=>'/(.+)/'), '[font=$1][size=$2]', '[/size][/font]'); - node2bbcode($doc, 'font', array('face'=>'/([\w ]+)/', 'color'=>'/(.+)/'), '[font=$1][color=$3]', '[/color][/font]'); - node2bbcode($doc, 'font', array('face'=>'/([\w ]+)/'), '[font=$1]', '[/font]'); - node2bbcode($doc, 'font', array('size'=>'/(\d+)/'), '[size=$1]', '[/size]'); - node2bbcode($doc, 'font', array('color'=>'/(.+)/'), '[color=$1]', '[/color]'); + /*node2bbcode($doc, 'font', array('face'=>'/([\w ]+)/', 'size'=>'/(\d+)/', 'color'=>'/(.+)/'), '[font=$1][size=$2][color=$3]', '[/color][/size][/font]'); + node2bbcode($doc, 'font', array('size'=>'/(\d+)/', 'color'=>'/(.+)/'), '[size=$1][color=$2]', '[/color][/size]'); + node2bbcode($doc, 'font', array('face'=>'/([\w ]+)/', 'size'=>'/(.+)/'), '[font=$1][size=$2]', '[/size][/font]'); + node2bbcode($doc, 'font', array('face'=>'/([\w ]+)/', 'color'=>'/(.+)/'), '[font=$1][color=$3]', '[/color][/font]'); + node2bbcode($doc, 'font', array('face'=>'/([\w ]+)/'), '[font=$1]', '[/font]'); + node2bbcode($doc, 'font', array('size'=>'/(\d+)/'), '[size=$1]', '[/size]'); + node2bbcode($doc, 'font', array('color'=>'/(.+)/'), '[color=$1]', '[/color]'); */ - // Untested - //node2bbcode($doc, 'span', array('style'=>'/.*font-size:\s*(.+?)[,;].*font-family:\s*(.+?)[,;].*color:\s*(.+?)[,;].*/'), '[size=$1][font=$2][color=$3]', '[/color][/font][/size]'); - //node2bbcode($doc, 'span', array('style'=>'/.*font-size:\s*(\d+)[,;].*/'), '[size=$1]', '[/size]'); - //node2bbcode($doc, 'span', array('style'=>'/.*font-size:\s*(.+?)[,;].*/'), '[size=$1]', '[/size]'); + // Untested + //node2bbcode($doc, 'span', array('style'=>'/.*font-size:\s*(.+?)[,;].*font-family:\s*(.+?)[,;].*color:\s*(.+?)[,;].*/'), '[size=$1][font=$2][color=$3]', '[/color][/font][/size]'); + //node2bbcode($doc, 'span', array('style'=>'/.*font-size:\s*(\d+)[,;].*/'), '[size=$1]', '[/size]'); + //node2bbcode($doc, 'span', array('style'=>'/.*font-size:\s*(.+?)[,;].*/'), '[size=$1]', '[/size]'); - node2bbcode($doc, 'span', array('style'=>'/.*color:\s*(.+?)[,;].*/'), '[color="$1"]', '[/color]'); - //node2bbcode($doc, 'span', array('style'=>'/.*font-family:\s*(.+?)[,;].*/'), '[font=$1]', '[/font]'); + node2bbcode($doc, 'span', array('style' => '/.*color:\s*(.+?)[,;].*/'), '[color="$1"]', '[/color]'); + //node2bbcode($doc, 'span', array('style'=>'/.*font-family:\s*(.+?)[,;].*/'), '[font=$1]', '[/font]'); - //node2bbcode($doc, 'div', array('style'=>'/.*font-family:\s*(.+?)[,;].*font-size:\s*(\d+?)pt.*/'), '[font=$1][size=$2]', '[/size][/font]'); - //node2bbcode($doc, 'div', array('style'=>'/.*font-family:\s*(.+?)[,;].*font-size:\s*(\d+?)px.*/'), '[font=$1][size=$2]', '[/size][/font]'); - //node2bbcode($doc, 'div', array('style'=>'/.*font-family:\s*(.+?)[,;].*/'), '[font=$1]', '[/font]'); + //node2bbcode($doc, 'div', array('style'=>'/.*font-family:\s*(.+?)[,;].*font-size:\s*(\d+?)pt.*/'), '[font=$1][size=$2]', '[/size][/font]'); + //node2bbcode($doc, 'div', array('style'=>'/.*font-family:\s*(.+?)[,;].*font-size:\s*(\d+?)px.*/'), '[font=$1][size=$2]', '[/size][/font]'); + //node2bbcode($doc, 'div', array('style'=>'/.*font-family:\s*(.+?)[,;].*/'), '[font=$1]', '[/font]'); - node2bbcode($doc, 'strong', [], '[b]', '[/b]'); - node2bbcode($doc, 'em', [], '[i]', '[/i]'); - node2bbcode($doc, 'b', [], '[b]', '[/b]'); - node2bbcode($doc, 'i', [], '[i]', '[/i]'); - node2bbcode($doc, 'u', [], '[u]', '[/u]'); - node2bbcode($doc, 's', [], '[s]', '[/s]'); + node2bbcode($doc, 'strong', [], '[b]', '[/b]'); + node2bbcode($doc, 'em', [], '[i]', '[/i]'); + node2bbcode($doc, 'b', [], '[b]', '[/b]'); + node2bbcode($doc, 'i', [], '[i]', '[/i]'); + node2bbcode($doc, 'u', [], '[u]', '[/u]'); + node2bbcode($doc, 's', [], '[s]', '[/s]'); - node2bbcode($doc, 'big', [], "[size=large]", "[/size]"); - node2bbcode($doc, 'small', [], "[size=small]", "[/size]"); + node2bbcode($doc, 'big', [], "[size=large]", "[/size]"); + node2bbcode($doc, 'small', [], "[size=small]", "[/size]"); - node2bbcode($doc, 'blockquote', [], '[quote]', '[/quote]'); + node2bbcode($doc, 'blockquote', [], '[quote]', '[/quote]'); - node2bbcode($doc, 'br', [], "\n", ''); + node2bbcode($doc, 'br', [], "\n", ''); - node2bbcode($doc, 'p', array('class'=>'MsoNormal'), "\n", ""); - node2bbcode($doc, 'div', array('class'=>'MsoNormal'), "\r", ""); + node2bbcode($doc, 'p', array('class' => 'MsoNormal'), "\n", ""); + node2bbcode($doc, 'div', array('class' => 'MsoNormal'), "\r", ""); - node2bbcode($doc, 'span', [], "", ""); + node2bbcode($doc, 'span', [], "", ""); - node2bbcode($doc, 'span', [], "", ""); - node2bbcode($doc, 'pre', [], "", ""); - node2bbcode($doc, 'div', [], "\r", "\r"); - node2bbcode($doc, 'p', [], "\n", "\n"); + node2bbcode($doc, 'span', [], "", ""); + node2bbcode($doc, 'pre', [], "", ""); + node2bbcode($doc, 'div', [], "\r", "\r"); + node2bbcode($doc, 'p', [], "\n", "\n"); - node2bbcode($doc, 'ul', [], "[list]", "[/list]"); - node2bbcode($doc, 'ol', [], "[list=1]", "[/list]"); - node2bbcode($doc, 'li', [], "[*]", ""); + node2bbcode($doc, 'ul', [], "[list]", "[/list]"); + node2bbcode($doc, 'ol', [], "[list=1]", "[/list]"); + node2bbcode($doc, 'li', [], "[*]", ""); - node2bbcode($doc, 'hr', [], "[hr]", ""); + node2bbcode($doc, 'hr', [], "[hr]", ""); -// node2bbcode($doc, 'table', [], "", ""); -// node2bbcode($doc, 'tr', [], "\n", ""); -// node2bbcode($doc, 'td', [], "\t", ""); +// node2bbcode($doc, 'table', [], "", ""); +// node2bbcode($doc, 'tr', [], "\n", ""); +// node2bbcode($doc, 'td', [], "\t", ""); - node2bbcode($doc, 'table', [], "[table]", "[/table]"); - node2bbcode($doc, 'th', [], "[th]", "[/th]"); - node2bbcode($doc, 'tr', [], "[tr]", "[/tr]"); - node2bbcode($doc, 'td', [], "[td]", "[/td]"); + node2bbcode($doc, 'table', [], "[table]", "[/table]"); + node2bbcode($doc, 'th', [], "[th]", "[/th]"); + node2bbcode($doc, 'tr', [], "[tr]", "[/tr]"); + node2bbcode($doc, 'td', [], "[td]", "[/td]"); - node2bbcode($doc, 'h1', [], "\n\n[h1]", "[/h1]\n"); - node2bbcode($doc, 'h2', [], "\n\n[h2]", "[/h2]\n"); - node2bbcode($doc, 'h3', [], "\n\n[h3]", "[/h3]\n"); - node2bbcode($doc, 'h4', [], "\n\n[h4]", "[/h4]\n"); - node2bbcode($doc, 'h5', [], "\n\n[h5]", "[/h5]\n"); - node2bbcode($doc, 'h6', [], "\n\n[h6]", "[/h6]\n"); + node2bbcode($doc, 'h1', [], "\n\n[h1]", "[/h1]\n"); + node2bbcode($doc, 'h2', [], "\n\n[h2]", "[/h2]\n"); + node2bbcode($doc, 'h3', [], "\n\n[h3]", "[/h3]\n"); + node2bbcode($doc, 'h4', [], "\n\n[h4]", "[/h4]\n"); + node2bbcode($doc, 'h5', [], "\n\n[h5]", "[/h5]\n"); + node2bbcode($doc, 'h6', [], "\n\n[h6]", "[/h6]\n"); - node2bbcode($doc, 'a', array('href'=>'/(.+)/'), '[url=$1]', '[/url]'); + node2bbcode($doc, 'a', array('href' => '/(.+)/'), '[url=$1]', '[/url]'); - node2bbcode($doc, 'img', array('src'=>'/(.+)/', 'width'=>'/(\d+)/', 'height'=>'/(\d+)/'), '[img=$2x$3]$1', '[/img]'); - node2bbcode($doc, 'img', array('src'=>'/(.+)/'), '[img]$1', '[/img]'); + node2bbcode($doc, 'img', array('src' => '/(.+)/', 'width' => '/(\d+)/', 'height' => '/(\d+)/'), '[img=$2x$3]$1', '[/img]'); + node2bbcode($doc, 'img', array('src' => '/(.+)/'), '[img]$1', '[/img]'); - node2bbcode($doc, 'video', array('src'=>'/(.+)/', 'poster'=>'/(.+)/'), '[video poster="$2"]$1', '[/video]'); - node2bbcode($doc, 'video', array('src'=>'/(.+)/'), '[video]$1', '[/video]'); - node2bbcode($doc, 'audio', array('src'=>'/(.+)/'), '[audio]$1', '[/audio]'); -// node2bbcode($doc, 'iframe', array('src'=>'/(.+)/'), '[iframe]$1', '[/iframe]'); + node2bbcode($doc, 'video', array('src' => '/(.+)/', 'poster' => '/(.+)/'), '[video poster="$2"]$1', '[/video]'); + node2bbcode($doc, 'video', array('src' => '/(.+)/'), '[video]$1', '[/video]'); + node2bbcode($doc, 'audio', array('src' => '/(.+)/'), '[audio]$1', '[/audio]'); +// node2bbcode($doc, 'iframe', array('src'=>'/(.+)/'), '[iframe]$1', '[/iframe]'); - node2bbcode($doc, 'code', [], '[code]', '[/code]'); + node2bbcode($doc, 'code', [], '[code]', '[/code]'); - $message = $doc->saveHTML(); + $message = $doc->saveHTML(); - // I'm removing something really disturbing - // Don't know exactly what it is - $message = str_replace(chr(194).chr(160), ' ', $message); + // I'm removing something really disturbing + // Don't know exactly what it is + $message = str_replace(chr(194) . chr(160), ' ', $message); - $message = str_replace(" ", " ", $message); + $message = str_replace(" ", " ", $message); - // removing multiple DIVs - $message = preg_replace('=\r *\r=i', "\n", $message); - $message = str_replace("\r", "\n", $message); + // removing multiple DIVs + $message = preg_replace('=\r *\r=i', "\n", $message); + $message = str_replace("\r", "\n", $message); - call_hooks('html2bbcode', $message); + call_hooks('html2bbcode', $message); - $message = strip_tags($message); + $message = strip_tags($message); - $message = html_entity_decode($message, ENT_QUOTES, 'UTF-8'); + $message = html_entity_decode($message, ENT_QUOTES, 'UTF-8'); - $message = str_replace(array("<"), array("<"), $message); + $message = str_replace(array("<"), array("<"), $message); - // remove quotes if they don't make sense - $message = preg_replace('=\[/quote\][\s]*\[quote\]=i', "\n", $message); + // remove quotes if they don't make sense + $message = preg_replace('=\[/quote\][\s]*\[quote\]=i', "\n", $message); - $message = preg_replace('=\[quote\]\s*=i', "[quote]", $message); - $message = preg_replace('=\s*\[/quote\]=i', "[/quote]", $message); + $message = preg_replace('=\[quote\]\s*=i', "[quote]", $message); + $message = preg_replace('=\s*\[/quote\]=i', "[/quote]", $message); - do { - $oldmessage = $message; - $message = str_replace("\n \n", "\n\n", $message); - } while ($oldmessage != $message); + do { + $oldmessage = $message; + $message = str_replace("\n \n", "\n\n", $message); + } while ($oldmessage != $message); - do { - $oldmessage = $message; - $message = str_replace("\n\n\n", "\n\n", $message); - } while ($oldmessage != $message); + do { + $oldmessage = $message; + $message = str_replace("\n\n\n", "\n\n", $message); + } while ($oldmessage != $message); - do { - $oldmessage = $message; - $message = str_replace(array( - "[/size]\n\n", - "\n[hr]", - "[hr]\n", - "\n[list", - "[/list]\n", - "\n[/", - "[list]\n", - "[list=1]\n", - "\n[*]"), - array( - "[/size]\n", - "[hr]", - "[hr]", - "[list", - "[/list]", - "[/", - "[list]", - "[list=1]", - "[*]"), - $message); - } while ($message != $oldmessage); + do { + $oldmessage = $message; + $message = str_replace( + array( + "[/size]\n\n", + "\n[hr]", + "[hr]\n", + "\n[list", + "[/list]\n", + "\n[/", + "[list]\n", + "[list=1]\n", + "\n[*]"), + array( + "[/size]\n", + "[hr]", + "[hr]", + "[list", + "[/list]", + "[/", + "[list]", + "[list=1]", + "[*]"), + $message + ); + } while ($message != $oldmessage); - $message = str_replace(array('[b][b]', '[/b][/b]', '[i][i]', '[/i][/i]'), - array('[b]', '[/b]', '[i]', '[/i]'), $message); + $message = str_replace( + array('[b][b]', '[/b][/b]', '[i][i]', '[/i][/i]'), + array('[b]', '[/b]', '[i]', '[/i]'), + $message + ); - // Handling Yahoo style of mails - // $message = str_replace('[hr][b]From:[/b]', '[quote][b]From:[/b]', $message); + // Handling Yahoo style of mails + // $message = str_replace('[hr][b]From:[/b]', '[quote][b]From:[/b]', $message); - $message = htmlspecialchars($message,ENT_COMPAT,'UTF-8',false); - return(trim($message)); + $message = htmlspecialchars($message, ENT_COMPAT, 'UTF-8', false); + return(trim($message)); } - diff --git a/include/html2plain.php b/include/html2plain.php index 8ad537242..65697c6f9 100644 --- a/include/html2plain.php +++ b/include/html2plain.php @@ -1,230 +1,246 @@ - 0) and strlen($line) > $wraplen) { - $newline = trim(substr($line, 0, $pos)); - if ($level > 0) - $newline = str_repeat(">", $level).' '.$newline; + if (($pos > 0) and strlen($line) > $wraplen) { + $newline = trim(substr($line, 0, $pos)); + if ($level > 0) { + $newline = str_repeat(">", $level) . ' ' . $newline; + } - $newlines[] = $newline." "; - $line = substr($line, $pos+1); - } + $newlines[] = $newline . " "; + $line = substr($line, $pos + 1); + } + } while ((strlen($line) > $wraplen) and !($oldline == $line)); - } while ((strlen($line) > $wraplen) and !($oldline == $line)); + if ($level > 0) { + $line = str_repeat(">", $level) . ' ' . $line; + } - if ($level > 0) - $line = str_repeat(">", $level).' '.$line; - - $newlines[] = $line; + $newlines[] = $line; - return(implode("\n", $newlines)); + return(implode("\n", $newlines)); } function quotelevel($message, $wraplength = 75) { - $lines = explode("\n", $message); + $lines = explode("\n", $message); - $newlines = []; - $level = 0; - foreach($lines as $line) {; - $line = trim($line); - $startquote = false; - while (strpos("*".$line, '[quote]') > 0) { - $level++; - $pos = strpos($line, '[quote]'); - $line = substr($line, 0, $pos).substr($line, $pos+7); - $startquote = true; - } + $newlines = []; + $level = 0; + foreach ($lines as $line) { + $line = trim($line); + $startquote = false; + while (strpos("*" . $line, '[quote]') > 0) { + $level++; + $pos = strpos($line, '[quote]'); + $line = substr($line, 0, $pos) . substr($line, $pos + 7); + $startquote = true; + } - $currlevel = $level; + $currlevel = $level; - while (strpos("*".$line, '[/quote]') > 0) { - $level--; - if ($level < 0) - $level = 0; + while (strpos("*" . $line, '[/quote]') > 0) { + $level--; + if ($level < 0) { + $level = 0; + } - $pos = strpos($line, '[/quote]'); - $line = substr($line, 0, $pos).substr($line, $pos+8); - } + $pos = strpos($line, '[/quote]'); + $line = substr($line, 0, $pos) . substr($line, $pos + 8); + } - if (!$startquote or ($line != '')) - $newlines[] = breaklines($line, $currlevel, $wraplength); - } - return(implode("\n", $newlines)); + if (!$startquote or ($line != '')) { + $newlines[] = breaklines($line, $currlevel, $wraplength); + } + } + return(implode("\n", $newlines)); } -function collecturls($message) { - $pattern = '/(.*?)<\/a>/is'; - preg_match_all($pattern, $message, $result, PREG_SET_ORDER); +function collecturls($message) +{ + $pattern = '/(.*?)<\/a>/is'; + preg_match_all($pattern, $message, $result, PREG_SET_ORDER); - $urls = []; - foreach ($result as $treffer) { - // A list of some links that should be ignored - $list = array("/user/", "/tag/", "/group/", "/profile/", "/channel/", "/search?search=", "/search?tag=", "mailto:", "/u/", "/node/", - "//facebook.com/profile.php?id=", "//plus.google.com/"); - foreach ($list as $listitem) - if (strpos($treffer[1], $listitem) !== false) - $ignore = true; + $urls = []; + foreach ($result as $treffer) { + // A list of some links that should be ignored + $list = array("/user/", "/tag/", "/group/", "/profile/", "/channel/", "/search?search=", "/search?tag=", "mailto:", "/u/", "/node/", + "//facebook.com/profile.php?id=", "//plus.google.com/"); + foreach ($list as $listitem) { + if (strpos($treffer[1], $listitem) !== false) { + $ignore = true; + } + } - if ((strpos($treffer[1], "//plus.google.com/") !== false) and (strpos($treffer[1], "/posts") !== false)) - $ignore = false; + if ((strpos($treffer[1], "//plus.google.com/") !== false) and (strpos($treffer[1], "/posts") !== false)) { + $ignore = false; + } - if (!$ignore) - $urls[$treffer[1]] = $treffer[1]; - } - return($urls); + if (!$ignore) { + $urls[$treffer[1]] = $treffer[1]; + } + } + return($urls); } function html2plain($html, $wraplength = 75, $compact = false) { - $message = str_replace("\r", "", $html); + $message = str_replace("\r", "", $html); - if (! $message) { - return $message; - } - $doc = new DOMDocument(); - $doc->preserveWhiteSpace = false; + if (! $message) { + return $message; + } + $doc = new DOMDocument(); + $doc->preserveWhiteSpace = false; - - $tmp_message = mb_convert_encoding($message, 'HTML-ENTITIES', "UTF-8"); - if ($tmp_message === false) { - logger('mb_convert_encoding failed: ' . $tmp_message); - return EMPTY_STR; - } - @$doc->loadHTML($tmp_message); + $tmp_message = mb_convert_encoding($message, 'HTML-ENTITIES', "UTF-8"); + if ($tmp_message === false) { + logger('mb_convert_encoding failed: ' . $tmp_message); + return EMPTY_STR; + } - $xpath = new DOMXPath($doc); - $list = $xpath->query("//pre"); - foreach ($list as $node) { - $node->nodeValue = str_replace("\n", "\r", htmlspecialchars($node->nodeValue)); - } + @$doc->loadHTML($tmp_message); - $message = $doc->saveHTML(); - $message = str_replace(array("\n<", ">\n", "\r", "\n", "\xC3\x82\xC2\xA0"), array("<", ">", "
                    ", " ", ""), $message); - $message = preg_replace('= [\s]*=i', " ", $message); + $xpath = new DOMXPath($doc); + $list = $xpath->query("//pre"); + foreach ($list as $node) { + $node->nodeValue = str_replace("\n", "\r", htmlspecialchars($node->nodeValue)); + } - // Collecting all links - $urls = collecturls($message); + $message = $doc->saveHTML(); + $message = str_replace(array("\n<", ">\n", "\r", "\n", "\xC3\x82\xC2\xA0"), array("<", ">", "
                    ", " ", ""), $message); + $message = preg_replace('= [\s]*=i', " ", $message); - @$doc->loadHTML($message); + // Collecting all links + $urls = collecturls($message); - node2bbcode($doc, 'html', [], '', ''); - node2bbcode($doc, 'body', [], '', ''); + @$doc->loadHTML($message); - // MyBB-Auszeichnungen - /* - node2bbcode($doc, 'span', array('style'=>'text-decoration: underline;'), '_', '_'); - node2bbcode($doc, 'span', array('style'=>'font-style: italic;'), '/', '/'); - node2bbcode($doc, 'span', array('style'=>'font-weight: bold;'), '*', '*'); + node2bbcode($doc, 'html', [], '', ''); + node2bbcode($doc, 'body', [], '', ''); - node2bbcode($doc, 'strong', [], '*', '*'); - node2bbcode($doc, 'b', [], '*', '*'); - node2bbcode($doc, 'i', [], '/', '/'); - node2bbcode($doc, 'u', [], '_', '_'); - */ + // MyBB-Auszeichnungen + /* + node2bbcode($doc, 'span', array('style'=>'text-decoration: underline;'), '_', '_'); + node2bbcode($doc, 'span', array('style'=>'font-style: italic;'), '/', '/'); + node2bbcode($doc, 'span', array('style'=>'font-weight: bold;'), '*', '*'); - if ($compact) - node2bbcode($doc, 'blockquote', [], "»", "«"); - else - node2bbcode($doc, 'blockquote', [], '[quote]', "[/quote]\n"); + node2bbcode($doc, 'strong', [], '*', '*'); + node2bbcode($doc, 'b', [], '*', '*'); + node2bbcode($doc, 'i', [], '/', '/'); + node2bbcode($doc, 'u', [], '_', '_'); + */ - node2bbcode($doc, 'br', [], "\n", ''); + if ($compact) { + node2bbcode($doc, 'blockquote', [], "»", "«"); + } else { + node2bbcode($doc, 'blockquote', [], '[quote]', "[/quote]\n"); + } - node2bbcode($doc, 'span', [], "", ""); - node2bbcode($doc, 'pre', [], "", ""); - node2bbcode($doc, 'div', [], "\r", "\r"); - node2bbcode($doc, 'p', [], "\n", "\n"); + node2bbcode($doc, 'br', [], "\n", ''); - //node2bbcode($doc, 'ul', [], "\n[list]", "[/list]\n"); - //node2bbcode($doc, 'ol', [], "\n[list=1]", "[/list]\n"); - node2bbcode($doc, 'li', [], "\n* ", "\n"); + node2bbcode($doc, 'span', [], "", ""); + node2bbcode($doc, 'pre', [], "", ""); + node2bbcode($doc, 'div', [], "\r", "\r"); + node2bbcode($doc, 'p', [], "\n", "\n"); - node2bbcode($doc, 'hr', [], "\n".str_repeat("-", 70)."\n", ""); + //node2bbcode($doc, 'ul', [], "\n[list]", "[/list]\n"); + //node2bbcode($doc, 'ol', [], "\n[list=1]", "[/list]\n"); + node2bbcode($doc, 'li', [], "\n* ", "\n"); - node2bbcode($doc, 'tr', [], "\n", ""); - node2bbcode($doc, 'td', [], "\t", ""); + node2bbcode($doc, 'hr', [], "\n" . str_repeat("-", 70) . "\n", ""); - node2bbcode($doc, 'h1', [], "\n\n*", "*\n"); - node2bbcode($doc, 'h2', [], "\n\n*", "*\n"); - node2bbcode($doc, 'h3', [], "\n\n*", "*\n"); - node2bbcode($doc, 'h4', [], "\n\n*", "*\n"); - node2bbcode($doc, 'h5', [], "\n\n*", "*\n"); - node2bbcode($doc, 'h6', [], "\n\n*", "*\n"); + node2bbcode($doc, 'tr', [], "\n", ""); + node2bbcode($doc, 'td', [], "\t", ""); - // Problem: there is no reliable way to detect if it is a link to a tag or profile - //node2bbcode($doc, 'a', array('href'=>'/(.+)/'), ' $1 ', '', true); - node2bbcode($doc, 'a', array('href'=>'/(.+)/', 'rel'=>'oembed'), ' $1 ', '', true); - //node2bbcode($doc, 'img', array('alt'=>'/(.+)/'), '$1', ''); - //node2bbcode($doc, 'img', array('title'=>'/(.+)/'), '$1', ''); - //node2bbcode($doc, 'img', [], '', ''); - if (!$compact) - node2bbcode($doc, 'img', array('src'=>'/(.+)/'), '[img]$1', '[/img]'); - else - node2bbcode($doc, 'img', array('src'=>'/(.+)/'), '', ''); + node2bbcode($doc, 'h1', [], "\n\n*", "*\n"); + node2bbcode($doc, 'h2', [], "\n\n*", "*\n"); + node2bbcode($doc, 'h3', [], "\n\n*", "*\n"); + node2bbcode($doc, 'h4', [], "\n\n*", "*\n"); + node2bbcode($doc, 'h5', [], "\n\n*", "*\n"); + node2bbcode($doc, 'h6', [], "\n\n*", "*\n"); - node2bbcode($doc, 'iframe', array('src'=>'/(.+)/'), ' $1 ', '', true); + // Problem: there is no reliable way to detect if it is a link to a tag or profile + //node2bbcode($doc, 'a', array('href'=>'/(.+)/'), ' $1 ', '', true); + node2bbcode($doc, 'a', array('href' => '/(.+)/', 'rel' => 'oembed'), ' $1 ', '', true); + //node2bbcode($doc, 'img', array('alt'=>'/(.+)/'), '$1', ''); + //node2bbcode($doc, 'img', array('title'=>'/(.+)/'), '$1', ''); + //node2bbcode($doc, 'img', [], '', ''); + if (!$compact) { + node2bbcode($doc, 'img', array('src' => '/(.+)/'), '[img]$1', '[/img]'); + } else { + node2bbcode($doc, 'img', array('src' => '/(.+)/'), '', ''); + } - $message = $doc->saveHTML(); + node2bbcode($doc, 'iframe', array('src' => '/(.+)/'), ' $1 ', '', true); - if (!$compact) { - $message = str_replace("[img]", "", $message); - $message = str_replace("[/img]", "", $message); - } + $message = $doc->saveHTML(); - // was ersetze ich da? - // Irgendein stoerrisches UTF-Zeug - $message = str_replace(chr(194).chr(160), ' ', $message); + if (!$compact) { + $message = str_replace("[img]", "", $message); + $message = str_replace("[/img]", "", $message); + } - $message = str_replace(" ", " ", $message); + // was ersetze ich da? + // Irgendein stoerrisches UTF-Zeug + $message = str_replace(chr(194) . chr(160), ' ', $message); - // Aufeinanderfolgende DIVs - $message = preg_replace('=\r *\r=i', "\n", $message); - $message = str_replace("\r", "\n", $message); + $message = str_replace(" ", " ", $message); - $message = strip_tags($message); + // Aufeinanderfolgende DIVs + $message = preg_replace('=\r *\r=i', "\n", $message); + $message = str_replace("\r", "\n", $message); - $message = html_entity_decode($message, ENT_QUOTES, 'UTF-8'); + $message = strip_tags($message); - if (!$compact) { - $counter = 1; - foreach ($urls as $id=>$url) - if ($url && strpos($message, $url) === false) - $message .= "\n".$url." "; - //$message .= "\n[".($counter++)."] ".$url; - } + $message = html_entity_decode($message, ENT_QUOTES, 'UTF-8'); - do { - $oldmessage = $message; - $message = str_replace("\n\n\n", "\n\n", $message); - } while ($oldmessage != $message); + if (!$compact) { + $counter = 1; + foreach ($urls as $id => $url) { + if ($url && strpos($message, $url) === false) { + $message .= "\n" . $url . " "; + } + } + //$message .= "\n[".($counter++)."] ".$url; + } - $message = quotelevel(trim($message), $wraplength); + do { + $oldmessage = $message; + $message = str_replace("\n\n\n", "\n\n", $message); + } while ($oldmessage != $message); - return(trim($message)); + $message = quotelevel(trim($message), $wraplength); + + return(trim($message)); } - diff --git a/include/hubloc.php b/include/hubloc.php index 7a9a65708..53ad35ccb 100644 --- a/include/hubloc.php +++ b/include/hubloc.php @@ -1,4 +1,5 @@ ((array_key_exists('hubloc_guid',$arr)) ? $arr['hubloc_guid'] : ''), - 'hubloc_guid_sig' => ((array_key_exists('hubloc_guid_sig',$arr)) ? $arr['hubloc_guid_sig'] : ''), - 'hubloc_id_url' => ((array_key_exists('hubloc_id_url',$arr)) ? $arr['hubloc_id_url'] : ''), - 'hubloc_hash' => ((array_key_exists('hubloc_hash',$arr)) ? $arr['hubloc_hash'] : ''), - 'hubloc_addr' => ((array_key_exists('hubloc_addr',$arr)) ? $arr['hubloc_addr'] : ''), - 'hubloc_network' => ((array_key_exists('hubloc_network',$arr)) ? $arr['hubloc_network'] : ''), - 'hubloc_flags' => ((array_key_exists('hubloc_flags',$arr)) ? $arr['hubloc_flags'] : 0), - 'hubloc_status' => ((array_key_exists('hubloc_status',$arr)) ? $arr['hubloc_status'] : 0), - 'hubloc_url' => ((array_key_exists('hubloc_url',$arr)) ? $arr['hubloc_url'] : ''), - 'hubloc_url_sig' => ((array_key_exists('hubloc_url_sig',$arr)) ? $arr['hubloc_url_sig'] : ''), - 'hubloc_site_id' => ((array_key_exists('hubloc_site_id',$arr)) ? $arr['hubloc_site_id'] : ''), - 'hubloc_host' => ((array_key_exists('hubloc_host',$arr)) ? $arr['hubloc_host'] : ''), - 'hubloc_callback' => ((array_key_exists('hubloc_callback',$arr)) ? $arr['hubloc_callback'] : ''), - 'hubloc_connect' => ((array_key_exists('hubloc_connect',$arr)) ? $arr['hubloc_connect'] : ''), - 'hubloc_sitekey' => ((array_key_exists('hubloc_sitekey',$arr)) ? $arr['hubloc_sitekey'] : ''), - 'hubloc_updated' => ((array_key_exists('hubloc_updated',$arr)) ? $arr['hubloc_updated'] : NULL_DATE), - 'hubloc_connected' => ((array_key_exists('hubloc_connected',$arr)) ? $arr['hubloc_connected'] : NULL_DATE), - 'hubloc_primary' => ((array_key_exists('hubloc_primary',$arr)) ? $arr['hubloc_primary'] : 0), - 'hubloc_orphancheck' => ((array_key_exists('hubloc_orphancheck',$arr)) ? $arr['hubloc_orphancheck'] : 0), - 'hubloc_error' => ((array_key_exists('hubloc_error',$arr)) ? $arr['hubloc_error'] : 0), - 'hubloc_deleted' => ((array_key_exists('hubloc_deleted',$arr)) ? $arr['hubloc_deleted'] : 0) - ]; + $store = [ + 'hubloc_guid' => ((array_key_exists('hubloc_guid', $arr)) ? $arr['hubloc_guid'] : ''), + 'hubloc_guid_sig' => ((array_key_exists('hubloc_guid_sig', $arr)) ? $arr['hubloc_guid_sig'] : ''), + 'hubloc_id_url' => ((array_key_exists('hubloc_id_url', $arr)) ? $arr['hubloc_id_url'] : ''), + 'hubloc_hash' => ((array_key_exists('hubloc_hash', $arr)) ? $arr['hubloc_hash'] : ''), + 'hubloc_addr' => ((array_key_exists('hubloc_addr', $arr)) ? $arr['hubloc_addr'] : ''), + 'hubloc_network' => ((array_key_exists('hubloc_network', $arr)) ? $arr['hubloc_network'] : ''), + 'hubloc_flags' => ((array_key_exists('hubloc_flags', $arr)) ? $arr['hubloc_flags'] : 0), + 'hubloc_status' => ((array_key_exists('hubloc_status', $arr)) ? $arr['hubloc_status'] : 0), + 'hubloc_url' => ((array_key_exists('hubloc_url', $arr)) ? $arr['hubloc_url'] : ''), + 'hubloc_url_sig' => ((array_key_exists('hubloc_url_sig', $arr)) ? $arr['hubloc_url_sig'] : ''), + 'hubloc_site_id' => ((array_key_exists('hubloc_site_id', $arr)) ? $arr['hubloc_site_id'] : ''), + 'hubloc_host' => ((array_key_exists('hubloc_host', $arr)) ? $arr['hubloc_host'] : ''), + 'hubloc_callback' => ((array_key_exists('hubloc_callback', $arr)) ? $arr['hubloc_callback'] : ''), + 'hubloc_connect' => ((array_key_exists('hubloc_connect', $arr)) ? $arr['hubloc_connect'] : ''), + 'hubloc_sitekey' => ((array_key_exists('hubloc_sitekey', $arr)) ? $arr['hubloc_sitekey'] : ''), + 'hubloc_updated' => ((array_key_exists('hubloc_updated', $arr)) ? $arr['hubloc_updated'] : NULL_DATE), + 'hubloc_connected' => ((array_key_exists('hubloc_connected', $arr)) ? $arr['hubloc_connected'] : NULL_DATE), + 'hubloc_primary' => ((array_key_exists('hubloc_primary', $arr)) ? $arr['hubloc_primary'] : 0), + 'hubloc_orphancheck' => ((array_key_exists('hubloc_orphancheck', $arr)) ? $arr['hubloc_orphancheck'] : 0), + 'hubloc_error' => ((array_key_exists('hubloc_error', $arr)) ? $arr['hubloc_error'] : 0), + 'hubloc_deleted' => ((array_key_exists('hubloc_deleted', $arr)) ? $arr['hubloc_deleted'] : 0) + ]; - return create_table_from_array('hubloc', $store); + return create_table_from_array('hubloc', $store); } -function site_store_lowlevel($arr) { +function site_store_lowlevel($arr) +{ - $store = [ - 'site_url' => ((array_key_exists('site_url',$arr)) ? $arr['site_url'] : ''), - 'site_access' => ((array_key_exists('site_access',$arr)) ? $arr['site_access'] : 0), - 'site_flags' => ((array_key_exists('site_flags',$arr)) ? $arr['site_flags'] : 0), - 'site_update' => ((array_key_exists('site_update',$arr)) ? $arr['site_update'] : NULL_DATE), - 'site_pull' => ((array_key_exists('site_pull',$arr)) ? $arr['site_pull'] : NULL_DATE), - 'site_sync' => ((array_key_exists('site_sync',$arr)) ? $arr['site_sync'] : NULL_DATE), - 'site_directory' => ((array_key_exists('site_directory',$arr)) ? $arr['site_directory'] : ''), - 'site_register' => ((array_key_exists('site_register',$arr)) ? $arr['site_register'] : 0), - 'site_sellpage' => ((array_key_exists('site_sellpage',$arr)) ? $arr['site_sellpage'] : ''), - 'site_location' => ((array_key_exists('site_location',$arr)) ? $arr['site_location'] : ''), - 'site_realm' => ((array_key_exists('site_realm',$arr)) ? $arr['site_realm'] : ''), - 'site_valid' => ((array_key_exists('site_valid',$arr)) ? $arr['site_valid'] : 0), - 'site_dead' => ((array_key_exists('site_dead',$arr)) ? $arr['site_dead'] : 0), - 'site_type' => ((array_key_exists('site_type',$arr)) ? $arr['site_type'] : 0), - 'site_project' => ((array_key_exists('site_project',$arr)) ? $arr['site_project'] : ''), - 'site_version' => ((array_key_exists('site_version',$arr)) ? $arr['site_version'] : ''), - 'site_crypto' => ((array_key_exists('site_crypto',$arr)) ? $arr['site_crypto'] : '') - ]; + $store = [ + 'site_url' => ((array_key_exists('site_url', $arr)) ? $arr['site_url'] : ''), + 'site_access' => ((array_key_exists('site_access', $arr)) ? $arr['site_access'] : 0), + 'site_flags' => ((array_key_exists('site_flags', $arr)) ? $arr['site_flags'] : 0), + 'site_update' => ((array_key_exists('site_update', $arr)) ? $arr['site_update'] : NULL_DATE), + 'site_pull' => ((array_key_exists('site_pull', $arr)) ? $arr['site_pull'] : NULL_DATE), + 'site_sync' => ((array_key_exists('site_sync', $arr)) ? $arr['site_sync'] : NULL_DATE), + 'site_directory' => ((array_key_exists('site_directory', $arr)) ? $arr['site_directory'] : ''), + 'site_register' => ((array_key_exists('site_register', $arr)) ? $arr['site_register'] : 0), + 'site_sellpage' => ((array_key_exists('site_sellpage', $arr)) ? $arr['site_sellpage'] : ''), + 'site_location' => ((array_key_exists('site_location', $arr)) ? $arr['site_location'] : ''), + 'site_realm' => ((array_key_exists('site_realm', $arr)) ? $arr['site_realm'] : ''), + 'site_valid' => ((array_key_exists('site_valid', $arr)) ? $arr['site_valid'] : 0), + 'site_dead' => ((array_key_exists('site_dead', $arr)) ? $arr['site_dead'] : 0), + 'site_type' => ((array_key_exists('site_type', $arr)) ? $arr['site_type'] : 0), + 'site_project' => ((array_key_exists('site_project', $arr)) ? $arr['site_project'] : ''), + 'site_version' => ((array_key_exists('site_version', $arr)) ? $arr['site_version'] : ''), + 'site_crypto' => ((array_key_exists('site_crypto', $arr)) ? $arr['site_crypto'] : '') + ]; - return create_table_from_array('site', $store); + return create_table_from_array('site', $store); } -function prune_hub_reinstalls() { +function prune_hub_reinstalls() +{ - $r = q("select site_url from site where site_type = %d", - intval(SITE_TYPE_ZOT) - ); - if($r) { - foreach($r as $rr) { - $x = q("select count(*) as t, hubloc_sitekey, max(hubloc_connected) as c from hubloc where hubloc_url = '%s' group by hubloc_sitekey order by c", - dbesc($rr['site_url']) - ); + $r = q( + "select site_url from site where site_type = %d", + intval(SITE_TYPE_ZOT) + ); + if ($r) { + foreach ($r as $rr) { + $x = q( + "select count(*) as t, hubloc_sitekey, max(hubloc_connected) as c from hubloc where hubloc_url = '%s' group by hubloc_sitekey order by c", + dbesc($rr['site_url']) + ); - // see if this url has more than one sitekey, indicating it has been re-installed. + // see if this url has more than one sitekey, indicating it has been re-installed. - if(count($x) > 1) { - $d1 = datetime_convert('UTC', 'UTC', $x[0]['c']); - $d2 = datetime_convert('UTC', 'UTC', 'now - 3 days'); + if (count($x) > 1) { + $d1 = datetime_convert('UTC', 'UTC', $x[0]['c']); + $d2 = datetime_convert('UTC', 'UTC', 'now - 3 days'); - // allow some slop period, say 3 days - just in case this is a glitch or transient occurrence - // Then remove any hublocs pointing to the oldest entry. + // allow some slop period, say 3 days - just in case this is a glitch or transient occurrence + // Then remove any hublocs pointing to the oldest entry. - if(($d1 < $d2) && ($x[0]['hubloc_sitekey'])) { - logger('prune_hub_reinstalls: removing dead hublocs at ' . $rr['site_url']); - $y = q("delete from hubloc where hubloc_sitekey = '%s'", - dbesc($x[0]['hubloc_sitekey']) - ); - } - } - } - } + if (($d1 < $d2) && ($x[0]['hubloc_sitekey'])) { + logger('prune_hub_reinstalls: removing dead hublocs at ' . $rr['site_url']); + $y = q( + "delete from hubloc where hubloc_sitekey = '%s'", + dbesc($x[0]['hubloc_sitekey']) + ); + } + } + } + } } @@ -116,60 +123,70 @@ function prune_hub_reinstalls() { * of an old bug or maybe a regression in some newer code. In any event, they * mess up communications and we have to take action if we find any. */ -function remove_obsolete_hublocs() { +function remove_obsolete_hublocs() +{ - logger('remove_obsolete_hublocs', LOGGER_DEBUG); + logger('remove_obsolete_hublocs', LOGGER_DEBUG); - // First make sure we have any hublocs (at all) with this URL and sitekey. - // We don't want to perform this operation while somebody is in the process - // of renaming their hub or installing certs. + // First make sure we have any hublocs (at all) with this URL and sitekey. + // We don't want to perform this operation while somebody is in the process + // of renaming their hub or installing certs. - $r = q("select hubloc_id from hubloc where hubloc_url = '%s' and hubloc_sitekey = '%s'", - dbesc(z_root()), - dbesc(get_config('system', 'pubkey')) - ); - if((! $r) || (! count($r))) - return; + $r = q( + "select hubloc_id from hubloc where hubloc_url = '%s' and hubloc_sitekey = '%s'", + dbesc(z_root()), + dbesc(get_config('system', 'pubkey')) + ); + if ((! $r) || (! count($r))) { + return; + } - // Good. We have at least one *valid* hubloc. + // Good. We have at least one *valid* hubloc. - // Do we have any invalid ones? + // Do we have any invalid ones? - $r = q("select hubloc_id from hubloc where hubloc_sitekey = '%s' and hubloc_url != '%s'", - dbesc(get_config('system', 'pubkey')), - dbesc(z_root()) - ); - $p = q("select hubloc_id from hubloc where hubloc_sitekey != '%s' and hubloc_url = '%s'", - dbesc(get_config('system', 'pubkey')), - dbesc(z_root()) - ); - if(is_array($r) && is_array($p)) - $r = array_merge($r, $p); + $r = q( + "select hubloc_id from hubloc where hubloc_sitekey = '%s' and hubloc_url != '%s'", + dbesc(get_config('system', 'pubkey')), + dbesc(z_root()) + ); + $p = q( + "select hubloc_id from hubloc where hubloc_sitekey != '%s' and hubloc_url = '%s'", + dbesc(get_config('system', 'pubkey')), + dbesc(z_root()) + ); + if (is_array($r) && is_array($p)) { + $r = array_merge($r, $p); + } - if(! $r) - return; + if (! $r) { + return; + } - // We've got invalid hublocs. Get rid of them. + // We've got invalid hublocs. Get rid of them. - logger('remove_obsolete_hublocs: removing ' . count($r) . ' hublocs.'); + logger('remove_obsolete_hublocs: removing ' . count($r) . ' hublocs.'); - $interval = ((get_config('system', 'delivery_interval') !== false) - ? intval(get_config('system', 'delivery_interval')) : 2 ); + $interval = ((get_config('system', 'delivery_interval') !== false) + ? intval(get_config('system', 'delivery_interval')) : 2 ); - foreach($r as $rr) { - q("update hubloc set hubloc_deleted = 1 where hubloc_id = %d", - intval($rr['hubloc_id']) - ); + foreach ($r as $rr) { + q( + "update hubloc set hubloc_deleted = 1 where hubloc_id = %d", + intval($rr['hubloc_id']) + ); - $x = q("select channel_id from channel where channel_hash = '%s' limit 1", - dbesc($rr['hubloc_hash']) - ); - if($x) { - Run::Summon( [ 'Notifier', 'refresh_all', $x[0]['channel_id'] ] ); - if($interval) - @time_sleep_until(microtime(true) + (float) $interval); - } - } + $x = q( + "select channel_id from channel where channel_hash = '%s' limit 1", + dbesc($rr['hubloc_hash']) + ); + if ($x) { + Run::Summon([ 'Notifier', 'refresh_all', $x[0]['channel_id'] ]); + if ($interval) { + @time_sleep_until(microtime(true) + (float) $interval); + } + } + } } @@ -180,67 +197,73 @@ function remove_obsolete_hublocs() { * hubloc primary selection. * * @param array $hubloc - * @return boolean + * @return bool */ -function hubloc_change_primary($hubloc) { +function hubloc_change_primary($hubloc) +{ - if(! is_array($hubloc)) { - logger('no hubloc'); - return false; - } + if (! is_array($hubloc)) { + logger('no hubloc'); + return false; + } - logger('setting primary: ' . $hubloc['hubloc_url'] . ((intval($hubloc['hubloc_primary'])) ? ' true' : ' false')); + logger('setting primary: ' . $hubloc['hubloc_url'] . ((intval($hubloc['hubloc_primary'])) ? ' true' : ' false')); - // See if this is a local hubloc and if so update the primary for the corresponding channel record. + // See if this is a local hubloc and if so update the primary for the corresponding channel record. - if($hubloc['hubloc_url'] === z_root()) { - $r = q("select channel_id from channel where channel_hash = '%s' limit 1", - dbesc($hubloc['hubloc_hash']) - ); - if($r) { - q("update channel set channel_primary = %d where channel_id = %d", - intval($hubloc['hubloc_primary']), - intval($r[0]['channel_id']) - ); - } - } + if ($hubloc['hubloc_url'] === z_root()) { + $r = q( + "select channel_id from channel where channel_hash = '%s' limit 1", + dbesc($hubloc['hubloc_hash']) + ); + if ($r) { + q( + "update channel set channel_primary = %d where channel_id = %d", + intval($hubloc['hubloc_primary']), + intval($r[0]['channel_id']) + ); + } + } - // we only need to proceed further if this particular hubloc is now primary + // we only need to proceed further if this particular hubloc is now primary - if(! (intval($hubloc['hubloc_primary']))) { - logger('not primary: ' . $hubloc['hubloc_url']); - return false; - } + if (! (intval($hubloc['hubloc_primary']))) { + logger('not primary: ' . $hubloc['hubloc_url']); + return false; + } - // do we even have an xchan for this hubloc and if so is it already set as primary? + // do we even have an xchan for this hubloc and if so is it already set as primary? - $r = q("select * from xchan where xchan_hash = '%s' limit 1", - dbesc($hubloc['hubloc_hash']) - ); - if(! $r) { - logger('xchan not found'); - return false; - } - if($r[0]['xchan_addr'] === $hubloc['hubloc_addr']) { - logger('xchan already changed'); - return false; - } + $r = q( + "select * from xchan where xchan_hash = '%s' limit 1", + dbesc($hubloc['hubloc_hash']) + ); + if (! $r) { + logger('xchan not found'); + return false; + } + if ($r[0]['xchan_addr'] === $hubloc['hubloc_addr']) { + logger('xchan already changed'); + return false; + } - $url = $hubloc['hubloc_url']; - $lwebbie = substr($hubloc['hubloc_addr'], 0, strpos($hubloc['hubloc_addr'], '@')); + $url = $hubloc['hubloc_url']; + $lwebbie = substr($hubloc['hubloc_addr'], 0, strpos($hubloc['hubloc_addr'], '@')); - $r = q("update xchan set xchan_addr = '%s', xchan_url = '%s', xchan_follow = '%s', xchan_connurl = '%s' where xchan_hash = '%s'", - dbesc($hubloc['hubloc_addr']), - dbesc($url . '/channel/' . $lwebbie), - dbesc($url . '/follow?f=&url=%s'), - dbesc($url . '/poco/' . $lwebbie), - dbesc($hubloc['hubloc_hash']) - ); - if(! $r) - logger('xchan_update failed.'); + $r = q( + "update xchan set xchan_addr = '%s', xchan_url = '%s', xchan_follow = '%s', xchan_connurl = '%s' where xchan_hash = '%s'", + dbesc($hubloc['hubloc_addr']), + dbesc($url . '/channel/' . $lwebbie), + dbesc($url . '/follow?f=&url=%s'), + dbesc($url . '/poco/' . $lwebbie), + dbesc($hubloc['hubloc_hash']) + ); + if (! $r) { + logger('xchan_update failed.'); + } - logger('primary hubloc changed.' . print_r($hubloc, true), LOGGER_DEBUG); - return true; + logger('primary hubloc changed.' . print_r($hubloc, true), LOGGER_DEBUG); + return true; } @@ -253,17 +276,20 @@ function hubloc_change_primary($hubloc) { * * @param string $posturl Hubloc callback url which to disable */ -function hubloc_mark_as_down($posturl) { - $r = q("update hubloc set hubloc_status = ( hubloc_status | %d ) where hubloc_callback = '%s'", - intval(HUBLOC_OFFLINE), - dbesc($posturl) - ); - // extract the baseurl and set site.site_dead to match - $m = parse_url($posturl); - $h = $m['scheme'] . '://' . $m['host']; - $r = q("update site set site_dead = 1 where site_url = '%s'", - dbesc($h) - ); +function hubloc_mark_as_down($posturl) +{ + $r = q( + "update hubloc set hubloc_status = ( hubloc_status | %d ) where hubloc_callback = '%s'", + intval(HUBLOC_OFFLINE), + dbesc($posturl) + ); + // extract the baseurl and set site.site_dead to match + $m = parse_url($posturl); + $h = $m['scheme'] . '://' . $m['host']; + $r = q( + "update site set site_dead = 1 where site_url = '%s'", + dbesc($h) + ); } @@ -272,40 +298,44 @@ function hubloc_mark_as_down($posturl) { * * @param string $netid network identity (typically xchan_hash or hubloc_hash) * @return string - */ + */ -function locations_by_netid($netid) { +function locations_by_netid($netid) +{ - $locs = q("select hubloc_addr as location from hubloc left join site on hubloc_url = site_url where hubloc_hash = '%s' and hubloc_deleted = 0 and site_dead = 0", - dbesc($netid) - ); + $locs = q( + "select hubloc_addr as location from hubloc left join site on hubloc_url = site_url where hubloc_hash = '%s' and hubloc_deleted = 0 and site_dead = 0", + dbesc($netid) + ); - - return array_elm_to_str($locs,'location',', ','trim_and_unpunify'); + return array_elm_to_str($locs, 'location', ', ', 'trim_and_unpunify'); } -function ping_site($url) { +function ping_site($url) +{ - $ret = array('success' => false); + $ret = array('success' => false); - $r = Zotlabs\Lib\Zotfinger::exec($url); + $r = Zotlabs\Lib\Zotfinger::exec($url); - if(! $r['data']) { - $ret['message'] = 'no answer from ' . $url; - return $ret; - } + if (! $r['data']) { + $ret['message'] = 'no answer from ' . $url; + return $ret; + } - $ret['success'] = true; - return $ret; + $ret['success'] = true; + return $ret; } -function get_hubloc_addrs_by_hash($hash) { +function get_hubloc_addrs_by_hash($hash) +{ - return q("select hubloc_addr from hubloc where hubloc_hash = '%s' and hubloc_deleted = 0", - dbesc($hash) - ); -} \ No newline at end of file + return q( + "select hubloc_addr from hubloc where hubloc_hash = '%s' and hubloc_deleted = 0", + dbesc($hash) + ); +} diff --git a/include/import.php b/include/import.php index 55702c28d..1b40f517b 100644 --- a/include/import.php +++ b/include/import.php @@ -10,7 +10,6 @@ use Zotlabs\Daemon\Run; use Zotlabs\Access\PermissionRoles; use Zotlabs\Access\PermissionLimits; - require_once('include/menu.php'); @@ -20,137 +19,142 @@ require_once('include/menu.php'); * @param array $channel * @param int $account_id * @param int $seize - * @return boolean|array + * @return bool|array */ -function import_channel($channel, $account_id, $seize, $newname = '') { +function import_channel($channel, $account_id, $seize, $newname = '') +{ - if (! array_key_exists('channel_system',$channel)) { - $channel['channel_system'] = (($channel['channel_pageflags'] & 0x1000) ? 1 : 0); - $channel['channel_removed'] = (($channel['channel_pageflags'] & 0x8000) ? 1 : 0); - } + if (! array_key_exists('channel_system', $channel)) { + $channel['channel_system'] = (($channel['channel_pageflags'] & 0x1000) ? 1 : 0); + $channel['channel_removed'] = (($channel['channel_pageflags'] & 0x8000) ? 1 : 0); + } - if (isset($channel['channel_removed']) && intval($channel['channel_removed'])) { - notice( t('Unable to import a removed channel.') . EOL); - return false; - } + if (isset($channel['channel_removed']) && intval($channel['channel_removed'])) { + notice(t('Unable to import a removed channel.') . EOL); + return false; + } - // Ignore the hash provided and re-calculate + // Ignore the hash provided and re-calculate - $channel['channel_hash'] = Libzot::make_xchan_hash($channel['channel_guid'],$channel['channel_pubkey']); + $channel['channel_hash'] = Libzot::make_xchan_hash($channel['channel_guid'], $channel['channel_pubkey']); - if ($newname) { - $channel['channel_address'] = $newname; - } + if ($newname) { + $channel['channel_address'] = $newname; + } - // Check for duplicate channels + // Check for duplicate channels - $r = q("select * from channel where (channel_guid = '%s' or channel_hash = '%s' or channel_address = '%s' ) limit 1", - dbesc($channel['channel_guid']), - dbesc($channel['channel_hash']), - dbesc($channel['channel_address']) - ); - if ($r && $r[0]['channel_guid'] == $channel['channel_guid'] && $r[0]['channel_pubkey'] === $channel['channel_pubkey'] && $r[0]['channel_hash'] === $channel['channel_hash']) { - // do not return a dead or deleted or system channel - if ($r[0]['channel_deleted'] > NULL_DATE - || intval($r[0]['channel_removed']) - || intval($r[0]['channel_moved']) - || intval($r[0]['channel_system'])) { - logger('attempt to import to a channel that was removed. ', print_r($channel,true)); - notice( t('A channel with these settings was discovered and is not usable as it was removed or reserved for system use. Import failed.') . EOL); - return false; - } - return $r[0]; - } + $r = q( + "select * from channel where (channel_guid = '%s' or channel_hash = '%s' or channel_address = '%s' ) limit 1", + dbesc($channel['channel_guid']), + dbesc($channel['channel_hash']), + dbesc($channel['channel_address']) + ); + if ($r && $r[0]['channel_guid'] == $channel['channel_guid'] && $r[0]['channel_pubkey'] === $channel['channel_pubkey'] && $r[0]['channel_hash'] === $channel['channel_hash']) { + // do not return a dead or deleted or system channel + if ( + $r[0]['channel_deleted'] > NULL_DATE + || intval($r[0]['channel_removed']) + || intval($r[0]['channel_moved']) + || intval($r[0]['channel_system']) + ) { + logger('attempt to import to a channel that was removed. ', print_r($channel, true)); + notice(t('A channel with these settings was discovered and is not usable as it was removed or reserved for system use. Import failed.') . EOL); + return false; + } + return $r[0]; + } - if (($r) || (check_webbie(array($channel['channel_address'])) !== $channel['channel_address'])) { - if ($r[0]['channel_guid'] === $channel['channel_guid'] || $r[0]['channel_hash'] === $channel['channel_hash']) { - logger('mod_import: duplicate channel. ', print_r($channel,true)); - notice( t('Cannot create a duplicate channel identifier on this system. Import failed.') . EOL); - return false; - } - else { - // try at most ten times to generate a unique address. - $x = 0; - $found_unique = false; - do { - $tmp = $channel['channel_address'] . mt_rand(1000,9999); - $r = q("select * from channel where channel_address = '%s' limit 1", - dbesc($tmp) - ); - if (! $r) { - $channel['channel_address'] = $tmp; - $found_unique = true; - break; - } - $x ++; - } while ($x < 10); - if (! $found_unique) { - logger('mod_import: duplicate channel. randomisation failed.', print_r($channel,true)); - notice( t('Unable to create a unique channel address. Import failed.') . EOL); - return false; - } - } - } + if (($r) || (check_webbie(array($channel['channel_address'])) !== $channel['channel_address'])) { + if ($r[0]['channel_guid'] === $channel['channel_guid'] || $r[0]['channel_hash'] === $channel['channel_hash']) { + logger('mod_import: duplicate channel. ', print_r($channel, true)); + notice(t('Cannot create a duplicate channel identifier on this system. Import failed.') . EOL); + return false; + } else { + // try at most ten times to generate a unique address. + $x = 0; + $found_unique = false; + do { + $tmp = $channel['channel_address'] . mt_rand(1000, 9999); + $r = q( + "select * from channel where channel_address = '%s' limit 1", + dbesc($tmp) + ); + if (! $r) { + $channel['channel_address'] = $tmp; + $found_unique = true; + break; + } + $x++; + } while ($x < 10); + if (! $found_unique) { + logger('mod_import: duplicate channel. randomisation failed.', print_r($channel, true)); + notice(t('Unable to create a unique channel address. Import failed.') . EOL); + return false; + } + } + } - unset($channel['channel_id']); - $channel['channel_account_id'] = $account_id; - $channel['channel_primary'] = (($seize) ? 1 : 0); + unset($channel['channel_id']); + $channel['channel_account_id'] = $account_id; + $channel['channel_primary'] = (($seize) ? 1 : 0); - if ($channel['channel_pageflags'] & PAGE_ALLOWCODE) { - if (! is_site_admin()) { - $channel['channel_pageflags'] = $channel['channel_pageflags'] ^ PAGE_ALLOWCODE; - } - } + if ($channel['channel_pageflags'] & PAGE_ALLOWCODE) { + if (! is_site_admin()) { + $channel['channel_pageflags'] = $channel['channel_pageflags'] ^ PAGE_ALLOWCODE; + } + } - // remove all the permissions related settings, we will import/upgrade them after the channel - // is created. + // remove all the permissions related settings, we will import/upgrade them after the channel + // is created. - $disallowed = [ - 'channel_id', 'channel_r_stream', 'channel_r_profile', 'channel_r_abook', - 'channel_r_storage', 'channel_r_pages', 'channel_w_stream', 'channel_w_wall', - 'channel_w_comment', 'channel_w_mail', 'channel_w_like', 'channel_w_tagwall', - 'channel_w_chat', 'channel_w_storage', 'channel_w_pages', 'channel_a_republish', - 'channel_a_delegate', 'perm_limits', 'channel_password', 'channel_salt', - 'channel_moved', 'channel_removed', 'channel_deleted', 'channel_system' - ]; + $disallowed = [ + 'channel_id', 'channel_r_stream', 'channel_r_profile', 'channel_r_abook', + 'channel_r_storage', 'channel_r_pages', 'channel_w_stream', 'channel_w_wall', + 'channel_w_comment', 'channel_w_mail', 'channel_w_like', 'channel_w_tagwall', + 'channel_w_chat', 'channel_w_storage', 'channel_w_pages', 'channel_a_republish', + 'channel_a_delegate', 'perm_limits', 'channel_password', 'channel_salt', + 'channel_moved', 'channel_removed', 'channel_deleted', 'channel_system' + ]; - $clean = []; - foreach ($channel as $k => $v) { - if (in_array($k,$disallowed)) { - continue; - } - $clean[$k] = $v; - } + $clean = []; + foreach ($channel as $k => $v) { + if (in_array($k, $disallowed)) { + continue; + } + $clean[$k] = $v; + } - if ($clean) { - channel_store_lowlevel($clean); - } + if ($clean) { + channel_store_lowlevel($clean); + } - $r = q("select * from channel where channel_account_id = %d and channel_guid = '%s' limit 1", - intval($account_id), - dbesc($channel['channel_guid']) - ); - if (! $r) { - logger('mod_import: channel not found. ' . print_r($channel,true)); - notice( t('Cloned channel not found. Import failed.') . EOL); - return false; - } + $r = q( + "select * from channel where channel_account_id = %d and channel_guid = '%s' limit 1", + intval($account_id), + dbesc($channel['channel_guid']) + ); + if (! $r) { + logger('mod_import: channel not found. ' . print_r($channel, true)); + notice(t('Cloned channel not found. Import failed.') . EOL); + return false; + } - // extract the permissions from the original imported array and use our new channel_id to set them - // These could be in the old channel permission stule or the new pconfig. We have a function to - // translate and store them no matter which they throw at us. + // extract the permissions from the original imported array and use our new channel_id to set them + // These could be in the old channel permission stule or the new pconfig. We have a function to + // translate and store them no matter which they throw at us. - $channel['channel_id'] = $r[0]['channel_id']; + $channel['channel_id'] = $r[0]['channel_id']; - // reset - $channel = $r[0]; + // reset + $channel = $r[0]; - set_default_login_identity($account_id,$channel['channel_id'],false); - logger('import step 1'); - $_SESSION['import_step'] = 1; + set_default_login_identity($account_id, $channel['channel_id'], false); + logger('import step 1'); + $_SESSION['import_step'] = 1; - return $channel; + return $channel; } /** @@ -159,168 +163,177 @@ function import_channel($channel, $account_id, $seize, $newname = '') { * @param array $channel * @param array $configs */ -function import_config($channel, $configs) { +function import_config($channel, $configs) +{ - if ($channel && $configs) { - foreach ($configs as $config) { - unset($config['id']); - $config['uid'] = $channel['channel_id']; - if ($config['cat'] === 'system' && $config['k'] === 'import_system_apps') { - continue; - } - set_pconfig($channel['channel_id'],$config['cat'],$config['k'],$config['v']); - } + if ($channel && $configs) { + foreach ($configs as $config) { + unset($config['id']); + $config['uid'] = $channel['channel_id']; + if ($config['cat'] === 'system' && $config['k'] === 'import_system_apps') { + continue; + } + set_pconfig($channel['channel_id'], $config['cat'], $config['k'], $config['v']); + } - load_pconfig($channel['channel_id']); - $permissions_role = get_pconfig($channel['channel_id'],'system','permissions_role'); - if ($permissions_role === 'social_federation') { - // Convert Hubzilla's social_federation role to 'social' with relaxed comment permissions - set_pconfig($channel['channel_id'],'systems','permissions_role','social'); - PermissionLimits::Set($channel['channel_id'],'post_comments', PERMS_AUTHED); - } - else { - // If the requested permissions_role doesn't exist on this system, - // convert it to 'social' - - $role_permissions = PermissionRoles::role_perms($permissions_role); + load_pconfig($channel['channel_id']); + $permissions_role = get_pconfig($channel['channel_id'], 'system', 'permissions_role'); + if ($permissions_role === 'social_federation') { + // Convert Hubzilla's social_federation role to 'social' with relaxed comment permissions + set_pconfig($channel['channel_id'], 'systems', 'permissions_role', 'social'); + PermissionLimits::Set($channel['channel_id'], 'post_comments', PERMS_AUTHED); + } else { + // If the requested permissions_role doesn't exist on this system, + // convert it to 'social' - if (! $role_permissions) { - set_pconfig($channel['channel_id'],'system','permissions_role','social'); - } - } + $role_permissions = PermissionRoles::role_perms($permissions_role); - } + if (! $role_permissions) { + set_pconfig($channel['channel_id'], 'system', 'permissions_role', 'social'); + } + } + } } -function import_atoken($channel, $atokens) { - if ($channel && $atokens) { - foreach ($atokens as $atoken) { - unset($atoken['atoken_id']); - $atoken['atoken_aid'] = $channel['channel_account_id']; - $atoken['atoken_uid'] = $channel['channel_id']; - create_table_from_array('atoken', $atoken); - } - } +function import_atoken($channel, $atokens) +{ + if ($channel && $atokens) { + foreach ($atokens as $atoken) { + unset($atoken['atoken_id']); + $atoken['atoken_aid'] = $channel['channel_account_id']; + $atoken['atoken_uid'] = $channel['channel_id']; + create_table_from_array('atoken', $atoken); + } + } } -function sync_atoken($channel, $atokens) { +function sync_atoken($channel, $atokens) +{ - if ($channel && $atokens) { - foreach ($atokens as $atoken) { - unset($atoken['atoken_id']); - $atoken['atoken_aid'] = $channel['channel_account_id']; - $atoken['atoken_uid'] = $channel['channel_id']; + if ($channel && $atokens) { + foreach ($atokens as $atoken) { + unset($atoken['atoken_id']); + $atoken['atoken_aid'] = $channel['channel_account_id']; + $atoken['atoken_uid'] = $channel['channel_id']; - if ($atoken['deleted']) { - q("delete from atoken where atoken_uid = %d and atoken_guid = '%s' ", - intval($atoken['atoken_uid']), - dbesc($atoken['atoken_guid']) - ); - continue; - } + if ($atoken['deleted']) { + q( + "delete from atoken where atoken_uid = %d and atoken_guid = '%s' ", + intval($atoken['atoken_uid']), + dbesc($atoken['atoken_guid']) + ); + continue; + } - $r = q("select * from atoken where atoken_uid = %d and atoken_guid = '%s' ", - intval($atoken['atoken_uid']), - dbesc($atoken['atoken_guid']) - ); - if (! $r) { - create_table_from_array('atoken', $atoken); - } - else { - $columns = db_columns('atoken'); - foreach ($atoken as $k => $v) { - if (! in_array($k,$columns)) { - continue; - } - - if (in_array($k, ['atoken_guid','atoken_uid','atoken_aid'])) { - continue; - } - - $r = q("UPDATE atoken SET " . TQUOT . "%s" . TQUOT . " = '%s' WHERE atoken_guid = '%s' AND atoken_uid = %d", - dbesc($k), - dbesc($v), - dbesc($atoken['atoken_guid']), - intval($channel['channel_id']) - ); - } - } - } - } + $r = q( + "select * from atoken where atoken_uid = %d and atoken_guid = '%s' ", + intval($atoken['atoken_uid']), + dbesc($atoken['atoken_guid']) + ); + if (! $r) { + create_table_from_array('atoken', $atoken); + } else { + $columns = db_columns('atoken'); + foreach ($atoken as $k => $v) { + if (! in_array($k, $columns)) { + continue; + } + + if (in_array($k, ['atoken_guid','atoken_uid','atoken_aid'])) { + continue; + } + + $r = q( + "UPDATE atoken SET " . TQUOT . "%s" . TQUOT . " = '%s' WHERE atoken_guid = '%s' AND atoken_uid = %d", + dbesc($k), + dbesc($v), + dbesc($atoken['atoken_guid']), + intval($channel['channel_id']) + ); + } + } + } + } } -function import_xign($channel, $xigns) { +function import_xign($channel, $xigns) +{ - if ($channel && $xigns) { - foreach ($xigns as $xign) { - unset($xign['id']); - $xign['uid'] = $channel['channel_id']; - create_table_from_array('xign', $xign); - } - } + if ($channel && $xigns) { + foreach ($xigns as $xign) { + unset($xign['id']); + $xign['uid'] = $channel['channel_id']; + create_table_from_array('xign', $xign); + } + } } -function sync_xign($channel, $xigns) { +function sync_xign($channel, $xigns) +{ - if ($channel && $xigns) { - foreach ($xigns as $xign) { - unset($xign['id']); - $xign['uid'] = $channel['channel_id']; - if (! $xign['xchan']) { - continue; - } - if ($xign['deleted']) { - q("delete from xign where uid = %d and xchan = '%s' ", - intval($xign['uid']), - dbesc($xign['xchan']) - ); - continue; - } + if ($channel && $xigns) { + foreach ($xigns as $xign) { + unset($xign['id']); + $xign['uid'] = $channel['channel_id']; + if (! $xign['xchan']) { + continue; + } + if ($xign['deleted']) { + q( + "delete from xign where uid = %d and xchan = '%s' ", + intval($xign['uid']), + dbesc($xign['xchan']) + ); + continue; + } - $r = q("select * from xign where uid = %d and xchan = '%s' ", - intval($xign['uid']), - dbesc($xign['xchan']) - ); - if (! $r) { - create_table_from_array('xign', $xign); - } - } - } + $r = q( + "select * from xign where uid = %d and xchan = '%s' ", + intval($xign['uid']), + dbesc($xign['xchan']) + ); + if (! $r) { + create_table_from_array('xign', $xign); + } + } + } } -function import_block($channel, $blocks) { +function import_block($channel, $blocks) +{ - if ($channel && $blocks) { - foreach ($blocks as $block) { - unset($block['block_id']); - $block['block_channel_id'] = $channel['channel_id']; - LibBlock::store($block); - } - } + if ($channel && $blocks) { + foreach ($blocks as $block) { + unset($block['block_id']); + $block['block_channel_id'] = $channel['channel_id']; + LibBlock::store($block); + } + } } -function sync_block($channel, $blocks) { +function sync_block($channel, $blocks) +{ - if ($channel && $blocks) { - foreach ($blocks as $block) { - unset($block['block_id']); - $block['block_channel_id'] = $channel['channel_id']; - if (! $block['block_entity']) { - continue; - } - if ($block['deleted']) { - LibBlock::remove($channel['channel_id'],$block['block_entity']); - continue; - } - LibBlock::store($channel['channel_id'],$block); - } - } + if ($channel && $blocks) { + foreach ($blocks as $block) { + unset($block['block_id']); + $block['block_channel_id'] = $channel['channel_id']; + if (! $block['block_entity']) { + continue; + } + if ($block['deleted']) { + LibBlock::remove($channel['channel_id'], $block['block_entity']); + continue; + } + LibBlock::store($channel['channel_id'], $block); + } + } } @@ -332,34 +345,34 @@ function sync_block($channel, $blocks) { * @param array $channel * @param array $profiles */ -function import_profiles($channel, $profiles) { +function import_profiles($channel, $profiles) +{ - if ($channel && $profiles) { - foreach ($profiles as $profile) { - unset($profile['id']); - $profile['aid'] = get_account_id(); - $profile['uid'] = $channel['channel_id']; + if ($channel && $profiles) { + foreach ($profiles as $profile) { + unset($profile['id']); + $profile['aid'] = get_account_id(); + $profile['uid'] = $channel['channel_id']; - convert_oldfields($profile,'name','fullname'); - convert_oldfields($profile,'with','partner'); - convert_oldfields($profile,'work','employment'); + convert_oldfields($profile, 'name', 'fullname'); + convert_oldfields($profile, 'with', 'partner'); + convert_oldfields($profile, 'work', 'employment'); - /** - * @TODO put all the applicable photos into the export. - */ + /** + * @TODO put all the applicable photos into the export. + */ - if ((strpos($profile['thumb'],'/photo/profile/l/') !== false) || intval($profile['is_default'])) { - $profile['photo'] = z_root() . '/photo/profile/l/' . $channel['channel_id']; - $profile['thumb'] = z_root() . '/photo/profile/m/' . $channel['channel_id']; - } - else { - $profile['photo'] = z_root() . '/photo/' . basename($profile['photo']); - $profile['thumb'] = z_root() . '/photo/' . basename($profile['thumb']); - } + if ((strpos($profile['thumb'], '/photo/profile/l/') !== false) || intval($profile['is_default'])) { + $profile['photo'] = z_root() . '/photo/profile/l/' . $channel['channel_id']; + $profile['thumb'] = z_root() . '/photo/profile/m/' . $channel['channel_id']; + } else { + $profile['photo'] = z_root() . '/photo/' . basename($profile['photo']); + $profile['thumb'] = z_root() . '/photo/' . basename($profile['thumb']); + } - profile_store_lowlevel($profile); - } - } + profile_store_lowlevel($profile); + } + } } /** @@ -367,70 +380,71 @@ function import_profiles($channel, $profiles) { * * @param array $channel * @param array $hublocs - * @param boolean $seize - * @param boolean $moving (optional) default false + * @param bool $seize + * @param bool $moving (optional) default false */ - -function import_hublocs($channel, $hublocs, $seize, $moving = false) { - if ($channel && $hublocs) { - foreach ($hublocs as $hubloc) { +function import_hublocs($channel, $hublocs, $seize, $moving = false) +{ - // Provide backward compatibility for zot11 based projects - - if ($hubloc['hubloc_network'] === 'nomad' && version_compare(ZOT_REVISION, '10.0') <= 0) { - $hubloc['hubloc_network'] = 'zot6'; - } + if ($channel && $hublocs) { + foreach ($hublocs as $hubloc) { + // Provide backward compatibility for zot11 based projects - // verify the hash. We can only do this if we already stored the xchan corresponding to this hubloc - // as we need the public key from there + if ($hubloc['hubloc_network'] === 'nomad' && version_compare(ZOT_REVISION, '10.0') <= 0) { + $hubloc['hubloc_network'] = 'zot6'; + } - if (in_array($hubloc['hubloc_network'],['nomad','zot6'])) { - $x = q("select xchan_pubkey from xchan where xchan_guid = '%s' and xchan_hash = '%s'", - dbesc($hubloc['hubloc_guid']), - dbesc($hubloc['hubloc_hash']) - ); + // verify the hash. We can only do this if we already stored the xchan corresponding to this hubloc + // as we need the public key from there - if (! $x) { - logger('hubloc could not be verified. ' . print_r($hubloc,true)); - continue; - } - $hash = Libzot::make_xchan_hash($hubloc['hubloc_guid'],$x[0]['xchan_pubkey']); - if ($hash !== $hubloc['hubloc_hash']) { - logger('forged hubloc: ' . print_r($hubloc,true)); - continue; - } - } + if (in_array($hubloc['hubloc_network'], ['nomad','zot6'])) { + $x = q( + "select xchan_pubkey from xchan where xchan_guid = '%s' and xchan_hash = '%s'", + dbesc($hubloc['hubloc_guid']), + dbesc($hubloc['hubloc_hash']) + ); - if ($moving && $hubloc['hubloc_hash'] === $channel['channel_hash'] && $hubloc['hubloc_url'] !== z_root()) { - $hubloc['hubloc_deleted'] = 1; - } + if (! $x) { + logger('hubloc could not be verified. ' . print_r($hubloc, true)); + continue; + } + $hash = Libzot::make_xchan_hash($hubloc['hubloc_guid'], $x[0]['xchan_pubkey']); + if ($hash !== $hubloc['hubloc_hash']) { + logger('forged hubloc: ' . print_r($hubloc, true)); + continue; + } + } - $arr = [ - 'id' => $hubloc['hubloc_guid'], - 'id_sig' => $hubloc['hubloc_guid_sig'], - 'location' => $hubloc['hubloc_url'], - 'location_sig' => $hubloc['hubloc_url_sig'], - 'site_id' => $hubloc['hubloc_site_id'] - ]; + if ($moving && $hubloc['hubloc_hash'] === $channel['channel_hash'] && $hubloc['hubloc_url'] !== z_root()) { + $hubloc['hubloc_deleted'] = 1; + } - if (($hubloc['hubloc_hash'] === $channel['channel_hash']) && intval($hubloc['hubloc_primary']) && ($seize)) { - $hubloc['hubloc_primary'] = 0; - } + $arr = [ + 'id' => $hubloc['hubloc_guid'], + 'id_sig' => $hubloc['hubloc_guid_sig'], + 'location' => $hubloc['hubloc_url'], + 'location_sig' => $hubloc['hubloc_url_sig'], + 'site_id' => $hubloc['hubloc_site_id'] + ]; - if (($x = Libzot::gethub($arr,false)) === false) { - unset($hubloc['hubloc_id']); - hubloc_store_lowlevel($hubloc); - } - else { - q("UPDATE hubloc set hubloc_primary = %d, hubloc_deleted = %d where hubloc_id = %d", - intval($hubloc['hubloc_primary']), - intval($hubloc['hubloc_deleted']), - intval($x['hubloc_id']) - ); - } - } - } + if (($hubloc['hubloc_hash'] === $channel['channel_hash']) && intval($hubloc['hubloc_primary']) && ($seize)) { + $hubloc['hubloc_primary'] = 0; + } + + if (($x = Libzot::gethub($arr, false)) === false) { + unset($hubloc['hubloc_id']); + hubloc_store_lowlevel($hubloc); + } else { + q( + "UPDATE hubloc set hubloc_primary = %d, hubloc_deleted = %d where hubloc_id = %d", + intval($hubloc['hubloc_primary']), + intval($hubloc['hubloc_deleted']), + intval($x['hubloc_id']) + ); + } + } + } } /** @@ -439,31 +453,31 @@ function import_hublocs($channel, $hublocs, $seize, $moving = false) { * @param array $channel * @param array $objs */ -function import_objs($channel, $objs) { +function import_objs($channel, $objs) +{ - if ($channel && $objs) { - foreach ($objs as $obj) { + if ($channel && $objs) { + foreach ($objs as $obj) { + $baseurl = $obj['obj_baseurl']; + unset($obj['obj_id']); + unset($obj['obj_baseurl']); - $baseurl = $obj['obj_baseurl']; - unset($obj['obj_id']); - unset($obj['obj_baseurl']); + $obj['obj_channel'] = $channel['channel_id']; - $obj['obj_channel'] = $channel['channel_id']; + if ($baseurl && (strpos($obj['obj_url'], $baseurl . '/thing/') !== false)) { + $obj['obj_url'] = str_replace($baseurl, z_root(), $obj['obj_url']); + } - if ($baseurl && (strpos($obj['obj_url'], $baseurl . '/thing/') !== false)) { - $obj['obj_url'] = str_replace($baseurl, z_root(), $obj['obj_url']); - } + if ($obj['obj_imgurl']) { + $x = import_remote_xchan_photo($obj['obj_imgurl'], $channel['channel_hash'], true); + if ($x) { + $obj['obj_imgurl'] = $x[0]; + } + } - if ($obj['obj_imgurl']) { - $x = import_remote_xchan_photo($obj['obj_imgurl'], $channel['channel_hash'], true); - if ($x) { - $obj['obj_imgurl'] = $x[0]; - } - } - - create_table_from_array('obj', $obj); - } - } + create_table_from_array('obj', $obj); + } + } } /** @@ -472,76 +486,78 @@ function import_objs($channel, $objs) { * @param array $channel * @param array $objs */ -function sync_objs($channel, $objs) { +function sync_objs($channel, $objs) +{ - if ($channel && $objs) { - $columns = db_columns('obj'); + if ($channel && $objs) { + $columns = db_columns('obj'); - foreach ($objs as $obj) { + foreach ($objs as $obj) { + if (array_key_exists('obj_deleted', $obj) && $obj['obj_deleted'] && $obj['obj_obj']) { + q( + "delete from obj where obj_obj = '%s' and obj_channel = %d", + dbesc($obj['obj_obj']), + intval($channel['channel_id']) + ); + continue; + } - if (array_key_exists('obj_deleted',$obj) && $obj['obj_deleted'] && $obj['obj_obj']) { - q("delete from obj where obj_obj = '%s' and obj_channel = %d", - dbesc($obj['obj_obj']), - intval($channel['channel_id']) - ); - continue; - } + $baseurl = $obj['obj_baseurl']; + unset($obj['obj_id']); + unset($obj['obj_baseurl']); - $baseurl = $obj['obj_baseurl']; - unset($obj['obj_id']); - unset($obj['obj_baseurl']); + $obj['obj_channel'] = $channel['channel_id']; - $obj['obj_channel'] = $channel['channel_id']; + if ($baseurl && (strpos($obj['obj_url'], $baseurl . '/thing/') !== false)) { + $obj['obj_url'] = str_replace($baseurl, z_root(), $obj['obj_url']); + } - if ($baseurl && (strpos($obj['obj_url'], $baseurl . '/thing/') !== false)) { - $obj['obj_url'] = str_replace($baseurl, z_root(), $obj['obj_url']); - } + $exists = false; - $exists = false; + $x = q( + "select * from obj where obj_obj = '%s' and obj_channel = %d limit 1", + dbesc($obj['obj_obj']), + intval($channel['channel_id']) + ); + if ($x) { + if ($x[0]['obj_edited'] >= $obj['obj_edited']) { + continue; + } - $x = q("select * from obj where obj_obj = '%s' and obj_channel = %d limit 1", - dbesc($obj['obj_obj']), - intval($channel['channel_id']) - ); - if ($x) { - if ($x[0]['obj_edited'] >= $obj['obj_edited']) { - continue; - } + $exists = true; + } - $exists = true; - } + if ($obj['obj_imgurl']) { + // import_xchan_photo() has a switch to store thing images + $x = import_remote_xchan_photo($obj['obj_imgurl'], $channel['channel_hash'], true); + if ($x) { + $obj['obj_imgurl'] = $x[0]; + } + } - if ($obj['obj_imgurl']) { - // import_xchan_photo() has a switch to store thing images - $x = import_remote_xchan_photo($obj['obj_imgurl'], $channel['channel_hash'], true); - if ($x) { - $obj['obj_imgurl'] = $x[0]; - } - } + $hash = $obj['obj_obj']; - $hash = $obj['obj_obj']; + if ($exists) { + unset($obj['obj_obj']); - if ($exists) { - unset($obj['obj_obj']); + foreach ($obj as $k => $v) { + if (! in_array($k, $columns)) { + continue; + } - foreach ($obj as $k => $v) { - if (! in_array($k,$columns)) { - continue; - } - - $r = q("UPDATE obj SET " . TQUOT . "%s" . TQUOT . " = '%s' WHERE obj_obj = '%s' AND obj_channel = %d", - dbesc($k), - dbesc($v), - dbesc($hash), - intval($channel['channel_id']) - ); - } - } - else { - create_table_from_array('obj', $obj); - } - } - } + $r = q( + "UPDATE obj SET " . TQUOT . "%s" . TQUOT . " = '%s' WHERE obj_obj = '%s' AND obj_channel = %d", + dbesc($k), + dbesc($v), + dbesc($hash), + intval($channel['channel_id']) + ); + } + } else { + create_table_from_array('obj', $obj); + } + } + } } /** @@ -550,52 +566,53 @@ function sync_objs($channel, $objs) { * @param array $channel * @param array $apps */ -function import_apps($channel, $apps) { +function import_apps($channel, $apps) +{ - if ($channel && $apps) { - foreach ($apps as $app) { + if ($channel && $apps) { + foreach ($apps as $app) { + if (array_key_exists('app_system', $app) && intval($app['app_system'])) { + continue; + } - if (array_key_exists('app_system',$app) && intval($app['app_system'])) { - continue; - } + $term = ((array_key_exists('term', $app) && is_array($app['term'])) ? $app['term'] : null); - $term = ((array_key_exists('term',$app) && is_array($app['term'])) ? $app['term'] : null); + unset($app['id']); + unset($app['app_channel']); + unset($app['term']); - unset($app['id']); - unset($app['app_channel']); - unset($app['term']); + $app['app_channel'] = $channel['channel_id']; - $app['app_channel'] = $channel['channel_id']; + if ($app['app_photo']) { + // overload import_xchan_photo() + $x = import_remote_xchan_photo($app['app_photo'], $channel['channel_hash'], true); + if ($x) { + $app['app_photo'] = $x[0]; + } + } - if ($app['app_photo']) { - // overload import_xchan_photo() - $x = import_remote_xchan_photo($app['app_photo'], $channel['channel_hash'], true); - if ($x) { - $app['app_photo'] = $x[0]; - } - } + $hash = $app['app_id']; - $hash = $app['app_id']; + create_table_from_array('app', $app); - create_table_from_array('app', $app); + if ($term) { + $x = q( + "select * from app where app_id = '%s' and app_channel = %d limit 1", + dbesc($hash), + intval($channel['channel_id']) + ); + if ($x) { + foreach ($term as $t) { + if (array_key_exists('type', $t)) { + $t['ttype'] = $t['type']; + } - if ($term) { - $x = q("select * from app where app_id = '%s' and app_channel = %d limit 1", - dbesc($hash), - intval($channel['channel_id']) - ); - if ($x) { - foreach ($term as $t) { - if (array_key_exists('type',$t)) { - $t['ttype'] = $t['type']; - } - - store_item_tag($channel['channel_id'],$x[0]['id'],TERM_OBJ_APP,$t['ttype'],escape_tags($t['term']),escape_tags($t['url'])); - } - } - } - } - } + store_item_tag($channel['channel_id'], $x[0]['id'], TERM_OBJ_APP, $t['ttype'], escape_tags($t['term']), escape_tags($t['url'])); + } + } + } + } + } } /** @@ -604,180 +621,183 @@ function import_apps($channel, $apps) { * @param array $channel * @param array $apps */ -function sync_apps($channel, $apps) { +function sync_apps($channel, $apps) +{ - if ($channel && $apps) { + if ($channel && $apps) { + $columns = db_columns('app'); - $columns = db_columns('app'); + foreach ($apps as $app) { + $exists = false; + $term = ((array_key_exists('term', $app)) ? $app['term'] : null); - foreach ($apps as $app) { + if (array_key_exists('app_system', $app) && intval($app['app_system'])) { + continue; + } - $exists = false; - $term = ((array_key_exists('term',$app)) ? $app['term'] : null); + $x = q( + "select * from app where app_id = '%s' and app_channel = %d limit 1", + dbesc($app['app_id']), + intval($channel['channel_id']) + ); + if ($x) { + $exists = $x[0]; + } - if (array_key_exists('app_system',$app) && intval($app['app_system'])) { - continue; - } + if (array_key_exists('app_deleted', $app) && $app['app_deleted'] && $app['app_id']) { + q( + "delete from app where app_id = '%s' and app_channel = %d", + dbesc($app['app_id']), + intval($channel['channel_id']) + ); + if ($exists) { + q( + "delete from term where otype = %d and oid = %d", + intval(TERM_OBJ_APP), + intval($exists['id']) + ); + } + continue; + } - $x = q("select * from app where app_id = '%s' and app_channel = %d limit 1", - dbesc($app['app_id']), - intval($channel['channel_id']) - ); - if ($x) { - $exists = $x[0]; - } + unset($app['id']); + unset($app['app_channel']); + unset($app['term']); - if (array_key_exists('app_deleted',$app) && $app['app_deleted'] && $app['app_id']) { - q("delete from app where app_id = '%s' and app_channel = %d", - dbesc($app['app_id']), - intval($channel['channel_id']) - ); - if ($exists) { - q("delete from term where otype = %d and oid = %d", - intval(TERM_OBJ_APP), - intval($exists['id']) - ); - } - continue; - } + if ($exists) { + q( + "delete from term where otype = %d and oid = %d", + intval(TERM_OBJ_APP), + intval($exists['id']) + ); + } - unset($app['id']); - unset($app['app_channel']); - unset($app['term']); + if ((! $app['app_created']) || ($app['app_created'] <= NULL_DATE)) { + $app['app_created'] = datetime_convert(); + } + if ((! $app['app_edited']) || ($app['app_edited'] <= NULL_DATE)) { + $app['app_edited'] = datetime_convert(); + } - if ($exists) { - q("delete from term where otype = %d and oid = %d", - intval(TERM_OBJ_APP), - intval($exists['id']) - ); - } + $app['app_channel'] = $channel['channel_id']; - if ((! $app['app_created']) || ($app['app_created'] <= NULL_DATE)) { - $app['app_created'] = datetime_convert(); - } - if ((! $app['app_edited']) || ($app['app_edited'] <= NULL_DATE)) { - $app['app_edited'] = datetime_convert(); - } + if ($app['app_photo']) { + // use import_xchan_photo() + $x = import_remote_xchan_photo($app['app_photo'], $channel['channel_hash'], true); + if ($x) { + $app['app_photo'] = $x[0]; + } + } - $app['app_channel'] = $channel['channel_id']; + if ($exists && $term) { + foreach ($term as $t) { + if (array_key_exists('type', $t)) { + $t['ttype'] = $t['type']; + } + store_item_tag($channel['channel_id'], $exists['id'], TERM_OBJ_APP, $t['ttype'], escape_tags($t['term']), escape_tags($t['url'])); + } + } - if ($app['app_photo']) { - // use import_xchan_photo() - $x = import_remote_xchan_photo($app['app_photo'],$channel['channel_hash'],true); - if ($x) { - $app['app_photo'] = $x[0]; - } - } + if ($exists) { + if ($exists['app_edited'] >= $app['app_edited']) { + continue; + } + } + $hash = $app['app_id']; - if ($exists && $term) { - foreach ($term as $t) { - if (array_key_exists('type',$t)) { - $t['ttype'] = $t['type']; - } - store_item_tag($channel['channel_id'],$exists['id'],TERM_OBJ_APP,$t['ttype'],escape_tags($t['term']),escape_tags($t['url'])); - } - } + if ($exists) { + unset($app['app_id']); + foreach ($app as $k => $v) { + if (! in_array($k, $columns)) { + continue; + } - if ($exists) { - if ($exists['app_edited'] >= $app['app_edited']) { - continue; - } - } - $hash = $app['app_id']; + $r = q( + "UPDATE app SET " . TQUOT . "%s" . TQUOT . " = '%s' WHERE app_id = '%s' AND app_channel = %d", + dbesc($k), + dbesc($v), + dbesc($hash), + intval($channel['channel_id']) + ); + } + } else { + create_table_from_array('app', $app); - if ($exists) { - unset($app['app_id']); - foreach ($app as $k => $v) { - - if (! in_array($k,$columns)) { - continue; - } - - $r = q("UPDATE app SET " . TQUOT . "%s" . TQUOT . " = '%s' WHERE app_id = '%s' AND app_channel = %d", - dbesc($k), - dbesc($v), - dbesc($hash), - intval($channel['channel_id']) - ); - } - } - else { - create_table_from_array('app',$app); - - if ($term) { - $x = q("select * from app where app_id = '%s' and app_channel = %d", - dbesc($hash), - intval($channel['channel_id']) - ); - if ($x) { - foreach ($term as $t) { - if (array_key_exists('type',$t)) { - $t['ttype'] = $t['type']; - } - store_item_tag($channel['channel_id'],$x[0]['id'],TERM_OBJ_APP,$t['ttype'],escape_tags($t['term']),escape_tags($t['url'])); - } - } - } - } - } - } + if ($term) { + $x = q( + "select * from app where app_id = '%s' and app_channel = %d", + dbesc($hash), + intval($channel['channel_id']) + ); + if ($x) { + foreach ($term as $t) { + if (array_key_exists('type', $t)) { + $t['ttype'] = $t['type']; + } + store_item_tag($channel['channel_id'], $x[0]['id'], TERM_OBJ_APP, $t['ttype'], escape_tags($t['term']), escape_tags($t['url'])); + } + } + } + } + } + } } /** * @brief Import system apps. - * System apps from the original server may not exist on this system + * System apps from the original server may not exist on this system * (e.g. apps associated with addons that are not installed here). * Check the system apps that were provided in the import file to see if they * exist here and if so, install them locally. Preserve categories that * might have been added by this channel on the other server. - * Do not use any paths from the original as they will point to a different server. + * Do not use any paths from the original as they will point to a different server. * @param array $channel * @param array $apps */ -function import_sysapps($channel, $apps) { +function import_sysapps($channel, $apps) +{ - if ($channel && $apps) { + if ($channel && $apps) { + $sysapps = Apps::get_system_apps(false); - $sysapps = Apps::get_system_apps(false); + foreach ($apps as $app) { + if (array_key_exists('app_system', $app) && (! intval($app['app_system']))) { + continue; + } - foreach ($apps as $app) { + if (array_key_exists('app_deleted', $app) && (intval($app['app_deleted']))) { + continue; + } - if (array_key_exists('app_system',$app) && (! intval($app['app_system']))) { - continue; - } + $term = ((array_key_exists('term', $app) && is_array($app['term'])) ? $app['term'] : null); - if (array_key_exists('app_deleted',$app) && (intval($app['app_deleted']))) { - continue; - } + foreach ($sysapps as $sysapp) { + if ($app['app_id'] === hash('whirlpool', $sysapp['app_name'])) { + // install this app on this server + $newapp = $sysapp; + $newapp['uid'] = $channel['channel_id']; + $newapp['guid'] = hash('whirlpool', $newapp['name']); - $term = ((array_key_exists('term',$app) && is_array($app['term'])) ? $app['term'] : null); + $installed = q( + "select id from app where app_id = '%s' and app_channel = %d limit 1", + dbesc($newapp['guid']), + intval($channel['channel_id']) + ); + if ($installed) { + break; + } - foreach ($sysapps as $sysapp) { - if ($app['app_id'] === hash('whirlpool',$sysapp['app_name'])) { - // install this app on this server - $newapp = $sysapp; - $newapp['uid'] = $channel['channel_id']; - $newapp['guid'] = hash('whirlpool',$newapp['name']); - - $installed = q("select id from app where app_id = '%s' and app_channel = %d limit 1", - dbesc($newapp['guid']), - intval($channel['channel_id']) - ); - if ($installed) { - break; - } - - $newapp['system'] = 1; - if ($term) { - $newapp['categories'] = array_elm_to_str($term,'term'); - } - Apps::app_install($channel['channel_id'],$newapp); - } - } - } - } + $newapp['system'] = 1; + if ($term) { + $newapp['categories'] = array_elm_to_str($term, 'term'); + } + Apps::app_install($channel['channel_id'], $newapp); + } + } + } + } } /** @@ -786,47 +806,46 @@ function import_sysapps($channel, $apps) { * @param array $channel * @param array $apps */ -function sync_sysapps($channel, $apps) { +function sync_sysapps($channel, $apps) +{ - $sysapps = Apps::get_system_apps(false); + $sysapps = Apps::get_system_apps(false); - if ($channel && $apps) { + if ($channel && $apps) { + $columns = db_columns('app'); - $columns = db_columns('app'); + foreach ($apps as $app) { + $exists = false; + $term = ((array_key_exists('term', $app)) ? $app['term'] : null); - foreach ($apps as $app) { + if (array_key_exists('app_system', $app) && (! intval($app['app_system']))) { + continue; + } - $exists = false; - $term = ((array_key_exists('term',$app)) ? $app['term'] : null); + foreach ($sysapps as $sysapp) { + if ($app['app_id'] === hash('whirlpool', $sysapp['app_name'])) { + if (array_key_exists('app_deleted', $app) && $app['app_deleted'] && $app['app_id']) { + q( + "update app set app_deleted = 1 where app_id = '%s' and app_channel = %d", + dbesc($app['app_id']), + intval($channel['channel_id']) + ); + } else { + // install this app on this server + $newapp = $sysapp; + $newapp['uid'] = $channel['channel_id']; + $newapp['guid'] = hash('whirlpool', $newapp['name']); - if (array_key_exists('app_system',$app) && (! intval($app['app_system']))) { - continue; - } - - foreach ($sysapps as $sysapp) { - if ($app['app_id'] === hash('whirlpool',$sysapp['app_name'])) { - if (array_key_exists('app_deleted',$app) && $app['app_deleted'] && $app['app_id']) { - q("update app set app_deleted = 1 where app_id = '%s' and app_channel = %d", - dbesc($app['app_id']), - intval($channel['channel_id']) - ); - } - else { - // install this app on this server - $newapp = $sysapp; - $newapp['uid'] = $channel['channel_id']; - $newapp['guid'] = hash('whirlpool',$newapp['name']); - - $newapp['system'] = 1; - if ($term) { - $newapp['categories'] = array_elm_to_str($term,'term'); - } - Apps::app_install($channel['channel_id'],$newapp); - } - } - } - } - } + $newapp['system'] = 1; + if ($term) { + $newapp['categories'] = array_elm_to_str($term, 'term'); + } + Apps::app_install($channel['channel_id'], $newapp); + } + } + } + } + } } @@ -839,25 +858,25 @@ function sync_sysapps($channel, $apps) { * @param array $channel * @param array $chatrooms */ -function import_chatrooms($channel, $chatrooms) { +function import_chatrooms($channel, $chatrooms) +{ - if ($channel && $chatrooms) { - foreach ($chatrooms as $chatroom) { + if ($channel && $chatrooms) { + foreach ($chatrooms as $chatroom) { + if (! $chatroom['cr_name']) { + continue; + } - if (! $chatroom['cr_name']) { - continue; - } + unset($chatroom['cr_id']); + unset($chatroom['cr_aid']); + unset($chatroom['cr_uid']); - unset($chatroom['cr_id']); - unset($chatroom['cr_aid']); - unset($chatroom['cr_uid']); + $chatroom['cr_aid'] = $channel['channel_account_id']; + $chatroom['cr_uid'] = $channel['channel_id']; - $chatroom['cr_aid'] = $channel['channel_account_id']; - $chatroom['cr_uid'] = $channel['channel_id']; - - create_table_from_array('chatroom', $chatroom); - } - } + create_table_from_array('chatroom', $chatroom); + } + } } /** @@ -866,74 +885,74 @@ function import_chatrooms($channel, $chatrooms) { * @param array $channel * @param array $chatrooms */ -function sync_chatrooms($channel, $chatrooms) { +function sync_chatrooms($channel, $chatrooms) +{ - if ($channel && $chatrooms) { + if ($channel && $chatrooms) { + $columns = db_columns('chatroom'); - $columns = db_columns('chatroom'); + foreach ($chatrooms as $chatroom) { + if (! $chatroom['cr_name']) { + continue; + } - foreach ($chatrooms as $chatroom) { + if (array_key_exists('cr_deleted', $chatroom) && $chatroom['cr_deleted']) { + q( + "delete from chatroom where cr_name = '%s' and cr_uid = %d", + dbesc($chatroom['cr_name']), + intval($channel['channel_id']) + ); + continue; + } - if (! $chatroom['cr_name']) { - continue; - } + unset($chatroom['cr_id']); + unset($chatroom['cr_aid']); + unset($chatroom['cr_uid']); - if (array_key_exists('cr_deleted',$chatroom) && $chatroom['cr_deleted']) { - q("delete from chatroom where cr_name = '%s' and cr_uid = %d", - dbesc($chatroom['cr_name']), - intval($channel['channel_id']) - ); - continue; - } + if ((! $chatroom['cr_created']) || ($chatroom['cr_created'] <= NULL_DATE)) { + $chatroom['cr_created'] = datetime_convert(); + } + if ((! $chatroom['cr_edited']) || ($chatroom['cr_edited'] <= NULL_DATE)) { + $chatroom['cr_edited'] = datetime_convert(); + } - unset($chatroom['cr_id']); - unset($chatroom['cr_aid']); - unset($chatroom['cr_uid']); + $chatroom['cr_aid'] = $channel['channel_account_id']; + $chatroom['cr_uid'] = $channel['channel_id']; - if ((! $chatroom['cr_created']) || ($chatroom['cr_created'] <= NULL_DATE)) { - $chatroom['cr_created'] = datetime_convert(); - } - if ((! $chatroom['cr_edited']) || ($chatroom['cr_edited'] <= NULL_DATE)) { - $chatroom['cr_edited'] = datetime_convert(); - } + $exists = false; - $chatroom['cr_aid'] = $channel['channel_account_id']; - $chatroom['cr_uid'] = $channel['channel_id']; + $x = q( + "select * from chatroom where cr_name = '%s' and cr_uid = %d limit 1", + dbesc($chatroom['cr_name']), + intval($channel['channel_id']) + ); + if ($x) { + if ($x[0]['cr_edited'] >= $chatroom['cr_edited']) { + continue; + } + $exists = true; + } + $name = $chatroom['cr_name']; - $exists = false; + if ($exists) { + foreach ($chatroom as $k => $v) { + if (! in_array($k, $columns)) { + continue; + } - $x = q("select * from chatroom where cr_name = '%s' and cr_uid = %d limit 1", - dbesc($chatroom['cr_name']), - intval($channel['channel_id']) - ); - if ($x) { - if ($x[0]['cr_edited'] >= $chatroom['cr_edited']) { - continue; - } - $exists = true; - } - $name = $chatroom['cr_name']; - - if ($exists) { - foreach ($chatroom as $k => $v) { - - if (! in_array($k,$columns)) { - continue; - } - - $r = q("UPDATE chatroom SET " . TQUOT . "%s" . TQUOT . " = '%s' WHERE cr_name = '%s' AND cr_uid = %d", - dbesc($k), - dbesc($v), - dbesc($name), - intval($channel['channel_id']) - ); - } - } - else { - create_table_from_array('chatroom', $chatroom); - } - } - } + $r = q( + "UPDATE chatroom SET " . TQUOT . "%s" . TQUOT . " = '%s' WHERE cr_name = '%s' AND cr_uid = %d", + dbesc($k), + dbesc($v), + dbesc($name), + intval($channel['channel_id']) + ); + } + } else { + create_table_from_array('chatroom', $chatroom); + } + } + } } @@ -942,67 +961,69 @@ function sync_chatrooms($channel, $chatrooms) { * * @param array $channel where to import to * @param array $items - * @param boolean $sync default false + * @param bool $sync default false * @param array $relocate default null */ -function import_items($channel, $items, $sync = false, $relocate = null) { +function import_items($channel, $items, $sync = false, $relocate = null) +{ - if ($channel && $items) { + if ($channel && $items) { + $allow_code = channel_codeallowed($channel['channel_id']); - $allow_code = channel_codeallowed($channel['channel_id']); + $deliver = false; // Don't deliver any messages or notifications when importing - $deliver = false; // Don't deliver any messages or notifications when importing + foreach ($items as $i) { + $item_result = false; + $item = get_item_elements($i, $allow_code); - foreach ($items as $i) { - $item_result = false; - $item = get_item_elements($i,$allow_code); + if (! $item) { + continue; + } - if (! $item) - continue; + if ($relocate && $item['mid'] === $item['parent_mid']) { + item_url_replace($channel, $item, $relocate['url'], z_root(), $relocate['channel_address']); + } - if ($relocate && $item['mid'] === $item['parent_mid']) { - item_url_replace($channel,$item,$relocate['url'],z_root(),$relocate['channel_address']); - } + $r = q( + "select id, edited from item where mid = '%s' and uid = %d limit 1", + dbesc($item['mid']), + intval($channel['channel_id']) + ); + if ($r) { + // flags may have changed and we are probably relocating the post, + // so force an update even if we have the same timestamp - $r = q("select id, edited from item where mid = '%s' and uid = %d limit 1", - dbesc($item['mid']), - intval($channel['channel_id']) - ); - if ($r) { + if ($item['edited'] >= $r[0]['edited']) { + $item['id'] = $r[0]['id']; + $item['uid'] = $channel['channel_id']; + $item_result = item_store_update($item, $allow_code, $deliver, false); + } + } else { + $item['aid'] = $channel['channel_account_id']; + $item['uid'] = $channel['channel_id']; + $item_result = item_store($item, $allow_code, $deliver, false); + } - // flags may have changed and we are probably relocating the post, - // so force an update even if we have the same timestamp + // preserve conversations you've been involved in from being expired - if ($item['edited'] >= $r[0]['edited']) { - $item['id'] = $r[0]['id']; - $item['uid'] = $channel['channel_id']; - $item_result = item_store_update($item,$allow_code,$deliver,false); - } - } - else { - $item['aid'] = $channel['channel_account_id']; - $item['uid'] = $channel['channel_id']; - $item_result = item_store($item,$allow_code,$deliver,false); - } + $stored = $item_result['item']; + if ( + (is_array($stored)) && ($stored['id'] != $stored['parent']) + && ($stored['author_xchan'] === $channel['channel_hash']) + ) { + retain_item($stored['item']['parent']); + } - // preserve conversations you've been involved in from being expired + fix_attached_permissions($channel['channel_id'], $item['body'], $item['allow_cid'], $item['allow_gid'], $item['deny_cid'], $item['deny_gid']); - $stored = $item_result['item']; - if ((is_array($stored)) && ($stored['id'] != $stored['parent']) - && ($stored['author_xchan'] === $channel['channel_hash'])) { - retain_item($stored['item']['parent']); - } - - fix_attached_permissions($channel['channel_id'],$item['body'],$item['allow_cid'],$item['allow_gid'],$item['deny_cid'],$item['deny_gid']); - - if ($sync && $item['item_wall']) { - // deliver singletons if we have any - if ($item_result && $item_result['success']) { - Run::Summon( [ 'Notifier','single_activity',$item_result['item_id'] ]); - } - } - } - } + if ($sync && $item['item_wall']) { + // deliver singletons if we have any + if ($item_result && $item_result['success']) { + Run::Summon([ 'Notifier','single_activity',$item_result['item_id'] ]); + } + } + } + } } /** @@ -1014,8 +1035,9 @@ function import_items($channel, $items, $sync = false, $relocate = null) { * @param array $items * @param array $relocate default null */ -function sync_items($channel, $items, $relocate = null) { - import_items($channel, $items, true, $relocate); +function sync_items($channel, $items, $relocate = null) +{ + import_items($channel, $items, true, $relocate); } @@ -1025,21 +1047,22 @@ function sync_items($channel, $items, $relocate = null) { * @param array $channel * @param array $events */ -function import_events($channel, $events) { +function import_events($channel, $events) +{ - if ($channel && $events) { - foreach ($events as $event) { - unset($event['id']); - $event['aid'] = $channel['channel_account_id']; - $event['uid'] = $channel['channel_id']; - convert_oldfields($event,'start','dtstart'); - convert_oldfields($event,'finish','dtend'); - convert_oldfields($event,'type','etype'); - convert_oldfields($event,'ignore','dismissed'); + if ($channel && $events) { + foreach ($events as $event) { + unset($event['id']); + $event['aid'] = $channel['channel_account_id']; + $event['uid'] = $channel['channel_id']; + convert_oldfields($event, 'start', 'dtstart'); + convert_oldfields($event, 'finish', 'dtend'); + convert_oldfields($event, 'type', 'etype'); + convert_oldfields($event, 'ignore', 'dismissed'); - create_table_from_array('event', $event); - } - } + create_table_from_array('event', $event); + } + } } /** @@ -1048,75 +1071,76 @@ function import_events($channel, $events) { * @param array $channel * @param array $events */ -function sync_events($channel, $events) { +function sync_events($channel, $events) +{ - if ($channel && $events) { + if ($channel && $events) { + $columns = db_columns('event'); - $columns = db_columns('event'); + foreach ($events as $event) { + if ((! $event['event_hash']) || (! $event['dtstart'])) { + continue; + } - foreach ($events as $event) { + if ($event['event_deleted']) { + $r = q( + "delete from event where event_hash = '%s' and uid = %d", + dbesc($event['event_hash']), + intval($channel['channel_id']) + ); + $r = q( + "select id from item where resource_type = 'event' and resource_id = '%s' and uid = %d", + dbesc($event['event_hash']), + intval($channel['channel_id']) + ); + if ($r) { + drop_item($r[0]['id'], false, (($event['event_xchan'] === $channel['channel_hash']) ? DROPITEM_PHASE1 : DROPITEM_NORMAL)); + } + continue; + } - if ((! $event['event_hash']) || (! $event['dtstart'])) { - continue; - } + unset($event['id']); + $event['aid'] = $channel['channel_account_id']; + $event['uid'] = $channel['channel_id']; - if ($event['event_deleted']) { - $r = q("delete from event where event_hash = '%s' and uid = %d", - dbesc($event['event_hash']), - intval($channel['channel_id']) - ); - $r = q("select id from item where resource_type = 'event' and resource_id = '%s' and uid = %d", - dbesc($event['event_hash']), - intval($channel['channel_id']) - ); - if ($r) { - drop_item($r[0]['id'],false,(($event['event_xchan'] === $channel['channel_hash']) ? DROPITEM_PHASE1 : DROPITEM_NORMAL)); - } - continue; - } + convert_oldfields($event, 'start', 'dtstart'); + convert_oldfields($event, 'finish', 'dtend'); + convert_oldfields($event, 'type', 'etype'); + convert_oldfields($event, 'ignore', 'dismissed'); - unset($event['id']); - $event['aid'] = $channel['channel_account_id']; - $event['uid'] = $channel['channel_id']; + $exists = false; - convert_oldfields($event,'start','dtstart'); - convert_oldfields($event,'finish','dtend'); - convert_oldfields($event,'type','etype'); - convert_oldfields($event,'ignore','dismissed'); + $x = q( + "select * from event where event_hash = '%s' and uid = %d limit 1", + dbesc($event['event_hash']), + intval($channel['channel_id']) + ); + if ($x) { + if ($x[0]['edited'] >= $event['edited']) { + continue; + } + $exists = true; + } - $exists = false; + if ($exists) { + foreach ($event as $k => $v) { + if (! in_array($k, $columns)) { + continue; + } - $x = q("select * from event where event_hash = '%s' and uid = %d limit 1", - dbesc($event['event_hash']), - intval($channel['channel_id']) - ); - if ($x) { - if ($x[0]['edited'] >= $event['edited']) { - continue; - } - $exists = true; - } - - if ($exists) { - foreach ($event as $k => $v) { - - if (! in_array($k,$columns)) { - continue; - } - - $r = q("UPDATE event SET " . TQUOT . "%s" . TQUOT . " = '%s' WHERE event_hash = '%s' AND uid = %d", - dbesc($k), - dbesc($v), - dbesc($event['event_hash']), - intval($channel['channel_id']) - ); - } - } - else { - create_table_from_array('event', $event); - } - } - } + $r = q( + "UPDATE event SET " . TQUOT . "%s" . TQUOT . " = '%s' WHERE event_hash = '%s' AND uid = %d", + dbesc($k), + dbesc($v), + dbesc($event['event_hash']), + intval($channel['channel_id']) + ); + } + } else { + create_table_from_array('event', $event); + } + } + } } /** @@ -1125,62 +1149,63 @@ function sync_events($channel, $events) { * @param array $channel * @param array $menus */ -function import_menus($channel, $menus) { +function import_menus($channel, $menus) +{ - if ($channel && $menus) { - foreach ($menus as $menu) { - $m = []; - $m['menu_channel_id'] = $channel['channel_id']; - $m['menu_name'] = $menu['pagetitle']; - $m['menu_desc'] = $menu['desc']; - if ($menu['created']) { - $m['menu_created'] = datetime_convert('UTC','UTC',$menu['created']); - } - if ($menu['edited']) { - $m['menu_edited'] = datetime_convert('UTC','UTC', $menu['edited']); - } - $m['menu_flags'] = 0; - if ($menu['flags']) { - if (in_array('bookmark',$menu['flags'])) { - $m['menu_flags'] |= MENU_BOOKMARK; - } - if (in_array('system',$menu['flags'])) { - $m['menu_flags'] |= MENU_SYSTEM; - } - } + if ($channel && $menus) { + foreach ($menus as $menu) { + $m = []; + $m['menu_channel_id'] = $channel['channel_id']; + $m['menu_name'] = $menu['pagetitle']; + $m['menu_desc'] = $menu['desc']; + if ($menu['created']) { + $m['menu_created'] = datetime_convert('UTC', 'UTC', $menu['created']); + } + if ($menu['edited']) { + $m['menu_edited'] = datetime_convert('UTC', 'UTC', $menu['edited']); + } + $m['menu_flags'] = 0; + if ($menu['flags']) { + if (in_array('bookmark', $menu['flags'])) { + $m['menu_flags'] |= MENU_BOOKMARK; + } + if (in_array('system', $menu['flags'])) { + $m['menu_flags'] |= MENU_SYSTEM; + } + } - $menu_id = menu_create($m); + $menu_id = menu_create($m); - if ($menu_id) { - if (is_array($menu['items'])) { - foreach ($menu['items'] as $it) { - $mitem = []; + if ($menu_id) { + if (is_array($menu['items'])) { + foreach ($menu['items'] as $it) { + $mitem = []; - $mitem['mitem_link'] = str_replace('[channelurl]',z_root() . '/channel/' . $channel['channel_address'],$it['link']); - $mitem['mitem_link'] = str_replace('[pageurl]',z_root() . '/page/' . $channel['channel_address'],$it['link']); - $mitem['mitem_link'] = str_replace('[cloudurl]',z_root() . '/cloud/' . $channel['channel_address'],$it['link']); - $mitem['mitem_link'] = str_replace('[baseurl]',z_root(),$it['link']); + $mitem['mitem_link'] = str_replace('[channelurl]', z_root() . '/channel/' . $channel['channel_address'], $it['link']); + $mitem['mitem_link'] = str_replace('[pageurl]', z_root() . '/page/' . $channel['channel_address'], $it['link']); + $mitem['mitem_link'] = str_replace('[cloudurl]', z_root() . '/cloud/' . $channel['channel_address'], $it['link']); + $mitem['mitem_link'] = str_replace('[baseurl]', z_root(), $it['link']); - $mitem['mitem_desc'] = escape_tags($it['desc']); - $mitem['mitem_order'] = intval($it['order']); - if (is_array($it['flags'])) { - $mitem['mitem_flags'] = 0; - if (in_array('zid',$it['flags'])) { - $mitem['mitem_flags'] |= MENU_ITEM_ZID; - } - if (in_array('new-window',$it['flags'])) { - $mitem['mitem_flags'] |= MENU_ITEM_NEWWIN; - } - if (in_array('chatroom',$it['flags'])) { - $mitem['mitem_flags'] |= MENU_ITEM_CHATROOM; - } - } - menu_add_item($menu_id,$channel['channel_id'],$mitem); - } - } - } - } - } + $mitem['mitem_desc'] = escape_tags($it['desc']); + $mitem['mitem_order'] = intval($it['order']); + if (is_array($it['flags'])) { + $mitem['mitem_flags'] = 0; + if (in_array('zid', $it['flags'])) { + $mitem['mitem_flags'] |= MENU_ITEM_ZID; + } + if (in_array('new-window', $it['flags'])) { + $mitem['mitem_flags'] |= MENU_ITEM_NEWWIN; + } + if (in_array('chatroom', $it['flags'])) { + $mitem['mitem_flags'] |= MENU_ITEM_CHATROOM; + } + } + menu_add_item($menu_id, $channel['channel_id'], $mitem); + } + } + } + } + } } /** @@ -1189,94 +1214,95 @@ function import_menus($channel, $menus) { * @param array $channel * @param array $menus */ -function sync_menus($channel, $menus) { +function sync_menus($channel, $menus) +{ - if ($channel && $menus) { + if ($channel && $menus) { + foreach ($menus as $menu) { + $m = []; + $m['menu_channel_id'] = $channel['channel_id']; + $m['menu_name'] = $menu['pagetitle']; + $m['menu_desc'] = $menu['desc']; + if ($menu['created']) { + $m['menu_created'] = datetime_convert('UTC', 'UTC', $menu['created']); + } + if ($menu['edited']) { + $m['menu_edited'] = datetime_convert('UTC', 'UTC', $menu['edited']); + } + $m['menu_flags'] = 0; + if ($menu['flags']) { + if (in_array('bookmark', $menu['flags'])) { + $m['menu_flags'] |= MENU_BOOKMARK; + } + if (in_array('system', $menu['flags'])) { + $m['menu_flags'] |= MENU_SYSTEM; + } + } + $editing = false; - foreach ($menus as $menu) { - $m = []; - $m['menu_channel_id'] = $channel['channel_id']; - $m['menu_name'] = $menu['pagetitle']; - $m['menu_desc'] = $menu['desc']; - if ($menu['created']) { - $m['menu_created'] = datetime_convert('UTC','UTC',$menu['created']); - } - if ($menu['edited']) { - $m['menu_edited'] = datetime_convert('UTC','UTC',$menu['edited']); - } - $m['menu_flags'] = 0; - if ($menu['flags']) { - if (in_array('bookmark',$menu['flags'])) { - $m['menu_flags'] |= MENU_BOOKMARK; - } - if (in_array('system',$menu['flags'])) { - $m['menu_flags'] |= MENU_SYSTEM; - } - } + $r = q( + "select * from menu where menu_name = '%s' and menu_channel_id = %d limit 1", + dbesc($m['menu_name']), + intval($channel['channel_id']) + ); + if ($r) { + if ($r[0]['menu_edited'] >= $m['menu_edited']) { + continue; + } + if ($menu['menu_deleted']) { + menu_delete_id($r[0]['menu_id'], $channel['channel_id']); + continue; + } + $menu_id = $r[0]['menu_id']; + $m['menu_id'] = $r[0]['menu_id']; + $x = menu_edit($m); + if (! $x) { + continue; + } + $editing = true; + } + if (! $editing) { + $menu_id = menu_create($m); + } + if ($menu_id) { + if ($editing) { + // don't try syncing - just delete all the entries and start over + q( + "delete from menu_item where mitem_menu_id = %d", + intval($menu_id) + ); + } - $editing = false; + if (is_array($menu['items'])) { + foreach ($menu['items'] as $it) { + $mitem = []; - $r = q("select * from menu where menu_name = '%s' and menu_channel_id = %d limit 1", - dbesc($m['menu_name']), - intval($channel['channel_id']) - ); - if ($r) { - if ($r[0]['menu_edited'] >= $m['menu_edited']) { - continue; - } - if ($menu['menu_deleted']) { - menu_delete_id($r[0]['menu_id'],$channel['channel_id']); - continue; - } - $menu_id = $r[0]['menu_id']; - $m['menu_id'] = $r[0]['menu_id']; - $x = menu_edit($m); - if (! $x) { - continue; - } - $editing = true; - } - if (! $editing) { - $menu_id = menu_create($m); - } - if ($menu_id) { - if ($editing) { - // don't try syncing - just delete all the entries and start over - q("delete from menu_item where mitem_menu_id = %d", - intval($menu_id) - ); - } + $mitem['mitem_link'] = str_replace('[channelurl]', z_root() . '/channel/' . $channel['channel_address'], $it['link']); + $mitem['mitem_link'] = str_replace('[pageurl]', z_root() . '/page/' . $channel['channel_address'], $it['link']); + $mitem['mitem_link'] = str_replace('[cloudurl]', z_root() . '/cloud/' . $channel['channel_address'], $it['link']); + $mitem['mitem_link'] = str_replace('[baseurl]', z_root(), $it['link']); - if (is_array($menu['items'])) { - foreach ($menu['items'] as $it) { - $mitem = []; - - $mitem['mitem_link'] = str_replace('[channelurl]',z_root() . '/channel/' . $channel['channel_address'],$it['link']); - $mitem['mitem_link'] = str_replace('[pageurl]',z_root() . '/page/' . $channel['channel_address'],$it['link']); - $mitem['mitem_link'] = str_replace('[cloudurl]',z_root() . '/cloud/' . $channel['channel_address'],$it['link']); - $mitem['mitem_link'] = str_replace('[baseurl]',z_root(),$it['link']); - - $mitem['mitem_desc'] = escape_tags($it['desc']); - $mitem['mitem_order'] = intval($it['order']); - if (is_array($it['flags'])) { - $mitem['mitem_flags'] = 0; - if (in_array('zid',$it['flags'])) { - $mitem['mitem_flags'] |= MENU_ITEM_ZID; - } - if (in_array('new-window',$it['flags'])) { - $mitem['mitem_flags'] |= MENU_ITEM_NEWWIN; - } - if (in_array('chatroom',$it['flags'])) { - $mitem['mitem_flags'] |= MENU_ITEM_CHATROOM; - } - } - menu_add_item($menu_id,$channel['channel_id'],$mitem); - } - } - } - } - } + $mitem['mitem_desc'] = escape_tags($it['desc']); + $mitem['mitem_order'] = intval($it['order']); + if (is_array($it['flags'])) { + $mitem['mitem_flags'] = 0; + if (in_array('zid', $it['flags'])) { + $mitem['mitem_flags'] |= MENU_ITEM_ZID; + } + if (in_array('new-window', $it['flags'])) { + $mitem['mitem_flags'] |= MENU_ITEM_NEWWIN; + } + if (in_array('chatroom', $it['flags'])) { + $mitem['mitem_flags'] |= MENU_ITEM_CHATROOM; + } + } + menu_add_item($menu_id, $channel['channel_id'], $mitem); + } + } + } + } + } } /** @@ -1285,65 +1311,71 @@ function sync_menus($channel, $menus) { * @param array $channel * @param array $likes */ -function import_likes($channel, $likes) { - if ($channel && $likes) { - foreach ($likes as $like) { - if ($like['deleted']) { - q("delete from likes where liker = '%s' and likee = '%s' and verb = '%s' and target_type = '%s' and target_id = '%s'", - dbesc($like['liker']), - dbesc($like['likee']), - dbesc($like['verb']), - dbesc($like['target_type']), - dbesc($like['target_id']) - ); - continue; - } +function import_likes($channel, $likes) +{ + if ($channel && $likes) { + foreach ($likes as $like) { + if ($like['deleted']) { + q( + "delete from likes where liker = '%s' and likee = '%s' and verb = '%s' and target_type = '%s' and target_id = '%s'", + dbesc($like['liker']), + dbesc($like['likee']), + dbesc($like['verb']), + dbesc($like['target_type']), + dbesc($like['target_id']) + ); + continue; + } - unset($like['id']); - unset($like['iid']); - $like['channel_id'] = $channel['channel_id']; - $r = q("select * from likes where liker = '%s' and likee = '%s' and verb = '%s' and target_type = '%s' and target_id = '%s' and i_mid = '%s'", - dbesc($like['liker']), - dbesc($like['likee']), - dbesc($like['verb']), - dbesc($like['target_type']), - dbesc($like['target_id']), - dbesc($like['i_mid']) - ); - if ($r) { - continue; - } - create_table_from_array('likes', $like); - } - } + unset($like['id']); + unset($like['iid']); + $like['channel_id'] = $channel['channel_id']; + $r = q( + "select * from likes where liker = '%s' and likee = '%s' and verb = '%s' and target_type = '%s' and target_id = '%s' and i_mid = '%s'", + dbesc($like['liker']), + dbesc($like['likee']), + dbesc($like['verb']), + dbesc($like['target_type']), + dbesc($like['target_id']), + dbesc($like['i_mid']) + ); + if ($r) { + continue; + } + create_table_from_array('likes', $like); + } + } } -function import_conv($channel,$convs) { - if ($channel && $convs) { - foreach ($convs as $conv) { - if ($conv['deleted']) { - q("delete from conv where guid = '%s' and uid = %d", - dbesc($conv['guid']), - intval($channel['channel_id']) - ); - continue; - } +function import_conv($channel, $convs) +{ + if ($channel && $convs) { + foreach ($convs as $conv) { + if ($conv['deleted']) { + q( + "delete from conv where guid = '%s' and uid = %d", + dbesc($conv['guid']), + intval($channel['channel_id']) + ); + continue; + } - unset($conv['id']); + unset($conv['id']); - $conv['uid'] = $channel['channel_id']; - $conv['subject'] = str_rot47(base64url_encode($conv['subject'])); + $conv['uid'] = $channel['channel_id']; + $conv['subject'] = str_rot47(base64url_encode($conv['subject'])); - $r = q("select id from conv where guid = '%s' and uid = %d limit 1", - dbesc($conv['guid']), - intval($channel['channel_id']) - ); - if ($r) { - continue; - } - create_table_from_array('conv',$conv); - } - } + $r = q( + "select id from conv where guid = '%s' and uid = %d limit 1", + dbesc($conv['guid']), + intval($channel['channel_id']) + ); + if ($r) { + continue; + } + create_table_from_array('conv', $conv); + } + } } /** @@ -1351,39 +1383,42 @@ function import_conv($channel,$convs) { * * @param array $channel * @param array $mails - * @param boolean $sync (optional) default false + * @param bool $sync (optional) default false */ -function import_mail($channel, $mails, $sync = false) { - if ($channel && $mails) { - foreach ($mails as $mail) { - if (array_key_exists('flags',$mail) && in_array('deleted',$mail['flags'])) { - q("delete from mail where mid = '%s' and uid = %d", - dbesc($mail['message_id']), - intval($channel['channel_id']) - ); - continue; - } - if (array_key_exists('flags',$mail) && in_array('recalled',$mail['flags'])) { - q("update mail set mail_recalled = 1 where mid = '%s' and uid = %d", - dbesc($mail['message_id']), - intval($channel['channel_id']) - ); - continue; - } +function import_mail($channel, $mails, $sync = false) +{ + if ($channel && $mails) { + foreach ($mails as $mail) { + if (array_key_exists('flags', $mail) && in_array('deleted', $mail['flags'])) { + q( + "delete from mail where mid = '%s' and uid = %d", + dbesc($mail['message_id']), + intval($channel['channel_id']) + ); + continue; + } + if (array_key_exists('flags', $mail) && in_array('recalled', $mail['flags'])) { + q( + "update mail set mail_recalled = 1 where mid = '%s' and uid = %d", + dbesc($mail['message_id']), + intval($channel['channel_id']) + ); + continue; + } - $m = get_mail_elements($mail); - if (! $m) { - continue; - } - $m['account_id'] = $channel['channel_account_id']; - $m['channel_id'] = $channel['channel_id']; - $mail_id = mail_store($m); - if ($sync && $mail_id) { - // Not applicable to Zap which does not support mail - // Run::Summon( [ 'Notifier','single_mail',$mail_id ] ); - } - } - } + $m = get_mail_elements($mail); + if (! $m) { + continue; + } + $m['account_id'] = $channel['channel_account_id']; + $m['channel_id'] = $channel['channel_id']; + $mail_id = mail_store($m); + if ($sync && $mail_id) { + // Not applicable to Zap which does not support mail + // Run::Summon( [ 'Notifier','single_mail',$mail_id ] ); + } + } + } } /** @@ -1393,8 +1428,9 @@ function import_mail($channel, $mails, $sync = false) { * @param array $channel * @param array $mails */ -function sync_mail($channel, $mails) { - import_mail($channel, $mails, true); +function sync_mail($channel, $mails) +{ + import_mail($channel, $mails, true); } /** @@ -1403,355 +1439,352 @@ function sync_mail($channel, $mails) { * @param array $channel * @param array $files */ -function sync_files($channel, $files) { +function sync_files($channel, $files) +{ - require_once('include/attach.php'); + require_once('include/attach.php'); - if ($channel && $files) { + if ($channel && $files) { + $limit = engr_units_to_bytes(service_class_fetch($channel['channel_id'], 'attach_upload_limit')); - $limit = engr_units_to_bytes(service_class_fetch($channel['channel_id'], 'attach_upload_limit')); + foreach ($files as $f) { + if (! $f) { + continue; + } + $fetch_url = $f['fetch_url']; + $oldbase = dirname($fetch_url); + $original_channel = $f['original_channel']; - foreach ($files as $f) { - if (! $f) { - continue; - } - $fetch_url = $f['fetch_url']; - $oldbase = dirname($fetch_url); - $original_channel = $f['original_channel']; + if (! ($fetch_url && $original_channel)) { + continue; + } - if (! ($fetch_url && $original_channel)) { - continue; - } + $has_undeleted_attachments = false; - $has_undeleted_attachments = false; - - if ($f['attach']) { - foreach ($f['attach'] as $att) { - $attachment_stored = false; - convert_oldfields($att,'data','content'); + if ($f['attach']) { + foreach ($f['attach'] as $att) { + $attachment_stored = false; + convert_oldfields($att, 'data', 'content'); - if (intval($att['deleted'])) { - logger('deleting attachment'); - attach_delete($channel['channel_id'],$att['hash']); - continue; - } + if (intval($att['deleted'])) { + logger('deleting attachment'); + attach_delete($channel['channel_id'], $att['hash']); + continue; + } - $has_undeleted_attachments = true; - $attach_exists = false; - $x = attach_by_hash($att['hash'],$channel['channel_hash']); - logger('sync_files duplicate check: attach_exists=' . $attach_exists, LOGGER_DEBUG); - logger('sync_files duplicate check: att=' . print_r($att,true), LOGGER_DEBUG); - logger('sync_files duplicate check: attach_by_hash() returned ' . print_r($x,true), LOGGER_DEBUG); + $has_undeleted_attachments = true; + $attach_exists = false; + $x = attach_by_hash($att['hash'], $channel['channel_hash']); + logger('sync_files duplicate check: attach_exists=' . $attach_exists, LOGGER_DEBUG); + logger('sync_files duplicate check: att=' . print_r($att, true), LOGGER_DEBUG); + logger('sync_files duplicate check: attach_by_hash() returned ' . print_r($x, true), LOGGER_DEBUG); - if ($x['success']) { - $orig_attach = $x['data']; - $attach_exists = true; - $attach_id = $orig_attach['id']; - } + if ($x['success']) { + $orig_attach = $x['data']; + $attach_exists = true; + $attach_id = $orig_attach['id']; + } - $newfname = 'store/' . $channel['channel_address'] . '/' . get_attach_binname($att['content']); + $newfname = 'store/' . $channel['channel_address'] . '/' . get_attach_binname($att['content']); - unset($att['id']); - $att['aid'] = $channel['channel_account_id']; - $att['uid'] = $channel['channel_id']; + unset($att['id']); + $att['aid'] = $channel['channel_account_id']; + $att['uid'] = $channel['channel_id']; - // check for duplicate folder names with the same parent. - // If we have a duplicate that doesn't match this hash value - // change the name so that the contents won't be "covered over" - // by the existing directory. Use the same logic we use for - // duplicate files. + // check for duplicate folder names with the same parent. + // If we have a duplicate that doesn't match this hash value + // change the name so that the contents won't be "covered over" + // by the existing directory. Use the same logic we use for + // duplicate files. - if (strpos($att['filename'],'.') !== false) { - $basename = substr($att['filename'],0,strrpos($att['filename'],'.')); - $ext = substr($att['filename'],strrpos($att['filename'],'.')); - } - else { - $basename = $att['filename']; - $ext = ''; - } + if (strpos($att['filename'], '.') !== false) { + $basename = substr($att['filename'], 0, strrpos($att['filename'], '.')); + $ext = substr($att['filename'], strrpos($att['filename'], '.')); + } else { + $basename = $att['filename']; + $ext = ''; + } - $r = q("select filename from attach where ( filename = '%s' OR filename like '%s' ) and folder = '%s' and hash != '%s' and uid = %d ", - dbesc($basename . $ext), - dbesc($basename . '(%)' . $ext), - dbesc($att['folder']), - dbesc($att['hash']), - intval($channel['channel_id']) - ); + $r = q( + "select filename from attach where ( filename = '%s' OR filename like '%s' ) and folder = '%s' and hash != '%s' and uid = %d ", + dbesc($basename . $ext), + dbesc($basename . '(%)' . $ext), + dbesc($att['folder']), + dbesc($att['hash']), + intval($channel['channel_id']) + ); - if ($r) { - $x = 1; + if ($r) { + $x = 1; - do { - $found = false; - foreach ($r as $rr) { - if ($rr['filename'] === $basename . '(' . $x . ')' . $ext) { - $found = true; - break; - } - } - if ($found) { - $x++; - } - } - while ($found); - $att['filename'] = $basename . '(' . $x . ')' . $ext; - } - else { - $att['filename'] = $basename . $ext; - } - // end duplicate detection + do { + $found = false; + foreach ($r as $rr) { + if ($rr['filename'] === $basename . '(' . $x . ')' . $ext) { + $found = true; + break; + } + } + if ($found) { + $x++; + } + } while ($found); + $att['filename'] = $basename . '(' . $x . ')' . $ext; + } else { + $att['filename'] = $basename . $ext; + } + // end duplicate detection - /// @FIXME update attachment structures if they are modified rather than created + /// @FIXME update attachment structures if they are modified rather than created - $att['content'] = $newfname; + $att['content'] = $newfname; - // Note: we use $att['hash'] below after it has been escaped to - // fetch the file contents. - // If the hash ever contains any escapable chars this could cause - // problems. Currently it does not. + // Note: we use $att['hash'] below after it has been escaped to + // fetch the file contents. + // If the hash ever contains any escapable chars this could cause + // problems. Currently it does not. - if (!isset($att['os_path'])) { - $att['os_path'] = ''; - } + if (!isset($att['os_path'])) { + $att['os_path'] = ''; + } - if ($attach_exists) { - logger('sync_files attach exists: ' . print_r($att,true), LOGGER_DEBUG); + if ($attach_exists) { + logger('sync_files attach exists: ' . print_r($att, true), LOGGER_DEBUG); - // process/sync a remote rename/move operation + // process/sync a remote rename/move operation - if ($orig_attach && $orig_attach['content'] && $orig_attach['content'] !== $newfname) { - logger('rename: ' . $orig_attach['content'] . ' -> ' . $newfname); - rename($orig_attach['content'],$newfname); - } + if ($orig_attach && $orig_attach['content'] && $orig_attach['content'] !== $newfname) { + logger('rename: ' . $orig_attach['content'] . ' -> ' . $newfname); + rename($orig_attach['content'], $newfname); + } - if (! dbesc_array($att)) { - continue; - } + if (! dbesc_array($att)) { + continue; + } - $columns = db_columns('attach'); + $columns = db_columns('attach'); - $str = ''; - foreach ($att as $k => $v) { - if (! in_array($k,$columns)) { - continue; - } - - if ($str) { - $str .= ","; - } - $str .= " " . TQUOT . $k . TQUOT . " = '" . $v . "' "; - } - $r = dbq("update attach set " . $str . " where id = " . intval($attach_id) ); - } - else { - logger('sync_files attach does not exists: ' . print_r($att,true), LOGGER_DEBUG); + $str = ''; + foreach ($att as $k => $v) { + if (! in_array($k, $columns)) { + continue; + } - if ($limit !== false) { - $r = q("select sum(filesize) as total from attach where aid = %d ", - intval($channel['channel_account_id']) - ); - if (($r) && (($r[0]['total'] + $att['filesize']) > $limit)) { - logger('service class limit exceeded'); - continue; - } - } + if ($str) { + $str .= ","; + } + $str .= " " . TQUOT . $k . TQUOT . " = '" . $v . "' "; + } + $r = dbq("update attach set " . $str . " where id = " . intval($attach_id)); + } else { + logger('sync_files attach does not exists: ' . print_r($att, true), LOGGER_DEBUG); - create_table_from_array('attach',$att); - } + if ($limit !== false) { + $r = q( + "select sum(filesize) as total from attach where aid = %d ", + intval($channel['channel_account_id']) + ); + if (($r) && (($r[0]['total'] + $att['filesize']) > $limit)) { + logger('service class limit exceeded'); + continue; + } + } + + create_table_from_array('attach', $att); + } - // is this a directory? + // is this a directory? - if ($att['filetype'] === 'multipart/mixed' && $att['is_dir']) { - os_mkdir($newfname, STORAGE_DEFAULT_PERMISSIONS,true); - $attachment_stored = true; - continue; - } - else { + if ($att['filetype'] === 'multipart/mixed' && $att['is_dir']) { + os_mkdir($newfname, STORAGE_DEFAULT_PERMISSIONS, true); + $attachment_stored = true; + continue; + } else { + // it's a file + // for the sync version of this algorithm (as opposed to 'offline import') + // we will fetch the actual file from the source server so it can be + // streamed directly to disk and avoid consuming PHP memory if it's a huge + // audio/video file or something. - // it's a file - // for the sync version of this algorithm (as opposed to 'offline import') - // we will fetch the actual file from the source server so it can be - // streamed directly to disk and avoid consuming PHP memory if it's a huge - // audio/video file or something. + $time = datetime_convert(); - $time = datetime_convert(); + $parr = [ + 'hash' => $channel['channel_hash'], + 'time' => $time, + 'resource' => $att['hash'], + 'revision' => 0, + 'signature' => Libzot::sign($channel['channel_hash'] . '.' . $time, $channel['channel_prvkey']) + ]; - $parr = [ - 'hash' => $channel['channel_hash'], - 'time' => $time, - 'resource' => $att['hash'], - 'revision' => 0, - 'signature' => Libzot::sign($channel['channel_hash'] . '.' . $time, $channel['channel_prvkey']) - ]; + $store_path = $newfname; - $store_path = $newfname; + $fp = fopen($newfname, 'w'); - $fp = fopen($newfname,'w'); + if (! $fp) { + logger('failed to open storage file.', LOGGER_NORMAL, LOG_ERR); + continue; + } - if (! $fp) { - logger('failed to open storage file.',LOGGER_NORMAL,LOG_ERR); - continue; - } + $redirects = 0; - $redirects = 0; + $m = parse_url($fetch_url); - $m = parse_url($fetch_url); + $headers = [ + 'Accept' => 'application/x-nomad+json, application/x-zot+json', + 'Sigtoken' => random_string(), + 'Host' => $m['host'], + '(request-target)' => 'post ' . $m['path'] . '/' . $att['hash'] + ]; - $headers = [ - 'Accept' => 'application/x-nomad+json, application/x-zot+json', - 'Sigtoken' => random_string(), - 'Host' => $m['host'], - '(request-target)' => 'post ' . $m['path'] . '/' . $att['hash'] - ]; + $headers = HTTPSig::create_sig($headers, $channel['channel_prvkey'], channel_url($channel), true, 'sha512'); - $headers = HTTPSig::create_sig($headers,$channel['channel_prvkey'], channel_url($channel),true,'sha512'); + $x = z_post_url($fetch_url . '/' . $att['hash'], $parr, $redirects, [ 'filep' => $fp, 'headers' => $headers]); - $x = z_post_url($fetch_url . '/' . $att['hash'],$parr,$redirects,[ 'filep' => $fp, 'headers' => $headers]); + fclose($fp); - fclose($fp); + if ($x['success']) { + $attachment_stored = true; + } + continue; + } + } + } + if (($has_undeleted_attachments) && (! $attachment_stored)) { + /// @TODO should we queue this and retry or delete everything or what? + logger('attachment store failed', LOGGER_NORMAL, LOG_ERR); + } + if ($f['photo']) { + foreach ($f['photo'] as $p) { + unset($p['id']); + $p['aid'] = $channel['channel_account_id']; + $p['uid'] = $channel['channel_id']; - if ($x['success']) { - $attachment_stored = true; - } - continue; - } - } - } - if (($has_undeleted_attachments) && (! $attachment_stored)) { - /// @TODO should we queue this and retry or delete everything or what? - logger('attachment store failed',LOGGER_NORMAL,LOG_ERR); - } - if ($f['photo']) { - foreach ($f['photo'] as $p) { - unset($p['id']); - $p['aid'] = $channel['channel_account_id']; - $p['uid'] = $channel['channel_id']; + convert_oldfields($p, 'data', 'content'); + convert_oldfields($p, 'scale', 'imgscale'); + convert_oldfields($p, 'size', 'filesize'); + convert_oldfields($p, 'type', 'mimetype'); - convert_oldfields($p,'data','content'); - convert_oldfields($p,'scale','imgscale'); - convert_oldfields($p,'size','filesize'); - convert_oldfields($p,'type','mimetype'); + // if this is a profile photo, undo the profile photo bit + // for any other photo which previously held it. - // if this is a profile photo, undo the profile photo bit - // for any other photo which previously held it. - - if ($p['photo_usage'] == PHOTO_PROFILE) { - $e = q("update photo set photo_usage = %d where photo_usage = %d + if ($p['photo_usage'] == PHOTO_PROFILE) { + $e = q( + "update photo set photo_usage = %d where photo_usage = %d and resource_id != '%s' and uid = %d ", - intval(PHOTO_NORMAL), - intval(PHOTO_PROFILE), - dbesc($p['resource_id']), - intval($channel['channel_id']) - ); - } + intval(PHOTO_NORMAL), + intval(PHOTO_PROFILE), + dbesc($p['resource_id']), + intval($channel['channel_id']) + ); + } - // same for cover photos + // same for cover photos - if ($p['photo_usage'] == PHOTO_COVER) { - $e = q("update photo set photo_usage = %d where photo_usage = %d + if ($p['photo_usage'] == PHOTO_COVER) { + $e = q( + "update photo set photo_usage = %d where photo_usage = %d and resource_id != '%s' and uid = %d ", - intval(PHOTO_NORMAL), - intval(PHOTO_COVER), - dbesc($p['resource_id']), - intval($channel['channel_id']) - ); - } + intval(PHOTO_NORMAL), + intval(PHOTO_COVER), + dbesc($p['resource_id']), + intval($channel['channel_id']) + ); + } - if (intval($p['os_storage'])) { - $p['content'] = $store_path . ((intval($p['imgscale'])) ? '-' . $p['imgscale'] : EMPTY_STR); - } - else { - $p['content'] = (($p['content']) ? base64_decode($p['content']) : ''); - } + if (intval($p['os_storage'])) { + $p['content'] = $store_path . ((intval($p['imgscale'])) ? '-' . $p['imgscale'] : EMPTY_STR); + } else { + $p['content'] = (($p['content']) ? base64_decode($p['content']) : ''); + } - if (intval($p['imgscale']) && ((intval($p['os_storage'])) || (! $p['content']))) { + if (intval($p['imgscale']) && ((intval($p['os_storage'])) || (! $p['content']))) { + $time = datetime_convert(); - $time = datetime_convert(); - - $parr = [ - 'hash' => $channel['channel_hash'], - 'time' => $time, - 'resource' => $att['hash'], - 'revision' => 0, - 'signature' => Libzot::sign($channel['channel_hash'] . '.' . $time, $channel['channel_prvkey']), - 'resolution' => $p['imgscale'] - ]; + $parr = [ + 'hash' => $channel['channel_hash'], + 'time' => $time, + 'resource' => $att['hash'], + 'revision' => 0, + 'signature' => Libzot::sign($channel['channel_hash'] . '.' . $time, $channel['channel_prvkey']), + 'resolution' => $p['imgscale'] + ]; - $stored_image = $newfname . '-' . intval($p['imgscale']); + $stored_image = $newfname . '-' . intval($p['imgscale']); - logger('fetching_photo: ' . $stored_image); + logger('fetching_photo: ' . $stored_image); - $fp = fopen($stored_image,'w'); - if (! $fp) { - logger('failed to open storage file.',LOGGER_NORMAL,LOG_ERR); - continue; - } - $redirects = 0; + $fp = fopen($stored_image, 'w'); + if (! $fp) { + logger('failed to open storage file.', LOGGER_NORMAL, LOG_ERR); + continue; + } + $redirects = 0; - $m = parse_url($fetch_url); + $m = parse_url($fetch_url); - $headers = [ + $headers = [ 'Accept' => 'application/x-nomad+json, application/x-zot+json', - 'Sigtoken' => random_string(), - 'Host' => $m['host'], - '(request-target)' => 'post ' . $m['path'] . '/' . $att['hash'] - ]; + 'Sigtoken' => random_string(), + 'Host' => $m['host'], + '(request-target)' => 'post ' . $m['path'] . '/' . $att['hash'] + ]; - $headers = HTTPSig::create_sig($headers,$channel['channel_prvkey'], channel_url($channel),true,'sha512'); + $headers = HTTPSig::create_sig($headers, $channel['channel_prvkey'], channel_url($channel), true, 'sha512'); - $x = z_post_url($fetch_url . '/' . $att['hash'],$parr,$redirects,[ 'filep' => $fp, 'headers' => $headers]); + $x = z_post_url($fetch_url . '/' . $att['hash'], $parr, $redirects, [ 'filep' => $fp, 'headers' => $headers]); - fclose($fp); + fclose($fp); - if (! intval($p['os_storage'])) { - $p['content'] = file_get_contents($stored_image); - unlink($stored_image); - } - } + if (! intval($p['os_storage'])) { + $p['content'] = file_get_contents($stored_image); + unlink($stored_image); + } + } - if (!isset($p['display_path'])) { - $p['display_path'] = ''; - } + if (!isset($p['display_path'])) { + $p['display_path'] = ''; + } - $exists = q("select * from photo where resource_id = '%s' and imgscale = %d and uid = %d limit 1", - dbesc($p['resource_id']), - intval($p['imgscale']), - intval($channel['channel_id']) - ); + $exists = q( + "select * from photo where resource_id = '%s' and imgscale = %d and uid = %d limit 1", + dbesc($p['resource_id']), + intval($p['imgscale']), + intval($channel['channel_id']) + ); - if ($exists) { + if ($exists) { + $str = ''; + foreach ($p as $k => $v) { + $matches = false; + if (preg_match('/([^a-zA-Z0-9\-\_\.])/', $k, $matches)) { + continue; + } - $str = ''; - foreach ($p as $k => $v) { - $matches = false; - if (preg_match('/([^a-zA-Z0-9\-\_\.])/',$k,$matches)) { - continue; - } + if ($str) { + $str .= ","; + } + $str .= " " . TQUOT . $k . TQUOT . " = '" . (($k === 'content') ? dbescbin($v) : dbesc($v)) . "' "; + } + $r = dbq("update photo set " . $str . " where id = " . intval($exists[0]['id'])); + } else { + create_table_from_array('photo', $p, [ 'content' ]); + } + } + } - if ($str) { - $str .= ","; - } - $str .= " " . TQUOT . $k . TQUOT . " = '" . (($k === 'content') ? dbescbin($v) : dbesc($v)) . "' "; - } - $r = dbq("update photo set " . $str . " where id = " . intval($exists[0]['id']) ); - } - else { - create_table_from_array('photo',$p, [ 'content' ] ); - } - } - } + Run::Summon([ 'Thumbnail' , $att['hash'] ]); - Run::Summon([ 'Thumbnail' , $att['hash'] ]); - - if ($f['item']) { - sync_items($channel,$f['item'], - ['channel_address' => $original_channel,'url' => $oldbase] - ); - } - } - } + if ($f['item']) { + sync_items( + $channel, + $f['item'], + ['channel_address' => $original_channel,'url' => $oldbase] + ); + } + } + } } /** @@ -1763,365 +1796,370 @@ function sync_files($channel, $files) { * @param string $old The old key in the array * @param string $new The new key in the array */ -function convert_oldfields(&$arr, $old, $new) { - if (array_key_exists($old, $arr)) { - $arr[$new] = $arr[$old]; - unset($arr[$old]); - } +function convert_oldfields(&$arr, $old, $new) +{ + if (array_key_exists($old, $arr)) { + $arr[$new] = $arr[$old]; + unset($arr[$old]); + } } -function scan_webpage_elements($path, $type, $cloud = false) { - $channel = App::get_channel(); - $dirtoscan = $path; - switch ($type) { - case 'page': - $dirtoscan .= '/pages/'; - $json_filename = 'page.json'; - break; - case 'layout': - $dirtoscan .= '/layouts/'; - $json_filename = 'layout.json'; - break; - case 'block': - $dirtoscan .= '/blocks/'; - $json_filename = 'block.json'; - break; - default : - return []; - } - if($cloud) { - $dirtoscan = get_dirpath_by_cloudpath($channel, $dirtoscan); - } - $elements = []; - if (is_dir($dirtoscan)) { - $dirlist = scandir($dirtoscan); - if ($dirlist) { - foreach ($dirlist as $element) { - if ($element === '.' || $element === '..') { - continue; - } - $folder = $dirtoscan . '/' . $element; - if (is_dir($folder)) { - if ($cloud) { - $jsonfilepath = $folder . '/' . get_filename_by_cloudname($json_filename, $channel, $folder); - } - else { - $jsonfilepath = $folder . '/' . $json_filename; - } - if (is_file($jsonfilepath)) { - $metadata = json_decode(file_get_contents($jsonfilepath), true); - if ($cloud) { - $contentfilename = get_filename_by_cloudname($metadata['contentfile'], $channel, $folder); - $metadata['path'] = $folder . '/' . $contentfilename; - } - else { - $contentfilename = $metadata['contentfile']; - $metadata['path'] = $folder . '/' . $contentfilename; - } - if ($metadata['contentfile'] === '') { - logger('Invalid ' . $type . ' content file'); - return false; - } - $content = file_get_contents($folder . '/' . $contentfilename); - if (!$content) { - if (is_readable($folder . '/' . $contentfilename)) { - $content = ''; - } - else { - logger('Failed to get file content for ' . $metadata['contentfile']); - return false; - } - } - $elements[] = $metadata; - } - } - } - } - } +function scan_webpage_elements($path, $type, $cloud = false) +{ + $channel = App::get_channel(); + $dirtoscan = $path; + switch ($type) { + case 'page': + $dirtoscan .= '/pages/'; + $json_filename = 'page.json'; + break; + case 'layout': + $dirtoscan .= '/layouts/'; + $json_filename = 'layout.json'; + break; + case 'block': + $dirtoscan .= '/blocks/'; + $json_filename = 'block.json'; + break; + default: + return []; + } + if ($cloud) { + $dirtoscan = get_dirpath_by_cloudpath($channel, $dirtoscan); + } + $elements = []; + if (is_dir($dirtoscan)) { + $dirlist = scandir($dirtoscan); + if ($dirlist) { + foreach ($dirlist as $element) { + if ($element === '.' || $element === '..') { + continue; + } + $folder = $dirtoscan . '/' . $element; + if (is_dir($folder)) { + if ($cloud) { + $jsonfilepath = $folder . '/' . get_filename_by_cloudname($json_filename, $channel, $folder); + } else { + $jsonfilepath = $folder . '/' . $json_filename; + } + if (is_file($jsonfilepath)) { + $metadata = json_decode(file_get_contents($jsonfilepath), true); + if ($cloud) { + $contentfilename = get_filename_by_cloudname($metadata['contentfile'], $channel, $folder); + $metadata['path'] = $folder . '/' . $contentfilename; + } else { + $contentfilename = $metadata['contentfile']; + $metadata['path'] = $folder . '/' . $contentfilename; + } + if ($metadata['contentfile'] === '') { + logger('Invalid ' . $type . ' content file'); + return false; + } + $content = file_get_contents($folder . '/' . $contentfilename); + if (!$content) { + if (is_readable($folder . '/' . $contentfilename)) { + $content = ''; + } else { + logger('Failed to get file content for ' . $metadata['contentfile']); + return false; + } + } + $elements[] = $metadata; + } + } + } + } + } - return $elements; + return $elements; } -function import_webpage_element($element, $channel, $type) { +function import_webpage_element($element, $channel, $type) +{ - $arr = []; // construct information for the webpage element item table record + $arr = []; // construct information for the webpage element item table record - switch ($type) { - // - // PAGES - // - case 'page': - $arr['item_type'] = ITEM_TYPE_WEBPAGE; - $namespace = 'WEBPAGE'; - $name = $element['pagelink']; - if ($name) { - require_once('library/urlify/URLify.php'); - $name = strtolower(\URLify::transliterate($name)); - } - $arr['title'] = $element['title']; - $arr['term'] = $element['term']; - $arr['layout_mid'] = ''; // by default there is no layout associated with the page - // If a layout was specified, find it in the database and get its info. If - // it does not exist, leave layout_mid empty - if ($element['layout'] !== '') { - $liid = q("select iid from iconfig where k = 'PDL' and v = '%s' and cat = 'system'", - dbesc($element['layout']) - ); - if ($liid) { - $linfo = q("select mid from item where id = %d", - intval($liid[0]['iid']) - ); - $arr['layout_mid'] = $linfo[0]['mid']; - } - } - break; - // - // LAYOUTS - // - case 'layout': - $arr['item_type'] = ITEM_TYPE_PDL; - $namespace = 'PDL'; - $name = $element['name']; - $arr['title'] = $element['description']; - $arr['term'] = $element['term']; - break; - // - // BLOCKS - // - case 'block': - $arr['item_type'] = ITEM_TYPE_BLOCK; - $namespace = 'BUILDBLOCK'; - $name = $element['name']; - $arr['title'] = $element['title']; + switch ($type) { + // + // PAGES + // + case 'page': + $arr['item_type'] = ITEM_TYPE_WEBPAGE; + $namespace = 'WEBPAGE'; + $name = $element['pagelink']; + if ($name) { + require_once('library/urlify/URLify.php'); + $name = strtolower(URLify::transliterate($name)); + } + $arr['title'] = $element['title']; + $arr['term'] = $element['term']; + $arr['layout_mid'] = ''; // by default there is no layout associated with the page + // If a layout was specified, find it in the database and get its info. If + // it does not exist, leave layout_mid empty + if ($element['layout'] !== '') { + $liid = q( + "select iid from iconfig where k = 'PDL' and v = '%s' and cat = 'system'", + dbesc($element['layout']) + ); + if ($liid) { + $linfo = q( + "select mid from item where id = %d", + intval($liid[0]['iid']) + ); + $arr['layout_mid'] = $linfo[0]['mid']; + } + } + break; + // + // LAYOUTS + // + case 'layout': + $arr['item_type'] = ITEM_TYPE_PDL; + $namespace = 'PDL'; + $name = $element['name']; + $arr['title'] = $element['description']; + $arr['term'] = $element['term']; + break; + // + // BLOCKS + // + case 'block': + $arr['item_type'] = ITEM_TYPE_BLOCK; + $namespace = 'BUILDBLOCK'; + $name = $element['name']; + $arr['title'] = $element['title']; - break; - default : - return null; // return null if invalid element type - } + break; + default: + return null; // return null if invalid element type + } - $arr['uid'] = local_channel(); - $arr['aid'] = $channel['channel_account_id']; + $arr['uid'] = local_channel(); + $arr['aid'] = $channel['channel_account_id']; - // Check if an item already exists based on the name - $iid = q("select iid from iconfig where k = '" . $namespace . "' and v = '%s' and cat = 'system'", - dbesc($name) - ); - if ($iid) { // If the item does exist, get the item metadata - $iteminfo = q("select mid,created,edited from item where id = %d", - intval($iid[0]['iid']) - ); - $arr['mid'] = $arr['parent_mid'] = $iteminfo[0]['mid']; - $arr['created'] = $iteminfo[0]['created']; - } - else { // otherwise, generate the creation times and unique id - $arr['created'] = datetime_convert('UTC', 'UTC'); - $arr['uuid'] = new_uuid(); - $arr['mid'] = $arr['parent_mid'] = z_root() . '/item/' . $arr['uuid']; - } - // Update the edited time whether or not the element already exists - $arr['edited'] = datetime_convert('UTC', 'UTC'); - // Import the actual element content - $arr['body'] = file_get_contents($element['path']); - // The element owner is the channel importing the elements - $arr['owner_xchan'] = get_observer_hash(); - // The author is either the owner or whomever was specified - $arr['author_xchan'] = (($element['author_xchan']) ? $element['author_xchan'] : get_observer_hash()); - // Import mimetype if it is a valid mimetype for the element - $mimetypes = [ - 'text/bbcode', + // Check if an item already exists based on the name + $iid = q( + "select iid from iconfig where k = '" . $namespace . "' and v = '%s' and cat = 'system'", + dbesc($name) + ); + if ($iid) { // If the item does exist, get the item metadata + $iteminfo = q( + "select mid,created,edited from item where id = %d", + intval($iid[0]['iid']) + ); + $arr['mid'] = $arr['parent_mid'] = $iteminfo[0]['mid']; + $arr['created'] = $iteminfo[0]['created']; + } else { // otherwise, generate the creation times and unique id + $arr['created'] = datetime_convert('UTC', 'UTC'); + $arr['uuid'] = new_uuid(); + $arr['mid'] = $arr['parent_mid'] = z_root() . '/item/' . $arr['uuid']; + } + // Update the edited time whether or not the element already exists + $arr['edited'] = datetime_convert('UTC', 'UTC'); + // Import the actual element content + $arr['body'] = file_get_contents($element['path']); + // The element owner is the channel importing the elements + $arr['owner_xchan'] = get_observer_hash(); + // The author is either the owner or whomever was specified + $arr['author_xchan'] = (($element['author_xchan']) ? $element['author_xchan'] : get_observer_hash()); + // Import mimetype if it is a valid mimetype for the element + $mimetypes = [ + 'text/bbcode', 'text/x-multicode', - 'text/html', - 'text/markdown', - 'text/plain', - 'application/x-pdl', - 'application/x-php' - ]; - // Blocks and pages can have any of the valid mimetypes, but layouts must be text/bbcode - if ((in_array($element['mimetype'], $mimetypes)) && in_array($type, [ 'page', 'block' ]) ) { - $arr['mimetype'] = $element['mimetype']; - } - else { - $arr['mimetype'] = 'text/x-multicode'; - } + 'text/html', + 'text/markdown', + 'text/plain', + 'application/x-pdl', + 'application/x-php' + ]; + // Blocks and pages can have any of the valid mimetypes, but layouts must be text/bbcode + if ((in_array($element['mimetype'], $mimetypes)) && in_array($type, [ 'page', 'block' ])) { + $arr['mimetype'] = $element['mimetype']; + } else { + $arr['mimetype'] = 'text/x-multicode'; + } - // Verify ability to use html or php!!! + // Verify ability to use html or php!!! - $execflag = channel_codeallowed(local_channel()); + $execflag = channel_codeallowed(local_channel()); - $i = q("select id, edited, item_deleted from item where mid = '%s' and uid = %d limit 1", - dbesc($arr['mid']), - intval(local_channel()) - ); + $i = q( + "select id, edited, item_deleted from item where mid = '%s' and uid = %d limit 1", + dbesc($arr['mid']), + intval(local_channel()) + ); - IConfig::Set($arr,'system',$namespace,(($name) ? $name : substr($arr['mid'],0,16)),true); + IConfig::Set($arr, 'system', $namespace, (($name) ? $name : substr($arr['mid'], 0, 16)), true); - if ($i) { - $arr['id'] = $i[0]['id']; - // don't update if it has the same timestamp as the original - if ($arr['edited'] > $i[0]['edited']) { - $x = item_store_update($arr,$execflag); - } - } - else { - if (($i) && (intval($i[0]['item_deleted']))) { - // was partially deleted already, finish it off - q("delete from item where mid = '%s' and uid = %d", - dbesc($arr['mid']), - intval(local_channel()) - ); - } - else { - $x = item_store($arr,$execflag); - } - } + if ($i) { + $arr['id'] = $i[0]['id']; + // don't update if it has the same timestamp as the original + if ($arr['edited'] > $i[0]['edited']) { + $x = item_store_update($arr, $execflag); + } + } else { + if (($i) && (intval($i[0]['item_deleted']))) { + // was partially deleted already, finish it off + q( + "delete from item where mid = '%s' and uid = %d", + dbesc($arr['mid']), + intval(local_channel()) + ); + } else { + $x = item_store($arr, $execflag); + } + } - if ($x && $x['success']) { - $element['import_success'] = 1; - } - else { - $element['import_success'] = 0; - } + if ($x && $x['success']) { + $element['import_success'] = 1; + } else { + $element['import_success'] = 0; + } - return $element; + return $element; } -function get_webpage_elements($channel, $type = 'all') { - $elements = []; - if (!$channel['channel_id']) { - return null; - } - switch ($type) { - case 'all': - // If all, execute all the pages, layouts, blocks case statements - case 'pages': - $elements['pages'] = null; - $owner = $channel['channel_id']; +function get_webpage_elements($channel, $type = 'all') +{ + $elements = []; + if (!$channel['channel_id']) { + return null; + } + switch ($type) { + case 'all': + // If all, execute all the pages, layouts, blocks case statements + case 'pages': + $elements['pages'] = null; + $owner = $channel['channel_id']; - $sql_extra = item_permissions_sql($owner); + $sql_extra = item_permissions_sql($owner); - $r = q("select * from iconfig left join item on iconfig.iid = item.id + $r = q( + "select * from iconfig left join item on iconfig.iid = item.id where item.uid = %d and iconfig.cat = 'system' and iconfig.k = 'WEBPAGE' and item_type = %d $sql_extra order by item.created desc", - intval($owner), - intval(ITEM_TYPE_WEBPAGE) - ); + intval($owner), + intval(ITEM_TYPE_WEBPAGE) + ); - $pages = null; + $pages = null; - if ($r) { - $elements['pages'] = []; - $pages = []; - foreach ($r as $rr) { - unobscure($rr); + if ($r) { + $elements['pages'] = []; + $pages = []; + foreach ($r as $rr) { + unobscure($rr); - //$lockstate = (($rr['allow_cid'] || $rr['allow_gid'] || $rr['deny_cid'] || $rr['deny_gid']) ? 'lock' : 'unlock'); + //$lockstate = (($rr['allow_cid'] || $rr['allow_gid'] || $rr['deny_cid'] || $rr['deny_gid']) ? 'lock' : 'unlock'); - $element_arr = [ - 'type' => 'webpage', - 'title' => $rr['title'], - 'body' => $rr['body'], - 'created' => $rr['created'], - 'edited' => $rr['edited'], - 'mimetype' => $rr['mimetype'], - 'pagetitle' => $rr['v'], - 'mid' => $rr['mid'], - 'layout_mid' => $rr['layout_mid'] - ]; - $pages[$rr['iid']][] = [ - 'url' => $rr['iid'], - 'pagetitle' => $rr['v'], - 'title' => $rr['title'], - 'created' => datetime_convert('UTC',date_default_timezone_get(),$rr['created']), - 'edited' => datetime_convert('UTC',date_default_timezone_get(),$rr['edited']), - 'bb_element' => '[element]' . base64url_encode(json_encode($element_arr)) . '[/element]', - //'lockstate' => $lockstate - ]; - $elements['pages'][] = $element_arr; - } - } - if ($type !== 'all') { - break; - } + $element_arr = [ + 'type' => 'webpage', + 'title' => $rr['title'], + 'body' => $rr['body'], + 'created' => $rr['created'], + 'edited' => $rr['edited'], + 'mimetype' => $rr['mimetype'], + 'pagetitle' => $rr['v'], + 'mid' => $rr['mid'], + 'layout_mid' => $rr['layout_mid'] + ]; + $pages[$rr['iid']][] = [ + 'url' => $rr['iid'], + 'pagetitle' => $rr['v'], + 'title' => $rr['title'], + 'created' => datetime_convert('UTC', date_default_timezone_get(), $rr['created']), + 'edited' => datetime_convert('UTC', date_default_timezone_get(), $rr['edited']), + 'bb_element' => '[element]' . base64url_encode(json_encode($element_arr)) . '[/element]', + //'lockstate' => $lockstate + ]; + $elements['pages'][] = $element_arr; + } + } + if ($type !== 'all') { + break; + } - case 'layouts': - $elements['layouts'] = null; - $owner = $channel['channel_id']; + case 'layouts': + $elements['layouts'] = null; + $owner = $channel['channel_id']; - $sql_extra = item_permissions_sql($owner); + $sql_extra = item_permissions_sql($owner); - $r = q("select * from iconfig left join item on iconfig.iid = item.id + $r = q( + "select * from iconfig left join item on iconfig.iid = item.id where item.uid = %d and iconfig.cat = 'system' and iconfig.k = 'PDL' and item_type = %d $sql_extra order by item.created desc", - intval($owner), - intval(ITEM_TYPE_PDL) - ); + intval($owner), + intval(ITEM_TYPE_PDL) + ); - if ($r) { - $elements['layouts'] = []; + if ($r) { + $elements['layouts'] = []; - foreach ($r as $rr) { - unobscure($rr); + foreach ($r as $rr) { + unobscure($rr); - $elements['layouts'][] = array( - 'type' => 'layout', - 'description' => $rr['title'], // description of the layout - 'body' => $rr['body'], - 'created' => $rr['created'], - 'edited' => $rr['edited'], - 'mimetype' => $rr['mimetype'], - 'name' => $rr['v'], // name of reference for the layout - 'mid' => $rr['mid'], - ); - } - } + $elements['layouts'][] = array( + 'type' => 'layout', + 'description' => $rr['title'], // description of the layout + 'body' => $rr['body'], + 'created' => $rr['created'], + 'edited' => $rr['edited'], + 'mimetype' => $rr['mimetype'], + 'name' => $rr['v'], // name of reference for the layout + 'mid' => $rr['mid'], + ); + } + } - if ($type !== 'all') { - break; - } + if ($type !== 'all') { + break; + } - case 'blocks': - $elements['blocks'] = null; - $owner = $channel['channel_id']; + case 'blocks': + $elements['blocks'] = null; + $owner = $channel['channel_id']; - $sql_extra = item_permissions_sql($owner); + $sql_extra = item_permissions_sql($owner); - $r = q("select iconfig.iid, iconfig.k, iconfig.v, mid, title, body, mimetype, created, edited from iconfig + $r = q( + "select iconfig.iid, iconfig.k, iconfig.v, mid, title, body, mimetype, created, edited from iconfig left join item on iconfig.iid = item.id where uid = %d and iconfig.cat = 'system' and iconfig.k = 'BUILDBLOCK' and item_type = %d order by item.created desc", - intval($owner), - intval(ITEM_TYPE_BLOCK) - ); + intval($owner), + intval(ITEM_TYPE_BLOCK) + ); - if ($r) { - $elements['blocks'] = []; + if ($r) { + $elements['blocks'] = []; - foreach ($r as $rr) { - unobscure($rr); + foreach ($r as $rr) { + unobscure($rr); - $elements['blocks'][] = [ - 'type' => 'block', - 'title' => $rr['title'], - 'body' => $rr['body'], - 'created' => $rr['created'], - 'edited' => $rr['edited'], - 'mimetype' => $rr['mimetype'], - 'name' => $rr['v'], - 'mid' => $rr['mid'] - ]; - } - } + $elements['blocks'][] = [ + 'type' => 'block', + 'title' => $rr['title'], + 'body' => $rr['body'], + 'created' => $rr['created'], + 'edited' => $rr['edited'], + 'mimetype' => $rr['mimetype'], + 'name' => $rr['v'], + 'mid' => $rr['mid'] + ]; + } + } - if ($type !== 'all') { - break; - } + if ($type !== 'all') { + break; + } - default: - break; - } + default: + break; + } - return $elements; + return $elements; } /** @@ -2129,47 +2167,47 @@ function get_webpage_elements($channel, $type = 'all') { * * @param array $files List of files to put in zip file * @param string $destination - * @param boolean $overwrite - * @return boolean Success status + * @param bool $overwrite + * @return bool Success status */ -function create_zip_file($files = [], $destination = '', $overwrite = false) { - // if the zip file already exists and overwrite is false, return false - if (file_exists($destination) && ! $overwrite) { - return false; - } - //vars - $valid_files = []; - // if files were passed in... - if (is_array($files)) { - // cycle through each file - foreach ($files as $file) { - // make sure the file exists - if (file_exists($file)) { - $valid_files[] = $file; - } - } - } +function create_zip_file($files = [], $destination = '', $overwrite = false) +{ + // if the zip file already exists and overwrite is false, return false + if (file_exists($destination) && ! $overwrite) { + return false; + } + //vars + $valid_files = []; + // if files were passed in... + if (is_array($files)) { + // cycle through each file + foreach ($files as $file) { + // make sure the file exists + if (file_exists($file)) { + $valid_files[] = $file; + } + } + } - // if we have good files... - if (count($valid_files)) { - //create the archive - $zip = new ZipArchive(); - if($zip->open($destination, $overwrite ? ZipArchive::OVERWRITE : ZipArchive::CREATE) !== true) { - return false; - } - // add the files - foreach ($valid_files as $file) { - $zip->addFile($file, $file); - } - //debug - //echo 'The zip archive contains ',$zip->numFiles,' files with a status of ',$zip->status; - //close the zip -- done! - $zip->close(); + // if we have good files... + if (count($valid_files)) { + //create the archive + $zip = new ZipArchive(); + if ($zip->open($destination, $overwrite ? ZipArchive::OVERWRITE : ZipArchive::CREATE) !== true) { + return false; + } + // add the files + foreach ($valid_files as $file) { + $zip->addFile($file, $file); + } + //debug + //echo 'The zip archive contains ',$zip->numFiles,' files with a status of ',$zip->status; + //close the zip -- done! + $zip->close(); - // check to make sure the file exists - return file_exists($destination); - } - else { - return false; - } + // check to make sure the file exists + return file_exists($destination); + } else { + return false; + } } diff --git a/include/items.php b/include/items.php index c221b957e..0f9dd7ab0 100644 --- a/include/items.php +++ b/include/items.php @@ -291,93 +291,92 @@ function is_item_normal($item) { * @param array $item * @return boolean */ -function can_comment_on_post($observer_xchan, $item) { +function can_comment_on_post($observer_xchan, $item) +{ -// logger('Comment_policy: ' . $item['comment_policy'], LOGGER_DEBUG); +// logger('Comment_policy: ' . $item['comment_policy'], LOGGER_DEBUG); - $x = [ - 'observer_hash' => $observer_xchan, - 'item' => $item, - 'allowed' => 'unset' - ]; + $x = [ + 'observer_hash' => $observer_xchan, + 'item' => $item, + 'allowed' => 'unset' + ]; - /** - * @hooks can_comment_on_post - * Called when deciding whether or not to present a comment box for a post. - * * \e string \b observer_hash - * * \e array \b item - * * \e boolean \b allowed - return value - */ + /** + * @hooks can_comment_on_post + * Called when deciding whether or not to present a comment box for a post. + * * \e string \b observer_hash + * * \e array \b item + * * \e boolean \b allowed - return value + */ - call_hooks('can_comment_on_post', $x); + call_hooks('can_comment_on_post', $x); - if ($x['allowed'] !== 'unset') { - return $x['allowed']; - } + if ($x['allowed'] !== 'unset') { + return $x['allowed']; + } - if (! $observer_xchan) { - return false; - } + if (! $observer_xchan) { + return false; + } - if ($item['comment_policy'] === 'none') { - return false; - } + if ($item['comment_policy'] === 'none') { + return false; + } - if (intval($item['item_nocomment'])) { - return false; - } + if (intval($item['item_nocomment'])) { + return false; + } - if (comments_are_now_closed($item)) { - return false; - } + if (comments_are_now_closed($item)) { + return false; + } - if ($observer_xchan === $item['author_xchan'] || $observer_xchan === $item['owner_xchan']) { - return true; - } + if ($observer_xchan === $item['author_xchan'] || $observer_xchan === $item['owner_xchan']) { + return true; + } - switch ($item['comment_policy']) { - case 'self': - if ($observer_xchan === $item['author_xchan'] || $observer_xchan === $item['owner_xchan']) { - return true; - } - break; - case 'public': - case 'authenticated': - // Anonymous folks won't ever reach this point (as $observer_xchan will be empty). - // This means the viewer has an xchan and we can identify them. - return true; - break; - case 'any connections': - case 'specific': - case 'contacts': - case '': + switch ($item['comment_policy']) { + case 'self': + if ($observer_xchan === $item['author_xchan'] || $observer_xchan === $item['owner_xchan']) { + return true; + } + break; + case 'public': + case 'authenticated': + // Anonymous folks won't ever reach this point (as $observer_xchan will be empty). + // This means the viewer has an xchan and we can identify them. + return true; + break; + case 'any connections': + case 'specific': + case 'contacts': + case '': + // local posts only - check if the post owner granted me comment permission + if (local_channel() && array_key_exists('owner', $item) && their_perms_contains(local_channel(), $item['owner']['abook_xchan'], 'post_comments')) { + return true; + } - // local posts only - check if the post owner granted me - // comment permission - if (local_channel() && array_key_exists('owner',$item) && their_perms_contains(local_channel(),$item['owner']['abook_xchan'],'post_comments')) { - return true; - } + if (intval($item['item_wall']) && perm_is_allowed($item['uid'], $observer_xchan, 'post_comments')) { + return true; + } + break; + default: + break; + } + if (strstr($item['comment_policy'], 'network:') && strstr($item['comment_policy'], 'red')) { + return true; + } - if (intval($item['item_wall']) && perm_is_allowed($item['uid'],$observer_xchan,'post_comments')) { - return true; - } - break; - default: - break; - } - if (strstr($item['comment_policy'],'network:') && strstr($item['comment_policy'],'red')) { - return true; - } - - if (strstr($item['comment_policy'],'network:') && strstr($item['comment_policy'],'activitypub')) { - return true; - } + if (strstr($item['comment_policy'], 'network:') && strstr($item['comment_policy'], 'activitypub')) { + return true; + } - if (strstr($item['comment_policy'],'site:') && strstr($item['comment_policy'],App::get_hostname())) { - return true; - } + if (strstr($item['comment_policy'], 'site:') && strstr($item['comment_policy'], App::get_hostname())) { + return true; + } - return false; + return false; } diff --git a/include/js_strings.php b/include/js_strings.php index 4b9485a89..b59832dde 100644 --- a/include/js_strings.php +++ b/include/js_strings.php @@ -1,117 +1,120 @@ - '/images/' . PLATFORM_NAME . '-64.png', - '$delitem' => t('Delete this item?'), - '$comment' => t('Comment'), - '$showmore' => sprintf( t('%s show all'), ''), - '$showfewer' => sprintf( t('%s show less'), ''), - '$divgrowmore' => sprintf( t('%s expand'), ''), - '$divgrowless' => sprintf( t('%s collapse'),''), - '$pwshort' => t("Password too short"), - '$pwnomatch' => t("Passwords do not match"), - '$everybody' => t('everybody'), - '$passphrase' => t('Secret Passphrase'), - '$passhint' => t('Passphrase hint'), - '$permschange' => t('Notice: Permissions have changed but have not yet been submitted.'), - '$closeAll' => t('close all'), - '$nothingnew' => t('Nothing new here'), - '$rating_desc' => t('Rate This Channel (this is public)'), - '$rating_val' => t('Rating'), - '$rating_text' => t('Describe (optional)'), - '$submit' => t('Submit'), - '$linkurl' => t('Please enter a link URL'), - '$leavethispage' => t('Unsaved changes. Are you sure you wish to leave this page?'), - '$location' => t('Location'), - '$lovely' => t('lovely'), - '$wonderful' => t('wonderful'), - '$fantastic' => t('fantastic'), - '$great' => t('great'), - '$nick_invld1' => t('Your chosen nickname was either already taken or not valid. Please use our suggestion ('), - '$nick_invld2' => t(') or enter a new one.'), - '$nick_valid' => t('Thank you, this nickname is valid.'), - '$name_empty' => t('A channel name is required.'), - '$name_ok1' => t('This is a '), - '$name_ok2' => t(' channel name'), - '$pinned' => t('Pinned'), - '$pin_item' => t('Pin this post'), - '$unpin_item' => t('Unpin this post'), - '$tos' => t('Please accept terms to continue'), - - // translatable prefix and suffix strings for jquery.timeago - - // using the defaults set below if left untranslated, empty strings if - // translated to "NONE" and the corresponding language strings - // if translated to anything else - '$t01' => ((t('timeago.prefixAgo') == 'timeago.prefixAgo') ? '' : ((t('timeago.prefixAgo') == 'NONE') ? '' : t('timeago.prefixAgo'))), - '$t02' => ((t('timeago.prefixFromNow') == 'timeago.prefixFromNow') ? '' : ((t('timeago.prefixFromNow') == 'NONE') ? '' : t('timeago.prefixFromNow'))), - '$t03' => ((t('timeago.suffixAgo') == 'timeago.suffixAgo') ? 'ago' : ((t('timeago.suffixAgo') == 'NONE') ? '' : t('timeago.suffixAgo'))), - '$t04' => ((t('timeago.suffixFromNow') == 'timeago.suffixFromNow') ? 'from now' : ((t('timeago.suffixFromNow') == 'NONE') ? '' : t('timeago.suffixFromNow'))), +/** @file */ - // translatable main strings for jquery.timeago - '$t05' => t('less than a minute'), - '$t06' => t('about a minute'), - '$t07' => t('%d minutes'), - '$t08' => t('about an hour'), - '$t09' => t('about %d hours'), - '$t10' => t('a day'), - '$t11' => t('%d days'), - '$t12' => t('about a month'), - '$t13' => t('%d months'), - '$t14' => t('about a year'), - '$t15' => t('%d years'), - '$t16' => t(' '), // wordSeparator - '$t17' => ((t('timeago.numbers') != 'timeago.numbers') ? t('timeago.numbers') : '[]'), +function js_strings() +{ + return replace_macros(get_markup_template('js_strings.tpl'), array( + '$icon' => '/images/' . PLATFORM_NAME . '-64.png', + '$delitem' => t('Delete this item?'), + '$comment' => t('Comment'), + '$showmore' => sprintf(t('%s show all'), ''), + '$showfewer' => sprintf(t('%s show less'), ''), + '$divgrowmore' => sprintf(t('%s expand'), ''), + '$divgrowless' => sprintf(t('%s collapse'), ''), + '$pwshort' => t("Password too short"), + '$pwnomatch' => t("Passwords do not match"), + '$everybody' => t('everybody'), + '$passphrase' => t('Secret Passphrase'), + '$passhint' => t('Passphrase hint'), + '$permschange' => t('Notice: Permissions have changed but have not yet been submitted.'), + '$closeAll' => t('close all'), + '$nothingnew' => t('Nothing new here'), + '$rating_desc' => t('Rate This Channel (this is public)'), + '$rating_val' => t('Rating'), + '$rating_text' => t('Describe (optional)'), + '$submit' => t('Submit'), + '$linkurl' => t('Please enter a link URL'), + '$leavethispage' => t('Unsaved changes. Are you sure you wish to leave this page?'), + '$location' => t('Location'), + '$lovely' => t('lovely'), + '$wonderful' => t('wonderful'), + '$fantastic' => t('fantastic'), + '$great' => t('great'), + '$nick_invld1' => t('Your chosen nickname was either already taken or not valid. Please use our suggestion ('), + '$nick_invld2' => t(') or enter a new one.'), + '$nick_valid' => t('Thank you, this nickname is valid.'), + '$name_empty' => t('A channel name is required.'), + '$name_ok1' => t('This is a '), + '$name_ok2' => t(' channel name'), + '$pinned' => t('Pinned'), + '$pin_item' => t('Pin this post'), + '$unpin_item' => t('Unpin this post'), + '$tos' => t('Please accept terms to continue'), - '$January' => t('January'), - '$February' => t('February'), - '$March' => t('March'), - '$April' => t('April'), - '$May' => t('May','long'), - '$June' => t('June'), - '$July' => t('July'), - '$August' => t('August'), - '$September' => t('September'), - '$October' => t('October'), - '$November' => t('November'), - '$December' => t('December'), - '$Jan' => t('Jan'), - '$Feb' => t('Feb'), - '$Mar' => t('Mar'), - '$Apr' => t('Apr'), - '$MayShort' => t('May','short'), - '$Jun' => t('Jun'), - '$Jul' => t('Jul'), - '$Aug' => t('Aug'), - '$Sep' => t('Sep'), - '$Oct' => t('Oct'), - '$Nov' => t('Nov'), - '$Dec' => t('Dec'), - '$Sunday' => t('Sunday'), - '$Monday' => t('Monday'), - '$Tuesday' => t('Tuesday'), - '$Wednesday' => t('Wednesday'), - '$Thursday' => t('Thursday'), - '$Friday' => t('Friday'), - '$Saturday' => t('Saturday'), - '$Sun' => t('Sun'), - '$Mon' => t('Mon'), - '$Tue' => t('Tue'), - '$Wed' => t('Wed'), - '$Thu' => t('Thu'), - '$Fri' => t('Fri'), - '$Sat' => t('Sat'), - '$today' => t('today','calendar'), - '$month' => t('month','calendar'), - '$week' => t('week','calendar'), - '$day' => t('day','calendar'), - '$allday' => t('All day','calendar'), - '$channel_social' => t('A social networking profile that is public by default and private if desired'), - '$channel_social_restricted' => t('A social networking profile where content is private to your [Friends] Access List by default but can be made public if desired'), - '$channel_forum' => t('A public group where members are allowed to upload media by default'), - '$channel_forum_restricted' => t('A private group with no upload permission'), - '$channel_forum_moderated' => t('A public group where posts are moderated by the owner. The [moderated] permission may be removed from any group member once trust is established'), - '$channel_collection' =>t('A sub-channel of your main channel - often devoted to a specific language or topic. Replies are sent back to your main channel'), - '$channel_collection_restricted' =>t('A private sub-channel of your main channel - often devoted to a specific language or topic. Replies are sent back to your main channel'), - )); + // translatable prefix and suffix strings for jquery.timeago - + // using the defaults set below if left untranslated, empty strings if + // translated to "NONE" and the corresponding language strings + // if translated to anything else + '$t01' => ((t('timeago.prefixAgo') == 'timeago.prefixAgo') ? '' : ((t('timeago.prefixAgo') == 'NONE') ? '' : t('timeago.prefixAgo'))), + '$t02' => ((t('timeago.prefixFromNow') == 'timeago.prefixFromNow') ? '' : ((t('timeago.prefixFromNow') == 'NONE') ? '' : t('timeago.prefixFromNow'))), + '$t03' => ((t('timeago.suffixAgo') == 'timeago.suffixAgo') ? 'ago' : ((t('timeago.suffixAgo') == 'NONE') ? '' : t('timeago.suffixAgo'))), + '$t04' => ((t('timeago.suffixFromNow') == 'timeago.suffixFromNow') ? 'from now' : ((t('timeago.suffixFromNow') == 'NONE') ? '' : t('timeago.suffixFromNow'))), + + // translatable main strings for jquery.timeago + '$t05' => t('less than a minute'), + '$t06' => t('about a minute'), + '$t07' => t('%d minutes'), + '$t08' => t('about an hour'), + '$t09' => t('about %d hours'), + '$t10' => t('a day'), + '$t11' => t('%d days'), + '$t12' => t('about a month'), + '$t13' => t('%d months'), + '$t14' => t('about a year'), + '$t15' => t('%d years'), + '$t16' => t(' '), // wordSeparator + '$t17' => ((t('timeago.numbers') != 'timeago.numbers') ? t('timeago.numbers') : '[]'), + + '$January' => t('January'), + '$February' => t('February'), + '$March' => t('March'), + '$April' => t('April'), + '$May' => t('May', 'long'), + '$June' => t('June'), + '$July' => t('July'), + '$August' => t('August'), + '$September' => t('September'), + '$October' => t('October'), + '$November' => t('November'), + '$December' => t('December'), + '$Jan' => t('Jan'), + '$Feb' => t('Feb'), + '$Mar' => t('Mar'), + '$Apr' => t('Apr'), + '$MayShort' => t('May', 'short'), + '$Jun' => t('Jun'), + '$Jul' => t('Jul'), + '$Aug' => t('Aug'), + '$Sep' => t('Sep'), + '$Oct' => t('Oct'), + '$Nov' => t('Nov'), + '$Dec' => t('Dec'), + '$Sunday' => t('Sunday'), + '$Monday' => t('Monday'), + '$Tuesday' => t('Tuesday'), + '$Wednesday' => t('Wednesday'), + '$Thursday' => t('Thursday'), + '$Friday' => t('Friday'), + '$Saturday' => t('Saturday'), + '$Sun' => t('Sun'), + '$Mon' => t('Mon'), + '$Tue' => t('Tue'), + '$Wed' => t('Wed'), + '$Thu' => t('Thu'), + '$Fri' => t('Fri'), + '$Sat' => t('Sat'), + '$today' => t('today', 'calendar'), + '$month' => t('month', 'calendar'), + '$week' => t('week', 'calendar'), + '$day' => t('day', 'calendar'), + '$allday' => t('All day', 'calendar'), + '$channel_social' => t('A social networking profile that is public by default and private if desired'), + '$channel_social_restricted' => t('A social networking profile where content is private to your [Friends] Access List by default but can be made public if desired'), + '$channel_forum' => t('A public group where members are allowed to upload media by default'), + '$channel_forum_restricted' => t('A private group with no upload permission'), + '$channel_forum_moderated' => t('A public group where posts are moderated by the owner. The [moderated] permission may be removed from any group member once trust is established'), + '$channel_collection' => t('A sub-channel of your main channel - often devoted to a specific language or topic. Replies are sent back to your main channel'), + '$channel_collection_restricted' => t('A private sub-channel of your main channel - often devoted to a specific language or topic. Replies are sent back to your main channel'), + )); } diff --git a/include/language.php b/include/language.php index 35cfdd1af..7e6cf90b3 100644 --- a/include/language.php +++ b/include/language.php @@ -1,4 +1,5 @@ 0.8 - $langs = array_combine($lang_parse[1], $lang_parse[4]); + if (count($lang_parse[1])) { + // create a list like "en" => 0.8 + $langs = array_combine($lang_parse[1], $lang_parse[4]); - // set default to 1 for any without q factor - foreach ($langs as $lang => $val) { - if ($val === '') $langs[$lang] = 1; - } + // set default to 1 for any without q factor + foreach ($langs as $lang => $val) { + if ($val === '') { + $langs[$lang] = 1; + } + } - // sort list based on value - arsort($langs, SORT_NUMERIC); - } - } + // sort list based on value + arsort($langs, SORT_NUMERIC); + } + } - return $langs; + return $langs; } /** @@ -61,57 +68,58 @@ function get_browser_language() { * * @return Language code in 2-letter ISO 639-1 (en). */ -function get_best_language() { - $langs = get_browser_language(); +function get_best_language() +{ + $langs = get_browser_language(); - if(isset($langs) && count($langs)) { - foreach ($langs as $lang => $v) { - $lang = strtolower($lang); - if(is_dir("view/$lang")) { - $preferred = $lang; - break; - } - } - } + if (isset($langs) && count($langs)) { + foreach ($langs as $lang => $v) { + $lang = strtolower($lang); + if (is_dir("view/$lang")) { + $preferred = $lang; + break; + } + } + } - if(! isset($preferred)) { + if (! isset($preferred)) { + /* + * We could find no perfect match for any of the preferred languages. + * For cases where the preference is fr-fr and we have fr but *not* fr-fr + * run the test again and only look for the language base + * which should provide an interface they can sort of understand + */ - /* - * We could find no perfect match for any of the preferred languages. - * For cases where the preference is fr-fr and we have fr but *not* fr-fr - * run the test again and only look for the language base - * which should provide an interface they can sort of understand - */ + if (isset($langs) && count($langs)) { + foreach ($langs as $lang => $v) { + if (strlen($lang) === 2) { + /* we have already checked this language */ + continue; + } + /* Check the base */ + $lang = strtolower(substr($lang, 0, 2)); + if (is_dir("view/$lang")) { + $preferred = $lang; + break; + } + } + } + } - if(isset($langs) && count($langs)) { - foreach ($langs as $lang => $v) { - if(strlen($lang) === 2) { - /* we have already checked this language */ - continue; - } - /* Check the base */ - $lang = strtolower(substr($lang,0,2)); - if(is_dir("view/$lang")) { - $preferred = $lang; - break; - } - } - } - } + if (! isset($preferred)) { + $preferred = 'unset'; + } - if(! isset($preferred)) { - $preferred = 'unset'; - } + $arr = array('langs' => $langs, 'preferred' => $preferred); - $arr = array('langs' => $langs, 'preferred' => $preferred); + call_hooks('get_best_language', $arr); - call_hooks('get_best_language',$arr); + if ($arr['preferred'] !== 'unset') { + return $arr['preferred']; + } - if($arr['preferred'] !== 'unset') - return $arr['preferred']; - - return ((isset(App::$config['system']['language'])) ? App::$config['system']['language'] : 'en'); + return ((isset(App::$config['system']['language'])) ? App::$config['system']['language'] : 'en'); } /* @@ -121,66 +129,72 @@ function get_best_language() { */ -function push_lang($language) { +function push_lang($language) +{ - App::$langsave = App::$language; + App::$langsave = App::$language; - if($language === App::$language) - return; + if ($language === App::$language) { + return; + } - if(isset(App::$strings) && count(App::$strings)) { - App::$stringsave = App::$strings; - } - App::$strings = []; - load_translation_table($language); - App::$language = $language; + if (isset(App::$strings) && count(App::$strings)) { + App::$stringsave = App::$strings; + } + App::$strings = []; + load_translation_table($language); + App::$language = $language; } -function pop_lang() { +function pop_lang() +{ - if(App::$language === App::$langsave) - return; + if (App::$language === App::$langsave) { + return; + } - if(isset(App::$stringsave) && is_array(App::$stringsave)) - App::$strings = App::$stringsave; - else - App::$strings = []; + if (isset(App::$stringsave) && is_array(App::$stringsave)) { + App::$strings = App::$stringsave; + } else { + App::$strings = []; + } - App::$language = App::$langsave; + App::$language = App::$langsave; } /** * @brief Load string translation table for alternate language. * * @param string $lang language code in 2-letter ISO 639-1 (en, de, fr) format - * @param boolean $install (optional) default false + * @param bool $install (optional) default false */ -function load_translation_table($lang, $install = false) { +function load_translation_table($lang, $install = false) +{ - App::$strings = []; + App::$strings = []; - if(file_exists("view/$lang/strings.php")) { - include("view/$lang/strings.php"); - } + if (file_exists("view/$lang/strings.php")) { + include("view/$lang/strings.php"); + } - if(! $install) { - $plugins = q("SELECT aname FROM addon WHERE installed=1;"); - if ($plugins !== false) { - foreach($plugins as $p) { - $name = $p['aname']; - if(file_exists("addon/$name/lang/$lang/strings.php")) { - include("addon/$name/lang/$lang/strings.php"); - } - } - } - } + if (! $install) { + $plugins = q("SELECT aname FROM addon WHERE installed=1;"); + if ($plugins !== false) { + foreach ($plugins as $p) { + $name = $p['aname']; + if (file_exists("addon/$name/lang/$lang/strings.php")) { + include("addon/$name/lang/$lang/strings.php"); + } + } + } + } - // Allow individual strings to be over-ridden on this site - // Either for the default language or for all languages + // Allow individual strings to be over-ridden on this site + // Either for the default language or for all languages - if(file_exists("view/local-$lang/strings.php")) { - include("view/local-$lang/strings.php"); - } + if (file_exists("view/local-$lang/strings.php")) { + include("view/local-$lang/strings.php"); + } } /** @@ -191,16 +205,17 @@ function load_translation_table($lang, $install = false) { * @return translated string if exists, otherwise return $s * */ -function t($s, $ctx = '') { +function t($s, $ctx = '') +{ - $cs = $ctx ? '__ctx:' . $ctx . '__ ' . $s : $s; - if (x(App::$strings, $cs)) { - $t = App::$strings[$cs]; + $cs = $ctx ? '__ctx:' . $ctx . '__ ' . $s : $s; + if (x(App::$strings, $cs)) { + $t = App::$strings[$cs]; - return ((is_array($t)) ? translate_projectname($t[0]) : translate_projectname($t)); - } + return ((is_array($t)) ? translate_projectname($t[0]) : translate_projectname($t)); + } - return translate_projectname($s); + return translate_projectname($s); } /** @@ -208,11 +223,12 @@ function t($s, $ctx = '') { * Merging strings from different project names is problematic so we'll do that with a string replacement */ -function translate_projectname($s) { - if (strpos($s,'rojectname') !== false) { - return str_replace( [ '$projectname','$Projectname' ], [ System::get_platform_name(), ucfirst(System::get_platform_name()) ],$s); - } - return $s; +function translate_projectname($s) +{ + if (strpos($s, 'rojectname') !== false) { + return str_replace([ '$projectname','$Projectname' ], [ System::get_platform_name(), ucfirst(System::get_platform_name()) ], $s); + } + return $s; } @@ -225,25 +241,27 @@ function translate_projectname($s) { * @param string $ctx * @return string */ -function tt($singular, $plural, $count, $ctx = ''){ +function tt($singular, $plural, $count, $ctx = '') +{ - $cs = $ctx ? "__ctx:" . $ctx . "__ " . $singular : $singular; - if (x(App::$strings,$cs)) { - $t = App::$strings[$cs]; - $f = 'string_plural_select_' . str_replace('-', '_', App::$language); - if (! function_exists($f)) - $f = 'string_plural_select_default'; + $cs = $ctx ? "__ctx:" . $ctx . "__ " . $singular : $singular; + if (x(App::$strings, $cs)) { + $t = App::$strings[$cs]; + $f = 'string_plural_select_' . str_replace('-', '_', App::$language); + if (! function_exists($f)) { + $f = 'string_plural_select_default'; + } - $k = $f(intval($count)); + $k = $f(intval($count)); - return is_array($t) ? $t[$k] : $t; - } + return is_array($t) ? $t[$k] : $t; + } - if ($count != 1) { - return $plural; - } else { - return $singular; - } + if ($count != 1) { + return $plural; + } else { + return $singular; + } } /** @@ -251,10 +269,11 @@ function tt($singular, $plural, $count, $ctx = ''){ * any language file. * * @param int $n - * @return boolean + * @return bool */ -function string_plural_select_default($n) { - return ($n != 1); +function string_plural_select_default($n) +{ + return ($n != 1); } /** @@ -275,47 +294,50 @@ function string_plural_select_default($n) { * This project: https://github.com/patrickschur/language-detection *may* be useful as a replacement. * */ -function detect_language($s) { +function detect_language($s) +{ - require_once('library/text_languagedetect/Text/LanguageDetect.php'); + require_once('library/text_languagedetect/Text/LanguageDetect.php'); - $min_length = get_config('system', 'language_detect_min_length'); - if ($min_length === false) - $min_length = LANGUAGE_DETECT_MIN_LENGTH; + $min_length = get_config('system', 'language_detect_min_length'); + if ($min_length === false) { + $min_length = LANGUAGE_DETECT_MIN_LENGTH; + } - $min_confidence = get_config('system', 'language_detect_min_confidence'); - if ($min_confidence === false) - $min_confidence = LANGUAGE_DETECT_MIN_CONFIDENCE; + $min_confidence = get_config('system', 'language_detect_min_confidence'); + if ($min_confidence === false) { + $min_confidence = LANGUAGE_DETECT_MIN_CONFIDENCE; + } - // embedded apps have long base64 strings which will trip up the detector. - $naked_body = preg_replace('/\[app\](.*?)\[\/app\]/', '', $s); - // strip off bbcode - $naked_body = preg_replace('/\[(.+?)\]/', '', $naked_body); - if (mb_strlen($naked_body) < intval($min_length)) { - logger('string length less than ' . intval($min_length), LOGGER_DATA); - return ''; - } + // embedded apps have long base64 strings which will trip up the detector. + $naked_body = preg_replace('/\[app\](.*?)\[\/app\]/', '', $s); + // strip off bbcode + $naked_body = preg_replace('/\[(.+?)\]/', '', $naked_body); + if (mb_strlen($naked_body) < intval($min_length)) { + logger('string length less than ' . intval($min_length), LOGGER_DATA); + return ''; + } - $l = new Text_LanguageDetect; - try { - // return 2-letter ISO 639-1 (en) language code - $l->setNameMode(2); - $lng = $l->detectConfidence($naked_body); - logger('detect language: ' . print_r($lng, true) . $naked_body, LOGGER_DATA); - } catch (Text_LanguageDetect_Exception $e) { - logger('detect language exception: ' . $e->getMessage(), LOGGER_DATA); - } + $l = new Text_LanguageDetect(); + try { + // return 2-letter ISO 639-1 (en) language code + $l->setNameMode(2); + $lng = $l->detectConfidence($naked_body); + logger('detect language: ' . print_r($lng, true) . $naked_body, LOGGER_DATA); + } catch (Text_LanguageDetect_Exception $e) { + logger('detect language exception: ' . $e->getMessage(), LOGGER_DATA); + } - if ((! $lng) || (! (x($lng,'language')))) { - return ''; - } + if ((! $lng) || (! (x($lng, 'language')))) { + return ''; + } - if ($lng['confidence'] < (float) $min_confidence) { - logger('detect language: confidence less than ' . (float) $min_confidence, LOGGER_DATA); - return ''; - } + if ($lng['confidence'] < (float) $min_confidence) { + logger('detect language: confidence less than ' . (float) $min_confidence, LOGGER_DATA); + return ''; + } - return($lng['language']); + return($lng['language']); } /** @@ -332,85 +354,95 @@ function detect_language($s) { * @param string $l (optional) In which language to return the name * @return string with the language name, or $s if unrecognized */ -function get_language_name($s, $l = null) { - // get() expects the second part to be in upper case - if (strpos($s, '-') !== false) $s = substr($s, 0, 2) . strtoupper(substr($s, 2)); - if ($l !== null && strpos($l, '-') !== false) $l = substr($l, 0, 2) . strtoupper(substr($l, 2)); +function get_language_name($s, $l = null) +{ + // get() expects the second part to be in upper case + if (strpos($s, '-') !== false) { + $s = substr($s, 0, 2) . strtoupper(substr($s, 2)); + } + if ($l !== null && strpos($l, '-') !== false) { + $l = substr($l, 0, 2) . strtoupper(substr($l, 2)); + } - $languageRepository = new LanguageRepository; + $languageRepository = new LanguageRepository(); - // Sometimes intl doesn't like the second part at all ... - try { - $language = $languageRepository->get($s, $l); - } catch(CommerceGuys\Intl\Exception\UnknownLanguageException $e) { - $s = substr($s, 0, 2); - if($l !== null) $l = substr($s, 0, 2); - try { - $language = $languageRepository->get($s, $l); - } catch (CommerceGuys\Intl\Exception\UnknownLanguageException $e) { - return $s; // Give up - } catch (CommerceGuys\Intl\Exception\UnknownLocaleException $e) { - return $s; // Give up - } - } + // Sometimes intl doesn't like the second part at all ... + try { + $language = $languageRepository->get($s, $l); + } catch (CommerceGuys\Intl\Exception\UnknownLanguageException $e) { + $s = substr($s, 0, 2); + if ($l !== null) { + $l = substr($s, 0, 2); + } + try { + $language = $languageRepository->get($s, $l); + } catch (CommerceGuys\Intl\Exception\UnknownLanguageException $e) { + return $s; // Give up + } catch (CommerceGuys\Intl\Exception\UnknownLocaleException $e) { + return $s; // Give up + } + } - return $language->getName(); + return $language->getName(); } -function language_list() { +function language_list() +{ - $langs = glob('view/*/strings.php'); + $langs = glob('view/*/strings.php'); - $lang_options = []; - $selected = ""; + $lang_options = []; + $selected = ""; - if(is_array($langs) && count($langs)) { - if(! in_array('view/en/strings.php',$langs)) - $langs[] = 'view/en/'; - asort($langs); - foreach($langs as $l) { - $ll = substr($l,5); - $ll = substr($ll,0,strrpos($ll,'/')); - $lang_options[$ll] = get_language_name($ll, $ll) . " ($ll)"; - } - } - return $lang_options; + if (is_array($langs) && count($langs)) { + if (! in_array('view/en/strings.php', $langs)) { + $langs[] = 'view/en/'; + } + asort($langs); + foreach ($langs as $l) { + $ll = substr($l, 5); + $ll = substr($ll, 0, strrpos($ll, '/')); + $lang_options[$ll] = get_language_name($ll, $ll) . " ($ll)"; + } + } + return $lang_options; } -function lang_selector() { +function lang_selector() +{ - $langs = glob('view/*/strings.php'); + $langs = glob('view/*/strings.php'); - $lang_options = []; - $selected = ""; + $lang_options = []; + $selected = ""; - if(is_array($langs) && count($langs)) { - $langs[] = ''; - if(! in_array('view/en/strings.php',$langs)) - $langs[] = 'view/en/'; - asort($langs); - foreach($langs as $l) { - if($l == '') { - $lang_options[""] = t('default'); - continue; - } - $ll = substr($l,5); - $ll = substr($ll,0,strrpos($ll,'/')); - $selected = (($ll === App::$language && (x($_SESSION, 'language'))) ? $ll : $selected); - $lang_options[$ll] = get_language_name($ll, $ll) . " ($ll)"; - } - } + if (is_array($langs) && count($langs)) { + $langs[] = ''; + if (! in_array('view/en/strings.php', $langs)) { + $langs[] = 'view/en/'; + } + asort($langs); + foreach ($langs as $l) { + if ($l == '') { + $lang_options[""] = t('default'); + continue; + } + $ll = substr($l, 5); + $ll = substr($ll, 0, strrpos($ll, '/')); + $selected = (($ll === App::$language && (x($_SESSION, 'language'))) ? $ll : $selected); + $lang_options[$ll] = get_language_name($ll, $ll) . " ($ll)"; + } + } - $tpl = get_markup_template('lang_selector.tpl'); + $tpl = get_markup_template('lang_selector.tpl'); - $o = replace_macros($tpl, array( - '$title' => t('Select an alternate language'), - '$langs' => array($lang_options, $selected), + $o = replace_macros($tpl, array( + '$title' => t('Select an alternate language'), + '$langs' => array($lang_options, $selected), - )); + )); - return $o; + return $o; } - diff --git a/include/menu.php b/include/menu.php index ac9674446..0ac4f9a78 100644 --- a/include/menu.php +++ b/include/menu.php @@ -1,5 +1,6 @@ - $r[0], 'items' => $x ); - } + intval($r[0]['menu_id']), + intval($uid) + ); + return array('menu' => $r[0], 'items' => $x ); + } - return null; + return null; } - -function menu_element($channel,$menu) { - $arr = []; - $arr['type'] = 'menu'; - $arr['pagetitle'] = $menu['menu']['menu_name']; - $arr['desc'] = $menu['menu']['menu_desc']; - $arr['created'] = $menu['menu']['menu_created']; - $arr['edited'] = $menu['menu']['menu_edited']; +function menu_element($channel, $menu) +{ - $arr['baseurl'] = z_root(); - if($menu['menu']['menu_flags']) { - $arr['flags'] = []; - if($menu['menu']['menu_flags'] & MENU_BOOKMARK) - $arr['flags'][] = 'bookmark'; - if($menu['menu']['menu_flags'] & MENU_SYSTEM) - $arr['flags'][] = 'system'; - } - if($menu['items']) { - $arr['items'] = []; - foreach($menu['items'] as $it) { - $entry = []; + $arr = []; + $arr['type'] = 'menu'; + $arr['pagetitle'] = $menu['menu']['menu_name']; + $arr['desc'] = $menu['menu']['menu_desc']; + $arr['created'] = $menu['menu']['menu_created']; + $arr['edited'] = $menu['menu']['menu_edited']; - $entry['link'] = str_replace(z_root() . '/channel/' . $channel['channel_address'],'[channelurl]',$it['mitem_link']); - $entry['link'] = str_replace(z_root() . '/page/' . $channel['channel_address'],'[pageurl]',$it['mitem_link']); - $entry['link'] = str_replace(z_root() . '/cloud/' . $channel['channel_address'],'[cloudurl]',$it['mitem_link']); - $entry['link'] = str_replace(z_root(),'[baseurl]',$it['mitem_link']); + $arr['baseurl'] = z_root(); + if ($menu['menu']['menu_flags']) { + $arr['flags'] = []; + if ($menu['menu']['menu_flags'] & MENU_BOOKMARK) { + $arr['flags'][] = 'bookmark'; + } + if ($menu['menu']['menu_flags'] & MENU_SYSTEM) { + $arr['flags'][] = 'system'; + } + } + if ($menu['items']) { + $arr['items'] = []; + foreach ($menu['items'] as $it) { + $entry = []; - $entry['desc'] = $it['mitem_desc']; - $entry['order'] = $it['mitem_order']; - if($it['mitem_flags']) { - $entry['flags'] = []; - if($it['mitem_flags'] & MENU_ITEM_ZID) - $entry['flags'][] = 'zid'; - if($it['mitem_flags'] & MENU_ITEM_NEWWIN) - $entry['flags'][] = 'new-window'; - if($it['mitem_flags'] & MENU_ITEM_CHATROOM) - $entry['flags'][] = 'chatroom'; - } - $arr['items'][] = $entry; - } - } + $entry['link'] = str_replace(z_root() . '/channel/' . $channel['channel_address'], '[channelurl]', $it['mitem_link']); + $entry['link'] = str_replace(z_root() . '/page/' . $channel['channel_address'], '[pageurl]', $it['mitem_link']); + $entry['link'] = str_replace(z_root() . '/cloud/' . $channel['channel_address'], '[cloudurl]', $it['mitem_link']); + $entry['link'] = str_replace(z_root(), '[baseurl]', $it['mitem_link']); - return $arr; + $entry['desc'] = $it['mitem_desc']; + $entry['order'] = $it['mitem_order']; + if ($it['mitem_flags']) { + $entry['flags'] = []; + if ($it['mitem_flags'] & MENU_ITEM_ZID) { + $entry['flags'][] = 'zid'; + } + if ($it['mitem_flags'] & MENU_ITEM_NEWWIN) { + $entry['flags'][] = 'new-window'; + } + if ($it['mitem_flags'] & MENU_ITEM_CHATROOM) { + $entry['flags'][] = 'chatroom'; + } + } + $arr['items'][] = $entry; + } + } + + return $arr; } -function menu_render($menu, $class='', $edit = false, $var = []) { +function menu_render($menu, $class = '', $edit = false, $var = []) +{ - if(! $menu) - return ''; + if (! $menu) { + return ''; + } - $channel_id = ((is_array(App::$profile)) ? App::$profile['profile_uid'] : 0); - if ((! $channel_id) && (local_channel())) - $channel_id = local_channel(); + $channel_id = ((is_array(App::$profile)) ? App::$profile['profile_uid'] : 0); + if ((! $channel_id) && (local_channel())) { + $channel_id = local_channel(); + } - $chan = channelx_by_n($channel_id); - if(! $chan) - return ''; + $chan = channelx_by_n($channel_id); + if (! $chan) { + return ''; + } - $menu_list = menu_list($channel_id); - $menu_names = []; + $menu_list = menu_list($channel_id); + $menu_names = []; - foreach($menu_list as $menus) { - if($menus['menu_name'] != $menu['menu']['menu_name']) - $menu_names[] = $menus['menu_name']; - } + foreach ($menu_list as $menus) { + if ($menus['menu_name'] != $menu['menu']['menu_name']) { + $menu_names[] = $menus['menu_name']; + } + } - for($x = 0; $x < count($menu['items']); $x ++) { - if(in_array($menu['items'][$x]['mitem_link'], $menu_names)) { - $m = menu_fetch($menu['items'][$x]['mitem_link'], $channel_id, get_observer_hash()); - $submenu = menu_render($m, 'dropdown-menu', $edit = false, array('wrap' => 'none')); - $menu['items'][$x]['submenu'] = $submenu; - } + for ($x = 0; $x < count($menu['items']); $x++) { + if (in_array($menu['items'][$x]['mitem_link'], $menu_names)) { + $m = menu_fetch($menu['items'][$x]['mitem_link'], $channel_id, get_observer_hash()); + $submenu = menu_render($m, 'dropdown-menu', $edit = false, array('wrap' => 'none')); + $menu['items'][$x]['submenu'] = $submenu; + } - if($menu['items'][$x]['mitem_flags'] & MENU_ITEM_ZID) - $menu['items'][$x]['mitem_link'] = zid($menu['items'][$x]['mitem_link']); + if ($menu['items'][$x]['mitem_flags'] & MENU_ITEM_ZID) { + $menu['items'][$x]['mitem_link'] = zid($menu['items'][$x]['mitem_link']); + } - if($menu['items'][$x]['mitem_flags'] & MENU_ITEM_NEWWIN) - $menu['items'][$x]['newwin'] = '1'; + if ($menu['items'][$x]['mitem_flags'] & MENU_ITEM_NEWWIN) { + $menu['items'][$x]['newwin'] = '1'; + } - $menu['items'][$x]['mitem_desc'] = zidify_links(smilies(bbcode($menu['items'][$x]['mitem_desc']))); - } + $menu['items'][$x]['mitem_desc'] = zidify_links(smilies(bbcode($menu['items'][$x]['mitem_desc']))); + } - $wrap = (($var['wrap'] === 'none') ? false : true); + $wrap = (($var['wrap'] === 'none') ? false : true); - $ret = replace_macros(get_markup_template('usermenu.tpl'),array( - '$menu' => $menu['menu'], - '$class' => $class, - '$nick' => $chan['channel_address'], - '$edit' => (($edit) ? t("Edit") : ''), - '$id' => $menu['menu']['menu_id'], - '$items' => $menu['items'], - '$wrap' => $wrap - )); + $ret = replace_macros(get_markup_template('usermenu.tpl'), array( + '$menu' => $menu['menu'], + '$class' => $class, + '$nick' => $chan['channel_address'], + '$edit' => (($edit) ? t("Edit") : ''), + '$id' => $menu['menu']['menu_id'], + '$items' => $menu['items'], + '$wrap' => $wrap + )); - return $ret; + return $ret; } -function menu_fetch_id($menu_id,$channel_id) { +function menu_fetch_id($menu_id, $channel_id) +{ - $r = q("select * from menu where menu_id = %d and menu_channel_id = %d limit 1", - intval($menu_id), - intval($channel_id) - ); + $r = q( + "select * from menu where menu_id = %d and menu_channel_id = %d limit 1", + intval($menu_id), + intval($channel_id) + ); - return (($r) ? $r[0] : false); + return (($r) ? $r[0] : false); } -function menu_create($arr) { - $menu_name = trim(escape_tags($arr['menu_name'])); - $menu_desc = trim(escape_tags($arr['menu_desc'])); - $menu_flags = intval($arr['menu_flags']); +function menu_create($arr) +{ + $menu_name = trim(escape_tags($arr['menu_name'])); + $menu_desc = trim(escape_tags($arr['menu_desc'])); + $menu_flags = intval($arr['menu_flags']); - //allow menu_desc (title) to be empty - //if(! $menu_desc) - // $menu_desc = $menu_name; + //allow menu_desc (title) to be empty + //if(! $menu_desc) + // $menu_desc = $menu_name; - if(! $menu_name) - return false; + if (! $menu_name) { + return false; + } - if(! $menu_flags) - $menu_flags = 0; + if (! $menu_flags) { + $menu_flags = 0; + } - $menu_channel_id = intval($arr['menu_channel_id']); + $menu_channel_id = intval($arr['menu_channel_id']); - $r = q("select * from menu where menu_name = '%s' and menu_channel_id = %d limit 1", - dbesc($menu_name), - intval($menu_channel_id) - ); + $r = q( + "select * from menu where menu_name = '%s' and menu_channel_id = %d limit 1", + dbesc($menu_name), + intval($menu_channel_id) + ); - if($r) - return false; + if ($r) { + return false; + } - $t = datetime_convert(); + $t = datetime_convert(); - $r = q("insert into menu ( menu_name, menu_desc, menu_flags, menu_channel_id, menu_created, menu_edited ) + $r = q( + "insert into menu ( menu_name, menu_desc, menu_flags, menu_channel_id, menu_created, menu_edited ) values( '%s', '%s', %d, %d, '%s', '%s' )", - dbesc($menu_name), - dbesc($menu_desc), - intval($menu_flags), - intval($menu_channel_id), - dbesc(datetime_convert('UTC','UTC',(($arr['menu_created']) ? $arr['menu_created'] : $t))), - dbesc(datetime_convert('UTC','UTC',(($arr['menu_edited']) ? $arr['menu_edited'] : $t))) - ); - if(! $r) - return false; - - $r = q("select menu_id from menu where menu_name = '%s' and menu_channel_id = %d limit 1", - dbesc($menu_name), - intval($menu_channel_id) - ); - if($r) - return $r[0]['menu_id']; - return false; + dbesc($menu_name), + dbesc($menu_desc), + intval($menu_flags), + intval($menu_channel_id), + dbesc(datetime_convert('UTC', 'UTC', (($arr['menu_created']) ? $arr['menu_created'] : $t))), + dbesc(datetime_convert('UTC', 'UTC', (($arr['menu_edited']) ? $arr['menu_edited'] : $t))) + ); + if (! $r) { + return false; + } + $r = q( + "select menu_id from menu where menu_name = '%s' and menu_channel_id = %d limit 1", + dbesc($menu_name), + intval($menu_channel_id) + ); + if ($r) { + return $r[0]['menu_id']; + } + return false; } /** @@ -197,218 +224,245 @@ function menu_create($arr) { * bits set. We will use this to find system generated bookmarks. */ -function menu_list($channel_id, $name = '', $flags = 0) { +function menu_list($channel_id, $name = '', $flags = 0) +{ - $sel_options = ''; - $sel_options .= (($name) ? " and menu_name = '" . protect_sprintf(dbesc($name)) . "' " : ''); - $sel_options .= (($flags) ? " and menu_flags = " . intval($flags) . " " : ''); + $sel_options = ''; + $sel_options .= (($name) ? " and menu_name = '" . protect_sprintf(dbesc($name)) . "' " : ''); + $sel_options .= (($flags) ? " and menu_flags = " . intval($flags) . " " : ''); - $r = q("select * from menu where menu_channel_id = %d $sel_options order by menu_desc", - intval($channel_id) - ); - return $r; + $r = q( + "select * from menu where menu_channel_id = %d $sel_options order by menu_desc", + intval($channel_id) + ); + return $r; } -function menu_list_count($channel_id, $name = '', $flags = 0) { +function menu_list_count($channel_id, $name = '', $flags = 0) +{ - $sel_options = ''; - $sel_options .= (($name) ? " and menu_name = '" . protect_sprintf(dbesc($name)) . "' " : ''); - $sel_options .= (($flags) ? " and menu_flags = " . intval($flags) . " " : ''); + $sel_options = ''; + $sel_options .= (($name) ? " and menu_name = '" . protect_sprintf(dbesc($name)) . "' " : ''); + $sel_options .= (($flags) ? " and menu_flags = " . intval($flags) . " " : ''); - $r = q("select count(*) as total from menu where menu_channel_id = %d $sel_options", - intval($channel_id) - ); - return $r[0]['total']; + $r = q( + "select count(*) as total from menu where menu_channel_id = %d $sel_options", + intval($channel_id) + ); + return $r[0]['total']; } -function menu_edit($arr) { +function menu_edit($arr) +{ - $menu_id = intval($arr['menu_id']); + $menu_id = intval($arr['menu_id']); - $menu_name = trim(escape_tags($arr['menu_name'])); - $menu_desc = trim(escape_tags($arr['menu_desc'])); - $menu_flags = intval($arr['menu_flags']); + $menu_name = trim(escape_tags($arr['menu_name'])); + $menu_desc = trim(escape_tags($arr['menu_desc'])); + $menu_flags = intval($arr['menu_flags']); - //allow menu_desc (title) to be empty - //if(! $menu_desc) - // $menu_desc = $menu_name; + //allow menu_desc (title) to be empty + //if(! $menu_desc) + // $menu_desc = $menu_name; - if(! $menu_name) - return false; + if (! $menu_name) { + return false; + } - if(! $menu_flags) - $menu_flags = 0; + if (! $menu_flags) { + $menu_flags = 0; + } - $menu_channel_id = intval($arr['menu_channel_id']); + $menu_channel_id = intval($arr['menu_channel_id']); - $r = q("select menu_id from menu where menu_name = '%s' and menu_channel_id = %d limit 1", - dbesc($menu_name), - intval($menu_channel_id) - ); - if(($r) && ($r[0]['menu_id'] != $menu_id)) { - logger('menu_edit: duplicate menu name for channel ' . $menu_channel_id); - return false; - } + $r = q( + "select menu_id from menu where menu_name = '%s' and menu_channel_id = %d limit 1", + dbesc($menu_name), + intval($menu_channel_id) + ); + if (($r) && ($r[0]['menu_id'] != $menu_id)) { + logger('menu_edit: duplicate menu name for channel ' . $menu_channel_id); + return false; + } - $r = q("select * from menu where menu_id = %d and menu_channel_id = %d limit 1", - intval($menu_id), - intval($menu_channel_id) - ); - if(! $r) { - logger('menu_edit: not found: ' . print_r($arr,true)); - return false; - } + $r = q( + "select * from menu where menu_id = %d and menu_channel_id = %d limit 1", + intval($menu_id), + intval($menu_channel_id) + ); + if (! $r) { + logger('menu_edit: not found: ' . print_r($arr, true)); + return false; + } - return q("update menu set menu_name = '%s', menu_desc = '%s', menu_flags = %d, menu_edited = '%s' - where menu_id = %d and menu_channel_id = %d", - dbesc($menu_name), - dbesc($menu_desc), - intval($menu_flags), - dbesc(datetime_convert()), - intval($menu_id), - intval($menu_channel_id) - ); + return q( + "update menu set menu_name = '%s', menu_desc = '%s', menu_flags = %d, menu_edited = '%s' + where menu_id = %d and menu_channel_id = %d", + dbesc($menu_name), + dbesc($menu_desc), + intval($menu_flags), + dbesc(datetime_convert()), + intval($menu_id), + intval($menu_channel_id) + ); } -function menu_delete($menu_name, $uid) { - $r = q("select menu_id from menu where menu_name = '%s' and menu_channel_id = %d limit 1", - dbesc($menu_name), - intval($uid) - ); +function menu_delete($menu_name, $uid) +{ + $r = q( + "select menu_id from menu where menu_name = '%s' and menu_channel_id = %d limit 1", + dbesc($menu_name), + intval($uid) + ); - if($r) - return menu_delete_id($r[0]['menu_id'],$uid); - return false; + if ($r) { + return menu_delete_id($r[0]['menu_id'], $uid); + } + return false; } -function menu_delete_id($menu_id, $uid) { - $r = q("select menu_id from menu where menu_id = %d and menu_channel_id = %d limit 1", - intval($menu_id), - intval($uid) - ); - if($r) { - $x = q("delete from menu_item where mitem_menu_id = %d and mitem_channel_id = %d", - intval($menu_id), - intval($uid) - ); - return q("delete from menu where menu_id = %d and menu_channel_id = %d", - intval($menu_id), - intval($uid) - ); - } - return false; +function menu_delete_id($menu_id, $uid) +{ + $r = q( + "select menu_id from menu where menu_id = %d and menu_channel_id = %d limit 1", + intval($menu_id), + intval($uid) + ); + if ($r) { + $x = q( + "delete from menu_item where mitem_menu_id = %d and mitem_channel_id = %d", + intval($menu_id), + intval($uid) + ); + return q( + "delete from menu where menu_id = %d and menu_channel_id = %d", + intval($menu_id), + intval($uid) + ); + } + return false; } -function menu_add_item($menu_id, $uid, $arr) { +function menu_add_item($menu_id, $uid, $arr) +{ - $mitem_link = escape_tags($arr['mitem_link']); - $mitem_desc = escape_tags($arr['mitem_desc']); - $mitem_order = intval($arr['mitem_order']); - $mitem_flags = intval($arr['mitem_flags']); + $mitem_link = escape_tags($arr['mitem_link']); + $mitem_desc = escape_tags($arr['mitem_desc']); + $mitem_order = intval($arr['mitem_order']); + $mitem_flags = intval($arr['mitem_flags']); - if(local_channel() == $uid) { - $channel = App::get_channel(); - } + if (local_channel() == $uid) { + $channel = App::get_channel(); + } - $acl = new Zotlabs\Access\AccessControl($channel); - $acl->set_from_array($arr); - $p = $acl->get(); + $acl = new Zotlabs\Access\AccessControl($channel); + $acl->set_from_array($arr); + $p = $acl->get(); - $r = q("insert into menu_item ( mitem_link, mitem_desc, mitem_flags, allow_cid, allow_gid, deny_cid, deny_gid, mitem_channel_id, mitem_menu_id, mitem_order ) values ( '%s', '%s', %d, '%s', '%s', '%s', '%s', %d, %d, %d ) ", - dbesc($mitem_link), - dbesc($mitem_desc), - intval($mitem_flags), - dbesc($p['allow_cid']), - dbesc($p['allow_gid']), - dbesc($p['deny_cid']), - dbesc($p['deny_gid']), - intval($uid), - intval($menu_id), - intval($mitem_order) - ); + $r = q( + "insert into menu_item ( mitem_link, mitem_desc, mitem_flags, allow_cid, allow_gid, deny_cid, deny_gid, mitem_channel_id, mitem_menu_id, mitem_order ) values ( '%s', '%s', %d, '%s', '%s', '%s', '%s', %d, %d, %d ) ", + dbesc($mitem_link), + dbesc($mitem_desc), + intval($mitem_flags), + dbesc($p['allow_cid']), + dbesc($p['allow_gid']), + dbesc($p['deny_cid']), + dbesc($p['deny_gid']), + intval($uid), + intval($menu_id), + intval($mitem_order) + ); - $x = q("update menu set menu_edited = '%s' where menu_id = %d and menu_channel_id = %d", - dbesc(datetime_convert()), - intval($menu_id), - intval($uid) - ); - - return $r; + $x = q( + "update menu set menu_edited = '%s' where menu_id = %d and menu_channel_id = %d", + dbesc(datetime_convert()), + intval($menu_id), + intval($uid) + ); + return $r; } -function menu_edit_item($menu_id, $uid, $arr) { +function menu_edit_item($menu_id, $uid, $arr) +{ - $mitem_id = intval($arr['mitem_id']); - $mitem_link = escape_tags($arr['mitem_link']); - $mitem_desc = escape_tags($arr['mitem_desc']); - $mitem_order = intval($arr['mitem_order']); - $mitem_flags = intval($arr['mitem_flags']); + $mitem_id = intval($arr['mitem_id']); + $mitem_link = escape_tags($arr['mitem_link']); + $mitem_desc = escape_tags($arr['mitem_desc']); + $mitem_order = intval($arr['mitem_order']); + $mitem_flags = intval($arr['mitem_flags']); - if(local_channel() == $uid) { - $channel = App::get_channel(); - } + if (local_channel() == $uid) { + $channel = App::get_channel(); + } - $acl = new Zotlabs\Access\AccessControl($channel); - $acl->set_from_array($arr); - $p = $acl->get(); + $acl = new Zotlabs\Access\AccessControl($channel); + $acl->set_from_array($arr); + $p = $acl->get(); - $r = q("update menu_item set mitem_link = '%s', mitem_desc = '%s', mitem_flags = %d, allow_cid = '%s', allow_gid = '%s', deny_cid = '%s', deny_gid = '%s', mitem_order = %d where mitem_channel_id = %d and mitem_menu_id = %d and mitem_id = %d", - dbesc($mitem_link), - dbesc($mitem_desc), - intval($mitem_flags), - dbesc($p['allow_cid']), - dbesc($p['allow_gid']), - dbesc($p['deny_cid']), - dbesc($p['deny_gid']), - intval($mitem_order), - intval($uid), - intval($menu_id), - intval($mitem_id) - ); + $r = q( + "update menu_item set mitem_link = '%s', mitem_desc = '%s', mitem_flags = %d, allow_cid = '%s', allow_gid = '%s', deny_cid = '%s', deny_gid = '%s', mitem_order = %d where mitem_channel_id = %d and mitem_menu_id = %d and mitem_id = %d", + dbesc($mitem_link), + dbesc($mitem_desc), + intval($mitem_flags), + dbesc($p['allow_cid']), + dbesc($p['allow_gid']), + dbesc($p['deny_cid']), + dbesc($p['deny_gid']), + intval($mitem_order), + intval($uid), + intval($menu_id), + intval($mitem_id) + ); - $x = q("update menu set menu_edited = '%s' where menu_id = %d and menu_channel_id = %d", - dbesc(datetime_convert()), - intval($menu_id), - intval($uid) - ); + $x = q( + "update menu set menu_edited = '%s' where menu_id = %d and menu_channel_id = %d", + dbesc(datetime_convert()), + intval($menu_id), + intval($uid) + ); - return $r; + return $r; } -function menu_del_item($menu_id,$uid,$item_id) { - $r = q("delete from menu_item where mitem_menu_id = %d and mitem_channel_id = %d and mitem_id = %d", - intval($menu_id), - intval($uid), - intval($item_id) - ); +function menu_del_item($menu_id, $uid, $item_id) +{ + $r = q( + "delete from menu_item where mitem_menu_id = %d and mitem_channel_id = %d and mitem_id = %d", + intval($menu_id), + intval($uid), + intval($item_id) + ); - $x = q("update menu set menu_edited = '%s' where menu_id = %d and menu_channel_id = %d", - dbesc(datetime_convert()), - intval($menu_id), - intval($uid) - ); + $x = q( + "update menu set menu_edited = '%s' where menu_id = %d and menu_channel_id = %d", + dbesc(datetime_convert()), + intval($menu_id), + intval($uid) + ); - return $r; + return $r; } -function menu_sync_packet($uid,$observer_hash,$menu_id,$delete = false) { - $r = menu_fetch_id($menu_id,$uid); - $c = channelx_by_n($uid); - if($r) { - $m = menu_fetch($r['menu_name'],$uid,$observer_hash); - if($m) { - if($delete) - $m['menu_delete'] = 1; - Libsync::build_sync_packet($uid,array('menu' => array(menu_element($c,$m)))); - } - } +function menu_sync_packet($uid, $observer_hash, $menu_id, $delete = false) +{ + $r = menu_fetch_id($menu_id, $uid); + $c = channelx_by_n($uid); + if ($r) { + $m = menu_fetch($r['menu_name'], $uid, $observer_hash); + if ($m) { + if ($delete) { + $m['menu_delete'] = 1; + } + Libsync::build_sync_packet($uid, array('menu' => array(menu_element($c, $m)))); + } + } } diff --git a/include/nav.php b/include/nav.php index ca3e08adc..d3cc8e381 100644 --- a/include/nav.php +++ b/include/nav.php @@ -1,534 +1,536 @@ -$(document).ready(function() { $("#nav-search-text").search_autocomplete(\'' . z_root() . '/acl' . '\');});'; - - $is_owner = (((local_channel()) && ((App::$profile_uid == local_channel()) || (App::$profile_uid == 0))) ? true : false); - - if(local_channel()) { - $channel = App::get_channel(); - $observer = App::get_observer(); - $prof = q("select id from profile where uid = %d and is_default = 1", - intval($channel['channel_id']) - ); - - if(! (isset($_SESSION['delegate']) && $_SESSION['delegate'])) { - $chans = q("select channel_name, channel_id from channel left join pconfig on channel_id = pconfig.uid where channel_account_id = %d and channel_removed = 0 and pconfig.cat = 'system' and pconfig.k = 'include_in_menu' and pconfig.v = '1' order by channel_name ", - intval(get_account_id()) - ); - $q = get_sys_channel(); - if (is_site_admin() && intval(get_pconfig($q['channel_id'],'system','include_in_menu'))) { - $chans = array_merge([$q],$chans); - } - } - - $sitelocation = (($is_owner) ? '' : App::$profile['reddress']); - } - elseif(remote_channel()) { - $observer = App::get_observer(); - $sitelocation = ((App::$profile['reddress']) ? App::$profile['reddress'] : '@' . App::get_hostname()); - } - - require_once('include/conversation.php'); - - - $channel_apps[] = ((isset(App::$profile)) ? channel_apps($is_owner, App::$profile['channel_address']) : []); - - $site_icon = System::get_site_icon(); - - $banner = System::get_site_name(); - if (! isset(App::$page['header'])) { - App::$page['header'] = EMPTY_STR; - } - App::$page['header'] .= replace_macros(get_markup_template('hdr.tpl'), array( - //we could additionally use this to display important system notifications e.g. for updates - )); - - - // nav links: array of array('href', 'text', 'extra css classes', 'title') - $nav = []; - - if(can_view_public_stream()) - $nav['pubs'] = true; - - /** - * Display login or logout - */ - - $nav['usermenu'] = []; - $userinfo = null; - $nav['loginmenu'] = []; - - if($observer) { - $userinfo = [ - 'icon' => $observer['xchan_photo_m'].'?rev='.strtotime($observer['xchan_photo_date']), - 'name' => $observer['xchan_addr'], - ]; - } - - elseif(! $_SESSION['authenticated']) { - $nav['remote_login'] = remote_login(); - $nav['loginmenu'][] = Array('rmagic',t('Remote authentication'),'',t('Click to authenticate to your home hub'),'rmagic_nav_btn'); - } - - if(local_channel()) { - - if(! (isset($_SESSION['delegate']) && $_SESSION['delegate'])) { - $nav['manage'] = array('manage', t('Channels'), "", t('Manage your channels'),'manage_nav_btn'); - } - - $nav['group'] = array('lists', t('Lists'),"", t('Manage your access lists'),'group_nav_btn'); - - $nav['settings'] = array('settings', t('Settings'),"", t('Account/Channel Settings'),'settings_nav_btn'); - - $nav['safe'] = array('safe', t('Safe Mode'), ((get_safemode()) ? t('(is on)') : t('(is off)')) , t('Content filtering'),'safe_nav_btn'); - - - if ($chans && count($chans) > 0) { - $nav['channels'] = $chans; - } - - $nav['logout'] = ['logout',t('Logout'), "", t('End this session'),'logout_nav_btn']; - - // user menu - $nav['usermenu'][] = ['profile/' . $channel['channel_address'], t('View Profile'), ((App::$nav_sel['raw_name'] == 'Profile') ? 'active' : ''), t('Your profile page'),'profile_nav_btn']; - - if(feature_enabled(local_channel(),'multi_profiles')) - $nav['usermenu'][] = ['profiles', t('Edit Profiles'), ((App::$nav_sel['raw_name'] == 'Profiles') ? 'active' : '') , t('Manage/Edit profiles'),'profiles_nav_btn']; - else - $nav['usermenu'][] = ['profiles/' . $prof[0]['id'], t('Edit Profile'), ((App::$nav_sel['raw_name'] == 'Profiles') ? 'active' : ''), t('Edit your profile'),'profiles_nav_btn']; - - } - else { - if(! get_account_id()) { - if(App::$module === 'channel') { - $nav['login'] = login(true,'navbar-login',false,false); - $nav['loginmenu'][] = ['login',t('Login'),'',t('Sign in'),'']; - } - else { - $nav['login'] = login(true,'navbar-login',false,false); - $nav['loginmenu'][] = ['login',t('Login'),'',t('Sign in'),'login_nav_btn']; - App::$page['content'] .= replace_macros(get_markup_template('nav_login.tpl'), - [ - '$nav' => $nav, - 'userinfo' => $userinfo - ] - ); - } - } - else - $nav['alogout'] = ['logout',t('Logout'), "", t('End this session'),'logout_nav_btn']; - - - } - - $my_url = get_my_url(); - if(! $my_url) { - $observer = App::get_observer(); - $my_url = (($observer) ? $observer['xchan_url'] : ''); - } - - $homelink_arr = parse_url($my_url); - $homelink = $homelink_arr['scheme'] . '://' . $homelink_arr['host']; - - if(! $is_owner) { - $nav['rusermenu'] = array( - $homelink, - t('Take me home'), - 'logout', - ((local_channel()) ? t('Logout') : t('Log me out of this site')) - ); - } - - if(((get_config('system','register_policy') == REGISTER_OPEN) || (get_config('system','register_policy') == REGISTER_APPROVE)) && (! $_SESSION['authenticated'])) - $nav['register'] = ['register',t('Register'), "", t('Create an account'),'register_nav_btn']; - - if(! get_config('system','hide_help',true)) { - $help_url = z_root() . '/help?f=&cmd=' . App::$cmd; - $context_help = ''; - $enable_context_help = ((intval(get_config('system','enable_context_help')) === 1 || get_config('system','enable_context_help') === false) ? true : false); - if($enable_context_help === true) { - require_once('include/help.php'); - $context_help = load_context_help(); - //point directly to /help if $context_help is empty - this can be removed once we have context help for all modules - $enable_context_help = (($context_help) ? true : false); - } - $nav['help'] = [$help_url, t('Help'), "", t('Help and documentation'), 'help_nav_btn', $context_help, $enable_context_help]; - } - - - $search_form_action = 'search'; - - - $nav['search'] = ['search', t('Search'), "", t('Search site @name, #tag, ?doc, content'), $search_form_action]; - - /** - * Admin page - */ - if (is_site_admin()) { - $nav['admin'] = array('admin/', t('Admin'), "", t('Site Setup and Configuration'),'admin_nav_btn'); - } - - $x = array('nav' => $nav, 'usermenu' => $userinfo ); - - call_hooks('nav', $x); - - // Not sure the best place to put this on the page. So I'm implementing it but leaving it - // turned off until somebody discovers this and figures out a good location for it. - $powered_by = ''; - - if(App::$profile_uid && App::$nav_sel['raw_name']) { - $active_app = q("SELECT app_url FROM app WHERE app_channel = %d AND app_name = '%s' LIMIT 1", - intval(App::$profile_uid), - dbesc(App::$nav_sel['raw_name']) - ); - - if($active_app) { - $url = $active_app[0]['app_url']; - } - } - - $pinned_list = []; - $syslist = []; - - //app bin - if($is_owner) { - if(get_pconfig(local_channel(), 'system','import_system_apps') !== datetime_convert('UTC','UTC','now','Y-m-d')) { - Apps::import_system_apps(); - set_pconfig(local_channel(), 'system','import_system_apps', datetime_convert('UTC','UTC','now','Y-m-d')); - } - - $list = Apps::app_list(local_channel(), false, [ 'nav_pinned_app' ]); - if($list) { - foreach($list as $li) { - $pinned_list[] = Apps::app_encode($li); - } - } - Apps::translate_system_apps($pinned_list); - - usort($pinned_list,'Zotlabs\\Lib\\Apps::app_name_compare'); - - $pinned_list = Apps::app_order(local_channel(),$pinned_list, 'nav_pinned_app'); - - - $syslist = []; - $list = Apps::app_list(local_channel(), false, [ 'nav_featured_app' ]); - - if($list) { - foreach($list as $li) { - $syslist[] = Apps::app_encode($li); - } - } - Apps::translate_system_apps($syslist); - - - } - else { - $syslist = Apps::get_system_apps(true); - } - - usort($syslist,'Zotlabs\\Lib\\Apps::app_name_compare'); - - $syslist = Apps::app_order(local_channel(),$syslist, 'nav_featured_app'); - - - if($pinned_list) { - foreach($pinned_list as $app) { - if(App::$nav_sel['name'] == $app['name']) - $app['active'] = true; - - if($is_owner) { - $navbar_apps[] = Apps::app_render($app,'navbar'); - } - elseif(! $is_owner && strpos($app['requires'], 'local_channel') === false) { - $navbar_apps[] = Apps::app_render($app,'navbar'); - } - } - } - - if($syslist) { - foreach($syslist as $app) { - if(App::$nav_sel['name'] == $app['name']) - $app['active'] = true; - - if($is_owner) { - $nav_apps[] = Apps::app_render($app,'nav'); - } - elseif(! $is_owner && strpos($app['requires'], 'local_channel') === false) { - $nav_apps[] = Apps::app_render($app,'nav'); - } - } - } - - $c = theme_include('navbar_' . purify_filename($template) . '.css'); - $tpl = get_markup_template('navbar_' . purify_filename($template) . '.tpl'); - - if($c && $tpl) { - head_add_css('navbar_' . $template . '.css'); - } - - if(! $tpl) { - $tpl = get_markup_template('navbar_default.tpl'); - } - - App::$page['nav'] .= replace_macros($tpl, array( - '$baseurl' => z_root(), - '$project_icon' => $site_icon, - '$project_title' => t('Powered by $Projectname'), - '$fulldocs' => t('Help'), - '$sitelocation' => $sitelocation, - '$nav' => $x['nav'], - '$banner' => $banner, - '$emptynotifications' => t('Loading'), - '$userinfo' => $x['usermenu'], - '$localuser' => local_channel(), - '$is_owner' => $is_owner, - '$sel' => App::$nav_sel, - '$powered_by' => $powered_by, - '$help' => t('@name, #tag, ?doc, content'), - '$pleasewait' => t('Please wait...'), - '$nav_apps' => ((isset($nav_apps)) ? $nav_apps : []), - '$navbar_apps' => ((isset($navbar_apps)) ? $navbar_apps : []), - '$channel_menu' => get_pconfig(App::$profile_uid,'system','channel_menu',get_config('system','channel_menu')), - '$channel_thumb' => ((App::$profile) ? App::$profile['thumb'] : ''), - '$channel_apps' => ((isset($channel_apps)) ? $channel_apps : []), - '$manageapps' => t('Installed Apps'), - '$addapps' => t('Available Apps'), - '$orderapps' => t('Arrange Apps'), - '$sysapps_toggle' => t('Toggle System Apps'), - '$url' => ((isset($url) && $url) ? $url : App::$cmd) - )); - - if(x($_SESSION, 'reload_avatar') && $observer) { - // The avatar has been changed on the server but the browser doesn't know that, - // force the browser to reload the image from the server instead of its cache. - $tpl = get_markup_template('force_image_reload.tpl'); - - App::$page['nav'] .= replace_macros($tpl, array( - '$imgUrl' => $observer['xchan_photo_m'] - )); - unset($_SESSION['reload_avatar']); - } - - call_hooks('page_header', App::$page['nav']); + if (! isset(App::$page['nav'])) { + App::$page['nav'] = EMPTY_STR; + } + if (! isset(App::$page['htmlhead'])) { + App::$page['htmlhead'] = EMPTY_STR; + } + + App::$page['htmlhead'] .= ''; + + $is_owner = (((local_channel()) && ((App::$profile_uid == local_channel()) || (App::$profile_uid == 0))) ? true : false); + + if (local_channel()) { + $channel = App::get_channel(); + $observer = App::get_observer(); + $prof = q( + "select id from profile where uid = %d and is_default = 1", + intval($channel['channel_id']) + ); + + if (! (isset($_SESSION['delegate']) && $_SESSION['delegate'])) { + $chans = q( + "select channel_name, channel_id from channel left join pconfig on channel_id = pconfig.uid where channel_account_id = %d and channel_removed = 0 and pconfig.cat = 'system' and pconfig.k = 'include_in_menu' and pconfig.v = '1' order by channel_name ", + intval(get_account_id()) + ); + $q = get_sys_channel(); + if (is_site_admin() && intval(get_pconfig($q['channel_id'], 'system', 'include_in_menu'))) { + $chans = array_merge([$q], $chans); + } + } + + $sitelocation = (($is_owner) ? '' : App::$profile['reddress']); + } elseif (remote_channel()) { + $observer = App::get_observer(); + $sitelocation = ((App::$profile['reddress']) ? App::$profile['reddress'] : '@' . App::get_hostname()); + } + + require_once('include/conversation.php'); + + + $channel_apps[] = ((isset(App::$profile)) ? channel_apps($is_owner, App::$profile['channel_address']) : []); + + $site_icon = System::get_site_icon(); + + $banner = System::get_site_name(); + if (! isset(App::$page['header'])) { + App::$page['header'] = EMPTY_STR; + } + App::$page['header'] .= replace_macros(get_markup_template('hdr.tpl'), array( + //we could additionally use this to display important system notifications e.g. for updates + )); + + + // nav links: array of array('href', 'text', 'extra css classes', 'title') + $nav = []; + + if (can_view_public_stream()) { + $nav['pubs'] = true; + } + + /** + * Display login or logout + */ + + $nav['usermenu'] = []; + $userinfo = null; + $nav['loginmenu'] = []; + + if ($observer) { + $userinfo = [ + 'icon' => $observer['xchan_photo_m'] . '?rev=' . strtotime($observer['xchan_photo_date']), + 'name' => $observer['xchan_addr'], + ]; + } elseif (! $_SESSION['authenticated']) { + $nav['remote_login'] = remote_login(); + $nav['loginmenu'][] = array('rmagic',t('Remote authentication'),'',t('Click to authenticate to your home hub'),'rmagic_nav_btn'); + } + + if (local_channel()) { + if (! (isset($_SESSION['delegate']) && $_SESSION['delegate'])) { + $nav['manage'] = array('manage', t('Channels'), "", t('Manage your channels'),'manage_nav_btn'); + } + + $nav['group'] = array('lists', t('Lists'),"", t('Manage your access lists'),'group_nav_btn'); + + $nav['settings'] = array('settings', t('Settings'),"", t('Account/Channel Settings'),'settings_nav_btn'); + + $nav['safe'] = array('safe', t('Safe Mode'), ((get_safemode()) ? t('(is on)') : t('(is off)')) , t('Content filtering'),'safe_nav_btn'); + + + if ($chans && count($chans) > 0) { + $nav['channels'] = $chans; + } + + $nav['logout'] = ['logout',t('Logout'), "", t('End this session'),'logout_nav_btn']; + + // user menu + $nav['usermenu'][] = ['profile/' . $channel['channel_address'], t('View Profile'), ((App::$nav_sel['raw_name'] == 'Profile') ? 'active' : ''), t('Your profile page'),'profile_nav_btn']; + + if (feature_enabled(local_channel(), 'multi_profiles')) { + $nav['usermenu'][] = ['profiles', t('Edit Profiles'), ((App::$nav_sel['raw_name'] == 'Profiles') ? 'active' : '') , t('Manage/Edit profiles'),'profiles_nav_btn']; + } else { + $nav['usermenu'][] = ['profiles/' . $prof[0]['id'], t('Edit Profile'), ((App::$nav_sel['raw_name'] == 'Profiles') ? 'active' : ''), t('Edit your profile'),'profiles_nav_btn']; + } + } else { + if (! get_account_id()) { + if (App::$module === 'channel') { + $nav['login'] = login(true, 'navbar-login', false, false); + $nav['loginmenu'][] = ['login',t('Login'),'',t('Sign in'),'']; + } else { + $nav['login'] = login(true, 'navbar-login', false, false); + $nav['loginmenu'][] = ['login',t('Login'),'',t('Sign in'),'login_nav_btn']; + App::$page['content'] .= replace_macros( + get_markup_template('nav_login.tpl'), + [ + '$nav' => $nav, + 'userinfo' => $userinfo + ] + ); + } + } else { + $nav['alogout'] = ['logout',t('Logout'), "", t('End this session'),'logout_nav_btn']; + } + } + + $my_url = get_my_url(); + if (! $my_url) { + $observer = App::get_observer(); + $my_url = (($observer) ? $observer['xchan_url'] : ''); + } + + $homelink_arr = parse_url($my_url); + $homelink = $homelink_arr['scheme'] . '://' . $homelink_arr['host']; + + if (! $is_owner) { + $nav['rusermenu'] = array( + $homelink, + t('Take me home'), + 'logout', + ((local_channel()) ? t('Logout') : t('Log me out of this site')) + ); + } + + if (((get_config('system', 'register_policy') == REGISTER_OPEN) || (get_config('system', 'register_policy') == REGISTER_APPROVE)) && (! $_SESSION['authenticated'])) { + $nav['register'] = ['register',t('Register'), "", t('Create an account'),'register_nav_btn']; + } + + if (! get_config('system', 'hide_help', true)) { + $help_url = z_root() . '/help?f=&cmd=' . App::$cmd; + $context_help = ''; + $enable_context_help = ((intval(get_config('system', 'enable_context_help')) === 1 || get_config('system', 'enable_context_help') === false) ? true : false); + if ($enable_context_help === true) { + require_once('include/help.php'); + $context_help = load_context_help(); + //point directly to /help if $context_help is empty - this can be removed once we have context help for all modules + $enable_context_help = (($context_help) ? true : false); + } + $nav['help'] = [$help_url, t('Help'), "", t('Help and documentation'), 'help_nav_btn', $context_help, $enable_context_help]; + } + + + $search_form_action = 'search'; + + + $nav['search'] = ['search', t('Search'), "", t('Search site @name, #tag, ?doc, content'), $search_form_action]; + + /** + * Admin page + */ + if (is_site_admin()) { + $nav['admin'] = array('admin/', t('Admin'), "", t('Site Setup and Configuration'),'admin_nav_btn'); + } + + $x = array('nav' => $nav, 'usermenu' => $userinfo ); + + call_hooks('nav', $x); + + // Not sure the best place to put this on the page. So I'm implementing it but leaving it + // turned off until somebody discovers this and figures out a good location for it. + $powered_by = ''; + + if (App::$profile_uid && App::$nav_sel['raw_name']) { + $active_app = q( + "SELECT app_url FROM app WHERE app_channel = %d AND app_name = '%s' LIMIT 1", + intval(App::$profile_uid), + dbesc(App::$nav_sel['raw_name']) + ); + + if ($active_app) { + $url = $active_app[0]['app_url']; + } + } + + $pinned_list = []; + $syslist = []; + + //app bin + if ($is_owner) { + if (get_pconfig(local_channel(), 'system', 'import_system_apps') !== datetime_convert('UTC', 'UTC', 'now', 'Y-m-d')) { + Apps::import_system_apps(); + set_pconfig(local_channel(), 'system', 'import_system_apps', datetime_convert('UTC', 'UTC', 'now', 'Y-m-d')); + } + + $list = Apps::app_list(local_channel(), false, [ 'nav_pinned_app' ]); + if ($list) { + foreach ($list as $li) { + $pinned_list[] = Apps::app_encode($li); + } + } + Apps::translate_system_apps($pinned_list); + + usort($pinned_list, 'Zotlabs\\Lib\\Apps::app_name_compare'); + + $pinned_list = Apps::app_order(local_channel(), $pinned_list, 'nav_pinned_app'); + + + $syslist = []; + $list = Apps::app_list(local_channel(), false, [ 'nav_featured_app' ]); + + if ($list) { + foreach ($list as $li) { + $syslist[] = Apps::app_encode($li); + } + } + Apps::translate_system_apps($syslist); + } else { + $syslist = Apps::get_system_apps(true); + } + + usort($syslist, 'Zotlabs\\Lib\\Apps::app_name_compare'); + + $syslist = Apps::app_order(local_channel(), $syslist, 'nav_featured_app'); + + + if ($pinned_list) { + foreach ($pinned_list as $app) { + if (App::$nav_sel['name'] == $app['name']) { + $app['active'] = true; + } + + if ($is_owner) { + $navbar_apps[] = Apps::app_render($app, 'navbar'); + } elseif (! $is_owner && strpos($app['requires'], 'local_channel') === false) { + $navbar_apps[] = Apps::app_render($app, 'navbar'); + } + } + } + + if ($syslist) { + foreach ($syslist as $app) { + if (App::$nav_sel['name'] == $app['name']) { + $app['active'] = true; + } + + if ($is_owner) { + $nav_apps[] = Apps::app_render($app, 'nav'); + } elseif (! $is_owner && strpos($app['requires'], 'local_channel') === false) { + $nav_apps[] = Apps::app_render($app, 'nav'); + } + } + } + + $c = theme_include('navbar_' . purify_filename($template) . '.css'); + $tpl = get_markup_template('navbar_' . purify_filename($template) . '.tpl'); + + if ($c && $tpl) { + head_add_css('navbar_' . $template . '.css'); + } + + if (! $tpl) { + $tpl = get_markup_template('navbar_default.tpl'); + } + + App::$page['nav'] .= replace_macros($tpl, array( + '$baseurl' => z_root(), + '$project_icon' => $site_icon, + '$project_title' => t('Powered by $Projectname'), + '$fulldocs' => t('Help'), + '$sitelocation' => $sitelocation, + '$nav' => $x['nav'], + '$banner' => $banner, + '$emptynotifications' => t('Loading'), + '$userinfo' => $x['usermenu'], + '$localuser' => local_channel(), + '$is_owner' => $is_owner, + '$sel' => App::$nav_sel, + '$powered_by' => $powered_by, + '$help' => t('@name, #tag, ?doc, content'), + '$pleasewait' => t('Please wait...'), + '$nav_apps' => ((isset($nav_apps)) ? $nav_apps : []), + '$navbar_apps' => ((isset($navbar_apps)) ? $navbar_apps : []), + '$channel_menu' => get_pconfig(App::$profile_uid, 'system', 'channel_menu', get_config('system', 'channel_menu')), + '$channel_thumb' => ((App::$profile) ? App::$profile['thumb'] : ''), + '$channel_apps' => ((isset($channel_apps)) ? $channel_apps : []), + '$manageapps' => t('Installed Apps'), + '$addapps' => t('Available Apps'), + '$orderapps' => t('Arrange Apps'), + '$sysapps_toggle' => t('Toggle System Apps'), + '$url' => ((isset($url) && $url) ? $url : App::$cmd) + )); + + if (x($_SESSION, 'reload_avatar') && $observer) { + // The avatar has been changed on the server but the browser doesn't know that, + // force the browser to reload the image from the server instead of its cache. + $tpl = get_markup_template('force_image_reload.tpl'); + + App::$page['nav'] .= replace_macros($tpl, array( + '$imgUrl' => $observer['xchan_photo_m'] + )); + unset($_SESSION['reload_avatar']); + } + + call_hooks('page_header', App::$page['nav']); } /* * Set a menu item in navbar as selected - * + * */ -function nav_set_selected($item){ - App::$nav_sel['raw_name'] = $item; - $item = ['name' => $item]; - Apps::translate_system_apps($item); - App::$nav_sel['name'] = $item['name']; +function nav_set_selected($item) +{ + App::$nav_sel['raw_name'] = $item; + $item = ['name' => $item]; + Apps::translate_system_apps($item); + App::$nav_sel['name'] = $item['name']; } -function channel_apps($is_owner = false, $nickname = null) { +function channel_apps($is_owner = false, $nickname = null) +{ - // Don't provide any channel apps if we're running as the sys channel + // Don't provide any channel apps if we're running as the sys channel - if(App::$is_sys) - return ''; + if (App::$is_sys) { + return ''; + } - $channel = App::get_channel(); + $channel = App::get_channel(); - if($channel && is_null($nickname)) - $nickname = $channel['channel_address']; + if ($channel && is_null($nickname)) { + $nickname = $channel['channel_address']; + } - $uid = ((App::$profile['profile_uid']) ? App::$profile['profile_uid'] : local_channel()); - $account_id = ((App::$profile['profile_uid']) ? App::$profile['channel_account_id'] : App::$channel['channel_account_id']); + $uid = ((App::$profile['profile_uid']) ? App::$profile['profile_uid'] : local_channel()); + $account_id = ((App::$profile['profile_uid']) ? App::$profile['channel_account_id'] : App::$channel['channel_account_id']); - if (! get_pconfig($uid, 'system', 'channelapps','1')) { - return ''; - } + if (! get_pconfig($uid, 'system', 'channelapps', '1')) { + return ''; + } - if($uid == local_channel()) { - return; - } - else { - $cal_link = '/cal/' . $nickname; - } + if ($uid == local_channel()) { + return; + } else { + $cal_link = '/cal/' . $nickname; + } - $sql_options = item_permissions_sql($uid); + $sql_options = item_permissions_sql($uid); - $r = q("select item.* from item left join iconfig on item.id = iconfig.iid + $r = q( + "select item.* from item left join iconfig on item.id = iconfig.iid where item.uid = %d and iconfig.cat = 'system' and iconfig.v = '%s' and item.item_delayed = 0 and item.item_deleted = 0 and ( iconfig.k = 'WEBPAGE' and item_type = %d ) $sql_options limit 1", - intval($uid), - dbesc('home'), - intval(ITEM_TYPE_WEBPAGE) - ); + intval($uid), + dbesc('home'), + intval(ITEM_TYPE_WEBPAGE) + ); - $has_webpages = (($r) ? true : false); + $has_webpages = (($r) ? true : false); - if(x($_GET, 'tab')) - $tab = notags(trim($_GET['tab'])); + if (x($_GET, 'tab')) { + $tab = notags(trim($_GET['tab'])); + } - $url = z_root() . '/channel/' . $nickname; - $pr = z_root() . '/profile/' . $nickname; + $url = z_root() . '/channel/' . $nickname; + $pr = z_root() . '/profile/' . $nickname; - $tabs = [ - [ - 'label' => t('Channel'), - 'url' => $url, - 'sel' => ((argv(0) == 'channel') ? 'active' : ''), - 'title' => t('Status Messages and Posts'), - 'id' => 'status-tab', - 'icon' => 'home' - ], - ]; + $tabs = [ + [ + 'label' => t('Channel'), + 'url' => $url, + 'sel' => ((argv(0) == 'channel') ? 'active' : ''), + 'title' => t('Status Messages and Posts'), + 'id' => 'status-tab', + 'icon' => 'home' + ], + ]; - $p = get_all_perms($uid,get_observer_hash()); + $p = get_all_perms($uid, get_observer_hash()); - if ($p['view_profile']) { - $tabs[] = [ - 'label' => t('About'), - 'url' => $pr, - 'sel' => ((argv(0) == 'profile') ? 'active' : ''), - 'title' => t('Profile Details'), - 'id' => 'profile-tab', - 'icon' => 'user' - ]; - } - if ($p['view_storage']) { - $tabs[] = [ - 'label' => t('Photos'), - 'url' => z_root() . '/photos/' . $nickname, - 'sel' => ((argv(0) == 'photos') ? 'active' : ''), - 'title' => t('Photo Albums'), - 'id' => 'photo-tab', - 'icon' => 'photo' - ]; - $tabs[] = [ - 'label' => t('Files'), - 'url' => z_root() . '/cloud/' . $nickname, - 'sel' => ((argv(0) == 'cloud' || argv(0) == 'sharedwithme') ? 'active' : ''), - 'title' => t('Files and Storage'), - 'id' => 'files-tab', - 'icon' => 'folder-open' - ]; - } + if ($p['view_profile']) { + $tabs[] = [ + 'label' => t('About'), + 'url' => $pr, + 'sel' => ((argv(0) == 'profile') ? 'active' : ''), + 'title' => t('Profile Details'), + 'id' => 'profile-tab', + 'icon' => 'user' + ]; + } + if ($p['view_storage']) { + $tabs[] = [ + 'label' => t('Photos'), + 'url' => z_root() . '/photos/' . $nickname, + 'sel' => ((argv(0) == 'photos') ? 'active' : ''), + 'title' => t('Photo Albums'), + 'id' => 'photo-tab', + 'icon' => 'photo' + ]; + $tabs[] = [ + 'label' => t('Files'), + 'url' => z_root() . '/cloud/' . $nickname, + 'sel' => ((argv(0) == 'cloud' || argv(0) == 'sharedwithme') ? 'active' : ''), + 'title' => t('Files and Storage'), + 'id' => 'files-tab', + 'icon' => 'folder-open' + ]; + } - if($p['view_stream'] && $cal_link) { - $tabs[] = [ - 'label' => t('Calendar'), - 'url' => z_root() . $cal_link, - 'sel' => ((argv(0) == 'cal') ? 'active' : ''), - 'title' => t('Calendar'), - 'id' => 'event-tab', - 'icon' => 'calendar' - ]; - } + if ($p['view_stream'] && $cal_link) { + $tabs[] = [ + 'label' => t('Calendar'), + 'url' => z_root() . $cal_link, + 'sel' => ((argv(0) == 'cal') ? 'active' : ''), + 'title' => t('Calendar'), + 'id' => 'event-tab', + 'icon' => 'calendar' + ]; + } - if ($p['chat'] && Apps::system_app_installed($uid,'Chatrooms')) { - $has_chats = Chatroom::list_count($uid); - if ($has_chats) { - $tabs[] = [ - 'label' => t('Chatrooms'), - 'url' => z_root() . '/chat/' . $nickname, - 'sel' => ((argv(0) == 'chat') ? 'active' : '' ), - 'title' => t('Chatrooms'), - 'id' => 'chat-tab', - 'icon' => 'comments-o' - ]; - } - } + if ($p['chat'] && Apps::system_app_installed($uid, 'Chatrooms')) { + $has_chats = Chatroom::list_count($uid); + if ($has_chats) { + $tabs[] = [ + 'label' => t('Chatrooms'), + 'url' => z_root() . '/chat/' . $nickname, + 'sel' => ((argv(0) == 'chat') ? 'active' : '' ), + 'title' => t('Chatrooms'), + 'id' => 'chat-tab', + 'icon' => 'comments-o' + ]; + } + } - $has_bookmarks = menu_list_count(local_channel(),'',MENU_BOOKMARK) + menu_list_count(local_channel(),'',MENU_SYSTEM|MENU_BOOKMARK); - if ($is_owner && $has_bookmarks) { - $tabs[] = [ - 'label' => t('Bookmarks'), - 'url' => z_root() . '/bookmarks', - 'sel' => ((argv(0) == 'bookmarks') ? 'active' : ''), - 'title' => t('Saved Bookmarks'), - 'id' => 'bookmarks-tab', - 'icon' => 'bookmark' - ]; - } + $has_bookmarks = menu_list_count(local_channel(), '', MENU_BOOKMARK) + menu_list_count(local_channel(), '', MENU_SYSTEM | MENU_BOOKMARK); + if ($is_owner && $has_bookmarks) { + $tabs[] = [ + 'label' => t('Bookmarks'), + 'url' => z_root() . '/bookmarks', + 'sel' => ((argv(0) == 'bookmarks') ? 'active' : ''), + 'title' => t('Saved Bookmarks'), + 'id' => 'bookmarks-tab', + 'icon' => 'bookmark' + ]; + } - if($p['view_pages'] && Apps::system_app_installed($uid, 'Cards')) { - $tabs[] = [ - 'label' => t('Cards'), - 'url' => z_root() . '/cards/' . $nickname , - 'sel' => ((argv(0) == 'cards') ? 'active' : ''), - 'title' => t('View Cards'), - 'id' => 'cards-tab', - 'icon' => 'list' - ]; - } + if ($p['view_pages'] && Apps::system_app_installed($uid, 'Cards')) { + $tabs[] = [ + 'label' => t('Cards'), + 'url' => z_root() . '/cards/' . $nickname , + 'sel' => ((argv(0) == 'cards') ? 'active' : ''), + 'title' => t('View Cards'), + 'id' => 'cards-tab', + 'icon' => 'list' + ]; + } - if($p['view_pages'] && Apps::system_app_installed($uid, 'Articles')) { - $tabs[] = [ - 'label' => t('Articles'), - 'url' => z_root() . '/articles/' . $nickname , - 'sel' => ((argv(0) == 'articles') ? 'active' : ''), - 'title' => t('View Articles'), - 'id' => 'articles-tab', - 'icon' => 'file-text-o' - ]; - } + if ($p['view_pages'] && Apps::system_app_installed($uid, 'Articles')) { + $tabs[] = [ + 'label' => t('Articles'), + 'url' => z_root() . '/articles/' . $nickname , + 'sel' => ((argv(0) == 'articles') ? 'active' : ''), + 'title' => t('View Articles'), + 'id' => 'articles-tab', + 'icon' => 'file-text-o' + ]; + } - if($has_webpages && Apps::system_app_installed($uid, 'Webpages')) { - $tabs[] = [ - 'label' => t('Webpages'), - 'url' => z_root() . '/page/' . $nickname . '/home', - 'sel' => ((argv(0) == 'webpages') ? 'active' : ''), - 'title' => t('View Webpages'), - 'id' => 'webpages-tab', - 'icon' => 'newspaper-o' - ]; - } - + if ($has_webpages && Apps::system_app_installed($uid, 'Webpages')) { + $tabs[] = [ + 'label' => t('Webpages'), + 'url' => z_root() . '/page/' . $nickname . '/home', + 'sel' => ((argv(0) == 'webpages') ? 'active' : ''), + 'title' => t('View Webpages'), + 'id' => 'webpages-tab', + 'icon' => 'newspaper-o' + ]; + } - if ($p['view_wiki'] && Apps::system_app_installed($uid, 'Wiki')) { - $tabs[] = [ - 'label' => t('Wikis'), - 'url' => z_root() . '/wiki/' . $nickname, - 'sel' => ((argv(0) == 'wiki') ? 'active' : ''), - 'title' => t('Wiki'), - 'id' => 'wiki-tab', - 'icon' => 'pencil-square-o' - ]; - } + if ($p['view_wiki'] && Apps::system_app_installed($uid, 'Wiki')) { + $tabs[] = [ + 'label' => t('Wikis'), + 'url' => z_root() . '/wiki/' . $nickname, + 'sel' => ((argv(0) == 'wiki') ? 'active' : ''), + 'title' => t('Wiki'), + 'id' => 'wiki-tab', + 'icon' => 'pencil-square-o' + ]; + } - $arr = array('is_owner' => $is_owner, 'nickname' => $nickname, 'tab' => (($tab) ? $tab : false), 'tabs' => $tabs); + $arr = array('is_owner' => $is_owner, 'nickname' => $nickname, 'tab' => (($tab) ? $tab : false), 'tabs' => $tabs); - call_hooks('channel_apps', $arr); + call_hooks('channel_apps', $arr); - return replace_macros(get_markup_template('profile_tabs.tpl'), - [ - '$tabs' => $arr['tabs'], - '$name' => App::$profile['channel_name'], - '$thumb' => App::$profile['thumb'], - ] - ); + return replace_macros( + get_markup_template('profile_tabs.tpl'), + [ + '$tabs' => $arr['tabs'], + '$name' => App::$profile['channel_name'], + '$thumb' => App::$profile['thumb'], + ] + ); } diff --git a/include/network.php b/include/network.php index 46c6b9d4b..d864cf419 100644 --- a/include/network.php +++ b/include/network.php @@ -13,7 +13,6 @@ use Zotlabs\Lib\LDSignatures; use Zotlabs\Web\HTTPSig; use Zotlabs\Daemon\Run; - /** * @file include/network.php * @brief Network related functions. @@ -24,8 +23,9 @@ use Zotlabs\Daemon\Run; * * @return string */ -function get_capath() { - return appdirpath() . '/library/cacert.pem'; +function get_capath() +{ + return appdirpath() . '/library/cacert.pem'; } /** @@ -33,7 +33,7 @@ function get_capath() { * * @param string $url * URL to fetch - * @param boolean $binary default false + * @param bool $binary default false * TRUE if asked to return binary results (file download) * @param int $redirects default 0 * internal use, recursion counter @@ -55,201 +55,218 @@ function get_capath() { * * \e string \b header => HTTP headers * * \e string \b body => fetched content */ -function z_fetch_url($url, $binary = false, $redirects = 0, $opts = []) { +function z_fetch_url($url, $binary = false, $redirects = 0, $opts = []) +{ - $ret = array('return_code' => 0, 'success' => false, 'header' => "", 'body' => ""); - $passthru = false; + $ret = array('return_code' => 0, 'success' => false, 'header' => "", 'body' => ""); + $passthru = false; - $ch = @curl_init($url); - if(($redirects > 8) || (! $ch)) - return $ret; + $ch = @curl_init($url); + if (($redirects > 8) || (! $ch)) { + return $ret; + } - if(! array_key_exists('request_target',$opts)) { - $opts['request_target'] = 'get ' . get_request_string($url); - } + if (! array_key_exists('request_target', $opts)) { + $opts['request_target'] = 'get ' . get_request_string($url); + } - @curl_setopt($ch, CURLOPT_HEADER, true); - @curl_setopt($ch, CURLINFO_HEADER_OUT, true); - @curl_setopt($ch, CURLOPT_CAINFO, get_capath()); - @curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true); - @curl_setopt($ch, CURLOPT_RETURNTRANSFER,true); - @curl_setopt($ch, CURLOPT_ENCODING, ''); - - $ciphers = @get_config('system','curl_ssl_ciphers'); - if($ciphers) - @curl_setopt($ch, CURLOPT_SSL_CIPHER_LIST, $ciphers); + @curl_setopt($ch, CURLOPT_HEADER, true); + @curl_setopt($ch, CURLINFO_HEADER_OUT, true); + @curl_setopt($ch, CURLOPT_CAINFO, get_capath()); + @curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true); + @curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + @curl_setopt($ch, CURLOPT_ENCODING, ''); - if(x($opts,'filep')) { - @curl_setopt($ch, CURLOPT_FILE, $opts['filep']); - @curl_setopt($ch, CURLOPT_HEADER, false); - @curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); - $passthru = true; - } + $ciphers = @get_config('system', 'curl_ssl_ciphers'); + if ($ciphers) { + @curl_setopt($ch, CURLOPT_SSL_CIPHER_LIST, $ciphers); + } - if (x($opts,'useragent')) { - @curl_setopt($ch, CURLOPT_USERAGENT, $opts['useragent']); - } - else { - @curl_setopt($ch, CURLOPT_USERAGENT, "Mozilla/5.0 (compatible; zot)"); - } + if (x($opts, 'filep')) { + @curl_setopt($ch, CURLOPT_FILE, $opts['filep']); + @curl_setopt($ch, CURLOPT_HEADER, false); + @curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); + $passthru = true; + } - if(x($opts,'upload')) - @curl_setopt($ch, CURLOPT_UPLOAD, $opts['upload']); + if (x($opts, 'useragent')) { + @curl_setopt($ch, CURLOPT_USERAGENT, $opts['useragent']); + } else { + @curl_setopt($ch, CURLOPT_USERAGENT, "Mozilla/5.0 (compatible; zot)"); + } - if(x($opts,'infile')) - @curl_setopt($ch, CURLOPT_INFILE, $opts['infile']); + if (x($opts, 'upload')) { + @curl_setopt($ch, CURLOPT_UPLOAD, $opts['upload']); + } - if(x($opts,'infilesize')) - @curl_setopt($ch, CURLOPT_INFILESIZE, $opts['infilesize']); + if (x($opts, 'infile')) { + @curl_setopt($ch, CURLOPT_INFILE, $opts['infile']); + } - if(x($opts,'readfunc')) - @curl_setopt($ch, CURLOPT_READFUNCTION, $opts['readfunc']); + if (x($opts, 'infilesize')) { + @curl_setopt($ch, CURLOPT_INFILESIZE, $opts['infilesize']); + } - // When using the session option and fetching from our own site, - // append the PHPSESSID cookie to any existing headers. - // Don't add to $opts['headers'] so that the cookie does not get - // sent to other sites via redirects + if (x($opts, 'readfunc')) { + @curl_setopt($ch, CURLOPT_READFUNCTION, $opts['readfunc']); + } - $instance_headers = ((array_key_exists('headers',$opts) && is_array($opts['headers'])) ? $opts['headers'] : []); + // When using the session option and fetching from our own site, + // append the PHPSESSID cookie to any existing headers. + // Don't add to $opts['headers'] so that the cookie does not get + // sent to other sites via redirects - if(x($opts,'session')) { - if(strpos($url,z_root()) === 0) { - $instance_headers[] = 'Cookie: PHPSESSID=' . session_id(); - } - } - if($instance_headers) - @curl_setopt($ch, CURLOPT_HTTPHEADER, $instance_headers); + $instance_headers = ((array_key_exists('headers', $opts) && is_array($opts['headers'])) ? $opts['headers'] : []); + + if (x($opts, 'session')) { + if (strpos($url, z_root()) === 0) { + $instance_headers[] = 'Cookie: PHPSESSID=' . session_id(); + } + } + if ($instance_headers) { + @curl_setopt($ch, CURLOPT_HTTPHEADER, $instance_headers); + } - if(x($opts,'nobody')) - @curl_setopt($ch, CURLOPT_NOBODY, $opts['nobody']); + if (x($opts, 'nobody')) { + @curl_setopt($ch, CURLOPT_NOBODY, $opts['nobody']); + } - if(x($opts,'custom')) - @curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $opts['custom']); + if (x($opts, 'custom')) { + @curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $opts['custom']); + } - if(x($opts,'timeout') && intval($opts['timeout'])) { - @curl_setopt($ch, CURLOPT_TIMEOUT, intval($opts['timeout'])); - } - else { - $curl_time = intval(get_config('system','curl_timeout',60)); - @curl_setopt($ch, CURLOPT_TIMEOUT, $curl_time); - } + if (x($opts, 'timeout') && intval($opts['timeout'])) { + @curl_setopt($ch, CURLOPT_TIMEOUT, intval($opts['timeout'])); + } else { + $curl_time = intval(get_config('system', 'curl_timeout', 60)); + @curl_setopt($ch, CURLOPT_TIMEOUT, $curl_time); + } - if(x($opts,'connecttimeout') && intval($opts['connecttimeout'])) { - @curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, intval($opts['connecttimeout'])); - } - else { - $curl_contime = intval(@get_config('system','curl_connecttimeout',60)); - @curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $curl_contime); - } + if (x($opts, 'connecttimeout') && intval($opts['connecttimeout'])) { + @curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, intval($opts['connecttimeout'])); + } else { + $curl_contime = intval(@get_config('system', 'curl_connecttimeout', 60)); + @curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $curl_contime); + } - if(x($opts,'http_auth')) { - // "username" . ':' . "password" - @curl_setopt($ch, CURLOPT_USERPWD, $opts['http_auth']); - } + if (x($opts, 'http_auth')) { + // "username" . ':' . "password" + @curl_setopt($ch, CURLOPT_USERPWD, $opts['http_auth']); + } - if(x($opts,'cookiejar')) - @curl_setopt($ch, CURLOPT_COOKIEJAR, $opts['cookiejar']); - if(x($opts,'cookiefile')) - @curl_setopt($ch, CURLOPT_COOKIEFILE, $opts['cookiefile']); + if (x($opts, 'cookiejar')) { + @curl_setopt($ch, CURLOPT_COOKIEJAR, $opts['cookiejar']); + } + if (x($opts, 'cookiefile')) { + @curl_setopt($ch, CURLOPT_COOKIEFILE, $opts['cookiefile']); + } - if(x($opts,'cookie')) - @curl_setopt($ch, CURLOPT_COOKIE, $opts['cookie']); + if (x($opts, 'cookie')) { + @curl_setopt($ch, CURLOPT_COOKIE, $opts['cookie']); + } - @curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, - ((x($opts,'novalidate') && intval($opts['novalidate'])) ? false : true)); + @curl_setopt( + $ch, + CURLOPT_SSL_VERIFYPEER, + ((x($opts, 'novalidate') && intval($opts['novalidate'])) ? false : true) + ); - $prx = @get_config('system','proxy'); - if(strlen($prx)) { - @curl_setopt($ch, CURLOPT_HTTPPROXYTUNNEL, 1); - @curl_setopt($ch, CURLOPT_PROXY, $prx); - $prxusr = @get_config('system','proxyuser'); - if(strlen($prxusr)) - @curl_setopt($ch, CURLOPT_PROXYUSERPWD, $prxusr); - } - if($binary) - @curl_setopt($ch, CURLOPT_BINARYTRANSFER,1); + $prx = @get_config('system', 'proxy'); + if (strlen($prx)) { + @curl_setopt($ch, CURLOPT_HTTPPROXYTUNNEL, 1); + @curl_setopt($ch, CURLOPT_PROXY, $prx); + $prxusr = @get_config('system', 'proxyuser'); + if (strlen($prxusr)) { + @curl_setopt($ch, CURLOPT_PROXYUSERPWD, $prxusr); + } + } + if ($binary) { + @curl_setopt($ch, CURLOPT_BINARYTRANSFER, 1); + } - // don't let curl abort the entire application' - // if it throws any errors. + // don't let curl abort the entire application' + // if it throws any errors. - $s = @curl_exec($ch); + $s = @curl_exec($ch); - $base = $s; - $curl_info = @curl_getinfo($ch); - $http_code = $curl_info['http_code']; - //logger('fetch_url:' . $http_code . ' data: ' . $s); - $header = ''; + $base = $s; + $curl_info = @curl_getinfo($ch); + $http_code = $curl_info['http_code']; + //logger('fetch_url:' . $http_code . ' data: ' . $s); + $header = ''; - // For file redirects, set curl to follow location (indicated by $passthru). - // Then just return success or failure. - - if ($passthru) { - if ($http_code >= 200 && $http_code < 300) { - $ret['success'] = true; - } - $ret['return_code'] = $http_code; - @curl_close($ch); - return $ret; - } - + // For file redirects, set curl to follow location (indicated by $passthru). + // Then just return success or failure. - // Pull out multiple headers, e.g. proxy and continuation headers - // allow for HTTP/2.x without fixing code + if ($passthru) { + if ($http_code >= 200 && $http_code < 300) { + $ret['success'] = true; + } + $ret['return_code'] = $http_code; + @curl_close($ch); + return $ret; + } - while(preg_match('/^HTTP\/[1-3][\.0-9]* [1-5][0-9][0-9]/',$base)) { - $chunk = substr($base,0,strpos($base,"\r\n\r\n")+4); - $header .= $chunk; - $base = substr($base,strlen($chunk)); - } - if($http_code == 301 || $http_code == 302 || $http_code == 303 || $http_code == 307 || $http_code == 308) { - $matches = []; - preg_match('/(Location:|URI:)(.*?)\n/i', $header, $matches); - $newurl = trim(array_pop($matches)); - if(strpos($newurl,'/') === 0) { - // We received a redirect to a relative path. - // Find the base component of the original url and re-assemble it with the new location - $base = @parse_url($url); - if ($base) { - unset($base['path']); unset($base['query']); unset($base['fragment']); - $newurl = unparse_url($base) . $newurl; - } - } - if ($newurl) { - @curl_close($ch); - return z_fetch_url($newurl,$binary,++$redirects,$opts); - } - } + // Pull out multiple headers, e.g. proxy and continuation headers + // allow for HTTP/2.x without fixing code - $rc = intval($http_code); - $ret['return_code'] = $rc; - $ret['success'] = (($rc >= 200 && $rc <= 299) ? true : false); - if(! $ret['success']) { - $ret['error'] = curl_error($ch); - $ret['debug'] = $curl_info; - $dbg = [ - 'url' => $ret['debug']['url'], - 'content_type' => $ret['debug']['content_type'], - 'error_code' => ((isset($ret['debug']['error_code'])) ? $ret['debug']['error_code'] : 0), - 'request_header' => $ret['debug']['request_header'] - ]; - logger('z_fetch_url: error: ' . $url . ': ' . $ret['error'], LOGGER_DEBUG); - logger('z_fetch_url: debug: ' . print_r($dbg,true), LOGGER_DATA); - } - $ret['body'] = substr($s,strlen($header)); - $ret['header'] = $header; - $ret['request_target'] = $opts['request_target']; + while (preg_match('/^HTTP\/[1-3][\.0-9]* [1-5][0-9][0-9]/', $base)) { + $chunk = substr($base, 0, strpos($base, "\r\n\r\n") + 4); + $header .= $chunk; + $base = substr($base, strlen($chunk)); + } - if(x($opts,'debug')) { - $ret['debug'] = $curl_info; - } + if ($http_code == 301 || $http_code == 302 || $http_code == 303 || $http_code == 307 || $http_code == 308) { + $matches = []; + preg_match('/(Location:|URI:)(.*?)\n/i', $header, $matches); + $newurl = trim(array_pop($matches)); + if (strpos($newurl, '/') === 0) { + // We received a redirect to a relative path. + // Find the base component of the original url and re-assemble it with the new location + $base = @parse_url($url); + if ($base) { + unset($base['path']); + unset($base['query']); + unset($base['fragment']); + $newurl = unparse_url($base) . $newurl; + } + } + if ($newurl) { + @curl_close($ch); + return z_fetch_url($newurl, $binary, ++$redirects, $opts); + } + } - @curl_close($ch); - return($ret); + $rc = intval($http_code); + $ret['return_code'] = $rc; + $ret['success'] = (($rc >= 200 && $rc <= 299) ? true : false); + if (! $ret['success']) { + $ret['error'] = curl_error($ch); + $ret['debug'] = $curl_info; + $dbg = [ + 'url' => $ret['debug']['url'], + 'content_type' => $ret['debug']['content_type'], + 'error_code' => ((isset($ret['debug']['error_code'])) ? $ret['debug']['error_code'] : 0), + 'request_header' => $ret['debug']['request_header'] + ]; + logger('z_fetch_url: error: ' . $url . ': ' . $ret['error'], LOGGER_DEBUG); + logger('z_fetch_url: debug: ' . print_r($dbg, true), LOGGER_DATA); + } + $ret['body'] = substr($s, strlen($header)); + $ret['header'] = $header; + $ret['request_target'] = $opts['request_target']; + + if (x($opts, 'debug')) { + $ret['debug'] = $curl_info; + } + + @curl_close($ch); + return($ret); } @@ -279,237 +296,250 @@ function z_fetch_url($url, $binary = false, $redirects = 0, $opts = []) { * * \e string \b body => content * * \e string \b debug => from curl_info() */ -function z_post_url($url, $params, $redirects = 0, $opts = []) { +function z_post_url($url, $params, $redirects = 0, $opts = []) +{ -// logger('url: ' . $url); -// logger('params: ' . print_r($params,true)); -// logger('opts: ' . print_r($opts,true)); +// logger('url: ' . $url); +// logger('params: ' . print_r($params,true)); +// logger('opts: ' . print_r($opts,true)); - $ret = array('return_code' => 0, 'success' => false, 'header' => "", 'body' => ""); + $ret = array('return_code' => 0, 'success' => false, 'header' => "", 'body' => ""); - $ch = curl_init($url); - if(($redirects > 8) || (! $ch)) - return $ret; + $ch = curl_init($url); + if (($redirects > 8) || (! $ch)) { + return $ret; + } - if(! array_key_exists('request_target',$opts)) { - $opts['request_target'] = 'post ' . get_request_string($url); - } + if (! array_key_exists('request_target', $opts)) { + $opts['request_target'] = 'post ' . get_request_string($url); + } - @curl_setopt($ch, CURLOPT_HEADER, true); - @curl_setopt($ch, CURLINFO_HEADER_OUT, true); - @curl_setopt($ch, CURLOPT_CAINFO, get_capath()); - @curl_setopt($ch, CURLOPT_RETURNTRANSFER,true); - @curl_setopt($ch, CURLOPT_POST,1); - @curl_setopt($ch, CURLOPT_POSTFIELDS,$params); - @curl_setopt($ch, CURLOPT_ENCODING, ''); - - $ciphers = @get_config('system','curl_ssl_ciphers'); - if($ciphers) - @curl_setopt($ch, CURLOPT_SSL_CIPHER_LIST, $ciphers); + @curl_setopt($ch, CURLOPT_HEADER, true); + @curl_setopt($ch, CURLINFO_HEADER_OUT, true); + @curl_setopt($ch, CURLOPT_CAINFO, get_capath()); + @curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + @curl_setopt($ch, CURLOPT_POST, 1); + @curl_setopt($ch, CURLOPT_POSTFIELDS, $params); + @curl_setopt($ch, CURLOPT_ENCODING, ''); - if(x($opts,'filep')) { - @curl_setopt($ch, CURLOPT_FILE, $opts['filep']); - @curl_setopt($ch, CURLOPT_HEADER, false); - } + $ciphers = @get_config('system', 'curl_ssl_ciphers'); + if ($ciphers) { + @curl_setopt($ch, CURLOPT_SSL_CIPHER_LIST, $ciphers); + } - if (x($opts,'useragent')) { - @curl_setopt($ch, CURLOPT_USERAGENT, $opts['useragent']); - } - else { - @curl_setopt($ch, CURLOPT_USERAGENT, "Mozilla/5.0 (compatible; zot)"); - } + if (x($opts, 'filep')) { + @curl_setopt($ch, CURLOPT_FILE, $opts['filep']); + @curl_setopt($ch, CURLOPT_HEADER, false); + } + + if (x($opts, 'useragent')) { + @curl_setopt($ch, CURLOPT_USERAGENT, $opts['useragent']); + } else { + @curl_setopt($ch, CURLOPT_USERAGENT, "Mozilla/5.0 (compatible; zot)"); + } - $instance_headers = ((array_key_exists('headers',$opts) && is_array($opts['headers'])) ? $opts['headers'] : []); + $instance_headers = ((array_key_exists('headers', $opts) && is_array($opts['headers'])) ? $opts['headers'] : []); - if(x($opts,'session')) { - if(strpos($url,z_root()) === 0) { - $instance_headers[] = 'Cookie: PHPSESSID=' . session_id(); - } - } - if($instance_headers) - @curl_setopt($ch, CURLOPT_HTTPHEADER, $instance_headers); + if (x($opts, 'session')) { + if (strpos($url, z_root()) === 0) { + $instance_headers[] = 'Cookie: PHPSESSID=' . session_id(); + } + } + if ($instance_headers) { + @curl_setopt($ch, CURLOPT_HTTPHEADER, $instance_headers); + } - if(x($opts,'nobody')) - @curl_setopt($ch, CURLOPT_NOBODY, $opts['nobody']); + if (x($opts, 'nobody')) { + @curl_setopt($ch, CURLOPT_NOBODY, $opts['nobody']); + } - if(x($opts,'custom')) { - @curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $opts['custom']); - @curl_setopt($ch, CURLOPT_POST,0); - } + if (x($opts, 'custom')) { + @curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $opts['custom']); + @curl_setopt($ch, CURLOPT_POST, 0); + } - if(x($opts,'timeout') && intval($opts['timeout'])) { - @curl_setopt($ch, CURLOPT_TIMEOUT, intval($opts['timeout'])); - } - else { - $curl_time = intval(@get_config('system','curl_post_timeout',90)); - @curl_setopt($ch, CURLOPT_TIMEOUT, $curl_time); - } + if (x($opts, 'timeout') && intval($opts['timeout'])) { + @curl_setopt($ch, CURLOPT_TIMEOUT, intval($opts['timeout'])); + } else { + $curl_time = intval(@get_config('system', 'curl_post_timeout', 90)); + @curl_setopt($ch, CURLOPT_TIMEOUT, $curl_time); + } - if(x($opts,'connecttimeout') && intval($opts['connecttimeout'])) { - @curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, intval($opts['connecttimeout'])); - } - else { - $curl_contime = intval(@get_config('system','curl_post_connecttimeout',90)); - @curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $curl_contime); - } + if (x($opts, 'connecttimeout') && intval($opts['connecttimeout'])) { + @curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, intval($opts['connecttimeout'])); + } else { + $curl_contime = intval(@get_config('system', 'curl_post_connecttimeout', 90)); + @curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $curl_contime); + } - if(x($opts,'http_auth')) { - // "username" . ':' . "password" - @curl_setopt($ch, CURLOPT_USERPWD, $opts['http_auth']); - } + if (x($opts, 'http_auth')) { + // "username" . ':' . "password" + @curl_setopt($ch, CURLOPT_USERPWD, $opts['http_auth']); + } - if(x($opts,'cookiejar')) - @curl_setopt($ch, CURLOPT_COOKIEJAR, $opts['cookiejar']); - if(x($opts,'cookiefile')) - @curl_setopt($ch, CURLOPT_COOKIEFILE, $opts['cookiefile']); - if(x($opts,'cookie')) - @curl_setopt($ch, CURLOPT_COOKIE, $opts['cookie']); + if (x($opts, 'cookiejar')) { + @curl_setopt($ch, CURLOPT_COOKIEJAR, $opts['cookiejar']); + } + if (x($opts, 'cookiefile')) { + @curl_setopt($ch, CURLOPT_COOKIEFILE, $opts['cookiefile']); + } + if (x($opts, 'cookie')) { + @curl_setopt($ch, CURLOPT_COOKIE, $opts['cookie']); + } - @curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, - ((x($opts,'novalidate') && intval($opts['novalidate'])) ? false : true)); + @curl_setopt( + $ch, + CURLOPT_SSL_VERIFYPEER, + ((x($opts, 'novalidate') && intval($opts['novalidate'])) ? false : true) + ); - $prx = get_config('system','proxy'); - if(strlen($prx)) { - @curl_setopt($ch, CURLOPT_HTTPPROXYTUNNEL, 1); - @curl_setopt($ch, CURLOPT_PROXY, $prx); - $prxusr = get_config('system','proxyuser'); - if(strlen($prxusr)) - @curl_setopt($ch, CURLOPT_PROXYUSERPWD, $prxusr); - } + $prx = get_config('system', 'proxy'); + if (strlen($prx)) { + @curl_setopt($ch, CURLOPT_HTTPPROXYTUNNEL, 1); + @curl_setopt($ch, CURLOPT_PROXY, $prx); + $prxusr = get_config('system', 'proxyuser'); + if (strlen($prxusr)) { + @curl_setopt($ch, CURLOPT_PROXYUSERPWD, $prxusr); + } + } - // don't let curl abort the entire application - // if it throws any errors. + // don't let curl abort the entire application + // if it throws any errors. - $s = @curl_exec($ch); + $s = @curl_exec($ch); - $base = $s; - $curl_info = @curl_getinfo($ch); - $http_code = $curl_info['http_code']; + $base = $s; + $curl_info = @curl_getinfo($ch); + $http_code = $curl_info['http_code']; - $header = ''; + $header = ''; - // Pull out multiple headers, e.g. proxy and continuation headers - // allow for HTTP/2.x without fixing code + // Pull out multiple headers, e.g. proxy and continuation headers + // allow for HTTP/2.x without fixing code - while(preg_match('/^HTTP\/[1-3][\.0-9]* [1-5][0-9][0-9]/',$base)) { - $chunk = substr($base,0,strpos($base,"\r\n\r\n")+4); - $header .= $chunk; - $base = substr($base,strlen($chunk)); - } + while (preg_match('/^HTTP\/[1-3][\.0-9]* [1-5][0-9][0-9]/', $base)) { + $chunk = substr($base, 0, strpos($base, "\r\n\r\n") + 4); + $header .= $chunk; + $base = substr($base, strlen($chunk)); + } - // would somebody take lighttpd and just shoot it? + // would somebody take lighttpd and just shoot it? - if($http_code == 417) { - curl_close($ch); - if($opts) { - if($opts['headers']) - $opts['headers'][] = 'Expect:'; - else - $opts['headers'] = array('Expect:'); - } - else - $opts = array('headers' => array('Expect:')); - return z_post_url($url,$params,++$redirects,$opts); - } + if ($http_code == 417) { + curl_close($ch); + if ($opts) { + if ($opts['headers']) { + $opts['headers'][] = 'Expect:'; + } else { + $opts['headers'] = array('Expect:'); + } + } else { + $opts = array('headers' => array('Expect:')); + } + return z_post_url($url, $params, ++$redirects, $opts); + } - if($http_code == 301 || $http_code == 302 || $http_code == 303 || $http_code == 307 || $http_code == 308) { - $matches = []; - preg_match('/(Location:|URI:)(.*?)\n/', $header, $matches); - $newurl = trim(array_pop($matches)); - if(strpos($newurl,'/') === 0) { - // We received a redirect to a relative path. - // Find the base component of the original url and re-assemble it with the new location - $base = @parse_url($url); - if ($base) { - unset($base['path']); unset($base['query']); unset($base['fragment']); - $newurl = unparse_url($base) . $newurl; - } - } - if ($newurl) { - curl_close($ch); - if($http_code == 303) { - return z_fetch_url($newurl,false,++$redirects,$opts); - } - else { - return z_post_url($newurl,$params,++$redirects,$opts); - } - } - } - $rc = intval($http_code); - $ret['return_code'] = $rc; - $ret['success'] = (($rc >= 200 && $rc <= 299) ? true : false); - if(! $ret['success']) { - $ret['error'] = curl_error($ch); - $ret['debug'] = $curl_info; - $dbg = [ - 'url' => $ret['debug']['url'], - 'content_type' => $ret['debug']['content_type'], - 'error_code' => $ret['debug']['error_code'], - 'request_header' => $ret['debug']['request_header'] - ]; - logger('z_post_url: error: ' . $url . ': ' . $ret['error'], LOGGER_DEBUG); - logger('z_post_url: debug: ' . print_r($dbg,true), LOGGER_DATA); - } + if ($http_code == 301 || $http_code == 302 || $http_code == 303 || $http_code == 307 || $http_code == 308) { + $matches = []; + preg_match('/(Location:|URI:)(.*?)\n/', $header, $matches); + $newurl = trim(array_pop($matches)); + if (strpos($newurl, '/') === 0) { + // We received a redirect to a relative path. + // Find the base component of the original url and re-assemble it with the new location + $base = @parse_url($url); + if ($base) { + unset($base['path']); + unset($base['query']); + unset($base['fragment']); + $newurl = unparse_url($base) . $newurl; + } + } + if ($newurl) { + curl_close($ch); + if ($http_code == 303) { + return z_fetch_url($newurl, false, ++$redirects, $opts); + } else { + return z_post_url($newurl, $params, ++$redirects, $opts); + } + } + } + $rc = intval($http_code); + $ret['return_code'] = $rc; + $ret['success'] = (($rc >= 200 && $rc <= 299) ? true : false); + if (! $ret['success']) { + $ret['error'] = curl_error($ch); + $ret['debug'] = $curl_info; + $dbg = [ + 'url' => $ret['debug']['url'], + 'content_type' => $ret['debug']['content_type'], + 'error_code' => $ret['debug']['error_code'], + 'request_header' => $ret['debug']['request_header'] + ]; + logger('z_post_url: error: ' . $url . ': ' . $ret['error'], LOGGER_DEBUG); + logger('z_post_url: debug: ' . print_r($dbg, true), LOGGER_DATA); + } - $ret['body'] = substr($s, strlen($header)); - $ret['header'] = $header; - $ret['request_target'] = $opts['request_target']; + $ret['body'] = substr($s, strlen($header)); + $ret['header'] = $header; + $ret['request_target'] = $opts['request_target']; - if(x($opts,'debug')) { - $ret['debug'] = $curl_info; - } + if (x($opts, 'debug')) { + $ret['debug'] = $curl_info; + } - curl_close($ch); - return($ret); + curl_close($ch); + return($ret); } -function z_curl_error($ret) { - $output = EMPTY_STR; - if (isset($ret['debug'])) { - $output .= datetime_convert() . EOL; - $output .= t('url: ') . $ret['debug']['url'] . EOL; - $output .= t('error_code: ') . $ret['debug']['error_code'] . EOL; - $output .= t('error_string: ') . $ret['error'] . EOL; - $output .= t('content-type: ') . $ret['debug']['content_type'] . EOL; - } - return $output; +function z_curl_error($ret) +{ + $output = EMPTY_STR; + if (isset($ret['debug'])) { + $output .= datetime_convert() . EOL; + $output .= t('url: ') . $ret['debug']['url'] . EOL; + $output .= t('error_code: ') . $ret['debug']['error_code'] . EOL; + $output .= t('error_string: ') . $ret['error'] . EOL; + $output .= t('content-type: ') . $ret['debug']['content_type'] . EOL; + } + return $output; } -function json_return_and_die($x, $content_type = 'application/json', $debug = false) { - header("Content-type: $content_type"); - if ($debug) { - logger('returned_json: ' . json_encode($x,JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES), LOGGER_DATA); - } - echo json_encode($x); - killme(); +function json_return_and_die($x, $content_type = 'application/json', $debug = false) +{ + header("Content-type: $content_type"); + if ($debug) { + logger('returned_json: ' . json_encode($x, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES), LOGGER_DATA); + } + echo json_encode($x); + killme(); } -function as_return_and_die($obj,$channel) { +function as_return_and_die($obj, $channel) +{ - $x = array_merge(['@context' => [ - ACTIVITYSTREAMS_JSONLD_REV, - 'https://w3id.org/security/v1', - Activity::ap_schema() - ]], $obj ); + $x = array_merge(['@context' => [ + ACTIVITYSTREAMS_JSONLD_REV, + 'https://w3id.org/security/v1', + Activity::ap_schema() + ]], $obj); - $headers = []; - $headers['Content-Type'] = 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"' ; - $x['signature'] = LDSignatures::sign($x,$channel); - $ret = json_encode($x, JSON_UNESCAPED_SLASHES); - logger('data: ' . jindent($ret), LOGGER_DATA); - $headers['Date'] = datetime_convert('UTC','UTC', 'now', 'D, d M Y H:i:s \\G\\M\\T'); - $headers['Digest'] = HTTPSig::generate_digest_header($ret); - $headers['(request-target)'] = strtolower($_SERVER['REQUEST_METHOD']) . ' ' . $_SERVER['REQUEST_URI']; + $headers = []; + $headers['Content-Type'] = 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"' ; + $x['signature'] = LDSignatures::sign($x, $channel); + $ret = json_encode($x, JSON_UNESCAPED_SLASHES); + logger('data: ' . jindent($ret), LOGGER_DATA); + $headers['Date'] = datetime_convert('UTC', 'UTC', 'now', 'D, d M Y H:i:s \\G\\M\\T'); + $headers['Digest'] = HTTPSig::generate_digest_header($ret); + $headers['(request-target)'] = strtolower($_SERVER['REQUEST_METHOD']) . ' ' . $_SERVER['REQUEST_URI']; - $h = HTTPSig::create_sig($headers,$channel['channel_prvkey'],channel_url($channel)); - HTTPSig::set_headers($h); - - echo $ret; - killme(); + $h = HTTPSig::create_sig($headers, $channel['channel_prvkey'], channel_url($channel)); + HTTPSig::set_headers($h); + echo $ret; + killme(); } @@ -521,14 +551,17 @@ function as_return_and_die($obj,$channel) { * @param string $msg * optional message */ -function http_status($val, $msg = '') { - if ($val >= 400) - $msg = (($msg) ? $msg : 'Error'); - if ($val >= 200 && $val < 300) - $msg = (($msg) ? $msg : 'OK'); +function http_status($val, $msg = '') +{ + if ($val >= 400) { + $msg = (($msg) ? $msg : 'Error'); + } + if ($val >= 200 && $val < 300) { + $msg = (($msg) ? $msg : 'OK'); + } - logger(\App::$query_string . ':' . $val . ' ' . $msg); - header($_SERVER['SERVER_PROTOCOL'] . ' ' . $val . ' ' . $msg); + logger(App::$query_string . ':' . $val . ' ' . $msg); + header($_SERVER['SERVER_PROTOCOL'] . ' ' . $val . ' ' . $msg); } @@ -541,9 +574,10 @@ function http_status($val, $msg = '') { * optional message * @return void does not return, process is terminated */ -function http_status_exit($val, $msg = '') { - http_status($val, $msg); - killme(); +function http_status_exit($val, $msg = '') +{ + http_status($val, $msg); + killme(); } /* @@ -551,19 +585,20 @@ function http_status_exit($val, $msg = '') { * Takes the output of parse_url and builds a URL from it * */ - -function unparse_url($parsed_url) { - $scheme = isset($parsed_url['scheme']) ? $parsed_url['scheme'] . '://' : ''; - $host = isset($parsed_url['host']) ? $parsed_url['host'] : ''; - $port = ((isset($parsed_url['port']) && intval($parsed_url['port'])) ? ':' . intval($parsed_url['port']) : ''); - $user = isset($parsed_url['user']) ? $parsed_url['user'] : ''; - $pass = isset($parsed_url['pass']) ? ':' . $parsed_url['pass'] : ''; - $pass = ($user || $pass) ? "$pass@" : ''; - $path = isset($parsed_url['path']) ? $parsed_url['path'] : ''; - $query = isset($parsed_url['query']) ? '?' . $parsed_url['query'] : ''; - $fragment = isset($parsed_url['fragment']) ? '#' . $parsed_url['fragment'] : ''; - return "$scheme$user$pass$host$port$path$query$fragment"; -} + +function unparse_url($parsed_url) +{ + $scheme = isset($parsed_url['scheme']) ? $parsed_url['scheme'] . '://' : ''; + $host = isset($parsed_url['host']) ? $parsed_url['host'] : ''; + $port = ((isset($parsed_url['port']) && intval($parsed_url['port'])) ? ':' . intval($parsed_url['port']) : ''); + $user = isset($parsed_url['user']) ? $parsed_url['user'] : ''; + $pass = isset($parsed_url['pass']) ? ':' . $parsed_url['pass'] : ''; + $pass = ($user || $pass) ? "$pass@" : ''; + $path = isset($parsed_url['path']) ? $parsed_url['path'] : ''; + $query = isset($parsed_url['query']) ? '?' . $parsed_url['query'] : ''; + $fragment = isset($parsed_url['fragment']) ? '#' . $parsed_url['fragment'] : ''; + return "$scheme$user$pass$host$port$path$query$fragment"; +} @@ -576,72 +611,80 @@ function unparse_url($parsed_url) { * @param[in,out] int $recursion_depth * @return NULL|string|array */ -function convert_xml_element_to_array($xml_element, &$recursion_depth=0) { +function convert_xml_element_to_array($xml_element, &$recursion_depth = 0) +{ - // If we're getting too deep, bail out - if ($recursion_depth > 512) { - return(null); - } + // If we're getting too deep, bail out + if ($recursion_depth > 512) { + return(null); + } - if (!is_string($xml_element) && - !is_array($xml_element) && - (get_class($xml_element) == 'SimpleXMLElement')) { - $xml_element_copy = $xml_element; - $xml_element = get_object_vars($xml_element); - } + if ( + !is_string($xml_element) && + !is_array($xml_element) && + (get_class($xml_element) == 'SimpleXMLElement') + ) { + $xml_element_copy = $xml_element; + $xml_element = get_object_vars($xml_element); + } - if (is_array($xml_element)) { - $result_array = []; - if (count($xml_element) <= 0) { - return (trim(strval($xml_element_copy))); - } + if (is_array($xml_element)) { + $result_array = []; + if (count($xml_element) <= 0) { + return (trim(strval($xml_element_copy))); + } - foreach($xml_element as $key=>$value) { - $recursion_depth++; - $result_array[strtolower($key)] = - convert_xml_element_to_array($value, $recursion_depth); - $recursion_depth--; - } - if ($recursion_depth == 0) { - $temp_array = $result_array; - $result_array = array( - strtolower($xml_element_copy->getName()) => $temp_array, - ); - } + foreach ($xml_element as $key => $value) { + $recursion_depth++; + $result_array[strtolower($key)] = + convert_xml_element_to_array($value, $recursion_depth); + $recursion_depth--; + } + if ($recursion_depth == 0) { + $temp_array = $result_array; + $result_array = array( + strtolower($xml_element_copy->getName()) => $temp_array, + ); + } - return ($result_array); - } else { - return (trim(strval($xml_element))); - } + return ($result_array); + } else { + return (trim(strval($xml_element))); + } } -function z_dns_check($h,$check_mx = 0) { +function z_dns_check($h, $check_mx = 0) +{ - // dns_get_record() has issues on some platforms - // so allow somebody to ignore it completely - // Use config values from memory as this can be called during setup - // before a database or even any config structure exists. + // dns_get_record() has issues on some platforms + // so allow somebody to ignore it completely + // Use config values from memory as this can be called during setup + // before a database or even any config structure exists. - if(is_array(App::$config) && array_path_exists('system/do_not_check_dns', App::$config) && App::$config['system']['do_not_check_dns']) - return true; + if (is_array(App::$config) && array_path_exists('system/do_not_check_dns', App::$config) && App::$config['system']['do_not_check_dns']) { + return true; + } - // This will match either Windows or Mac ('Darwin') - if(stripos(PHP_OS,'win') !== false) - return true; + // This will match either Windows or Mac ('Darwin') + if (stripos(PHP_OS, 'win') !== false) { + return true; + } - // BSD variants have dns_get_record() but it only works reliably without any options - if(stripos(PHP_OS,'bsd') !== false) - return((@dns_get_record($h) || filter_var($h, FILTER_VALIDATE_IP)) ? true : false); + // BSD variants have dns_get_record() but it only works reliably without any options + if (stripos(PHP_OS, 'bsd') !== false) { + return((@dns_get_record($h) || filter_var($h, FILTER_VALIDATE_IP)) ? true : false); + } - // Otherwise we will assume dns_get_record() works as documented + // Otherwise we will assume dns_get_record() works as documented - $opts = DNS_A + DNS_CNAME + DNS_AAAA; - if($check_mx) - $opts += DNS_MX; + $opts = DNS_A + DNS_CNAME + DNS_AAAA; + if ($check_mx) { + $opts += DNS_MX; + } - return((@dns_get_record($h,$opts) || filter_var($h, FILTER_VALIDATE_IP)) ? true : false); + return((@dns_get_record($h, $opts) || filter_var($h, FILTER_VALIDATE_IP)) ? true : false); } /** @@ -653,47 +696,53 @@ function z_dns_check($h,$check_mx = 0) { * @see z_dns_check() * * @param[in,out] string $url URL to check - * @return boolean Return true if it's OK, false if something is wrong with it + * @return bool Return true if it's OK, false if something is wrong with it */ -function validate_url(&$url) { +function validate_url(&$url) +{ - // no naked subdomains (allow localhost for tests) - if(strpos($url, '.') === false && strpos($url, '/localhost/') === false) - return false; + // no naked subdomains (allow localhost for tests) + if (strpos($url, '.') === false && strpos($url, '/localhost/') === false) { + return false; + } - if(substr($url, 0, 4) != 'http') - $url = 'http://' . $url; + if (substr($url, 0, 4) != 'http') { + $url = 'http://' . $url; + } - $h = @parse_url($url); + $h = @parse_url($url); - if(($h) && z_dns_check($h['host'])) { - return true; - } + if (($h) && z_dns_check($h['host'])) { + return true; + } - return false; + return false; } /** * @brief Checks that email is an actual resolvable internet address. * * @param string $addr - * @return boolean + * @return bool */ -function validate_email($addr) { +function validate_email($addr) +{ - if(get_config('system', 'disable_email_validation')) - return true; + if (get_config('system', 'disable_email_validation')) { + return true; + } - if(! strpos($addr, '@')) - return false; + if (! strpos($addr, '@')) { + return false; + } - $h = substr($addr, strpos($addr, '@') + 1); + $h = substr($addr, strpos($addr, '@') + 1); - if(($h) && z_dns_check($h, true)) { - return true; - } + if (($h) && z_dns_check($h, true)) { + return true; + } - return false; + return false; } /** @@ -702,93 +751,99 @@ function validate_email($addr) { * Compare against our list (wildcards allowed). * * @param string $email - * @return boolean Returns false if not allowed, true if allowed or if allowed list is + * @return bool Returns false if not allowed, true if allowed or if allowed list is * not configured. */ -function allowed_email($email) { - - $domain = strtolower(substr($email, strpos($email, '@') + 1)); - if(! $domain) - return false; - - $str_allowed = get_config('system', 'allowed_email'); - $str_not_allowed = get_config('system', 'not_allowed_email'); - - if(! $str_allowed && ! $str_not_allowed) - return true; - - $return = false; - $found_allowed = false; - $found_not_allowed = false; - - $fnmatch = function_exists('fnmatch'); - - $allowed = explode(',', $str_allowed); - - if(count($allowed)) { - foreach($allowed as $a) { - $pat = strtolower(trim($a)); - if(($fnmatch && fnmatch($pat,$email)) || ($pat == $domain)) { - $found_allowed = true; - break; - } - } - } - - $not_allowed = explode(',', $str_not_allowed); - - if(count($not_allowed)) { - foreach($not_allowed as $na) { - $pat = strtolower(trim($na)); - if(($fnmatch && fnmatch($pat,$email)) || ($pat == $domain)) { - $found_not_allowed = true; - break; - } - } - } - - if ($found_allowed) { - $return = true; - } elseif (!$str_allowed && !$found_not_allowed) { - $return = true; - } - - return $return; -} - - - -function parse_xml_string($s, $strict = true) { - if($strict) { - if(! strstr($s,'code . ' at ' . $err->line - . ':' . $err->column . ' : ' . $err->message, LOGGER_DATA); - } - libxml_clear_errors(); - } - - return $x; -} - - -function sxml2array ( $xmlObject, $out = array () ) +function allowed_email($email) { - foreach ( (array) $xmlObject as $index => $node ) - $out[$index] = ( is_object ( $node ) ) ? sxml2array ( $node ) : $node; + + $domain = strtolower(substr($email, strpos($email, '@') + 1)); + if (! $domain) { + return false; + } + + $str_allowed = get_config('system', 'allowed_email'); + $str_not_allowed = get_config('system', 'not_allowed_email'); + + if (! $str_allowed && ! $str_not_allowed) { + return true; + } + + $return = false; + $found_allowed = false; + $found_not_allowed = false; + + $fnmatch = function_exists('fnmatch'); + + $allowed = explode(',', $str_allowed); + + if (count($allowed)) { + foreach ($allowed as $a) { + $pat = strtolower(trim($a)); + if (($fnmatch && fnmatch($pat, $email)) || ($pat == $domain)) { + $found_allowed = true; + break; + } + } + } + + $not_allowed = explode(',', $str_not_allowed); + + if (count($not_allowed)) { + foreach ($not_allowed as $na) { + $pat = strtolower(trim($na)); + if (($fnmatch && fnmatch($pat, $email)) || ($pat == $domain)) { + $found_not_allowed = true; + break; + } + } + } + + if ($found_allowed) { + $return = true; + } elseif (!$str_allowed && !$found_not_allowed) { + $return = true; + } + + return $return; +} + + + +function parse_xml_string($s, $strict = true) +{ + if ($strict) { + if (! strstr($s, 'code . ' at ' . $err->line + . ':' . $err->column . ' : ' . $err->message, LOGGER_DATA); + } + libxml_clear_errors(); + } + + return $x; +} + + +function sxml2array($xmlObject, $out = array ()) +{ + foreach ((array) $xmlObject as $index => $node) { + $out[$index] = ( is_object($node) ) ? sxml2array($node) : $node; + } return $out; } @@ -797,219 +852,227 @@ function sxml2array ( $xmlObject, $out = array () ) * @brief xml2[] will convert the given XML text to an array in the XML structure. * * Link: http://www.bin-co.com/php/scripts/xml2array/ - * Portions significantly re-written by mike@macgirvin.com + * Portions significantly re-written by mike@macgirvin.com * (namespaces, lowercase tags, get_attribute default changed, more...) * * Examples: $array = xml2array(file_get_contents('feed.xml')); * $array = xml2array(file_get_contents('feed.xml', true, 1, 'attribute')); * * @param string $contents The XML text - * @param boolean $namespaces true or false include namespace information in the returned array as array elements + * @param bool $namespaces true or false include namespace information in the returned array as array elements * @param int $get_attributes 1 or 0. If this is 1 the function will get the attributes as well as the tag values - this results in a different array structure in the return value. * @param string $priority Can be 'tag' or 'attribute'. This will change the way the resulting array sturcture. For 'tag', the tags are given more importance. * * @return array The parsed XML in an array form. Use print_r() to see the resulting array structure. */ -function xml2array($contents, $namespaces = true, $get_attributes=1, $priority = 'attribute') { - if(!$contents) - return []; +function xml2array($contents, $namespaces = true, $get_attributes = 1, $priority = 'attribute') +{ + if (!$contents) { + return []; + } - if(!function_exists('xml_parser_create')) { - logger('xml2array: parser function missing'); - return []; - } + if (!function_exists('xml_parser_create')) { + logger('xml2array: parser function missing'); + return []; + } - libxml_use_internal_errors(true); - libxml_clear_errors(); + libxml_use_internal_errors(true); + libxml_clear_errors(); - if($namespaces) - $parser = @xml_parser_create_ns("UTF-8",':'); - else - $parser = @xml_parser_create(); + if ($namespaces) { + $parser = @xml_parser_create_ns("UTF-8", ':'); + } else { + $parser = @xml_parser_create(); + } - if(! $parser) { - logger('xml2array: xml_parser_create: no resource'); - return []; - } + if (! $parser) { + logger('xml2array: xml_parser_create: no resource'); + return []; + } - xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, "UTF-8"); - // http://minutillo.com/steve/weblog/2004/6/17/php-xml-and-character-encodings-a-tale-of-sadness-rage-and-data-loss - xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0); - xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1); - @xml_parse_into_struct($parser, trim($contents), $xml_values); - @xml_parser_free($parser); + xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, "UTF-8"); + // http://minutillo.com/steve/weblog/2004/6/17/php-xml-and-character-encodings-a-tale-of-sadness-rage-and-data-loss + xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0); + xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1); + @xml_parse_into_struct($parser, trim($contents), $xml_values); + @xml_parser_free($parser); - if(! $xml_values) { - logger('xml2array: libxml: parse error: ' . $contents, LOGGER_DATA); - foreach(libxml_get_errors() as $err) - logger('libxml: parse: ' . $err->code . " at " . $err->line . ":" . $err->column . " : " . $err->message, LOGGER_DATA); - libxml_clear_errors(); + if (! $xml_values) { + logger('xml2array: libxml: parse error: ' . $contents, LOGGER_DATA); + foreach (libxml_get_errors() as $err) { + logger('libxml: parse: ' . $err->code . " at " . $err->line . ":" . $err->column . " : " . $err->message, LOGGER_DATA); + } + libxml_clear_errors(); - return; - } + return; + } - //Initializations - $xml_array = []; - $parents = []; - $opened_tags = []; - $arr = []; + //Initializations + $xml_array = []; + $parents = []; + $opened_tags = []; + $arr = []; - $current = &$xml_array; // Reference + $current = &$xml_array; // Reference - // Go through the tags. - $repeated_tag_index = []; // Multiple tags with same name will be turned into an array - foreach($xml_values as $data) { - unset($attributes,$value); // Remove existing values, or there will be trouble + // Go through the tags. + $repeated_tag_index = []; // Multiple tags with same name will be turned into an array + foreach ($xml_values as $data) { + unset($attributes, $value); // Remove existing values, or there will be trouble - // This command will extract these variables into the foreach scope - // tag(string), type(string), level(int), attributes(array). - extract($data); // We could use the array by itself, but this cooler. + // This command will extract these variables into the foreach scope + // tag(string), type(string), level(int), attributes(array). + extract($data); // We could use the array by itself, but this cooler. - $result = []; - $attributes_data = []; + $result = []; + $attributes_data = []; - if(isset($value)) { - if($priority == 'tag') $result = $value; - else $result['value'] = $value; // Put the value in a assoc array if we are in the 'Attribute' mode - } + if (isset($value)) { + if ($priority == 'tag') { + $result = $value; + } else { + $result['value'] = $value; // Put the value in a assoc array if we are in the 'Attribute' mode + } + } - //Set the attributes too. - if(isset($attributes) and $get_attributes) { - foreach($attributes as $attr => $val) { - if($priority == 'tag') $attributes_data[$attr] = $val; - else $result['@attributes'][$attr] = $val; // Set all the attributes in a array called 'attr' - } - } + //Set the attributes too. + if (isset($attributes) and $get_attributes) { + foreach ($attributes as $attr => $val) { + if ($priority == 'tag') { + $attributes_data[$attr] = $val; + } else { + $result['@attributes'][$attr] = $val; // Set all the attributes in a array called 'attr' + } + } + } - // See tag status and do the needed. - if($namespaces && strpos($tag,':')) { - $namespc = substr($tag,0,strrpos($tag,':')); - $tag = strtolower(substr($tag,strlen($namespc)+1)); - $result['@namespace'] = $namespc; - } - $tag = strtolower($tag); + // See tag status and do the needed. + if ($namespaces && strpos($tag, ':')) { + $namespc = substr($tag, 0, strrpos($tag, ':')); + $tag = strtolower(substr($tag, strlen($namespc) + 1)); + $result['@namespace'] = $namespc; + } + $tag = strtolower($tag); - if($type == "open") { // The starting of the tag '' - $parent[$level-1] = &$current; - if(!is_array($current) or (!in_array($tag, array_keys($current)))) { // Insert New tag - $current[$tag] = $result; - if($attributes_data) $current[$tag. '_attr'] = $attributes_data; - $repeated_tag_index[$tag.'_'.$level] = 1; + if ($type == "open") { // The starting of the tag '' + $parent[$level - 1] = &$current; + if (!is_array($current) or (!in_array($tag, array_keys($current)))) { // Insert New tag + $current[$tag] = $result; + if ($attributes_data) { + $current[$tag . '_attr'] = $attributes_data; + } + $repeated_tag_index[$tag . '_' . $level] = 1; - $current = &$current[$tag]; + $current = &$current[$tag]; + } else { // There was another element with the same tag name + if (isset($current[$tag][0])) { // If there is a 0th element it is already an array + $current[$tag][$repeated_tag_index[$tag . '_' . $level]] = $result; + $repeated_tag_index[$tag . '_' . $level]++; + } else { // This section will make the value an array if multiple tags with the same name appear together + $current[$tag] = array($current[$tag],$result); // This will combine the existing item and the new item together to make an array + $repeated_tag_index[$tag . '_' . $level] = 2; - } else { // There was another element with the same tag name + if (isset($current[$tag . '_attr'])) { // The attribute of the last(0th) tag must be moved as well + $current[$tag]['0_attr'] = $current[$tag . '_attr']; + unset($current[$tag . '_attr']); + } + } + $last_item_index = $repeated_tag_index[$tag . '_' . $level] - 1; + $current = &$current[$tag][$last_item_index]; + } + } elseif ($type == "complete") { // Tags that ends in 1 line '' + //See if the key is already taken. + if (!isset($current[$tag])) { //New Key + $current[$tag] = $result; + $repeated_tag_index[$tag . '_' . $level] = 1; + if ($priority == 'tag' and $attributes_data) { + $current[$tag . '_attr'] = $attributes_data; + } + } else { // If taken, put all things inside a list(array) + if (isset($current[$tag][0]) and is_array($current[$tag])) { // If it is already an array... + // ...push the new element into that array. + $current[$tag][$repeated_tag_index[$tag . '_' . $level]] = $result; - if(isset($current[$tag][0])) { // If there is a 0th element it is already an array - $current[$tag][$repeated_tag_index[$tag.'_'.$level]] = $result; - $repeated_tag_index[$tag.'_'.$level]++; - } else { // This section will make the value an array if multiple tags with the same name appear together - $current[$tag] = array($current[$tag],$result); // This will combine the existing item and the new item together to make an array - $repeated_tag_index[$tag.'_'.$level] = 2; + if ($priority == 'tag' and $get_attributes and $attributes_data) { + $current[$tag][$repeated_tag_index[$tag . '_' . $level] . '_attr'] = $attributes_data; + } + $repeated_tag_index[$tag . '_' . $level]++; + } else { // If it is not an array... + $current[$tag] = array($current[$tag],$result); //...Make it an array using using the existing value and the new value + $repeated_tag_index[$tag . '_' . $level] = 1; + if ($priority == 'tag' and $get_attributes) { + if (isset($current[$tag . '_attr'])) { // The attribute of the last(0th) tag must be moved as well + $current[$tag]['0_attr'] = $current[$tag . '_attr']; + unset($current[$tag . '_attr']); + } - if(isset($current[$tag.'_attr'])) { // The attribute of the last(0th) tag must be moved as well - $current[$tag]['0_attr'] = $current[$tag.'_attr']; - unset($current[$tag.'_attr']); - } - } - $last_item_index = $repeated_tag_index[$tag.'_'.$level]-1; - $current = &$current[$tag][$last_item_index]; - } + if ($attributes_data) { + $current[$tag][$repeated_tag_index[$tag . '_' . $level] . '_attr'] = $attributes_data; + } + } + $repeated_tag_index[$tag . '_' . $level]++; // 0 and 1 indexes are already taken + } + } + } elseif ($type == 'close') { // End of tag '' + $current = &$parent[$level - 1]; + } + } - } elseif($type == "complete") { // Tags that ends in 1 line '' - //See if the key is already taken. - if(!isset($current[$tag])) { //New Key - $current[$tag] = $result; - $repeated_tag_index[$tag.'_'.$level] = 1; - if($priority == 'tag' and $attributes_data) - $current[$tag. '_attr'] = $attributes_data; - - } else { // If taken, put all things inside a list(array) - if(isset($current[$tag][0]) and is_array($current[$tag])) { // If it is already an array... - - // ...push the new element into that array. - $current[$tag][$repeated_tag_index[$tag.'_'.$level]] = $result; - - if($priority == 'tag' and $get_attributes and $attributes_data) { - $current[$tag][$repeated_tag_index[$tag.'_'.$level] . '_attr'] = $attributes_data; - } - $repeated_tag_index[$tag.'_'.$level]++; - } else { // If it is not an array... - $current[$tag] = array($current[$tag],$result); //...Make it an array using using the existing value and the new value - $repeated_tag_index[$tag.'_'.$level] = 1; - if($priority == 'tag' and $get_attributes) { - if(isset($current[$tag.'_attr'])) { // The attribute of the last(0th) tag must be moved as well - $current[$tag]['0_attr'] = $current[$tag.'_attr']; - unset($current[$tag.'_attr']); - } - - if($attributes_data) { - $current[$tag][$repeated_tag_index[$tag.'_'.$level] . '_attr'] = $attributes_data; - } - } - $repeated_tag_index[$tag.'_'.$level]++; // 0 and 1 indexes are already taken - } - } - - } elseif($type == 'close') { // End of tag '' - $current = &$parent[$level-1]; - } - } - - return($xml_array); + return($xml_array); } -function email_header_encode($in_str, $charset = 'UTF-8', $header = 'Subject') { - $out_str = $in_str; - $need_to_convert = false; +function email_header_encode($in_str, $charset = 'UTF-8', $header = 'Subject') +{ + $out_str = $in_str; + $need_to_convert = false; - for($x = 0; $x < strlen($in_str); $x ++) { - if((ord($in_str[$x]) == 0) || ((ord($in_str[$x]) > 128))) { - $need_to_convert = true; - break; - } - } + for ($x = 0; $x < strlen($in_str); $x++) { + if ((ord($in_str[$x]) == 0) || ((ord($in_str[$x]) > 128))) { + $need_to_convert = true; + break; + } + } - if(! $need_to_convert) - return $in_str; + if (! $need_to_convert) { + return $in_str; + } - if ($out_str && $charset) { + if ($out_str && $charset) { + // define start delimimter, end delimiter and spacer + $end = "?="; + $start = "=?" . $charset . "?B?"; + $spacer = $end . PHP_EOL . " " . $start; - // define start delimimter, end delimiter and spacer - $end = "?="; - $start = "=?" . $charset . "?B?"; - $spacer = $end . PHP_EOL . " " . $start; + // determine length of encoded text within chunks + // and ensure length is even + $length = 75 - strlen($start) - strlen($end) - (strlen($header) + 2); - // determine length of encoded text within chunks - // and ensure length is even - $length = 75 - strlen($start) - strlen($end) - (strlen($header) + 2); + /* + [EDIT BY danbrown AT php DOT net: The following + is a bugfix provided by (gardan AT gmx DOT de) + on 31-MAR-2005 with the following note: + "This means: $length should not be even, + but divisible by 4. The reason is that in + base64-encoding 3 8-bit-chars are represented + by 4 6-bit-chars. These 4 chars must not be + split between two encoded words, according + to RFC-2047. + */ + $length = $length - ($length % 4); - /* - [EDIT BY danbrown AT php DOT net: The following - is a bugfix provided by (gardan AT gmx DOT de) - on 31-MAR-2005 with the following note: - "This means: $length should not be even, - but divisible by 4. The reason is that in - base64-encoding 3 8-bit-chars are represented - by 4 6-bit-chars. These 4 chars must not be - split between two encoded words, according - to RFC-2047. - */ - $length = $length - ($length % 4); + // encode the string and split it into chunks + // with spacers after each chunk + $out_str = base64_encode($out_str); + $out_str = chunk_split($out_str, $length, $spacer); - // encode the string and split it into chunks - // with spacers after each chunk - $out_str = base64_encode($out_str); - $out_str = chunk_split($out_str, $length, $spacer); - - // remove trailing spacer and - // add start and end delimiters - $spacer = preg_quote($spacer,'/'); - $out_str = preg_replace("/" . $spacer . "$/", "", $out_str); - $out_str = $start . $out_str . $end; - } - return $out_str; + // remove trailing spacer and + // add start and end delimiters + $spacer = preg_quote($spacer, '/'); + $out_str = preg_replace("/" . $spacer . "$/", "", $out_str); + $out_str = $start . $out_str . $end; + } + return $out_str; } /** @@ -1017,42 +1080,44 @@ function email_header_encode($in_str, $charset = 'UTF-8', $header = 'Subject') { * * @param string $webbie * @param string $protocol (optional) default empty - * @param boolean $verify (optional) default true, verify HTTP signatures on Zot discovery packets. - * @return boolean + * @param bool $verify (optional) default true, verify HTTP signatures on Zot discovery packets. + * @return bool */ -function discover_by_webbie($webbie, $protocol = '', $verify = true) { +function discover_by_webbie($webbie, $protocol = '', $verify = true) +{ - $result = []; + $result = []; - $network = null; + $network = null; - $x = Webfinger::exec($webbie); + $x = Webfinger::exec($webbie); - $address = EMPTY_STR; + $address = EMPTY_STR; - if($x && array_key_exists('subject',$x) && strpos($x['subject'],'acct:') === 0) - $address = str_replace('acct:','',$x['subject']); - if($x && array_key_exists('aliases',$x) && count($x['aliases'])) { - foreach($x['aliases'] as $a) { - if(strpos($a,'acct:') === 0) { - $address = str_replace('acct:','',$a); + if ($x && array_key_exists('subject', $x) && strpos($x['subject'], 'acct:') === 0) { + $address = str_replace('acct:', '', $x['subject']); + } + if ($x && array_key_exists('aliases', $x) && count($x['aliases'])) { + foreach ($x['aliases'] as $a) { + if (strpos($a, 'acct:') === 0) { + $address = str_replace('acct:', '', $a); break; } } } - if($x && array_key_exists('links',$x) && is_array($x['links'])) { + if ($x && array_key_exists('links', $x) && is_array($x['links'])) { // look for Nomad first - foreach($x['links'] as $link) { - if(array_key_exists('rel',$link)) { + foreach ($x['links'] as $link) { + if (array_key_exists('rel',$link)) { $apurl = null; // If we discover zot - don't search further; grab the info and get out of // here. - if(in_array($link['rel'], [ PROTOCOL_NOMAD ]) && ((! $protocol) || (in_array(strtolower($protocol), [ 'nomad' ])))) { + if (in_array($link['rel'], [ PROTOCOL_NOMAD ]) && ((! $protocol) || (in_array(strtolower($protocol), [ 'nomad' ])))) { logger('nomad found for ' . $webbie, LOGGER_DEBUG); $record = Zotfinger::exec($link['href'], null, $verify); @@ -1081,77 +1146,76 @@ function discover_by_webbie($webbie, $protocol = '', $verify = true) { // if we reached this point, nomad wasn't found. - foreach($x['links'] as $link) { - if(array_key_exists('rel',$link)) { + foreach ($x['links'] as $link) { + if (array_key_exists('rel', $link)) { + $apurl = null; - $apurl = null; + // If we discover zot - don't search further; grab the info and get out of + // here. - // If we discover zot - don't search further; grab the info and get out of - // here. + if ($link['rel'] === PROTOCOL_ZOT6 && ((! $protocol) || (strtolower($protocol) === 'zot6'))) { + logger('zot6 found for ' . $webbie, LOGGER_DEBUG); + $record = Zotfinger::exec($link['href'], null, $verify); - if(in_array($link['rel'], [ PROTOCOL_ZOT6 ]) && ((! $protocol) || (in_array(strtolower($protocol), [ 'zot6' ])))) { - logger('zot6 found for ' . $webbie, LOGGER_DEBUG); - $record = Zotfinger::exec($link['href'], null, $verify); + // Check the HTTP signature - // Check the HTTP signature + if ($verify) { + $hsig = $record['signature']; + if ($hsig && ($hsig['signer'] === $url || $hsig['signer'] === $link['href']) && $hsig['header_valid'] === true && $hsig['content_valid'] === true) { + $hsig_valid = true; + } - if ($verify) { + if (! $hsig_valid) { + logger('http signature not valid: ' . print_r($hsig, true)); + continue; + } + } - $hsig = $record['signature']; - if($hsig && ($hsig['signer'] === $url || $hsig['signer'] === $link['href']) && $hsig['header_valid'] === true && $hsig['content_valid'] === true) { - $hsig_valid = true; - } + $x = Libzot::import_xchan($record['data']); + if ($x['success']) { + return $x['hash']; + } + } + if ($link['rel'] === 'self' && ($link['type'] === 'application/activity+json' || strpos($link['type'], 'ld+json') !== false) && ((! $protocol) || (strtolower($protocol) === 'activitypub'))) { + $ap = ActivityPub::discover($link['href']); + if ($ap) { + return $ap; + } + } + } + } + } - if(! $hsig_valid) { - logger('http signature not valid: ' . print_r($hsig,true)); - continue; - } - } + if (strpos($webbie, 'http') === 0) { + $ap = ActivityPub::discover($webbie); + if ($ap) { + return $ap; + } + } - $x = Libzot::import_xchan($record['data']); - if($x['success']) { - return $x['hash']; - } - } - if($link['rel'] === 'self' && ($link['type'] === 'application/activity+json' || strpos($link['type'],'ld+json') !== false) && ((! $protocol) || (strtolower($protocol) === 'activitypub'))) { - $ap = ActivityPub::discover($link['href']); - if($ap) { - return $ap; - } - } - } - } - } + logger('webfinger: ' . print_r($x, true), LOGGER_DATA, LOG_INFO); - if(strpos($webbie,'http') === 0) { - $ap = ActivityPub::discover($webbie); - if($ap) { - return $ap; - } - } + $arr = [ + 'address' => $webbie, + 'protocol' => $protocol, + 'success' => false, + 'xchan' => '', + 'webfinger' => $x + ]; + /** + * @hooks discover_channel_webfinger + * Called when performing a webfinger lookup. + * * \e string \b address - The webbie + * * \e string \b protocol + * * \e array \b webfinger - The result from webfinger_rfc7033() + * * \e boolean \b success - The return value, default false + */ + call_hooks('discover_channel_webfinger', $arr); + if ($arr['success']) { + return $arr['xchan']; + } - logger('webfinger: ' . print_r($x,true), LOGGER_DATA, LOG_INFO); - - $arr = [ - 'address' => $webbie, - 'protocol' => $protocol, - 'success' => false, - 'xchan' => '', - 'webfinger' => $x - ]; - /** - * @hooks discover_channel_webfinger - * Called when performing a webfinger lookup. - * * \e string \b address - The webbie - * * \e string \b protocol - * * \e array \b webfinger - The result from webfinger_rfc7033() - * * \e boolean \b success - The return value, default false - */ - call_hooks('discover_channel_webfinger', $arr); - if($arr['success']) - return $arr['xchan']; - - return false; + return false; } /** @@ -1159,319 +1223,335 @@ function discover_by_webbie($webbie, $protocol = '', $verify = true) { * No longer used - see Zotlabs/Lib/Webfinger.php * * @param string $webbie - The resource - * @return boolean|string false or associative array from result JSON + * @return bool|string false or associative array from result JSON */ -function webfinger_rfc7033($webbie) { +function webfinger_rfc7033($webbie) +{ - if(strpos($webbie,'@')) { - $lhs = substr($webbie,0,strpos($webbie,'@')); - $rhs = substr($webbie,strpos($webbie,'@')+1); - $resource = urlencode('acct:' . $webbie); - } - else { - $m = parse_url($webbie); - if($m) { - if($m['scheme'] !== 'https') - return false; + if (strpos($webbie, '@')) { + $lhs = substr($webbie, 0, strpos($webbie, '@')); + $rhs = substr($webbie, strpos($webbie, '@') + 1); + $resource = urlencode('acct:' . $webbie); + } else { + $m = parse_url($webbie); + if ($m) { + if ($m['scheme'] !== 'https') { + return false; + } - $rhs = $m['host'] . ((isset($m['port']) && intval($m['port'])) ? ':' . intval($m['port']) : ''); - $resource = urlencode($webbie); - } - else - return false; - } - logger('fetching url from resource: ' . $rhs . ':' . $webbie); + $rhs = $m['host'] . ((isset($m['port']) && intval($m['port'])) ? ':' . intval($m['port']) : ''); + $resource = urlencode($webbie); + } else { + return false; + } + } + logger('fetching url from resource: ' . $rhs . ':' . $webbie); - $counter = 0; - $s = z_fetch_url('https://' . $rhs . '/.well-known/webfinger?f=&resource=' . $resource . (($zot) ? '&zot=1' : ''), - false, $counter, [ 'headers' => [ 'Accept: application/jrd+json, application/json, */*' ] ]); + $counter = 0; + $s = z_fetch_url( + 'https://' . $rhs . '/.well-known/webfinger?f=&resource=' . $resource . (($zot) ? '&zot=1' : ''), + false, + $counter, + [ 'headers' => [ 'Accept: application/jrd+json, application/json, */*' ] ] + ); - if($s['success']) { - $j = json_decode($s['body'], true); - return($j); - } + if ($s['success']) { + $j = json_decode($s['body'], true); + return($j); + } - return false; + return false; } -function do_delivery($deliveries, $force = false) { +function do_delivery($deliveries, $force = false) +{ - // $force is set if a site that wasn't responding suddenly returns to life. - // Try and shove through everything going to that site while it's responding. + // $force is set if a site that wasn't responding suddenly returns to life. + // Try and shove through everything going to that site while it's responding. - if(! (is_array($deliveries) && count($deliveries))) - return; + if (! (is_array($deliveries) && count($deliveries))) { + return; + } - $x = q("select count(outq_hash) as total from outq where outq_delivered = 0"); - if(intval($x[0]['total']) > intval(get_config('system','force_queue_threshold',3000)) && (! $force)) { - logger('immediate delivery deferred.', LOGGER_DEBUG, LOG_INFO); - foreach($deliveries as $d) { - Queue::update($d); - } - return; - } + $x = q("select count(outq_hash) as total from outq where outq_delivered = 0"); + if (intval($x[0]['total']) > intval(get_config('system', 'force_queue_threshold', 3000)) && (! $force)) { + logger('immediate delivery deferred.', LOGGER_DEBUG, LOG_INFO); + foreach ($deliveries as $d) { + Queue::update($d); + } + return; + } - $interval = ((get_config('system','delivery_interval') !== false) - ? intval(get_config('system','delivery_interval')) : 2 ); + $interval = ((get_config('system', 'delivery_interval') !== false) + ? intval(get_config('system', 'delivery_interval')) : 2 ); - $deliveries_per_process = intval(get_config('system','delivery_batch_count')); + $deliveries_per_process = intval(get_config('system', 'delivery_batch_count')); - if($deliveries_per_process <= 0) - $deliveries_per_process = 1; + if ($deliveries_per_process <= 0) { + $deliveries_per_process = 1; + } - $deliver = []; - foreach($deliveries as $d) { + $deliver = []; + foreach ($deliveries as $d) { + if (! $d) { + continue; + } - if(! $d) - continue; + $deliver[] = $d; - $deliver[] = $d; + if (count($deliver) >= $deliveries_per_process) { + Run::Summon([ 'Deliver',$deliver ]); + $deliver = []; + if ($interval) { + @time_sleep_until(microtime(true) + (float) $interval); + } + } + } - if(count($deliver) >= $deliveries_per_process) { - Run::Summon( [ 'Deliver',$deliver ] ); - $deliver = []; - if($interval) - @time_sleep_until(microtime(true) + (float) $interval); - } - } + // catch any stragglers - // catch any stragglers - - if($deliver) { - Run::Summon( [ 'Deliver',$deliver ] ); - } + if ($deliver) { + Run::Summon([ 'Deliver',$deliver ]); + } } -function get_site_info() { +function get_site_info() +{ - $register_policy = Array('REGISTER_CLOSED', 'REGISTER_APPROVE', 'REGISTER_OPEN'); - $directory_mode = Array('DIRECTORY_MODE_NORMAL', 'DIRECTORY_MODE_PRIMARY', 'DIRECTORY_MODE_SECONDARY', 256 => 'DIRECTORY_MODE_STANDALONE'); + $register_policy = array('REGISTER_CLOSED', 'REGISTER_APPROVE', 'REGISTER_OPEN'); + $directory_mode = array('DIRECTORY_MODE_NORMAL', 'DIRECTORY_MODE_PRIMARY', 'DIRECTORY_MODE_SECONDARY', 256 => 'DIRECTORY_MODE_STANDALONE'); - $sql_extra = ''; + $sql_extra = ''; - $r = q("select * from channel left join account on account_id = channel_account_id where ( account_roles & 4096 ) > 0 and account_default_channel = channel_id"); + $r = q("select * from channel left join account on account_id = channel_account_id where ( account_roles & 4096 ) > 0 and account_default_channel = channel_id"); - if($r) { - $admin = []; - foreach($r as $rr) { - if($rr['channel_pageflags'] & PAGE_HUBADMIN) - $admin[] = array( 'name' => $rr['channel_name'], 'address' => channel_reddress($rr), 'channel' => z_root() . '/channel/' . $rr['channel_address']); - } - if(! $admin) { - foreach($r as $rr) { - $admin[] = array( 'name' => $rr['channel_name'], 'address' => channel_reddress($rr), 'channel' => z_root() . '/channel/' . $rr['channel_address']); - } - } - } - else { - $admin = false; - } + if ($r) { + $admin = []; + foreach ($r as $rr) { + if ($rr['channel_pageflags'] & PAGE_HUBADMIN) { + $admin[] = array( 'name' => $rr['channel_name'], 'address' => channel_reddress($rr), 'channel' => z_root() . '/channel/' . $rr['channel_address']); + } + } + if (! $admin) { + foreach ($r as $rr) { + $admin[] = array( 'name' => $rr['channel_name'], 'address' => channel_reddress($rr), 'channel' => z_root() . '/channel/' . $rr['channel_address']); + } + } + } else { + $admin = false; + } - $def_service_class = get_config('system','default_service_class'); - if($def_service_class) - $service_class = get_config('service_class',$def_service_class); - else - $service_class = false; + $def_service_class = get_config('system', 'default_service_class'); + if ($def_service_class) { + $service_class = get_config('service_class', $def_service_class); + } else { + $service_class = false; + } - $visible_plugins = visible_plugin_list(); + $visible_plugins = visible_plugin_list(); - if(@is_dir('.git') && function_exists('shell_exec')) - $commit = trim(@shell_exec('git log -1 --format="%h"')); - if(! isset($commit) || strlen($commit) > 16) - $commit = ''; + if (@is_dir('.git') && function_exists('shell_exec')) { + $commit = trim(@shell_exec('git log -1 --format="%h"')); + } + if (! isset($commit) || strlen($commit) > 16) { + $commit = ''; + } - $site_info = get_config('system','info'); - $site_name = get_config('system','sitename'); - if(! get_config('system','hidden_version_siteinfo')) { - $version = System::get_project_version(); + $site_info = get_config('system', 'info'); + $site_name = get_config('system', 'sitename'); + if (! get_config('system', 'hidden_version_siteinfo')) { + $version = System::get_project_version(); - if(@is_dir('.git') && function_exists('shell_exec')) { - $commit = trim( @shell_exec('git log -1 --format="%h"')); - } + if (@is_dir('.git') && function_exists('shell_exec')) { + $commit = trim(@shell_exec('git log -1 --format="%h"')); + } - if(! isset($commit) || strlen($commit) > 16) - $commit = ''; - } - else { - $version = $commit = ''; - } + if (! isset($commit) || strlen($commit) > 16) { + $commit = ''; + } + } else { + $version = $commit = ''; + } - //Statistics -// $channels_total_stat = intval(get_config('system','channels_total_stat')); -// $channels_active_halfyear_stat = intval(get_config('system','channels_active_halfyear_stat')); -// $channels_active_monthly_stat = intval(get_config('system','channels_active_monthly_stat')); -// $local_posts_stat = intval(get_config('system','local_posts_stat')); -// $local_comments_stat = intval(get_config('system','local_comments_stat')); -// $hide_in_statistics = intval(get_config('system','hide_in_statistics')); + //Statistics +// $channels_total_stat = intval(get_config('system','channels_total_stat')); +// $channels_active_halfyear_stat = intval(get_config('system','channels_active_halfyear_stat')); +// $channels_active_monthly_stat = intval(get_config('system','channels_active_monthly_stat')); +// $local_posts_stat = intval(get_config('system','local_posts_stat')); +// $local_comments_stat = intval(get_config('system','local_comments_stat')); +// $hide_in_statistics = intval(get_config('system','hide_in_statistics')); - $site_expire = intval(get_config('system', 'default_expire_days')); + $site_expire = intval(get_config('system', 'default_expire_days')); - load_config('feature_lock'); - $locked_features = []; - if(is_array(App::$config['feature_lock']) && count(App::$config['feature_lock'])) { - foreach(App::$config['feature_lock'] as $k => $v) { - if($k === 'config_loaded') - continue; + load_config('feature_lock'); + $locked_features = []; + if (is_array(App::$config['feature_lock']) && count(App::$config['feature_lock'])) { + foreach (App::$config['feature_lock'] as $k => $v) { + if ($k === 'config_loaded') { + continue; + } - $locked_features[$k] = intval($v); - } - } + $locked_features[$k] = intval($v); + } + } - $protocols = [ 'nomad' ]; - if (get_config('system','activitypub', ACTIVITYPUB_ENABLED)) { - $protocols[] = 'activitypub'; - } + $protocols = [ 'nomad' ]; + if (get_config('system', 'activitypub', ACTIVITYPUB_ENABLED)) { + $protocols[] = 'activitypub'; + } - $data = [ - 'url' => z_root(), - 'platform' => System::get_platform_name(), - 'site_name' => (($site_name) ? $site_name : ''), - 'version' => $version, -// 'version_tag' => $tag, - 'addon_version' => defined('ADDON_VERSION') ? ADDON_VERSION : 'unknown', - 'commit' => $commit, - 'protocols' => $protocols, - 'plugins' => $visible_plugins, - 'register_policy' => $register_policy[get_config('system','register_policy')], - 'invitation_only' => (bool) (defined('INVITE_WORKING') && intval(get_config('system','invitation_only'))), - 'directory_mode' => $directory_mode[get_config('system','directory_mode')], -// 'directory_server' => get_config('system','directory_server'), - 'language' => get_config('system','language'), -// 'rss_connections' => (bool) intval(get_config('system','feed_contacts')), - 'expiration' => $site_expire, - 'default_service_restrictions' => $service_class, - 'locked_features' => $locked_features, - 'admin' => $admin, - 'dbdriver' => DBA::$dba->getdriver() . ' ' . ((ACTIVE_DBTYPE == DBTYPE_POSTGRES) ? 'postgres' : 'mysql'), - 'lastpoll' => get_config('system','lastpoll'), - 'ebs' => System::ebs(), // bit.ly/3DGCmki - 'info' => (($site_info) ? $site_info : '') + $data = [ + 'url' => z_root(), + 'platform' => System::get_platform_name(), + 'site_name' => (($site_name) ? $site_name : ''), + 'version' => $version, +// 'version_tag' => $tag, + 'addon_version' => defined('ADDON_VERSION') ? ADDON_VERSION : 'unknown', + 'commit' => $commit, + 'protocols' => $protocols, + 'plugins' => $visible_plugins, + 'register_policy' => $register_policy[get_config('system', 'register_policy')], + 'invitation_only' => (bool) (defined('INVITE_WORKING') && intval(get_config('system', 'invitation_only'))), + 'directory_mode' => $directory_mode[get_config('system', 'directory_mode')], +// 'directory_server' => get_config('system','directory_server'), + 'language' => get_config('system', 'language'), +// 'rss_connections' => (bool) intval(get_config('system','feed_contacts')), + 'expiration' => $site_expire, + 'default_service_restrictions' => $service_class, + 'locked_features' => $locked_features, + 'admin' => $admin, + 'dbdriver' => DBA::$dba->getdriver() . ' ' . ((ACTIVE_DBTYPE == DBTYPE_POSTGRES) ? 'postgres' : 'mysql'), + 'lastpoll' => get_config('system', 'lastpoll'), + 'ebs' => System::ebs(), // bit.ly/3DGCmki + 'info' => (($site_info) ? $site_info : '') - ]; + ]; - return $data; + return $data; } /** * @brief * * @param string $url - * @return boolean + * @return bool */ -function check_siteallowed($url) { +function check_siteallowed($url) +{ - $retvalue = true; + $retvalue = true; - $arr = [ 'url' => $url ]; - /** - * @hooks check_siteallowed - * Used to over-ride or bypass the site black/white block lists. - * * \e string \b url - * * \e boolean \b allowed - optional return value set in hook - */ - call_hooks('check_siteallowed', $arr); + $arr = [ 'url' => $url ]; + /** + * @hooks check_siteallowed + * Used to over-ride or bypass the site black/white block lists. + * * \e string \b url + * * \e boolean \b allowed - optional return value set in hook + */ + call_hooks('check_siteallowed', $arr); - if (array_key_exists('allowed',$arr)) { - return $arr['allowed']; - } + if (array_key_exists('allowed', $arr)) { + return $arr['allowed']; + } - // your own site is always allowed - if (strpos($url, z_root()) !== false) { - return $retvalue; - } + // your own site is always allowed + if (strpos($url, z_root()) !== false) { + return $retvalue; + } - $bl1 = get_config('system','allowed_sites'); - $bl2 = get_config('system','denied_sites'); + $bl1 = get_config('system', 'allowed_sites'); + $bl2 = get_config('system', 'denied_sites'); - if (is_array($bl1) && $bl1) { - if (! (is_array($bl2) && $bl2)) { - $retvalue = false; - } - foreach ($bl1 as $bl) { - if ($bl === '*') { - $retvalue = true; - } - if ($bl && (strpos($url,$bl) !== false || wildmat($bl,$url))) { - return true; - } - } - } - if (is_array($bl2) && $bl2) { - foreach ($bl2 as $bl) { - if ($bl === '*') { - $retvalue = false; - } - if ($bl && (strpos($url,$bl) !== false || wildmat($bl,$url))) { - return false; - } - } - } + if (is_array($bl1) && $bl1) { + if (! (is_array($bl2) && $bl2)) { + $retvalue = false; + } + foreach ($bl1 as $bl) { + if ($bl === '*') { + $retvalue = true; + } + if ($bl && (strpos($url, $bl) !== false || wildmat($bl, $url))) { + return true; + } + } + } + if (is_array($bl2) && $bl2) { + foreach ($bl2 as $bl) { + if ($bl === '*') { + $retvalue = false; + } + if ($bl && (strpos($url, $bl) !== false || wildmat($bl, $url))) { + return false; + } + } + } - return $retvalue; + return $retvalue; } /** * @brief * * @param string $url - * @return boolean + * @return bool */ -function check_pubstream_siteallowed($url) { +function check_pubstream_siteallowed($url) +{ - $retvalue = true; + $retvalue = true; - $arr = array('url' => $url); - /** - * @hooks check_siteallowed - * Used to over-ride or bypass the site black/white block lists. - * * \e string \b url - * * \e boolean \b allowed - optional return value set in hook - */ - call_hooks('pubstream_check_siteallowed', $arr); + $arr = array('url' => $url); + /** + * @hooks check_siteallowed + * Used to over-ride or bypass the site black/white block lists. + * * \e string \b url + * * \e boolean \b allowed - optional return value set in hook + */ + call_hooks('pubstream_check_siteallowed', $arr); - if (array_key_exists('allowed',$arr)) { - return $arr['allowed']; - } + if (array_key_exists('allowed', $arr)) { + return $arr['allowed']; + } - // your own site is always allowed - if (strpos($url, z_root()) !== false) { - return $retvalue; - } + // your own site is always allowed + if (strpos($url, z_root()) !== false) { + return $retvalue; + } - $bl1 = get_config('system','pubstream_allowed_sites'); - $bl2 = get_config('system','pubstream_denied_sites'); + $bl1 = get_config('system', 'pubstream_allowed_sites'); + $bl2 = get_config('system', 'pubstream_denied_sites'); - if (is_array($bl1) && $bl1) { - if (! (is_array($bl2) && $bl2)) { - $retvalue = false; - } - foreach ($bl1 as $bl) { - if ($bl === '*') { - $retvalue = true; - } - if ($bl && (strpos($url,$bl) !== false || wildmat($bl,$url))) { - return true; - } - } - } - if (is_array($bl2) && $bl2) { - foreach ($bl2 as $bl) { - if ($bl === '*') { - $retvalue = false; - } - if ($bl && (strpos($url,$bl) !== false || wildmat($bl,$url))) { - return false; - } - } - } + if (is_array($bl1) && $bl1) { + if (! (is_array($bl2) && $bl2)) { + $retvalue = false; + } + foreach ($bl1 as $bl) { + if ($bl === '*') { + $retvalue = true; + } + if ($bl && (strpos($url, $bl) !== false || wildmat($bl, $url))) { + return true; + } + } + } + if (is_array($bl2) && $bl2) { + foreach ($bl2 as $bl) { + if ($bl === '*') { + $retvalue = false; + } + if ($bl && (strpos($url, $bl) !== false || wildmat($bl, $url))) { + return false; + } + } + } - return $retvalue; + return $retvalue; } @@ -1480,53 +1560,54 @@ function check_pubstream_siteallowed($url) { * @brief * * @param string $hash - * @return boolean + * @return bool */ -function check_channelallowed($hash) { +function check_channelallowed($hash) +{ - $retvalue = true; + $retvalue = true; - $arr = [ 'hash' => $hash ]; - /** - * @hooks check_channelallowed - * Used to over-ride or bypass the channel black/white block lists. - * * \e string \b hash - * * \e boolean \b allowed - optional return value set in hook - */ - call_hooks('check_channelallowed', $arr); + $arr = [ 'hash' => $hash ]; + /** + * @hooks check_channelallowed + * Used to over-ride or bypass the channel black/white block lists. + * * \e string \b hash + * * \e boolean \b allowed - optional return value set in hook + */ + call_hooks('check_channelallowed', $arr); - if (array_key_exists('allowed',$arr)) { - return $arr['allowed']; - } + if (array_key_exists('allowed', $arr)) { + return $arr['allowed']; + } - $bl1 = get_config('system','allowed_channels'); - $bl2 = get_config('system','denied_channels'); + $bl1 = get_config('system', 'allowed_channels'); + $bl2 = get_config('system', 'denied_channels'); - if (is_array($bl1) && $bl1) { - if (! (is_array($bl2) && $bl2)) { - $retvalue = false; - } - foreach ($bl1 as $bl) { - if ($bl === '*') { - $retvalue = true; - } - if ($bl && (strpos($url,$bl) !== false || wildmat($bl,$url))) { - return true; - } - } - } - if (is_array($bl2) && $bl2) { - foreach ($bl2 as $bl) { - if ($bl === '*') { - $retvalue = false; - } - if ($bl && (strpos($url,$bl) !== false || wildmat($bl,$url))) { - return false; - } - } - } + if (is_array($bl1) && $bl1) { + if (! (is_array($bl2) && $bl2)) { + $retvalue = false; + } + foreach ($bl1 as $bl) { + if ($bl === '*') { + $retvalue = true; + } + if ($bl && (strpos($url, $bl) !== false || wildmat($bl, $url))) { + return true; + } + } + } + if (is_array($bl2) && $bl2) { + foreach ($bl2 as $bl) { + if ($bl === '*') { + $retvalue = false; + } + if ($bl && (strpos($url, $bl) !== false || wildmat($bl, $url))) { + return false; + } + } + } - return $retvalue; + return $retvalue; } @@ -1534,82 +1615,88 @@ function check_channelallowed($hash) { * @brief * * @param string $hash - * @return boolean + * @return bool */ -function check_pubstream_channelallowed($hash) { +function check_pubstream_channelallowed($hash) +{ - $retvalue = true; + $retvalue = true; - $arr = array('hash' => $hash); - /** - * @hooks check_channelallowed - * Used to over-ride or bypass the channel black/white block lists. - * * \e string \b hash - * * \e boolean \b allowed - optional return value set in hook - */ - call_hooks('check_pubstream_channelallowed', $arr); + $arr = array('hash' => $hash); + /** + * @hooks check_channelallowed + * Used to over-ride or bypass the channel black/white block lists. + * * \e string \b hash + * * \e boolean \b allowed - optional return value set in hook + */ + call_hooks('check_pubstream_channelallowed', $arr); - if (array_key_exists('allowed',$arr)) { - return $arr['allowed']; - } + if (array_key_exists('allowed', $arr)) { + return $arr['allowed']; + } - $bl1 = get_config('system','pubstream_allowed_channels'); - $bl2 = get_config('system','pubstream_denied_channels'); + $bl1 = get_config('system', 'pubstream_allowed_channels'); + $bl2 = get_config('system', 'pubstream_denied_channels'); - if (is_array($bl1) && $bl1) { - if (! (is_array($bl2) && $bl2)) { - $retvalue = false; - } - foreach ($bl1 as $bl) { - if ($bl === '*') { - $retvalue = true; - } - if ($bl && (strpos($url,$bl) !== false || wildmat($bl,$url))) { - return true; - } - } - } - if (is_array($bl2) && $bl2) { - foreach ($bl2 as $bl) { - if ($bl === '*') { - $retvalue = false; - } - if ($bl && (strpos($url,$bl) !== false || wildmat($bl,$url))) { - return false; - } - } - } + if (is_array($bl1) && $bl1) { + if (! (is_array($bl2) && $bl2)) { + $retvalue = false; + } + foreach ($bl1 as $bl) { + if ($bl === '*') { + $retvalue = true; + } + if ($bl && (strpos($url, $bl) !== false || wildmat($bl, $url))) { + return true; + } + } + } + if (is_array($bl2) && $bl2) { + foreach ($bl2 as $bl) { + if ($bl === '*') { + $retvalue = false; + } + if ($bl && (strpos($url, $bl) !== false || wildmat($bl, $url))) { + return false; + } + } + } - return $retvalue; + return $retvalue; } -function deliverable_singleton($channel_id,$xchan) { +function deliverable_singleton($channel_id, $xchan) +{ - if(array_key_exists('xchan_hash',$xchan)) - $xchan_hash = $xchan['xchan_hash']; - elseif(array_key_exists('hubloc_hash',$xchan)) - $xchan_hash = $xchan['hubloc_hash']; - else - return true; + if (array_key_exists('xchan_hash', $xchan)) { + $xchan_hash = $xchan['xchan_hash']; + } elseif (array_key_exists('hubloc_hash', $xchan)) { + $xchan_hash = $xchan['hubloc_hash']; + } else { + return true; + } - $r = q("select abook_instance from abook where abook_channel = %d and abook_xchan = '%s' limit 1", - intval($channel_id), - dbesc($xchan_hash) - ); - if($r) { - if(! $r[0]['abook_instance']) - return true; - if(strpos($r[0]['abook_instance'],z_root()) !== false) - return true; - } - return false; + $r = q( + "select abook_instance from abook where abook_channel = %d and abook_xchan = '%s' limit 1", + intval($channel_id), + dbesc($xchan_hash) + ); + if ($r) { + if (! $r[0]['abook_instance']) { + return true; + } + if (strpos($r[0]['abook_instance'], z_root()) !== false) { + return true; + } + } + return false; } -function get_repository_version($branch = 'release') { - +function get_repository_version($branch = 'release') +{ if (PLATFORM_NAME === 'streams') { $path = "https://codeberg.org/streams/" . PLATFORM_NAME . "/raw/$branch/boot.php"; @@ -1618,13 +1705,14 @@ function get_repository_version($branch = 'release') { $path = "https://codeberg.org/zot/" . ((PLATFORM_NAME === 'mistpark') ? 'misty' : PLATFORM_NAME) . "/raw/$branch/boot.php"; } - $x = z_fetch_url($path); - if($x['success']) { - $y = preg_match('/define(.*?)STD_VERSION(.*?)([0-9.].*)\'/',$x['body'],$matches); - if($y) - return $matches[3]; - } - return '?.?'; + $x = z_fetch_url($path); + if ($x['success']) { + $y = preg_match('/define(.*?)STD_VERSION(.*?)([0-9.].*)\'/', $x['body'], $matches); + if ($y) { + return $matches[3]; + } + } + return '?.?'; } /** @@ -1633,36 +1721,37 @@ function get_repository_version($branch = 'release') { * @param string $s Network string, see boot.php * @return string Translated name of the network */ -function network_to_name($s) { +function network_to_name($s) +{ - $nets = array( - NETWORK_DFRN => t('Friendica'), - NETWORK_FRND => t('Friendica'), - NETWORK_OSTATUS => t('OStatus'), - NETWORK_GNUSOCIAL => t('GNU-Social'), - NETWORK_FEED => t('RSS/Atom'), - NETWORK_ACTIVITYPUB => t('ActivityPub'), - NETWORK_MAIL => t('Email'), - NETWORK_DIASPORA => t('Diaspora'), - NETWORK_FACEBOOK => t('Facebook'), + $nets = array( + NETWORK_DFRN => t('Friendica'), + NETWORK_FRND => t('Friendica'), + NETWORK_OSTATUS => t('OStatus'), + NETWORK_GNUSOCIAL => t('GNU-Social'), + NETWORK_FEED => t('RSS/Atom'), + NETWORK_ACTIVITYPUB => t('ActivityPub'), + NETWORK_MAIL => t('Email'), + NETWORK_DIASPORA => t('Diaspora'), + NETWORK_FACEBOOK => t('Facebook'), NETWORK_NOMAD => t('Nomad'), - NETWORK_ZOT6 => t('Nomad'), - NETWORK_ZOT => t('Zot'), - NETWORK_LINKEDIN => t('LinkedIn'), - NETWORK_XMPP => t('XMPP/IM'), - NETWORK_MYSPACE => t('MySpace'), - ); + NETWORK_ZOT6 => t('Nomad'), + NETWORK_ZOT => t('Zot'), + NETWORK_LINKEDIN => t('LinkedIn'), + NETWORK_XMPP => t('XMPP/IM'), + NETWORK_MYSPACE => t('MySpace'), + ); - /** - * @hooks network_to_name - * @deprecated - */ - call_hooks('network_to_name', $nets); + /** + * @hooks network_to_name + * @deprecated + */ + call_hooks('network_to_name', $nets); - $search = array_keys($nets); - $replace = array_values($nets); + $search = array_keys($nets); + $replace = array_values($nets); - return str_replace($search, $replace, $s); + return str_replace($search, $replace, $s); } /** @@ -1678,57 +1767,61 @@ function network_to_name($s) { * * \e string \b textVersion text only version of the message * * \e string \b additionalMailHeader additions to the smtp mail header */ -function z_mail($params) { +function z_mail($params) +{ - if(! $params['fromEmail']) { - $params['fromEmail'] = get_config('system','from_email'); - if(! $params['fromEmail']) - $params['fromEmail'] = 'Administrator' . '@' . App::get_hostname(); - } - if(! $params['fromName']) { - $params['fromName'] = get_config('system','from_email_name'); - if(! $params['fromName']) - $params['fromName'] = System::get_site_name(); - } - if(! $params['replyTo']) { - $params['replyTo'] = get_config('system','reply_address'); - if(! $params['replyTo']) - $params['replyTo'] = 'noreply' . '@' . App::get_hostname(); - } + if (! $params['fromEmail']) { + $params['fromEmail'] = get_config('system', 'from_email'); + if (! $params['fromEmail']) { + $params['fromEmail'] = 'Administrator' . '@' . App::get_hostname(); + } + } + if (! $params['fromName']) { + $params['fromName'] = get_config('system', 'from_email_name'); + if (! $params['fromName']) { + $params['fromName'] = System::get_site_name(); + } + } + if (! $params['replyTo']) { + $params['replyTo'] = get_config('system', 'reply_address'); + if (! $params['replyTo']) { + $params['replyTo'] = 'noreply' . '@' . App::get_hostname(); + } + } - $params['sent'] = false; - $params['result'] = false; + $params['sent'] = false; + $params['result'] = false; - /** - * @hooks email_send - * * \e params @see z_mail() - */ - call_hooks('email_send', $params); + /** + * @hooks email_send + * * \e params @see z_mail() + */ + call_hooks('email_send', $params); - if($params['sent']) { - logger('notification: z_mail returns ' . (($params['result']) ? 'success' : 'failure'), LOGGER_DEBUG); - return $params['result']; - } + if ($params['sent']) { + logger('notification: z_mail returns ' . (($params['result']) ? 'success' : 'failure'), LOGGER_DEBUG); + return $params['result']; + } - $fromName = email_header_encode(html_entity_decode($params['fromName'],ENT_QUOTES,'UTF-8'),'UTF-8'); - $messageSubject = email_header_encode(html_entity_decode($params['messageSubject'],ENT_QUOTES,'UTF-8'),'UTF-8'); + $fromName = email_header_encode(html_entity_decode($params['fromName'], ENT_QUOTES, 'UTF-8'), 'UTF-8'); + $messageSubject = email_header_encode(html_entity_decode($params['messageSubject'], ENT_QUOTES, 'UTF-8'), 'UTF-8'); - $messageHeader = - $params['additionalMailHeader'] . + $messageHeader = + $params['additionalMailHeader'] . - "From: $fromName <{$params['fromEmail']}>" .PHP_EOL . - "Reply-To: $fromName <{$params['replyTo']}>" . PHP_EOL . - "Content-Type: text/plain; charset=UTF-8"; + "From: $fromName <{$params['fromEmail']}>" . PHP_EOL . + "Reply-To: $fromName <{$params['replyTo']}>" . PHP_EOL . + "Content-Type: text/plain; charset=UTF-8"; - // send the message - $res = mail( - $params['toEmail'], // send to address - $messageSubject, // subject - $params['textVersion'], - $messageHeader // message headers - ); - logger('notification: z_mail returns ' . (($res) ? 'success' : 'failure'), LOGGER_DEBUG); - return $res; + // send the message + $res = mail( + $params['toEmail'], // send to address + $messageSubject, // subject + $params['textVersion'], + $messageHeader // message headers + ); + logger('notification: z_mail returns ' . (($res) ? 'success' : 'failure'), LOGGER_DEBUG); + return $res; } @@ -1738,50 +1831,52 @@ function z_mail($params) { * @param string $host * @return string */ -function probe_api_path($host) { +function probe_api_path($host) +{ - $schemes = ['https', 'http' ]; - $paths = ['/api/z/1.0/version', '/api/red/version' ]; + $schemes = ['https', 'http' ]; + $paths = ['/api/z/1.0/version', '/api/red/version' ]; - foreach($schemes as $scheme) { - foreach($paths as $path) { - $curpath = $scheme . '://' . $host . $path; - $x = z_fetch_url($curpath); - if($x['success'] && ! strpos($x['body'], 'not implemented')) - return str_replace('version', '', $curpath); - } - } + foreach ($schemes as $scheme) { + foreach ($paths as $path) { + $curpath = $scheme . '://' . $host . $path; + $x = z_fetch_url($curpath); + if ($x['success'] && ! strpos($x['body'], 'not implemented')) { + return str_replace('version', '', $curpath); + } + } + } - return ''; + return ''; } -function service_plink($contact, $guid) { +function service_plink($contact, $guid) +{ - $plink = ''; + $plink = ''; - $m = parse_url($contact['xchan_url']); - if($m) { - $url = $m['scheme'] . '://' . $m['host'] . ((isset($m['port']) && intval($m['port'])) ? ':' . intval($m['port']) : ''); - } - else { - $url = 'https://' . substr($contact['xchan_addr'],strpos($contact['xchan_addr'],'@')+1); - } + $m = parse_url($contact['xchan_url']); + if ($m) { + $url = $m['scheme'] . '://' . $m['host'] . ((isset($m['port']) && intval($m['port'])) ? ':' . intval($m['port']) : ''); + } else { + $url = 'https://' . substr($contact['xchan_addr'], strpos($contact['xchan_addr'], '@') + 1); + } - $handle = substr($contact['xchan_addr'], 0, strpos($contact['xchan_addr'],'@')); + $handle = substr($contact['xchan_addr'], 0, strpos($contact['xchan_addr'], '@')); - $plink = $url . '/channel/' . $handle . '?f=&mid=' . $guid; + $plink = $url . '/channel/' . $handle . '?f=&mid=' . $guid; - $x = [ 'xchan' => $contact, 'guid' => $guid, 'url' => $url, 'plink' => $plink ]; - /** - * @hooks service_plink - * * \e array \b xchan - * * \e string \b guid - * * \e string \b url - * * \e string \b plink will get returned - */ - call_hooks('service_plink', $x); + $x = [ 'xchan' => $contact, 'guid' => $guid, 'url' => $url, 'plink' => $plink ]; + /** + * @hooks service_plink + * * \e array \b xchan + * * \e string \b guid + * * \e string \b url + * * \e string \b plink will get returned + */ + call_hooks('service_plink', $x); - return $x['plink']; + return $x['plink']; } @@ -1792,111 +1887,117 @@ function service_plink($contact, $guid) { * @param string $acceptedTypes by default false will use $_SERVER['HTTP_ACCEPT'] * @return array|NULL */ -function getBestSupportedMimeType($mimeTypes = null, $acceptedTypes = false) { - // Values will be stored in this array - $AcceptTypes = []; +function getBestSupportedMimeType($mimeTypes = null, $acceptedTypes = false) +{ + // Values will be stored in this array + $AcceptTypes = []; - if($acceptedTypes === false) { - $acceptedTypes = ((isset($_SERVER['HTTP_ACCEPT']) && $_SERVER['HTTP_ACCEPT']) ? $_SERVER['HTTP_ACCEPT'] : EMPTY_STR); - } + if ($acceptedTypes === false) { + $acceptedTypes = ((isset($_SERVER['HTTP_ACCEPT']) && $_SERVER['HTTP_ACCEPT']) ? $_SERVER['HTTP_ACCEPT'] : EMPTY_STR); + } - // Accept header is case insensitive, and whitespace isn’t important - $accept = strtolower(str_replace(' ', '', $acceptedTypes)); - // divide it into parts in the place of a "," - $accept = explode(',', $accept); - foreach ($accept as $a) { - // the default quality is 1. - $q = 1; - // check if there is a different quality - if (strpos($a, ';q=')) { - // divide "mime/type;q=X" into two parts: "mime/type" i "X" - list($a, $q) = explode(';q=', $a); - } - // mime-type $a is accepted with the quality $q - // WARNING: $q == 0 means, that mime-type isn’t supported! - $AcceptTypes[$a] = $q; - } - arsort($AcceptTypes); + // Accept header is case insensitive, and whitespace isn’t important + $accept = strtolower(str_replace(' ', '', $acceptedTypes)); + // divide it into parts in the place of a "," + $accept = explode(',', $accept); + foreach ($accept as $a) { + // the default quality is 1. + $q = 1; + // check if there is a different quality + if (strpos($a, ';q=')) { + // divide "mime/type;q=X" into two parts: "mime/type" i "X" + list($a, $q) = explode(';q=', $a); + } + // mime-type $a is accepted with the quality $q + // WARNING: $q == 0 means, that mime-type isn’t supported! + $AcceptTypes[$a] = $q; + } + arsort($AcceptTypes); - // if no parameter was passed, just return parsed data - if (!$mimeTypes) return $AcceptTypes; + // if no parameter was passed, just return parsed data + if (!$mimeTypes) { + return $AcceptTypes; + } - $mimeTypes = array_map('strtolower', (array)$mimeTypes); + $mimeTypes = array_map('strtolower', (array)$mimeTypes); - // let’s check our supported types: - foreach ($AcceptTypes as $mime => $q) { - if ($q && in_array($mime, $mimeTypes)) return $mime; - } + // let’s check our supported types: + foreach ($AcceptTypes as $mime => $q) { + if ($q && in_array($mime, $mimeTypes)) { + return $mime; + } + } - // no mime-type found - return null; + // no mime-type found + return null; } /** * @brief Perform caching for jsonld normaliser. * * @param string $url - * @return mixed|boolean|array + * @return mixed|bool|array */ -function jsonld_document_loader($url) { +function jsonld_document_loader($url) +{ - require_once('library/jsonld/jsonld.php'); + require_once('library/jsonld/jsonld.php'); - $recursion = 0; + $recursion = 0; - $x = debug_backtrace(); - if($x) { - foreach($x as $n) { - if($n['function'] === __FUNCTION__) { - $recursion ++; - } - } - } - if($recursion > 5) { - logger('jsonld bomb detected at: ' . $url); - killme(); - } + $x = debug_backtrace(); + if ($x) { + foreach ($x as $n) { + if ($n['function'] === __FUNCTION__) { + $recursion++; + } + } + } + if ($recursion > 5) { + logger('jsonld bomb detected at: ' . $url); + killme(); + } - $cachepath = 'cache/ldcache'; - if(! is_dir($cachepath)) - os_mkdir($cachepath, STORAGE_DEFAULT_PERMISSIONS, true); + $cachepath = 'cache/ldcache'; + if (! is_dir($cachepath)) { + os_mkdir($cachepath, STORAGE_DEFAULT_PERMISSIONS, true); + } - $filename = $cachepath . '/' . urlencode($url); - if(file_exists($filename) && filemtime($filename) > time() - (12 * 60 * 60)) { - return json_decode(file_get_contents($filename)); - } + $filename = $cachepath . '/' . urlencode($url); + if (file_exists($filename) && filemtime($filename) > time() - (12 * 60 * 60)) { + return json_decode(file_get_contents($filename)); + } - $r = jsonld_default_document_loader($url); - if($r) { - file_put_contents($filename, json_encode($r)); - return $r; - } + $r = jsonld_default_document_loader($url); + if ($r) { + file_put_contents($filename, json_encode($r)); + return $r; + } - logger('not found'); - if(file_exists($filename)) { - return json_decode(file_get_contents($filename)); - } + logger('not found'); + if (file_exists($filename)) { + return json_decode(file_get_contents($filename)); + } - return []; + return []; } -function is_https_request() { +function is_https_request() +{ - $https = false; + $https = false; - if (array_key_exists('HTTPS',$_SERVER) && $_SERVER['HTTPS']) { - $https = true; - } - elseif (array_key_exists('SERVER_PORT',$_SERVER) && intval($_SERVER['SERVER_PORT']) === 443) { - $https = true; - } - elseif (array_key_exists('HTTP_X_FORWARDED_PROTO',$_SERVER) && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https') { - $https = true; // technically this is not true, but pretending it is will allow reverse proxies to work - } + if (array_key_exists('HTTPS', $_SERVER) && $_SERVER['HTTPS']) { + $https = true; + } elseif (array_key_exists('SERVER_PORT', $_SERVER) && intval($_SERVER['SERVER_PORT']) === 443) { + $https = true; + } elseif (array_key_exists('HTTP_X_FORWARDED_PROTO', $_SERVER) && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https') { + $https = true; // technically this is not true, but pretending it is will allow reverse proxies to work + } - return $https; + return $https; } /** @@ -1908,15 +2009,13 @@ function is_https_request() { * result always returns the leading slash */ -function get_request_string($url) { +function get_request_string($url) +{ - $m = parse_url($url); - if ($m) { - return ( (isset($m['path']) ? $m['path'] : '/' ) . (isset($m['query']) ? '?' . $m['query'] : EMPTY_STR) ); - } - - return EMPTY_STR; + $m = parse_url($url); + if ($m) { + return ( (isset($m['path']) ? $m['path'] : '/' ) . (isset($m['query']) ? '?' . $m['query'] : EMPTY_STR) ); + } + return EMPTY_STR; } - - diff --git a/include/oauth.php b/include/oauth.php index e27e770b5..2d71cac1f 100644 --- a/include/oauth.php +++ b/include/oauth.php @@ -1,8 +1,10 @@ -key), - dbesc($token_type), - dbesc($token) - ); + logger(__function__ . ':' . $consumer . ', ' . $token_type . ', ' . $token, LOGGER_DEBUG); - if ($r) { - $ot = new OAuth1Token($r[0]['id'],$r[0]['secret']); - $ot->scope = $r[0]['auth_scope']; - $ot->expires = $r[0]['expires']; - $ot->uid = $r[0]['uid']; - return $ot; - } - return null; - } + $r = q( + "SELECT id, secret, auth_scope, expires, uid FROM tokens WHERE client_id = '%s' AND auth_scope = '%s' AND id = '%s'", + dbesc($consumer->key), + dbesc($token_type), + dbesc($token) + ); - function lookup_nonce($consumer, $token, $nonce, $timestamp) { + if ($r) { + $ot = new OAuth1Token($r[0]['id'], $r[0]['secret']); + $ot->scope = $r[0]['auth_scope']; + $ot->expires = $r[0]['expires']; + $ot->uid = $r[0]['uid']; + return $ot; + } + return null; + } - $r = q("SELECT id, secret FROM tokens WHERE client_id = '%s' AND id = '%s' AND expires = %d", - dbesc($consumer->key), - dbesc($nonce), - intval($timestamp) - ); + public function lookup_nonce($consumer, $token, $nonce, $timestamp) + { - if ($r) { - return new OAuth1Token($r[0]['id'],$r[0]['secret']); - } - return null; - } + $r = q( + "SELECT id, secret FROM tokens WHERE client_id = '%s' AND id = '%s' AND expires = %d", + dbesc($consumer->key), + dbesc($nonce), + intval($timestamp) + ); - function new_request_token($consumer, $callback = null) { + if ($r) { + return new OAuth1Token($r[0]['id'], $r[0]['secret']); + } + return null; + } - logger(__function__ . ':' . $consumer . ', ' . $callback, LOGGER_DEBUG); + public function new_request_token($consumer, $callback = null) + { - $key = $this->gen_token(); - $sec = $this->gen_token(); - - if ($consumer->key){ - $k = $consumer->key; - } - else { - $k = $consumer; - } + logger(__function__ . ':' . $consumer . ', ' . $callback, LOGGER_DEBUG); - $r = q("INSERT INTO tokens (id, secret, client_id, auth_scope, expires, uid) VALUES ('%s','%s','%s','%s', %d, 0)", - dbesc($key), - dbesc($sec), - dbesc($k), - 'request', - time()+intval(REQUEST_TOKEN_DURATION)); + $key = $this->gen_token(); + $sec = $this->gen_token(); - if (! $r) { - return null; - } - return new OAuth1Token($key,$sec); - } + if ($consumer->key) { + $k = $consumer->key; + } else { + $k = $consumer; + } - function new_access_token($token, $consumer, $verifier = null) { + $r = q( + "INSERT INTO tokens (id, secret, client_id, auth_scope, expires, uid) VALUES ('%s','%s','%s','%s', %d, 0)", + dbesc($key), + dbesc($sec), + dbesc($k), + 'request', + time() + intval(REQUEST_TOKEN_DURATION) + ); - logger(__function__ . ':' . $token . ', ' . $consumer .', ' . $verifier, LOGGER_DEBUG); - - // return a new access token attached to this consumer - // for the user associated with this token if the request token - // is authorized - // should also invalidate the request token - - $ret = null; - - // get user for this verifier - $uverifier = get_config("oauth", $verifier); - logger(__function__ . ':' . $verifier . ', ' . $uverifier, LOGGER_DEBUG); - if (is_null($verifier) || ($uverifier !== false)) { - - $key = $this->gen_token(); - $sec = $this->gen_token(); + if (!$r) { + return null; + } + return new OAuth1Token($key, $sec); + } - $r = q("INSERT INTO tokens (id, secret, client_id, auth_scope, expires, uid) VALUES ('%s','%s','%s','%s', %d, %d)", - dbesc($key), - dbesc($sec), - dbesc($consumer->key), - 'access', - time()+intval(ACCESS_TOKEN_DURATION), - intval($uverifier)); + public function new_access_token($token, $consumer, $verifier = null) + { - if ($r) { - $ret = new OAuth1Token($key,$sec); - } - } - - - q("DELETE FROM tokens WHERE id='%s'", $token->key); - - - if (! is_null($ret) && $uverifier !== false) { - del_config('oauth', $verifier); - } - return $ret; - } + logger(__function__ . ':' . $token . ', ' . $consumer . ', ' . $verifier, LOGGER_DEBUG); + + // return a new access token attached to this consumer + // for the user associated with this token if the request token + // is authorized + // should also invalidate the request token + + $ret = null; + + // get user for this verifier + $uverifier = get_config("oauth", $verifier); + logger(__function__ . ':' . $verifier . ', ' . $uverifier, LOGGER_DEBUG); + if (is_null($verifier) || ($uverifier !== false)) { + $key = $this->gen_token(); + $sec = $this->gen_token(); + + $r = q( + "INSERT INTO tokens (id, secret, client_id, auth_scope, expires, uid) VALUES ('%s','%s','%s','%s', %d, %d)", + dbesc($key), + dbesc($sec), + dbesc($consumer->key), + 'access', + time() + intval(ACCESS_TOKEN_DURATION), + intval($uverifier) + ); + + if ($r) { + $ret = new OAuth1Token($key, $sec); + } + } + + + q("DELETE FROM tokens WHERE id='%s'", $token->key); + + + if (!is_null($ret) && $uverifier !== false) { + del_config('oauth', $verifier); + } + return $ret; + } } -class ZotOAuth1 extends OAuth1Server { +class ZotOAuth1 extends OAuth1Server +{ - function __construct() { - parent::__construct(new ZotOAuth1DataStore()); - $this->add_signature_method(new OAuth1SignatureMethod_PLAINTEXT()); - $this->add_signature_method(new OAuth1SignatureMethod_HMAC_SHA1()); - } - - function loginUser($uid) { + public function __construct() + { + parent::__construct(new ZotOAuth1DataStore()); + $this->add_signature_method(new OAuth1SignatureMethod_PLAINTEXT()); + $this->add_signature_method(new OAuth1SignatureMethod_HMAC_SHA1()); + } - logger("ZotOAuth1::loginUser $uid"); + public function loginUser($uid) + { - $r = q("SELECT * FROM channel WHERE channel_id = %d LIMIT 1", - intval($uid) - ); - if ($r) { - $record = $r[0]; - } - else { - logger('ZotOAuth1::loginUser failure: ' . print_r($_SERVER,true), LOGGER_DEBUG); - header('HTTP/1.0 401 Unauthorized'); - echo('This api requires login'); - killme(); - } + logger("ZotOAuth1::loginUser $uid"); - $_SESSION['uid'] = $record['channel_id']; - $_SESSION['addr'] = $_SERVER['REMOTE_ADDR']; + $r = q( + "SELECT * FROM channel WHERE channel_id = %d LIMIT 1", + intval($uid) + ); + if ($r) { + $record = $r[0]; + } else { + logger('ZotOAuth1::loginUser failure: ' . print_r($_SERVER, true), LOGGER_DEBUG); + header('HTTP/1.0 401 Unauthorized'); + echo('This api requires login'); + killme(); + } - $x = q("select * from account where account_id = %d limit 1", - intval($record['channel_account_id']) - ); - if ($x) { - require_once('include/security.php'); - authenticate_success($x[0],null,true,false,true,true); - $_SESSION['allow_api'] = true; - } - } - + $_SESSION['uid'] = $record['channel_id']; + $_SESSION['addr'] = $_SERVER['REMOTE_ADDR']; + + $x = q( + "select * from account where account_id = %d limit 1", + intval($record['channel_account_id']) + ); + if ($x) { + require_once('include/security.php'); + authenticate_success($x[0], null, true, false, true, true); + $_SESSION['allow_api'] = true; + } + } } - diff --git a/include/oembed.php b/include/oembed.php index e3053f164..50f68b581 100644 --- a/include/oembed.php +++ b/include/oembed.php @@ -1,450 +1,472 @@ -' . $result['url'] . ''; + } - $result = oembed_action($embedurl); - if($result['action'] === 'block') { - return '' . $result['url'] . ''; - } - - $j = oembed_fetch_url($result['url']); - $s = oembed_format_object($j); - return $s; + $j = oembed_fetch_url($result['url']); + $s = oembed_format_object($j); + return $s; } -function oembed_action($embedurl) { +function oembed_action($embedurl) +{ - $host = ''; - $action = 'filter'; + $host = ''; + $action = 'filter'; - $embedurl = trim(str_replace('&','&', $embedurl)); + $embedurl = trim(str_replace('&', '&', $embedurl)); - //logger('oembed_action: ' . $embedurl, LOGGER_DEBUG, LOG_INFO); + //logger('oembed_action: ' . $embedurl, LOGGER_DEBUG, LOG_INFO); - if(strpos($embedurl,'http://') === 0) { - if(intval(get_config('system','embed_sslonly'))) { - $action = 'block'; - } - } + if (strpos($embedurl, 'http://') === 0) { + if (intval(get_config('system', 'embed_sslonly'))) { + $action = 'block'; + } + } - if(strpos($embedurl,'.well-known') !== false) - $action = 'block'; + if (strpos($embedurl, '.well-known') !== false) { + $action = 'block'; + } - // site allow/deny list + // site allow/deny list - if(($x = get_config('system','embed_deny'))) { - if(($x) && (! is_array($x))) - $x = explode("\n",$x); - if($x) { - foreach($x as $ll) { - $t = trim($ll); - if(($t) && (strpos($embedurl,$t) !== false)) { - $action = 'block'; - break; - } - } - } - } - - $found = false; + if (($x = get_config('system', 'embed_deny'))) { + if (($x) && (! is_array($x))) { + $x = explode("\n", $x); + } + if ($x) { + foreach ($x as $ll) { + $t = trim($ll); + if (($t) && (strpos($embedurl, $t) !== false)) { + $action = 'block'; + break; + } + } + } + } - if(($x = get_config('system','embed_allow'))) { - if(($x) && (! is_array($x))) - $x = explode("\n",$x); - if($x) { - foreach($x as $ll) { - $t = trim($ll); - if(($t) && (strpos($embedurl,$t) !== false) && ($action !== 'block')) { - $found = true; - $action = 'allow'; - break; - } - } - } - if((! $found) && ($action !== 'block')) { - $action = 'filter'; - } - } + $found = false; - // allow individual members to block something that wasn't blocked already. - // They cannot over-ride the site to allow or change the filtering on an - // embed that is not allowed by the site admin. + if (($x = get_config('system', 'embed_allow'))) { + if (($x) && (! is_array($x))) { + $x = explode("\n", $x); + } + if ($x) { + foreach ($x as $ll) { + $t = trim($ll); + if (($t) && (strpos($embedurl, $t) !== false) && ($action !== 'block')) { + $found = true; + $action = 'allow'; + break; + } + } + } + if ((! $found) && ($action !== 'block')) { + $action = 'filter'; + } + } - if(local_channel()) { - if(($x = get_pconfig(local_channel(),'system','embed_deny'))) { - if(($x) && (! is_array($x))) - $x = explode("\n",$x); - if($x) { - foreach($x as $ll) { - $t = trim($ll); - if(($t) && (strpos($embedurl,$t) !== false)) { - $action = 'block'; - break; - } - } - } - } - } + // allow individual members to block something that wasn't blocked already. + // They cannot over-ride the site to allow or change the filtering on an + // embed that is not allowed by the site admin. - $arr = array('url' => $embedurl, 'action' => $action); - call_hooks('oembed_action',$arr); + if (local_channel()) { + if (($x = get_pconfig(local_channel(), 'system', 'embed_deny'))) { + if (($x) && (! is_array($x))) { + $x = explode("\n", $x); + } + if ($x) { + foreach ($x as $ll) { + $t = trim($ll); + if (($t) && (strpos($embedurl, $t) !== false)) { + $action = 'block'; + break; + } + } + } + } + } - //logger('action: ' . $arr['action'] . ' url: ' . $arr['url'], LOGGER_DEBUG,LOG_DEBUG); + $arr = array('url' => $embedurl, 'action' => $action); + call_hooks('oembed_action', $arr); - return $arr; + //logger('action: ' . $arr['action'] . ' url: ' . $arr['url'], LOGGER_DEBUG,LOG_DEBUG); + return $arr; } // if the url is embeddable with oembed, return the bbcode link. -function oembed_process($url) { +function oembed_process($url) +{ - $j = oembed_fetch_url($url); - logger('oembed_process: ' . print_r($j,true), LOGGER_DATA, LOG_DEBUG); - if($j && $j['type'] !== 'error') - return '[embed]' . $url . '[/embed]'; - return false; + $j = oembed_fetch_url($url); + logger('oembed_process: ' . print_r($j, true), LOGGER_DATA, LOG_DEBUG); + if ($j && $j['type'] !== 'error') { + return '[embed]' . $url . '[/embed]'; + } + return false; } -function oembed_fetch_url($embedurl){ +function oembed_fetch_url($embedurl) +{ - $noexts = [ '.mp3', '.mp4', '.ogg', '.ogv', '.oga', '.ogm', '.webm', '.opus', '.m4a', '.mov' ]; + $noexts = [ '.mp3', '.mp4', '.ogg', '.ogv', '.oga', '.ogm', '.webm', '.opus', '.m4a', '.mov' ]; - $result = oembed_action($embedurl); + $result = oembed_action($embedurl); - $embedurl = $result['url']; - $action = $result['action']; + $embedurl = $result['url']; + $action = $result['action']; - foreach($noexts as $ext) { - if(strpos(strtolower($embedurl),$ext) !== false) { - $action = 'block'; - } - } + foreach ($noexts as $ext) { + if (strpos(strtolower($embedurl), $ext) !== false) { + $action = 'block'; + } + } - $txt = null; + $txt = null; - // we should try to cache this and avoid a lookup on each render - $is_matrix = is_matrix_url($embedurl); + // we should try to cache this and avoid a lookup on each render + $is_matrix = is_matrix_url($embedurl); - $zrl = ((get_config('system','oembed_zrl')) ? $is_matrix : false); + $zrl = ((get_config('system', 'oembed_zrl')) ? $is_matrix : false); - $furl = ((local_channel() && $zrl) ? zid($embedurl) : $embedurl); + $furl = ((local_channel() && $zrl) ? zid($embedurl) : $embedurl); - if($action !== 'block' && (! get_config('system','oembed_cache_disable'))) { - $txt = Cache::get('[' . App::$videowidth . '] ' . $furl); - } + if ($action !== 'block' && (! get_config('system', 'oembed_cache_disable'))) { + $txt = Cache::get('[' . App::$videowidth . '] ' . $furl); + } - if(strpos(strtolower($embedurl),'.pdf') !== false && get_config('system','inline_pdf')) { - $action = 'allow'; - $j = [ - 'html' => '', - 'title' => t('View PDF'), - 'type' => 'pdf' - ]; - - // set $txt to something so that we don't attempt to fetch what could be a lengthy pdf. - $txt = EMPTY_STR; - } + if (strpos(strtolower($embedurl), '.pdf') !== false && get_config('system', 'inline_pdf')) { + $action = 'allow'; + $j = [ + 'html' => '', + 'title' => t('View PDF'), + 'type' => 'pdf' + ]; - if(is_null($txt)) { + // set $txt to something so that we don't attempt to fetch what could be a lengthy pdf. + $txt = EMPTY_STR; + } - $txt = ""; + if (is_null($txt)) { + $txt = ""; - if ($action !== 'block') { - // try oembed autodiscovery - $redirects = 0; - $result = z_fetch_url($furl, false, $redirects, - [ - 'timeout' => 30, - 'accept_content' => "text/*", - 'novalidate' => true, - 'session' => ((local_channel() && $zrl) ? true : false) - ] - ); + if ($action !== 'block') { + // try oembed autodiscovery + $redirects = 0; + $result = z_fetch_url( + $furl, + false, + $redirects, + [ + 'timeout' => 30, + 'accept_content' => "text/*", + 'novalidate' => true, + 'session' => ((local_channel() && $zrl) ? true : false) + ] + ); - if($result['success']) - $html_text = $result['body']; - else - logger('fetch failure: ' . $furl); + if ($result['success']) { + $html_text = $result['body']; + } else { + logger('fetch failure: ' . $furl); + } - if($html_text) { - $dom = new DOMDocument(); - @$dom->loadHTML($html_text); - if ($dom) { - $xpath = new DOMXPath($dom); - $attr = "oembed"; - $xattr = oe_build_xpath("class","oembed"); + if ($html_text) { + $dom = new DOMDocument(); + @$dom->loadHTML($html_text); + if ($dom) { + $xpath = new DOMXPath($dom); + $attr = "oembed"; + $xattr = oe_build_xpath("class", "oembed"); - $entries = $xpath->query("//link[@type='application/json+oembed']"); - foreach($entries as $e){ - $href = $e->getAttributeNode("href")->nodeValue; - - $x = z_fetch_url($href . '&maxwidth=' . App::$videowidth); - if($x['success']) - $txt = $x['body']; - else - logger('fetch failed: ' . $href); - break; - } - // soundcloud is now using text/json+oembed instead of application/json+oembed, - // others may be also - $entries = $xpath->query("//link[@type='text/json+oembed']"); - foreach($entries as $e){ - $href = $e->getAttributeNode("href")->nodeValue; - $x = z_fetch_url($href . '&maxwidth=' . App::$videowidth); - if($x['success']) - $txt = $x['body']; - else - logger('json fetch failed: ' . $href); - break; - } - } - } - } - - if ($txt==false || $txt=="") { - $x = array('url' => $embedurl,'videowidth' => App::$videowidth); - call_hooks('oembed_probe',$x); - if(array_key_exists('embed',$x)) - $txt = $x['embed']; - } - - $txt=trim($txt); + $entries = $xpath->query("//link[@type='application/json+oembed']"); + foreach ($entries as $e) { + $href = $e->getAttributeNode("href")->nodeValue; - if ($txt[0]!="{") $txt='{"type":"error"}'; - - // save in cache + $x = z_fetch_url($href . '&maxwidth=' . App::$videowidth); + if ($x['success']) { + $txt = $x['body']; + } else { + logger('fetch failed: ' . $href); + } + break; + } + // soundcloud is now using text/json+oembed instead of application/json+oembed, + // others may be also + $entries = $xpath->query("//link[@type='text/json+oembed']"); + foreach ($entries as $e) { + $href = $e->getAttributeNode("href")->nodeValue; + $x = z_fetch_url($href . '&maxwidth=' . App::$videowidth); + if ($x['success']) { + $txt = $x['body']; + } else { + logger('json fetch failed: ' . $href); + } + break; + } + } + } + } - if(! get_config('system','oembed_cache_disable')) - Cache::set('[' . App::$videowidth . '] ' . $furl, $txt); + if ($txt == false || $txt == "") { + $x = array('url' => $embedurl,'videowidth' => App::$videowidth); + call_hooks('oembed_probe', $x); + if (array_key_exists('embed', $x)) { + $txt = $x['embed']; + } + } - } + $txt = trim($txt); + + if ($txt[0] != "{") { + $txt = '{"type":"error"}'; + } + + // save in cache + + if (! get_config('system', 'oembed_cache_disable')) { + Cache::set('[' . App::$videowidth . '] ' . $furl, $txt); + } + } - if(! $j) { - $j = json_decode($txt,true); - } + if (! $j) { + $j = json_decode($txt, true); + } - if(! $j) { - $j = []; - } + if (! $j) { + $j = []; + } - if($action === 'filter') { - if($j['html']) { - $orig = $j['html']; - $allow_position = (($is_matrix) ? true : false); + if ($action === 'filter') { + if ($j['html']) { + $orig = $j['html']; + $allow_position = (($is_matrix) ? true : false); - // some sites (e.g. Mastodon) wrap their entire embed in an iframe - // which we will purify away and which we provide anyway. - // So if we see this, grab the frame src url and use that - // as the embed content - which will still need to be purified. + // some sites (e.g. Mastodon) wrap their entire embed in an iframe + // which we will purify away and which we provide anyway. + // So if we see this, grab the frame src url and use that + // as the embed content - which will still need to be purified. - if(preg_match('#\"; - switch ($j['type']) { - case "video": { - if (isset($j['thumbnail_url'])) { - $tw = (isset($j['thumbnail_width'])) ? $j['thumbnail_width'] : 200; - $th = (isset($j['thumbnail_height'])) ? $j['thumbnail_height'] : 180; - $tr = $tw/$th; - - $th=120; $tw = $th*$tr; - $tpl=get_markup_template('oembed_video.tpl'); + $ret = ""; + switch ($j['type']) { + case "video": { + if (isset($j['thumbnail_url'])) { + $tw = (isset($j['thumbnail_width'])) ? $j['thumbnail_width'] : 200; + $th = (isset($j['thumbnail_height'])) ? $j['thumbnail_height'] : 180; + $tr = $tw / $th; - $ret.=replace_macros($tpl, array( + $th = 120; + $tw = $th * $tr; + $tpl = get_markup_template('oembed_video.tpl'); + + $ret .= replace_macros($tpl, array( '$baseurl' => z_root(), - '$embedurl'=>$embedurl, - '$escapedhtml'=>base64_encode($jhtml), - '$tw'=>$tw, - '$th'=>$th, - '$turl'=> $j['thumbnail_url'], - )); - - } else { - $ret=$jhtml; - } - $ret.="
                    "; - }; break; - case "photo": { - $ret.= ""; - $ret.="
                    "; - }; break; - case "link": { - if($j['thumbnail_url']) { - if(is_matrix_url($embedurl)) { - $embedurl = zid($embedurl); - $j['thumbnail_url'] = zid($j['thumbnail_url']); - } - $ret = 'thumbnail

                    '; - } + '$embedurl' => $embedurl, + '$escapedhtml' => base64_encode($jhtml), + '$tw' => $tw, + '$th' => $th, + '$turl' => $j['thumbnail_url'], + )); + } else { + $ret = $jhtml; + } + $ret .= "
                    "; + } + break; + case "photo": { + $ret .= ""; + $ret .= "
                    "; + } + break; + case "link": { + if ($j['thumbnail_url']) { + if (is_matrix_url($embedurl)) { + $embedurl = zid($embedurl); + $j['thumbnail_url'] = zid($j['thumbnail_url']); + } + $ret = 'thumbnail

                    '; + } - //$ret = "".$j['title'].""; - }; break; - case 'pdf': { - $ret = $j['html']; - break; - } + //$ret = "".$j['title'].""; + } + break; + case 'pdf': { + $ret = $j['html']; + break; + } - case "rich": - if($j['zrl']) { - $ret = ((preg_match('/^]+>(.*?)<\/div>$/is',$j['html'],$o)) ? $o[1] : $j['html']); - } - else { - $ret.= $jhtml; - } - break; - } + case "rich": + if ($j['zrl']) { + $ret = ((preg_match('/^]+>(.*?)<\/div>$/is', $j['html'], $o)) ? $o[1] : $j['html']); + } else { + $ret .= $jhtml; + } + break; + } - // add link to source if not present in "rich" type - if ( $j['type'] != 'rich' || !strpos($j['html'],$embedurl) ){ - $embedlink = (isset($j['title']))?$j['title'] : $embedurl; - $ret .= '
                    ' . "$embedlink"; - $ret .= "
                    "; - if (isset($j['author_name'])) $ret .= t(' by ') . $j['author_name']; - if (isset($j['provider_name'])) $ret .= t(' on ') . $j['provider_name']; - } else { - // add for html2bbcode conversion - $ret .= "
                    $embedurl"; - } - $ret.="
                    "; - return mb_convert_encoding($ret, 'HTML-ENTITIES', mb_detect_encoding($ret)); + // add link to source if not present in "rich" type + if ($j['type'] != 'rich' || !strpos($j['html'], $embedurl)) { + $embedlink = (isset($j['title'])) ? $j['title'] : $embedurl; + $ret .= '
                    ' . "$embedlink"; + $ret .= "
                    "; + if (isset($j['author_name'])) { + $ret .= t(' by ') . $j['author_name']; + } + if (isset($j['provider_name'])) { + $ret .= t(' on ') . $j['provider_name']; + } + } else { + // add for html2bbcode conversion + $ret .= "
                    $embedurl"; + } + $ret .= "
                    "; + return mb_convert_encoding($ret, 'HTML-ENTITIES', mb_detect_encoding($ret)); } -function oembed_iframe($src,$width,$height) { - $scroll = ' scrolling="no" '; - if(! $width || strstr($width,'%')) { - $width = '640'; - $scroll = ' scrolling="auto" '; - } - if(! $height || strstr($height,'%')) { - $height = '300'; - $scroll = ' scrolling="auto" '; - } +function oembed_iframe($src, $width, $height) +{ + $scroll = ' scrolling="no" '; + if (! $width || strstr($width, '%')) { + $width = '640'; + $scroll = ' scrolling="auto" '; + } + if (! $height || strstr($height, '%')) { + $height = '300'; + $scroll = ' scrolling="auto" '; + } - // try and leave some room for the description line. - $height = intval($height) + 80; - $width = intval($width) + 40; + // try and leave some room for the description line. + $height = intval($height) + 80; + $width = intval($width) + 40; - $s = z_root() . '/oembed/' . base64url_encode($src); + $s = z_root() . '/oembed/' . base64url_encode($src); - // Make sure any children are sandboxed within their own iframe. - - return ''; + // Make sure any children are sandboxed within their own iframe. + return ''; } -function oembed_bbcode2html($text){ - $stopoembed = get_config("system","no_oembed"); - if ($stopoembed == true){ - return preg_replace("/\[embed\](.+?)\[\/embed\]/is", "". t('Embedding disabled') ." : $1" ,$text); - } - return preg_replace_callback("/\[embed\](.+?)\[\/embed\]/is", 'oembed_replacecb' ,$text); +function oembed_bbcode2html($text) +{ + $stopoembed = get_config("system", "no_oembed"); + if ($stopoembed == true) { + return preg_replace("/\[embed\](.+?)\[\/embed\]/is", "" . t('Embedding disabled') . " : $1", $text); + } + return preg_replace_callback("/\[embed\](.+?)\[\/embed\]/is", 'oembed_replacecb', $text); } -function oe_build_xpath($attr, $value){ - // http://westhoffswelt.de/blog/0036_xpath_to_select_html_by_class.html - return "contains( normalize-space( @$attr ), ' $value ' ) or substring( normalize-space( @$attr ), 1, string-length( '$value' ) + 1 ) = '$value ' or substring( normalize-space( @$attr ), string-length( @$attr ) - string-length( '$value' ) ) = ' $value' or @$attr = '$value'"; +function oe_build_xpath($attr, $value) +{ + // http://westhoffswelt.de/blog/0036_xpath_to_select_html_by_class.html + return "contains( normalize-space( @$attr ), ' $value ' ) or substring( normalize-space( @$attr ), 1, string-length( '$value' ) + 1 ) = '$value ' or substring( normalize-space( @$attr ), string-length( @$attr ) - string-length( '$value' ) ) = ' $value' or @$attr = '$value'"; } -function oe_get_inner_html( $node ) { - $innerHTML= ''; +function oe_get_inner_html($node) +{ + $innerHTML = ''; $children = $node->childNodes; foreach ($children as $child) { - $innerHTML .= $child->ownerDocument->saveXML( $child ); + $innerHTML .= $child->ownerDocument->saveXML($child); } return $innerHTML; -} +} /** * Find .... * and replace it with [embed]url[/embed] */ -function oembed_html2bbcode($text) { - // start parser only if 'oembed' is in text - if (strpos($text, "oembed")) { - - // convert non ascii chars to html entities - $html_text = mb_convert_encoding($text, 'HTML-ENTITIES', mb_detect_encoding($text)); - - // If it doesn't parse at all, just return the text. - $dom = new DOMDocument(); - @$dom->loadHTML($html_text); - if ($dom) { - $xpath = new DOMXPath($dom); - $attr = "oembed"; - - $xattr = oe_build_xpath("class","oembed"); - $entries = $xpath->query("//span[$xattr]"); +function oembed_html2bbcode($text) +{ + // start parser only if 'oembed' is in text + if (strpos($text, "oembed")) { + // convert non ascii chars to html entities + $html_text = mb_convert_encoding($text, 'HTML-ENTITIES', mb_detect_encoding($text)); - $xattr = "@rel='oembed'";//oe_build_xpath("rel","oembed"); - foreach($entries as $e) { - $href = $xpath->evaluate("a[$xattr]/@href", $e)->item(0)->nodeValue; - if(!is_null($href)) $e->parentNode->replaceChild(new DOMText("[embed]".$href."[/embed]"), $e); - } - return oe_get_inner_html( $dom->getElementsByTagName("body")->item(0) ); - } - } - return $text; + // If it doesn't parse at all, just return the text. + $dom = new DOMDocument(); + @$dom->loadHTML($html_text); + if ($dom) { + $xpath = new DOMXPath($dom); + $attr = "oembed"; + $xattr = oe_build_xpath("class", "oembed"); + $entries = $xpath->query("//span[$xattr]"); + + $xattr = "@rel='oembed'";//oe_build_xpath("rel","oembed"); + foreach ($entries as $e) { + $href = $xpath->evaluate("a[$xattr]/@href", $e)->item(0)->nodeValue; + if (!is_null($href)) { + $e->parentNode->replaceChild(new DOMText("[embed]" . $href . "[/embed]"), $e); + } + } + return oe_get_inner_html($dom->getElementsByTagName("body")->item(0)); + } + } + return $text; } - - - diff --git a/include/perm_upgrade.php b/include/perm_upgrade.php index 9eb1efba2..e09be409d 100644 --- a/include/perm_upgrade.php +++ b/include/perm_upgrade.php @@ -1,239 +1,290 @@ $v) { - set_pconfig($channel['channel_id'],'autoperms',$k,$v); - } - } - } +function autoperms_upgrade($channel) +{ + $x = get_pconfig($channel['channel_id'], 'system', 'autoperms'); + if (intval($x)) { + $y = perms_int_to_array($x); + if ($y) { + foreach ($y as $k => $v) { + set_pconfig($channel['channel_id'], 'autoperms', $k, $v); + } + } + } } -function perm_abook_upgrade($abook) { +function perm_abook_upgrade($abook) +{ - $x = perms_int_to_array($abook['abook_their_perms']); - if($x) { - foreach($x as $k => $v) { - set_abconfig($abook['abook_channel'],$abook['abook_xchan'],'their_perms',$k, $v); - } - } + $x = perms_int_to_array($abook['abook_their_perms']); + if ($x) { + foreach ($x as $k => $v) { + set_abconfig($abook['abook_channel'], $abook['abook_xchan'], 'their_perms', $k, $v); + } + } - $x = perms_int_to_array($abook['abook_my_perms']); - if($x) { - foreach($x as $k => $v) { - set_abconfig($abook['abook_channel'],$abook['abook_xchan'],'my_perms',$k, $v); - } - } + $x = perms_int_to_array($abook['abook_my_perms']); + if ($x) { + foreach ($x as $k => $v) { + set_abconfig($abook['abook_channel'], $abook['abook_xchan'], 'my_perms', $k, $v); + } + } } -function translate_channel_perms_outbound(&$channel) { - $r = q("select * from pconfig where uid = %d and cat = 'perm_limits' ", - intval($channel['channel_id']) - ); +function translate_channel_perms_outbound(&$channel) +{ + $r = q( + "select * from pconfig where uid = %d and cat = 'perm_limits' ", + intval($channel['channel_id']) + ); - if($r) { - foreach($r as $rr) { - if($rr['k'] === 'view_stream') - $channel['channel_r_stream'] = $rr['v']; - if($rr['k'] === 'view_profile') - $channel['channel_r_profile'] = $rr['v']; - if($rr['k'] === 'view_contacts') - $channel['channel_r_abook'] = $rr['v']; - if($rr['k'] === 'view_storage') - $channel['channel_r_storage'] = $rr['v']; - if($rr['k'] === 'view_pages') - $channel['channel_r_pages'] = $rr['v']; - if($rr['k'] === 'send_stream') - $channel['channel_w_stream'] = $rr['v']; - if($rr['k'] === 'post_wall') - $channel['channel_w_wall'] = $rr['v']; - if($rr['k'] === 'post_comments') - $channel['channel_w_comment'] = $rr['v']; - if($rr['k'] === 'post_mail') - $channel['channel_w_mail'] = $rr['v']; - if($rr['k'] === 'post_like') - $channel['channel_w_like'] = $rr['v']; - if($rr['k'] === 'tag_deliver') - $channel['channel_w_tagwall'] = $rr['v']; - if($rr['k'] === 'chat') - $channel['channel_w_chat'] = $rr['v']; - if($rr['k'] === 'write_storage') - $channel['channel_w_storage'] = $rr['v']; - if($rr['k'] === 'write_pages') - $channel['channel_w_pages'] = $rr['v']; - if($rr['k'] === 'republish') - $channel['channel_a_republish'] = $rr['v']; - if($rr['k'] === 'delegate') - $channel['channel_a_delegate'] = $rr['v']; - - } - $channel['perm_limits'] = $r; - } + if ($r) { + foreach ($r as $rr) { + if ($rr['k'] === 'view_stream') { + $channel['channel_r_stream'] = $rr['v']; + } + if ($rr['k'] === 'view_profile') { + $channel['channel_r_profile'] = $rr['v']; + } + if ($rr['k'] === 'view_contacts') { + $channel['channel_r_abook'] = $rr['v']; + } + if ($rr['k'] === 'view_storage') { + $channel['channel_r_storage'] = $rr['v']; + } + if ($rr['k'] === 'view_pages') { + $channel['channel_r_pages'] = $rr['v']; + } + if ($rr['k'] === 'send_stream') { + $channel['channel_w_stream'] = $rr['v']; + } + if ($rr['k'] === 'post_wall') { + $channel['channel_w_wall'] = $rr['v']; + } + if ($rr['k'] === 'post_comments') { + $channel['channel_w_comment'] = $rr['v']; + } + if ($rr['k'] === 'post_mail') { + $channel['channel_w_mail'] = $rr['v']; + } + if ($rr['k'] === 'post_like') { + $channel['channel_w_like'] = $rr['v']; + } + if ($rr['k'] === 'tag_deliver') { + $channel['channel_w_tagwall'] = $rr['v']; + } + if ($rr['k'] === 'chat') { + $channel['channel_w_chat'] = $rr['v']; + } + if ($rr['k'] === 'write_storage') { + $channel['channel_w_storage'] = $rr['v']; + } + if ($rr['k'] === 'write_pages') { + $channel['channel_w_pages'] = $rr['v']; + } + if ($rr['k'] === 'republish') { + $channel['channel_a_republish'] = $rr['v']; + } + if ($rr['k'] === 'delegate') { + $channel['channel_a_delegate'] = $rr['v']; + } + } + $channel['perm_limits'] = $r; + } } -function translate_channel_perms_inbound($channel) { - - if($channel['perm_limits']) { - foreach($channel['perm_limits'] as $p) { - set_pconfig($channel['channel_id'],'perm_limits',$p['k'],$p['v']); - } - } - else { - perm_limits_upgrade($channel); - } +function translate_channel_perms_inbound($channel) +{ + if ($channel['perm_limits']) { + foreach ($channel['perm_limits'] as $p) { + set_pconfig($channel['channel_id'], 'perm_limits', $p['k'], $p['v']); + } + } else { + perm_limits_upgrade($channel); + } } -function translate_abook_perms_outbound(&$abook) { - $my_perms = 0; - $their_perms = 0; +function translate_abook_perms_outbound(&$abook) +{ + $my_perms = 0; + $their_perms = 0; - if(! $abook) - return; + if (! $abook) { + return; + } - if(array_key_exists('abconfig',$abook) && is_array($abook['abconfig']) && $abook['abconfig']) { - foreach($abook['abconfig'] as $p) { - if($p['cat'] === 'their_perms') { - if($p['k'] === 'view_stream' && intval($p['v'])) - $their_perms += PERMS_R_STREAM; - if($p['k'] === 'view_profile' && intval($p['v'])) - $their_perms += PERMS_R_PROFILE; - if($p['k'] === 'view_contacts' && intval($p['v'])) - $their_perms += PERMS_R_ABOOK; - if($p['k'] === 'view_storage' && intval($p['v'])) - $their_perms += PERMS_R_STORAGE; - if($p['k'] === 'view_pages' && intval($p['v'])) - $their_perms += PERMS_R_PAGES; - if($p['k'] === 'send_stream' && intval($p['v'])) - $their_perms += PERMS_W_STREAM; - if($p['k'] === 'post_wall' && intval($p['v'])) - $their_perms += PERMS_W_WALL; - if($p['k'] === 'post_comments' && intval($p['v'])) - $their_perms += PERMS_W_COMMENT; - if($p['k'] === 'post_mail' && intval($p['v'])) - $their_perms += PERMS_W_MAIL; - if($p['k'] === 'post_like' && intval($p['v'])) - $their_perms += PERMS_W_LIKE; - if($p['k'] === 'tag_deliver' && intval($p['v'])) - $their_perms += PERMS_W_TAGWALL; - if($p['k'] === 'chat' && intval($p['v'])) - $their_perms += PERMS_W_CHAT; - if($p['k'] === 'write_storage' && intval($p['v'])) - $their_perms += PERMS_W_STORAGE; - if($p['k'] === 'write_pages' && intval($p['v'])) - $their_perms += PERMS_W_PAGES; - if($p['k'] === 'republish' && intval($p['v'])) - $their_perms += PERMS_A_REPUBLISH; - if($p['k'] === 'delegate' && intval($p['v'])) - $their_perms += PERMS_A_DELEGATE; - } - if($p['cat'] === 'my_perms') { - if($p['k'] === 'view_stream' && intval($p['v'])) - $my_perms += PERMS_R_STREAM; - if($p['k'] === 'view_profile' && intval($p['v'])) - $my_perms += PERMS_R_PROFILE; - if($p['k'] === 'view_contacts' && intval($p['v'])) - $my_perms += PERMS_R_ABOOK; - if($p['k'] === 'view_storage' && intval($p['v'])) - $my_perms += PERMS_R_STORAGE; - if($p['k'] === 'view_pages' && intval($p['v'])) - $my_perms += PERMS_R_PAGES; - if($p['k'] === 'send_stream' && intval($p['v'])) - $my_perms += PERMS_W_STREAM; - if($p['k'] === 'post_wall' && intval($p['v'])) - $my_perms += PERMS_W_WALL; - if($p['k'] === 'post_comments' && intval($p['v'])) - $my_perms += PERMS_W_COMMENT; - if($p['k'] === 'post_mail' && intval($p['v'])) - $my_perms += PERMS_W_MAIL; - if($p['k'] === 'post_like' && intval($p['v'])) - $my_perms += PERMS_W_LIKE; - if($p['k'] === 'tag_deliver' && intval($p['v'])) - $my_perms += PERMS_W_TAGWALL; - if($p['k'] === 'chat' && intval($p['v'])) - $my_perms += PERMS_W_CHAT; - if($p['k'] === 'write_storage' && intval($p['v'])) - $my_perms += PERMS_W_STORAGE; - if($p['k'] === 'write_pages' && intval($p['v'])) - $my_perms += PERMS_W_PAGES; - if($p['k'] === 'republish' && intval($p['v'])) - $my_perms += PERMS_A_REPUBLISH; - if($p['k'] === 'delegate' && intval($p['v'])) - $my_perms += PERMS_A_DELEGATE; - } - } + if (array_key_exists('abconfig', $abook) && is_array($abook['abconfig']) && $abook['abconfig']) { + foreach ($abook['abconfig'] as $p) { + if ($p['cat'] === 'their_perms') { + if ($p['k'] === 'view_stream' && intval($p['v'])) { + $their_perms += PERMS_R_STREAM; + } + if ($p['k'] === 'view_profile' && intval($p['v'])) { + $their_perms += PERMS_R_PROFILE; + } + if ($p['k'] === 'view_contacts' && intval($p['v'])) { + $their_perms += PERMS_R_ABOOK; + } + if ($p['k'] === 'view_storage' && intval($p['v'])) { + $their_perms += PERMS_R_STORAGE; + } + if ($p['k'] === 'view_pages' && intval($p['v'])) { + $their_perms += PERMS_R_PAGES; + } + if ($p['k'] === 'send_stream' && intval($p['v'])) { + $their_perms += PERMS_W_STREAM; + } + if ($p['k'] === 'post_wall' && intval($p['v'])) { + $their_perms += PERMS_W_WALL; + } + if ($p['k'] === 'post_comments' && intval($p['v'])) { + $their_perms += PERMS_W_COMMENT; + } + if ($p['k'] === 'post_mail' && intval($p['v'])) { + $their_perms += PERMS_W_MAIL; + } + if ($p['k'] === 'post_like' && intval($p['v'])) { + $their_perms += PERMS_W_LIKE; + } + if ($p['k'] === 'tag_deliver' && intval($p['v'])) { + $their_perms += PERMS_W_TAGWALL; + } + if ($p['k'] === 'chat' && intval($p['v'])) { + $their_perms += PERMS_W_CHAT; + } + if ($p['k'] === 'write_storage' && intval($p['v'])) { + $their_perms += PERMS_W_STORAGE; + } + if ($p['k'] === 'write_pages' && intval($p['v'])) { + $their_perms += PERMS_W_PAGES; + } + if ($p['k'] === 'republish' && intval($p['v'])) { + $their_perms += PERMS_A_REPUBLISH; + } + if ($p['k'] === 'delegate' && intval($p['v'])) { + $their_perms += PERMS_A_DELEGATE; + } + } + if ($p['cat'] === 'my_perms') { + if ($p['k'] === 'view_stream' && intval($p['v'])) { + $my_perms += PERMS_R_STREAM; + } + if ($p['k'] === 'view_profile' && intval($p['v'])) { + $my_perms += PERMS_R_PROFILE; + } + if ($p['k'] === 'view_contacts' && intval($p['v'])) { + $my_perms += PERMS_R_ABOOK; + } + if ($p['k'] === 'view_storage' && intval($p['v'])) { + $my_perms += PERMS_R_STORAGE; + } + if ($p['k'] === 'view_pages' && intval($p['v'])) { + $my_perms += PERMS_R_PAGES; + } + if ($p['k'] === 'send_stream' && intval($p['v'])) { + $my_perms += PERMS_W_STREAM; + } + if ($p['k'] === 'post_wall' && intval($p['v'])) { + $my_perms += PERMS_W_WALL; + } + if ($p['k'] === 'post_comments' && intval($p['v'])) { + $my_perms += PERMS_W_COMMENT; + } + if ($p['k'] === 'post_mail' && intval($p['v'])) { + $my_perms += PERMS_W_MAIL; + } + if ($p['k'] === 'post_like' && intval($p['v'])) { + $my_perms += PERMS_W_LIKE; + } + if ($p['k'] === 'tag_deliver' && intval($p['v'])) { + $my_perms += PERMS_W_TAGWALL; + } + if ($p['k'] === 'chat' && intval($p['v'])) { + $my_perms += PERMS_W_CHAT; + } + if ($p['k'] === 'write_storage' && intval($p['v'])) { + $my_perms += PERMS_W_STORAGE; + } + if ($p['k'] === 'write_pages' && intval($p['v'])) { + $my_perms += PERMS_W_PAGES; + } + if ($p['k'] === 'republish' && intval($p['v'])) { + $my_perms += PERMS_A_REPUBLISH; + } + if ($p['k'] === 'delegate' && intval($p['v'])) { + $my_perms += PERMS_A_DELEGATE; + } + } + } - $abook['abook_their_perms'] = $their_perms; - $abook['abook_my_perms'] = $my_perms; - } + $abook['abook_their_perms'] = $their_perms; + $abook['abook_my_perms'] = $my_perms; + } } -function translate_abook_perms_inbound($channel,$abook) { +function translate_abook_perms_inbound($channel, $abook) +{ - $new_perms = false; - $abook['abook_channel'] = $channel['channel_id']; + $new_perms = false; + $abook['abook_channel'] = $channel['channel_id']; - if(array_key_exists('abconfig',$abook) && is_array($abook['abconfig']) && $abook['abconfig']) { - foreach($abook['abconfig'] as $p) { - if($p['cat'] == 'their_perms' || $p['cat'] == 'my_perms') { - $new_perms = true; - break; - } - } - } - - if($new_perms == false) { - perm_abook_upgrade($abook); - } + if (array_key_exists('abconfig', $abook) && is_array($abook['abconfig']) && $abook['abconfig']) { + foreach ($abook['abconfig'] as $p) { + if ($p['cat'] == 'their_perms' || $p['cat'] == 'my_perms') { + $new_perms = true; + break; + } + } + } + if ($new_perms == false) { + perm_abook_upgrade($abook); + } } - - - diff --git a/include/permissions.php b/include/permissions.php index 63e2c284b..22069bdcc 100644 --- a/include/permissions.php +++ b/include/permissions.php @@ -9,7 +9,7 @@ require_once('include/security.php'); * @file include/permissions.php * * This file conntains functions to check and work with permissions. - * + * */ @@ -26,213 +26,224 @@ require_once('include/security.php'); * * @returns array of all permissions, key is permission name, value is true or false */ -function get_all_perms($uid, $observer_xchan, $check_siteblock = true, $default_ignored = true) { +function get_all_perms($uid, $observer_xchan, $check_siteblock = true, $default_ignored = true) +{ - $api = App::get_oauth_key(); - if ($api) { - return get_all_api_perms($uid,$api); - } - - $global_perms = Permissions::Perms(); + $api = App::get_oauth_key(); + if ($api) { + return get_all_api_perms($uid, $api); + } - // Save lots of individual lookups + $global_perms = Permissions::Perms(); - $r = null; - $c = null; - $x = null; + // Save lots of individual lookups - $channel_checked = false; - $onsite_checked = false; - $abook_checked = false; + $r = null; + $c = null; + $x = null; - $ret = []; + $channel_checked = false; + $onsite_checked = false; + $abook_checked = false; - $abperms = (($uid && $observer_xchan) ? get_abconfig($uid,$observer_xchan,'system','my_perms','') : ''); + $ret = []; - foreach ($global_perms as $perm_name => $permission) { + $abperms = (($uid && $observer_xchan) ? get_abconfig($uid, $observer_xchan, 'system', 'my_perms', '') : ''); - // First find out what the channel owner declared permissions to be. + foreach ($global_perms as $perm_name => $permission) { + // First find out what the channel owner declared permissions to be. - $channel_perm = intval(PermissionLimits::Get($uid,$perm_name)); + $channel_perm = intval(PermissionLimits::Get($uid, $perm_name)); - if (! $channel_checked) { - $r = q("select * from channel where channel_id = %d limit 1", - intval($uid) - ); - $channel_checked = true; - } + if (! $channel_checked) { + $r = q( + "select * from channel where channel_id = %d limit 1", + intval($uid) + ); + $channel_checked = true; + } - // The uid provided doesn't exist. This would be a big fail. + // The uid provided doesn't exist. This would be a big fail. - if(! $r) { - $ret[$perm_name] = false; - continue; - } + if (! $r) { + $ret[$perm_name] = false; + continue; + } - // Next we're going to check for blocked or ignored contacts. - // These take priority over all other settings. + // Next we're going to check for blocked or ignored contacts. + // These take priority over all other settings. - if($observer_xchan) { - if($channel_perm & PERMS_AUTHED) { - $ret[$perm_name] = true; - continue; - } + if ($observer_xchan) { + if ($channel_perm & PERMS_AUTHED) { + $ret[$perm_name] = true; + continue; + } - if(! $abook_checked) { - $x = q("select abook_blocked, abook_ignored, abook_pending, xchan_network from abook + if (! $abook_checked) { + $x = q( + "select abook_blocked, abook_ignored, abook_pending, xchan_network from abook left join xchan on abook_xchan = xchan_hash where abook_channel = %d and abook_xchan = '%s' and abook_self = 0 limit 1", - intval($uid), - dbesc($observer_xchan) - ); + intval($uid), + dbesc($observer_xchan) + ); - $abook_checked = true; - } + $abook_checked = true; + } - // If they're blocked - they can't read or write - - if(($x) && intval($x[0]['abook_blocked'])) { - $ret[$perm_name] = false; - continue; - } - - // Check if this is a write permission and they are being ignored - // This flag is only visible internally. - - $blocked_anon_perms = Permissions::BlockedAnonPerms(); - - - if(($x) && ($default_ignored) && in_array($perm_name,$blocked_anon_perms) && intval($x[0]['abook_ignored'])) { - $ret[$perm_name] = false; - continue; - } - } - - // system is blocked to anybody who is not authenticated - - if(($check_siteblock) && (! $observer_xchan) && intval(get_config('system', 'block_public'))) { - $ret[$perm_name] = false; - continue; - } - - // Check if this $uid is actually the $observer_xchan - if it's your content - // you always have permission to do anything - // if you've moved elsewhere, you will only have read only access - - if(($observer_xchan) && ($r[0]['channel_hash'] === $observer_xchan)) { - - if($r[0]['channel_moved'] && (in_array($perm_name,$blocked_anon_perms))) - $ret[$perm_name] = false; - else - $ret[$perm_name] = true; - // moderated is a negative permission, don't moderate your own posts - if($perm_name === 'moderated') - $ret[$perm_name] = false; - - continue; - } - - // Anybody at all (that wasn't blocked or ignored). They have permission. - - if($channel_perm & PERMS_PUBLIC) { - $ret[$perm_name] = true; - continue; - } - - // From here on out, we need to know who they are. If we can't figure it - // out, permission is denied. - - if(! $observer_xchan) { - $ret[$perm_name] = false; - continue; - } - - // If we're still here, we have an observer, check the network. - - if($channel_perm & PERMS_NETWORK) { - if($x && in_array($x[0]['xchan_network'],['nomad','zot6'])) { - $ret[$perm_name] = true; - continue; - } - } - - // If PERMS_SITE is specified, find out if they've got an account on this hub - - if($channel_perm & PERMS_SITE) { - if(! $onsite_checked) { - $c = q("select channel_hash from channel where channel_hash = '%s' limit 1", - dbesc($observer_xchan) - ); - - $onsite_checked = true; - } - - if($c) - $ret[$perm_name] = true; - else - $ret[$perm_name] = false; - - continue; - } - - // From here on we require that the observer be a connection and - // handle whether we're allowing any, approved or specific ones - - if(! $x) { - $ret[$perm_name] = false; - continue; - } - - // They are in your address book, but haven't been approved - - if($channel_perm & PERMS_PENDING) { - $ret[$perm_name] = true; - continue; - } - - if(intval($x[0]['abook_pending'])) { - $ret[$perm_name] = false; - continue; - } - - // They're a contact, so they have permission - - if($channel_perm & PERMS_CONTACTS) { - $ret[$perm_name] = true; - continue; - } - - // Permission granted to certain channels. Let's see if the observer is one of them - - if($channel_perm & PERMS_SPECIFIC) { - if($abperms) { - $arr = explode(',',$abperms); - if($arr) { - if (in_array($perm_name,$arr)) { - $ret[$perm_name] = true; - } - else { - $ret[$perm_name] = false; - } + if($channel_perm & PERMS_NETWORK) { + if($x && in_array($x[0]['xchan_network'],['nomad','zot6'])) { + $ret[$perm_name] = true; + continue; } - continue; } - } - // No permissions allowed. + // If they're blocked - they can't read or write - $ret[$perm_name] = false; - continue; - } + if (($x) && intval($x[0]['abook_blocked'])) { + $ret[$perm_name] = false; + continue; + } - $arr = array( - 'channel_id' => $uid, - 'observer_hash' => $observer_xchan, - 'permissions' => $ret); + // Check if this is a write permission and they are being ignored + // This flag is only visible internally. - call_hooks('get_all_perms',$arr); + $blocked_anon_perms = Permissions::BlockedAnonPerms(); - return $arr['permissions']; + + if (($x) && ($default_ignored) && in_array($perm_name, $blocked_anon_perms) && intval($x[0]['abook_ignored'])) { + $ret[$perm_name] = false; + continue; + } + } + + // system is blocked to anybody who is not authenticated + + if (($check_siteblock) && (! $observer_xchan) && intval(get_config('system', 'block_public'))) { + $ret[$perm_name] = false; + continue; + } + + // Check if this $uid is actually the $observer_xchan - if it's your content + // you always have permission to do anything + // if you've moved elsewhere, you will only have read only access + + if (($observer_xchan) && ($r[0]['channel_hash'] === $observer_xchan)) { + if ($r[0]['channel_moved'] && (in_array($perm_name, $blocked_anon_perms))) { + $ret[$perm_name] = false; + } else { + $ret[$perm_name] = true; + } + // moderated is a negative permission, don't moderate your own posts + if ($perm_name === 'moderated') { + $ret[$perm_name] = false; + } + + continue; + } + + // Anybody at all (that wasn't blocked or ignored). They have permission. + + if ($channel_perm & PERMS_PUBLIC) { + $ret[$perm_name] = true; + continue; + } + + // From here on out, we need to know who they are. If we can't figure it + // out, permission is denied. + + if (! $observer_xchan) { + $ret[$perm_name] = false; + continue; + } + + // If we're still here, we have an observer, check the network. + + if ($channel_perm & PERMS_NETWORK) { + if ($x && $x[0]['xchan_network'] === 'zot6') { + $ret[$perm_name] = true; + continue; + } + } + + // If PERMS_SITE is specified, find out if they've got an account on this hub + + if ($channel_perm & PERMS_SITE) { + if (! $onsite_checked) { + $c = q( + "select channel_hash from channel where channel_hash = '%s' limit 1", + dbesc($observer_xchan) + ); + + $onsite_checked = true; + } + + if ($c) { + $ret[$perm_name] = true; + } else { + $ret[$perm_name] = false; + } + + continue; + } + + // From here on we require that the observer be a connection and + // handle whether we're allowing any, approved or specific ones + + if (! $x) { + $ret[$perm_name] = false; + continue; + } + + // They are in your address book, but haven't been approved + + if ($channel_perm & PERMS_PENDING) { + $ret[$perm_name] = true; + continue; + } + + if (intval($x[0]['abook_pending'])) { + $ret[$perm_name] = false; + continue; + } + + // They're a contact, so they have permission + + if ($channel_perm & PERMS_CONTACTS) { + $ret[$perm_name] = true; + continue; + } + + // Permission granted to certain channels. Let's see if the observer is one of them + + if ($channel_perm & PERMS_SPECIFIC) { + if ($abperms) { + $arr = explode(',', $abperms); + if ($arr) { + if (in_array($perm_name, $arr)) { + $ret[$perm_name] = true; + } else { + $ret[$perm_name] = false; + } + } + continue; + } + } + + // No permissions allowed. + + $ret[$perm_name] = false; + continue; + } + + $arr = array( + 'channel_id' => $uid, + 'observer_hash' => $observer_xchan, + 'permissions' => $ret); + + call_hooks('get_all_perms', $arr); + + return $arr['permissions']; } /** @@ -244,106 +255,108 @@ function get_all_perms($uid, $observer_xchan, $check_siteblock = true, $default_ * @param int $uid The channel_id associated with the resource owner * @param string $observer_xchan The xchan_hash representing the observer * @param string $permission - * @param boolean $check_siteblock (default true) + * @param bool $check_siteblock (default true) * if false bypass check for "Block Public" at the site level * @return bool true if permission is allowed for observer on channel */ -function perm_is_allowed($uid, $observer_xchan, $permission, $check_siteblock = true) { +function perm_is_allowed($uid, $observer_xchan, $permission, $check_siteblock = true) +{ - $api = App::get_oauth_key(); - if ($api) { - return api_perm_is_allowed($uid,$api,$permission); - } + $api = App::get_oauth_key(); + if ($api) { + return api_perm_is_allowed($uid, $api, $permission); + } - $arr = [ - 'channel_id' => $uid, - 'observer_hash' => $observer_xchan, - 'permission' => $permission, - 'result' => 'unset' - ]; + $arr = [ + 'channel_id' => $uid, + 'observer_hash' => $observer_xchan, + 'permission' => $permission, + 'result' => 'unset' + ]; - call_hooks('perm_is_allowed', $arr); - if ($arr['result'] !== 'unset') { - return $arr['result']; - } + call_hooks('perm_is_allowed', $arr); + if ($arr['result'] !== 'unset') { + return $arr['result']; + } - $global_perms = Permissions::Perms(); + $global_perms = Permissions::Perms(); - // First find out what the channel owner declared permissions to be. + // First find out what the channel owner declared permissions to be. - $channel_perm = PermissionLimits::Get($uid,$permission); - if ($channel_perm === false) { - return false; - } + $channel_perm = PermissionLimits::Get($uid, $permission); + if ($channel_perm === false) { + return false; + } - $r = q("select channel_pageflags, channel_moved, channel_hash from channel where channel_id = %d limit 1", - intval($uid) - ); - if (! $r) { - return false; - } + $r = q( + "select channel_pageflags, channel_moved, channel_hash from channel where channel_id = %d limit 1", + intval($uid) + ); + if (! $r) { + return false; + } - $blocked_anon_perms = Permissions::BlockedAnonPerms(); + $blocked_anon_perms = Permissions::BlockedAnonPerms(); - if ($observer_xchan) { - if ($channel_perm & PERMS_AUTHED) { - return true; - } + if ($observer_xchan) { + if ($channel_perm & PERMS_AUTHED) { + return true; + } - $x = q("select abook_blocked, abook_ignored, abook_pending, xchan_network from abook left join xchan on abook_xchan = xchan_hash + $x = q( + "select abook_blocked, abook_ignored, abook_pending, xchan_network from abook left join xchan on abook_xchan = xchan_hash where abook_channel = %d and abook_xchan = '%s' and abook_self = 0 limit 1", - intval($uid), - dbesc($observer_xchan) - ); + intval($uid), + dbesc($observer_xchan) + ); - // If they're blocked - they can't read or write - - if (($x) && intval($x[0]['abook_blocked'])) { - return false; - } + // If they're blocked - they can't read or write - if (($x) && in_array($permission,$blocked_anon_perms) && intval($x[0]['abook_ignored'])) { - return false; - } + if (($x) && intval($x[0]['abook_blocked'])) { + return false; + } - $abperms = get_abconfig($uid,$observer_xchan,'system','my_perms',''); - } - + if (($x) && in_array($permission, $blocked_anon_perms) && intval($x[0]['abook_ignored'])) { + return false; + } - // system is blocked to anybody who is not authenticated + $abperms = get_abconfig($uid, $observer_xchan, 'system', 'my_perms', ''); + } - if (($check_siteblock) && (! $observer_xchan) && intval(get_config('system', 'block_public'))) { - return false; - } - // Check if this $uid is actually the $observer_xchan - // you will have full access unless the channel was moved - - // in which case you will have read_only access + // system is blocked to anybody who is not authenticated - if ($r[0]['channel_hash'] === $observer_xchan) { - // moderated is a negative permission - if ($permission === 'moderated') { - return false; - } - if ($r[0]['channel_moved'] && (in_array($permission,$blocked_anon_perms))) { - return false; - } - else { - return true; - } - } + if (($check_siteblock) && (! $observer_xchan) && intval(get_config('system', 'block_public'))) { + return false; + } - if ($channel_perm & PERMS_PUBLIC) { - return true; - } + // Check if this $uid is actually the $observer_xchan + // you will have full access unless the channel was moved - + // in which case you will have read_only access - // If it's an unauthenticated observer, we only need to see if PERMS_PUBLIC is set + if ($r[0]['channel_hash'] === $observer_xchan) { + // moderated is a negative permission + if ($permission === 'moderated') { + return false; + } + if ($r[0]['channel_moved'] && (in_array($permission, $blocked_anon_perms))) { + return false; + } else { + return true; + } + } - if (! $observer_xchan) { - return false; - } + if ($channel_perm & PERMS_PUBLIC) { + return true; + } - // If we're still here, we have an observer, check the network. + // If it's an unauthenticated observer, we only need to see if PERMS_PUBLIC is set + + if (! $observer_xchan) { + return false; + } + + // If we're still here, we have an observer, check the network. if ($channel_perm & PERMS_NETWORK) { if ($x && in_array($x[0]['xchan_network'],['nomad','zot6'])) { @@ -351,133 +364,141 @@ function perm_is_allowed($uid, $observer_xchan, $permission, $check_siteblock = } } - // If PERMS_SITE is specified, find out if they've got an account on this hub + // If PERMS_SITE is specified, find out if they've got an account on this hub - if ($channel_perm & PERMS_SITE) { - $c = q("select channel_hash from channel where channel_hash = '%s' limit 1", - dbesc($observer_xchan) - ); - if ($c) { - return true; - } - return false; - } + if ($channel_perm & PERMS_SITE) { + $c = q( + "select channel_hash from channel where channel_hash = '%s' limit 1", + dbesc($observer_xchan) + ); + if ($c) { + return true; + } + return false; + } - // From here on we require that the observer be a connection and - // handle whether we're allowing any, approved or specific ones + // From here on we require that the observer be a connection and + // handle whether we're allowing any, approved or specific ones - if (! $x) { - return false; - } + if (! $x) { + return false; + } - // They are in your address book, but haven't been approved + // They are in your address book, but haven't been approved - if ($channel_perm & PERMS_PENDING) { - return true; - } + if ($channel_perm & PERMS_PENDING) { + return true; + } - if (intval($x[0]['abook_pending'])) { - return false; - } + if (intval($x[0]['abook_pending'])) { + return false; + } - // They're a contact, so they have permission + // They're a contact, so they have permission - if ($channel_perm & PERMS_CONTACTS) { - return true; - } + if ($channel_perm & PERMS_CONTACTS) { + return true; + } - // Permission granted to certain channels. Let's see if the observer is one of them + // Permission granted to certain channels. Let's see if the observer is one of them - if (($r) && ($channel_perm & PERMS_SPECIFIC)) { - if ($abperms) { - $arr = explode(',',$abperms); - if ($arr) { - if (in_array($permission,$arr)) { - return true; - } - } - } - return false; - } + if (($r) && ($channel_perm & PERMS_SPECIFIC)) { + if ($abperms) { + $arr = explode(',', $abperms); + if ($arr) { + if (in_array($permission, $arr)) { + return true; + } + } + } + return false; + } - // No permissions allowed. + // No permissions allowed. - return false; + return false; } -function get_all_api_perms($uid,$api) { +function get_all_api_perms($uid, $api) +{ - $global_perms = Permissions::Perms(); + $global_perms = Permissions::Perms(); - $ret = []; + $ret = []; - $r = q("select * from xperm where xp_client = '%s' and xp_channel = %d", - dbesc($api), - intval($uid) - ); + $r = q( + "select * from xperm where xp_client = '%s' and xp_channel = %d", + dbesc($api), + intval($uid) + ); - if(! $r) - return false; + if (! $r) { + return false; + } - $allow_all = false; - $allowed = []; - foreach($r as $rr) { - if($rr['xp_perm'] === 'all') - $allow_all = true; - if(! in_array($rr['xp_perm'],$allowed)) - $allowed[] = $rr['xp_perm']; - } + $allow_all = false; + $allowed = []; + foreach ($r as $rr) { + if ($rr['xp_perm'] === 'all') { + $allow_all = true; + } + if (! in_array($rr['xp_perm'], $allowed)) { + $allowed[] = $rr['xp_perm']; + } + } - foreach($global_perms as $perm_name => $permission) { - if($allow_all || in_array($perm_name,$allowed)) - $ret[$perm_name] = true; - else - $ret[$perm_name] = false; + foreach ($global_perms as $perm_name => $permission) { + if ($allow_all || in_array($perm_name, $allowed)) { + $ret[$perm_name] = true; + } else { + $ret[$perm_name] = false; + } + } - } + $arr = array( + 'channel_id' => $uid, + 'observer_hash' => $observer_xchan, + 'permissions' => $ret); - $arr = array( - 'channel_id' => $uid, - 'observer_hash' => $observer_xchan, - 'permissions' => $ret); - - call_hooks('get_all_api_perms',$arr); - - return $arr['permissions']; + call_hooks('get_all_api_perms', $arr); + return $arr['permissions']; } -function api_perm_is_allowed($uid,$api,$permission) { +function api_perm_is_allowed($uid, $api, $permission) +{ - $arr = array( - 'channel_id' => $uid, - 'observer_hash' => $observer_xchan, - 'permission' => $permission, - 'result' => false - ); + $arr = array( + 'channel_id' => $uid, + 'observer_hash' => $observer_xchan, + 'permission' => $permission, + 'result' => false + ); - call_hooks('api_perm_is_allowed', $arr); - if($arr['result']) - return true; + call_hooks('api_perm_is_allowed', $arr); + if ($arr['result']) { + return true; + } - $r = q("select * from xperm where xp_client = '%s' and xp_channel = %d and ( xp_perm = 'all' OR xp_perm = '%s' )", - dbesc($api), - intval($uid), - dbesc($permission) - ); + $r = q( + "select * from xperm where xp_client = '%s' and xp_channel = %d and ( xp_perm = 'all' OR xp_perm = '%s' )", + dbesc($api), + intval($uid), + dbesc($permission) + ); - if(! $r) - return false; + if (! $r) { + return false; + } - foreach($r as $rr) { - if($rr['xp_perm'] === 'all' || $rr['xp_perm'] === $permission) - return true; - - } - - return false; + foreach ($r as $rr) { + if ($rr['xp_perm'] === 'all' || $rr['xp_perm'] === $permission) { + return true; + } + } + return false; } @@ -485,14 +506,18 @@ function api_perm_is_allowed($uid,$api,$permission) { // Check a simple array of observers against a permissions // return a simple array of those with permission -function check_list_permissions($uid, $arr, $perm) { - $result = []; - if($arr) - foreach($arr as $x) - if(perm_is_allowed($uid, $x, $perm)) - $result[] = $x; +function check_list_permissions($uid, $arr, $perm) +{ + $result = []; + if ($arr) { + foreach ($arr as $x) { + if (perm_is_allowed($uid, $x, $perm)) { + $result[] = $x; + } + } + } - return($result); + return($result); } /** @@ -500,59 +525,62 @@ function check_list_permissions($uid, $arr, $perm) { * * @return array */ -function site_default_perms() { +function site_default_perms() +{ - $ret = []; + $ret = []; - $typical = array( - 'view_stream' => PERMS_PUBLIC, - 'view_profile' => PERMS_PUBLIC, - 'view_contacts' => PERMS_PUBLIC, - 'view_storage' => PERMS_PUBLIC, - 'view_pages' => PERMS_PUBLIC, - 'view_wiki' => PERMS_PUBLIC, - 'send_stream' => PERMS_SPECIFIC, - 'post_wall' => PERMS_SPECIFIC, - 'post_comments' => PERMS_SPECIFIC, - 'post_mail' => PERMS_SPECIFIC, - 'tag_deliver' => PERMS_SPECIFIC, - 'chat' => PERMS_SPECIFIC, - 'write_storage' => PERMS_SPECIFIC, - 'write_pages' => PERMS_SPECIFIC, - 'write_wiki' => PERMS_SPECIFIC, - 'delegate' => PERMS_SPECIFIC, - 'post_like' => PERMS_NETWORK - ); + $typical = array( + 'view_stream' => PERMS_PUBLIC, + 'view_profile' => PERMS_PUBLIC, + 'view_contacts' => PERMS_PUBLIC, + 'view_storage' => PERMS_PUBLIC, + 'view_pages' => PERMS_PUBLIC, + 'view_wiki' => PERMS_PUBLIC, + 'send_stream' => PERMS_SPECIFIC, + 'post_wall' => PERMS_SPECIFIC, + 'post_comments' => PERMS_SPECIFIC, + 'post_mail' => PERMS_SPECIFIC, + 'tag_deliver' => PERMS_SPECIFIC, + 'chat' => PERMS_SPECIFIC, + 'write_storage' => PERMS_SPECIFIC, + 'write_pages' => PERMS_SPECIFIC, + 'write_wiki' => PERMS_SPECIFIC, + 'delegate' => PERMS_SPECIFIC, + 'post_like' => PERMS_NETWORK + ); - $global_perms = Permissions::Perms(); + $global_perms = Permissions::Perms(); - foreach($global_perms as $perm => $v) { - $x = get_config('default_perms', $perm, $typical[$perm]); - $ret[$perm] = $x; - } + foreach ($global_perms as $perm => $v) { + $x = get_config('default_perms', $perm, $typical[$perm]); + $ret[$perm] = $x; + } - return $ret; + return $ret; } -function their_perms_contains($channel_id,$xchan_hash,$perm) { - $x = get_abconfig($channel_id,$xchan_hash,'system','their_perms'); - if($x) { - $y = explode(',',$x); - if(in_array($perm,$y)) { - return true; - } - } - return false; +function their_perms_contains($channel_id, $xchan_hash, $perm) +{ + $x = get_abconfig($channel_id, $xchan_hash, 'system', 'their_perms'); + if ($x) { + $y = explode(',', $x); + if (in_array($perm, $y)) { + return true; + } + } + return false; } -function my_perms_contains($channel_id,$xchan_hash,$perm) { - $x = get_abconfig($channel_id,$xchan_hash,'system','my_perms'); - if($x) { - $y = explode(',',$x); - if(in_array($perm,$y)) { - return true; - } - } - return false; -} \ No newline at end of file +function my_perms_contains($channel_id, $xchan_hash, $perm) +{ + $x = get_abconfig($channel_id, $xchan_hash, 'system', 'my_perms'); + if ($x) { + $y = explode(',', $x); + if (in_array($perm, $y)) { + return true; + } + } + return false; +} diff --git a/include/photo_factory.php b/include/photo_factory.php index f27d64998..48509b4f6 100644 --- a/include/photo_factory.php +++ b/include/photo_factory.php @@ -18,43 +18,44 @@ use Zotlabs\Lib\Hashpath; * @return null|PhotoDriver * NULL if unsupported image type or failure, otherwise photo driver object */ -function photo_factory($data, $type = null) { - $ph = null; - $m = null; +function photo_factory($data, $type = null) +{ + $ph = null; + $m = null; - $unsupported_types = [ - 'image/bmp', - 'image/vnd.microsoft.icon', - 'image/tiff', - 'image/svg+xml', - ]; + $unsupported_types = [ + 'image/bmp', + 'image/vnd.microsoft.icon', + 'image/tiff', + 'image/svg+xml', + ]; - if ($type && in_array(strtolower($type), $unsupported_types)) { - logger('Unsupported image type ' . $type); - return null; - } + if ($type && in_array(strtolower($type), $unsupported_types)) { + logger('Unsupported image type ' . $type); + return null; + } - $ignore_imagick = get_config('system','ignore_imagick'); + $ignore_imagick = get_config('system', 'ignore_imagick'); - if (class_exists('Imagick') && !$ignore_imagick) { - $ph = new PhotoImagick($data, $type); + if (class_exists('Imagick') && !$ignore_imagick) { + $ph = new PhotoImagick($data, $type); - // As of August 2020, webp support is still poor in both imagick and gd. Both claim to support it, - // but both may require additional configuration. If it's a webp and the imagick driver failed, - // we'll try again with GD just in case that one handles it. If not, you may need to install libwebp - // which should make imagick work and/or re-compile php-gd with the option to include that library. + // As of August 2020, webp support is still poor in both imagick and gd. Both claim to support it, + // but both may require additional configuration. If it's a webp and the imagick driver failed, + // we'll try again with GD just in case that one handles it. If not, you may need to install libwebp + // which should make imagick work and/or re-compile php-gd with the option to include that library. - if (! $ph->is_valid() && $type === 'image/webp') { - $ph = null; - } - } + if (! $ph->is_valid() && $type === 'image/webp') { + $ph = null; + } + } - if (! $ph) { - $ph = new PhotoGd($data, $type); - } + if (! $ph) { + $ph = new PhotoGd($data, $type); + } - return $ph; + return $ph; } @@ -67,81 +68,79 @@ function photo_factory($data, $type = null) { * Headers to check for Content-Type (from curl request) * @return null|string Guessed mimetype */ - -function guess_image_type($filename, $headers = '') { - // logger('Photo: guess_image_type: '.$filename . ($headers?' from curl headers':''), LOGGER_DEBUG); - $type = null; - $m = null; - if($headers) { - $hdrs = []; - $h = explode("\n", $headers); - foreach ($h as $l) { - if (strpos($l,':') !== false) { - list($k, $v) = array_map('trim', explode(':', trim($l), 2)); - $hdrs[strtolower($k)] = $v; - } - } - logger('Curl headers: ' . print_r($hdrs, true), LOGGER_DEBUG); - if (array_key_exists('content-type', $hdrs)) { - $ph = photo_factory(''); - $types = $ph->supportedTypes(); +function guess_image_type($filename, $headers = '') +{ + // logger('Photo: guess_image_type: '.$filename . ($headers?' from curl headers':''), LOGGER_DEBUG); + $type = null; + $m = null; - if (array_key_exists($hdrs['content-type'], $types)) - $type = $hdrs['content-type']; - } - } + if ($headers) { + $hdrs = []; + $h = explode("\n", $headers); + foreach ($h as $l) { + if (strpos($l, ':') !== false) { + list($k, $v) = array_map('trim', explode(':', trim($l), 2)); + $hdrs[strtolower($k)] = $v; + } + } + logger('Curl headers: ' . print_r($hdrs, true), LOGGER_DEBUG); + if (array_key_exists('content-type', $hdrs)) { + $ph = photo_factory(''); + $types = $ph->supportedTypes(); - if (is_null($type)) { - $ignore_imagick = get_config('system', 'ignore_imagick'); - // Guessing from extension? Isn't that... dangerous? - if(class_exists('Imagick') && file_exists($filename) && is_readable($filename) && !$ignore_imagick) { - /** - * Well, this not much better, - * but at least it comes from the data inside the image, - * we won't be tricked by a manipulated extension - */ - $image = new Imagick($filename); - $type = $image->getImageMimeType(); - } + if (array_key_exists($hdrs['content-type'], $types)) { + $type = $hdrs['content-type']; + } + } + } - if (is_null($type)) { - $ext = pathinfo($filename, PATHINFO_EXTENSION); - $ph = photo_factory(''); - $types = $ph->supportedTypes(); - foreach ($types as $m => $e) { - if ($ext === $e) { - $type = $m; - } - } - } + if (is_null($type)) { + $ignore_imagick = get_config('system', 'ignore_imagick'); + // Guessing from extension? Isn't that... dangerous? + if (class_exists('Imagick') && file_exists($filename) && is_readable($filename) && !$ignore_imagick) { + /** + * Well, this not much better, + * but at least it comes from the data inside the image, + * we won't be tricked by a manipulated extension + */ + $image = new Imagick($filename); + $type = $image->getImageMimeType(); + } - if (is_null($type) && (strpos($filename, 'http') === false)) { - $size = getimagesize($filename); - $ph = photo_factory(''); - $types = $ph->supportedTypes(); - $type = ((array_key_exists($size['mime'], $types)) ? $size['mime'] : 'image/jpeg'); - } - if (is_null($type)) { - if (strpos(strtolower($filename),'jpg') !== false) { - $type = 'image/jpeg'; - } - elseif (strpos(strtolower($filename),'jpeg') !== false) { - $type = 'image/jpeg'; - } - elseif (strpos(strtolower($filename),'gif') !== false) { - $type = 'image/gif'; - } - elseif (strpos(strtolower($filename),'png') !== false) { - $type = 'image/png'; - } - } + if (is_null($type)) { + $ext = pathinfo($filename, PATHINFO_EXTENSION); + $ph = photo_factory(''); + $types = $ph->supportedTypes(); + foreach ($types as $m => $e) { + if ($ext === $e) { + $type = $m; + } + } + } - } - - logger('Photo: guess_image_type: filename = ' . $filename . ' type = ' . $type, LOGGER_DEBUG); + if (is_null($type) && (strpos($filename, 'http') === false)) { + $size = getimagesize($filename); + $ph = photo_factory(''); + $types = $ph->supportedTypes(); + $type = ((array_key_exists($size['mime'], $types)) ? $size['mime'] : 'image/jpeg'); + } + if (is_null($type)) { + if (strpos(strtolower($filename), 'jpg') !== false) { + $type = 'image/jpeg'; + } elseif (strpos(strtolower($filename), 'jpeg') !== false) { + $type = 'image/jpeg'; + } elseif (strpos(strtolower($filename), 'gif') !== false) { + $type = 'image/gif'; + } elseif (strpos(strtolower($filename), 'png') !== false) { + $type = 'image/png'; + } + } + } - return $type; + logger('Photo: guess_image_type: filename = ' . $filename . ' type = ' . $type, LOGGER_DEBUG); + + return $type; } /** @@ -151,38 +150,39 @@ function guess_image_type($filename, $headers = '') { * @param string $ob_hash * @return void */ -function delete_thing_photo($url, $ob_hash) { +function delete_thing_photo($url, $ob_hash) +{ - $hash = basename($url); - $dbhash = substr($hash, 0, strpos($hash, '-')); + $hash = basename($url); + $dbhash = substr($hash, 0, strpos($hash, '-')); - // hashes should be 32 bytes. + // hashes should be 32 bytes. - if ((! $ob_hash) || (strlen($dbhash) < 16)) { - return; - } + if ((! $ob_hash) || (strlen($dbhash) < 16)) { + return; + } - if (strpos($url,'/xp/') !== false && strpos($url,'.obj') !== false) { - $xppath = 'cache/xp/' . substr($hash,0,2) . '/' . substr($hash,2,2) . '/' . $hash; - if (file_exists($xppath)) { - unlink($xppath); - } - $xppath = str_replace('-4','-5',$xppath); - if (file_exists($xppath)) { - unlink($xppath); - } - $xppath = str_replace('-5','-6',$xppath); - if (file_exists($xppath)) { - unlink($xppath); - } - } - else { - q("delete from photo where xchan = '%s' and photo_usage = %d and resource_id = '%s'", - dbesc($ob_hash), - intval(PHOTO_THING), - dbesc($dbhash) - ); - } + if (strpos($url, '/xp/') !== false && strpos($url, '.obj') !== false) { + $xppath = 'cache/xp/' . substr($hash, 0, 2) . '/' . substr($hash, 2, 2) . '/' . $hash; + if (file_exists($xppath)) { + unlink($xppath); + } + $xppath = str_replace('-4', '-5', $xppath); + if (file_exists($xppath)) { + unlink($xppath); + } + $xppath = str_replace('-5', '-6', $xppath); + if (file_exists($xppath)) { + unlink($xppath); + } + } else { + q( + "delete from photo where xchan = '%s' and photo_usage = %d and resource_id = '%s'", + dbesc($ob_hash), + intval(PHOTO_THING), + dbesc($dbhash) + ); + } } /** @@ -192,9 +192,9 @@ function delete_thing_photo($url, $ob_hash) { * external URL to fetch base image * @param string $xchan * channel unique hash - * @param boolean $thing + * @param bool $thing * TRUE if this is a thing URL - * @param boolean $force + * @param bool $force * TRUE if ignore image modification date check (force fetch) * * @return array of results @@ -205,281 +205,269 @@ function delete_thing_photo($url, $ob_hash) { * * \e boolean \b 4 => TRUE if fetch failure * * \e string \b 5 => modification date */ -function import_xchan_photo($photo, $xchan, $thing = false, $force = false) { +function import_xchan_photo($photo, $xchan, $thing = false, $force = false) +{ - logger('Updating channel photo from ' . $photo . ' for ' . $xchan, LOGGER_DEBUG); + logger('Updating channel photo from ' . $photo . ' for ' . $xchan, LOGGER_DEBUG); - $flags = (($thing) ? PHOTO_THING : PHOTO_XCHAN); - $album = (($thing) ? 'Things' : 'Contact Photos'); - $modified = EMPTY_STR; - $matches = null; - $failed = true; - $img_str = EMPTY_STR; - $hash = photo_new_resource(); - $os_storage = false; + $flags = (($thing) ? PHOTO_THING : PHOTO_XCHAN); + $album = (($thing) ? 'Things' : 'Contact Photos'); + $modified = EMPTY_STR; + $matches = null; + $failed = true; + $img_str = EMPTY_STR; + $hash = photo_new_resource(); + $os_storage = false; - if (! $thing) { - $r = q("select resource_id, edited, mimetype from photo where xchan = '%s' and photo_usage = %d and imgscale = 4 limit 1", - dbesc($xchan), - intval(PHOTO_XCHAN) - ); - if ($r) { - $r = array_shift($r); - $hash = $r['resource_id']; - $modified = $r['edited']; - $type = $r['mimetype']; - } - else { - $os_storage = true; - } - } + if (! $thing) { + $r = q( + "select resource_id, edited, mimetype from photo where xchan = '%s' and photo_usage = %d and imgscale = 4 limit 1", + dbesc($xchan), + intval(PHOTO_XCHAN) + ); + if ($r) { + $r = array_shift($r); + $hash = $r['resource_id']; + $modified = $r['edited']; + $type = $r['mimetype']; + } else { + $os_storage = true; + } + } - if ($photo) { + if ($photo) { + if ($modified === EMPTY_STR || $force) { + $result = z_fetch_url($photo, true); + } else { + $h = [ 'headers' => [ 'If-Modified-Since: ' . gmdate('D, d M Y H:i:s', strtotime($modified . 'Z')) . ' GMT' ] ]; + $result = z_fetch_url($photo, true, 0, $h); + } - if ($modified === EMPTY_STR || $force) { - $result = z_fetch_url($photo, true); - } - else { - $h = [ 'headers' => [ 'If-Modified-Since: ' . gmdate('D, d M Y H:i:s', strtotime($modified . 'Z')) . ' GMT' ] ]; - $result = z_fetch_url($photo, true, 0, $h); - } + if ($result['success']) { + $img_str = $result['body']; + $type = guess_image_type($photo, $result['header']); + $modified = gmdate('Y-m-d H:i:s', (preg_match('/last-modified: (.+) \S+/i', $result['header'], $matches) ? strtotime($matches[1] . 'Z') : time())); + if (! is_null($type)) { + $failed = false; + } + } elseif ($result['return_code'] == 304) { + $photo = z_root() . '/photo/' . $hash . '-4'; + $thumb = z_root() . '/photo/' . $hash . '-5'; + $micro = z_root() . '/photo/' . $hash . '-6'; + $failed = false; + } + } - if ($result['success']) { - $img_str = $result['body']; - $type = guess_image_type($photo, $result['header']); - $modified = gmdate('Y-m-d H:i:s', (preg_match('/last-modified: (.+) \S+/i', $result['header'], $matches) ? strtotime($matches[1] . 'Z') : time())); - if (! is_null($type)) { - $failed = false; - } - } - elseif ($result['return_code'] == 304) { - $photo = z_root() . '/photo/' . $hash . '-4'; - $thumb = z_root() . '/photo/' . $hash . '-5'; - $micro = z_root() . '/photo/' . $hash . '-6'; - $failed = false; - } - } + if (! $failed && $result['return_code'] != 304) { + $img = photo_factory($img_str, $type); + if ($img && $img->is_valid()) { + $width = $img->getWidth(); + $height = $img->getHeight(); - if (! $failed && $result['return_code'] != 304) { - $img = photo_factory($img_str, $type); - if ($img && $img->is_valid()) { - $width = $img->getWidth(); - $height = $img->getHeight(); + if ($width && $height) { + if (($width / $height) > 1.2) { + // crop out the sides + $margin = $width - $height; + $img->cropImage(300, ($margin / 2), 0, $height, $height); + } elseif (($height / $width) > 1.2) { + // crop out the bottom + $margin = $height - $width; + $img->cropImage(300, 0, 0, $width, $width); + } else { + $img->scaleImageSquare(300); + } + } else { + $failed = true; + } - if ($width && $height) { - if (($width / $height) > 1.2) { - // crop out the sides - $margin = $width - $height; - $img->cropImage(300, ($margin / 2), 0, $height, $height); - } - elseif (($height / $width) > 1.2) { - // crop out the bottom - $margin = $height - $width; - $img->cropImage(300, 0, 0, $width, $width); - } - else { - $img->scaleImageSquare(300); - } - } - else { - $failed = true; - } + $p = [ + 'xchan' => $xchan, + 'resource_id' => $hash, + 'filename' => basename($photo), + 'album' => $album, + 'photo_usage' => $flags, + 'imgscale' => 4, + 'edited' => $modified, + ]; - $p = [ - 'xchan' => $xchan, - 'resource_id' => $hash, - 'filename' => basename($photo), - 'album' => $album, - 'photo_usage' => $flags, - 'imgscale' => 4, - 'edited' => $modified, - ]; + $r = $img->save($p); + if ($r === false) { + $failed = true; + } + $img->scaleImage(80); + $p['imgscale'] = 5; + $r = $img->save($p); + if ($r === false) { + $failed = true; + } + $img->scaleImage(48); + $p['imgscale'] = 6; + $r = $img->save($p); + if ($r === false) { + $failed = true; + } + $photo = z_root() . '/photo/' . $hash . '-4'; + $thumb = z_root() . '/photo/' . $hash . '-5'; + $micro = z_root() . '/photo/' . $hash . '-6'; + } else { + logger('Invalid image from ' . $photo); + $failed = true; + } + } - $r = $img->save($p); - if ($r === false) { - $failed = true; - } - $img->scaleImage(80); - $p['imgscale'] = 5; - $r = $img->save($p); - if ($r === false) { - $failed = true; - } - $img->scaleImage(48); - $p['imgscale'] = 6; - $r = $img->save($p); - if ($r === false) { - $failed = true; - } - $photo = z_root() . '/photo/' . $hash . '-4'; - $thumb = z_root() . '/photo/' . $hash . '-5'; - $micro = z_root() . '/photo/' . $hash . '-6'; - } - else { - logger('Invalid image from ' . $photo); - $failed = true; - } - } - - if ($failed) { - $default = get_default_profile_photo(); - $photo = z_root() . '/' . $default; - $thumb = z_root() . '/' . get_default_profile_photo(80); - $micro = z_root() . '/' . get_default_profile_photo(48); - $type = 'image/png'; - $modified = gmdate('Y-m-d H:i:s', filemtime($default)); - } + if ($failed) { + $default = get_default_profile_photo(); + $photo = z_root() . '/' . $default; + $thumb = z_root() . '/' . get_default_profile_photo(80); + $micro = z_root() . '/' . get_default_profile_photo(48); + $type = 'image/png'; + $modified = gmdate('Y-m-d H:i:s', filemtime($default)); + } - logger('HTTP code: ' . $result['return_code'] . '; modified: ' . $modified - . '; failure: ' . ($failed ? 'yes' : 'no') . '; URL: ' . $photo, LOGGER_DEBUG); + logger('HTTP code: ' . $result['return_code'] . '; modified: ' . $modified + . '; failure: ' . ($failed ? 'yes' : 'no') . '; URL: ' . $photo, LOGGER_DEBUG); - return([$photo, $thumb, $micro, $type, $failed, $modified]); + return([$photo, $thumb, $micro, $type, $failed, $modified]); } -function import_remote_xchan_photo($photo, $xchan, $thing = false) { +function import_remote_xchan_photo($photo, $xchan, $thing = false) +{ -// logger('Updating channel photo from ' . $photo . ' for ' . $xchan, LOGGER_DEBUG); +// logger('Updating channel photo from ' . $photo . ' for ' . $xchan, LOGGER_DEBUG); - // Assume the worst. - $failed = true; - $type = EMPTY_STR; - - $path = Hashpath::path((($thing) ? $photo . $xchan : $xchan),'cache/xp',2); - $hash = basename($path); + // Assume the worst. + $failed = true; + $type = EMPTY_STR; - $cached_file = $path . '-4' . (($thing) ? '.obj' : EMPTY_STR); + $path = Hashpath::path((($thing) ? $photo . $xchan : $xchan), 'cache/xp', 2); + $hash = basename($path); - $animated = get_config('system','animated_avatars',true); + $cached_file = $path . '-4' . (($thing) ? '.obj' : EMPTY_STR); - $modified = ((file_exists($cached_file)) ? @filemtime($cached_file) : 0); + $animated = get_config('system', 'animated_avatars', true); - // Maybe it's already a cached xchan photo - - if (strpos($photo, z_root() . '/xp/') === 0) { - return false; - } - - if ($modified) { - $h = [ 'headers' => [ 'If-Modified-Since: ' . gmdate('D, d M Y H:i:s', $modified) . ' GMT' ] ]; - $recurse = 0; - $result = z_fetch_url($photo, true, $recurse, $h); - } - else { - $result = z_fetch_url($photo, true); - } + $modified = ((file_exists($cached_file)) ? @filemtime($cached_file) : 0); - if ($result['success']) { - $type = guess_image_type($photo, $result['header']); - if ((! $type) || strpos($type,'image') === false) { - @file_put_contents('cache/' . $hash, $result['body']); - $info = getimagesize('cache/' . $hash); - @unlink('cache/' . $hash); - if (isset($info) && is_array($info) && array_key_exists('mime',$info)) { - $type = $info['mime']; - } - } - if ($type) { - $failed = false; - } - } - elseif (intval($result['return_code']) === 304) { - - // continue using our cached copy, although we still need to figure out the type - // for the benefit of upstream callers that may require it + // Maybe it's already a cached xchan photo - if (file_exists($cached_file)) { - $info = getimagesize($cached_file); - if (isset($info) && is_array($info) && array_key_exists('mime',$info)) { - $type = $info['mime']; - } - } - - $photo = z_root() . '/xp/' . $hash . '-4' . (($thing) ? '.obj' : EMPTY_STR); - $thumb = z_root() . '/xp/' . $hash . '-5' . (($thing) ? '.obj' : EMPTY_STR); - $micro = z_root() . '/xp/' . $hash . '-6' . (($thing) ? '.obj' : EMPTY_STR); - $failed = false; - } + if (strpos($photo, z_root() . '/xp/') === 0) { + return false; + } + + if ($modified) { + $h = [ 'headers' => [ 'If-Modified-Since: ' . gmdate('D, d M Y H:i:s', $modified) . ' GMT' ] ]; + $recurse = 0; + $result = z_fetch_url($photo, true, $recurse, $h); + } else { + $result = z_fetch_url($photo, true); + } + + if ($result['success']) { + $type = guess_image_type($photo, $result['header']); + if ((! $type) || strpos($type, 'image') === false) { + @file_put_contents('cache/' . $hash, $result['body']); + $info = getimagesize('cache/' . $hash); + @unlink('cache/' . $hash); + if (isset($info) && is_array($info) && array_key_exists('mime', $info)) { + $type = $info['mime']; + } + } + if ($type) { + $failed = false; + } + } elseif (intval($result['return_code']) === 304) { + // continue using our cached copy, although we still need to figure out the type + // for the benefit of upstream callers that may require it + + if (file_exists($cached_file)) { + $info = getimagesize($cached_file); + if (isset($info) && is_array($info) && array_key_exists('mime', $info)) { + $type = $info['mime']; + } + } + + $photo = z_root() . '/xp/' . $hash . '-4' . (($thing) ? '.obj' : EMPTY_STR); + $thumb = z_root() . '/xp/' . $hash . '-5' . (($thing) ? '.obj' : EMPTY_STR); + $micro = z_root() . '/xp/' . $hash . '-6' . (($thing) ? '.obj' : EMPTY_STR); + $failed = false; + } - if (! $failed && intval($result['return_code']) !== 304) { - $img = photo_factory($result['body'], $type); - if ($img && $img->is_valid()) { - $width = $img->getWidth(); - $height = $img->getHeight(); + if (! $failed && intval($result['return_code']) !== 304) { + $img = photo_factory($result['body'], $type); + if ($img && $img->is_valid()) { + $width = $img->getWidth(); + $height = $img->getHeight(); - if ($width && $height) { - if (($width / $height) > 1.2) { - // crop out the sides - $margin = $width - $height; - $img->cropImage(300, ($margin / 2), 0, $height, $height); - } - elseif (($height / $width) > 1.2) { - // crop out the bottom - $margin = $height - $width; - $img->cropImage(300, 0, 0, $width, $width); - } - else { - $img->scaleImageSquare(300); - } - } - else { - $failed = true; - } + if ($width && $height) { + if (($width / $height) > 1.2) { + // crop out the sides + $margin = $width - $height; + $img->cropImage(300, ($margin / 2), 0, $height, $height); + } elseif (($height / $width) > 1.2) { + // crop out the bottom + $margin = $height - $width; + $img->cropImage(300, 0, 0, $width, $width); + } else { + $img->scaleImageSquare(300); + } + } else { + $failed = true; + } - $p = [ - 'xchan' => $xchan, - 'resource_id' => $hash, - 'filename' => basename($photo), - 'album' => EMPTY_STR, - 'photo_usage' => 0, - 'imgscale' => 4, - 'edited' => $modified, - ]; + $p = [ + 'xchan' => $xchan, + 'resource_id' => $hash, + 'filename' => basename($photo), + 'album' => EMPTY_STR, + 'photo_usage' => 0, + 'imgscale' => 4, + 'edited' => $modified, + ]; - $savepath = $path . '-' . $p['imgscale'] . (($thing) ? '.obj' : EMPTY_STR); - $photo = z_root() . '/xp/' . $hash . '-' . $p['imgscale'] . (($thing) ? '.obj' : EMPTY_STR); - $r = $img->saveImage($savepath,$animated); - if ($r === false) { - $failed = true; - } - $img->scaleImage(80); - $p['imgscale'] = 5; - $savepath = $path . '-' . $p['imgscale'] . (($thing) ? '.obj' : EMPTY_STR); - $thumb = z_root() . '/xp/' . $hash . '-' . $p['imgscale'] . (($thing) ? '.obj' : EMPTY_STR); - $r = $img->saveImage($savepath,$animated); - if ($r === false) { - $failed = true; - } - $img->scaleImage(48); - $p['imgscale'] = 6; - $savepath = $path . '-' . $p['imgscale'] . (($thing) ? '.obj' : EMPTY_STR); - $micro = z_root() . '/xp/' . $hash . '-' . $p['imgscale'] . (($thing) ? '.obj' : EMPTY_STR); - $r = $img->saveImage($savepath,$animated); - if ($r === false) { - $failed = true; - } - } - else { - logger('Invalid image from ' . $photo); - $failed = true; - } - } - - if ($failed) { - $default = get_default_profile_photo(); - $photo = z_root() . '/' . $default; - $thumb = z_root() . '/' . get_default_profile_photo(80); - $micro = z_root() . '/' . get_default_profile_photo(48); - $type = 'image/png'; - $modified = filemtime($default); - } + $savepath = $path . '-' . $p['imgscale'] . (($thing) ? '.obj' : EMPTY_STR); + $photo = z_root() . '/xp/' . $hash . '-' . $p['imgscale'] . (($thing) ? '.obj' : EMPTY_STR); + $r = $img->saveImage($savepath, $animated); + if ($r === false) { + $failed = true; + } + $img->scaleImage(80); + $p['imgscale'] = 5; + $savepath = $path . '-' . $p['imgscale'] . (($thing) ? '.obj' : EMPTY_STR); + $thumb = z_root() . '/xp/' . $hash . '-' . $p['imgscale'] . (($thing) ? '.obj' : EMPTY_STR); + $r = $img->saveImage($savepath, $animated); + if ($r === false) { + $failed = true; + } + $img->scaleImage(48); + $p['imgscale'] = 6; + $savepath = $path . '-' . $p['imgscale'] . (($thing) ? '.obj' : EMPTY_STR); + $micro = z_root() . '/xp/' . $hash . '-' . $p['imgscale'] . (($thing) ? '.obj' : EMPTY_STR); + $r = $img->saveImage($savepath, $animated); + if ($r === false) { + $failed = true; + } + } else { + logger('Invalid image from ' . $photo); + $failed = true; + } + } - logger('HTTP code: ' . $result['return_code'] . '; modified: ' . (($modified) ? gmdate('D, d M Y H:i:s', $modified) . ' GMT' : 0 ) - . '; failure: ' . ($failed ? 'yes' : 'no') . '; URL: ' . $photo, LOGGER_DEBUG); + if ($failed) { + $default = get_default_profile_photo(); + $photo = z_root() . '/' . $default; + $thumb = z_root() . '/' . get_default_profile_photo(80); + $micro = z_root() . '/' . get_default_profile_photo(48); + $type = 'image/png'; + $modified = filemtime($default); + } - return([$photo, $thumb, $micro, $type, $failed, $modified]); + logger('HTTP code: ' . $result['return_code'] . '; modified: ' . (($modified) ? gmdate('D, d M Y H:i:s', $modified) . ' GMT' : 0 ) + . '; failure: ' . ($failed ? 'yes' : 'no') . '; URL: ' . $photo, LOGGER_DEBUG); + + return([$photo, $thumb, $micro, $type, $failed, $modified]); } @@ -495,21 +483,22 @@ function import_remote_xchan_photo($photo, $xchan, $thing = false) { * @param int $uid channel_id * @return null|string Guessed image mimetype or null. */ -function import_channel_photo_from_url($photo, $aid, $uid) { - $type = null; +function import_channel_photo_from_url($photo, $aid, $uid) +{ + $type = null; - if ($photo) { - $result = z_fetch_url($photo, true); + if ($photo) { + $result = z_fetch_url($photo, true); - if ($result['success']) { - $img_str = $result['body']; - $type = guess_image_type($photo, $result['header']); + if ($result['success']) { + $img_str = $result['body']; + $type = guess_image_type($photo, $result['header']); - import_channel_photo($img_str, $type, $aid, $uid); - } - } + import_channel_photo($img_str, $type, $aid, $uid); + } + } - return $type; + return $type; } /** @@ -519,71 +508,69 @@ function import_channel_photo_from_url($photo, $aid, $uid) { * @param string $type * @param int $aid * @param int $uid channel_id - * @return boolean|string false on failure, otherwise resource_id of photo + * @return bool|string false on failure, otherwise resource_id of photo */ -function import_channel_photo($photo, $type, $aid, $uid) { +function import_channel_photo($photo, $type, $aid, $uid) +{ - logger('Importing channel photo for ' . $uid, LOGGER_DEBUG); + logger('Importing channel photo for ' . $uid, LOGGER_DEBUG); - $r = q("select resource_id from photo where uid = %d and photo_usage = 1 and imgscale = 4", - intval($uid) - ); - if ($r) { - $hash = $r[0]['resource_id']; - } - else { - $hash = photo_new_resource(); - } - - $photo_failure = false; - $filename = $hash; + $r = q( + "select resource_id from photo where uid = %d and photo_usage = 1 and imgscale = 4", + intval($uid) + ); + if ($r) { + $hash = $r[0]['resource_id']; + } else { + $hash = photo_new_resource(); + } - $img = photo_factory($photo, $type); - if ($img->is_valid()) { + $photo_failure = false; + $filename = $hash; - // config array for image save method - $p = [ - 'aid' => $aid, - 'uid' => $uid, - 'resource_id' => $hash, - 'filename' => $filename, - 'album' => t('Profile Photos'), - 'photo_usage' => PHOTO_PROFILE, - 'imgscale' => PHOTO_RES_PROFILE_300, - ]; + $img = photo_factory($photo, $type); + if ($img->is_valid()) { + // config array for image save method + $p = [ + 'aid' => $aid, + 'uid' => $uid, + 'resource_id' => $hash, + 'filename' => $filename, + 'album' => t('Profile Photos'), + 'photo_usage' => PHOTO_PROFILE, + 'imgscale' => PHOTO_RES_PROFILE_300, + ]; - // photo size - $img->scaleImageSquare(300); - $r = $img->save($p); - if ($r === false) { - $photo_failure = true; - } - - // thumb size - $img->scaleImage(80); - $p['imgscale'] = 5; - $r = $img->save($p); - if ($r === false) { - $photo_failure = true; - } - - // micro size - $img->scaleImage(48); - $p['imgscale'] = 6; - $r = $img->save($p); - if ($r === false) { - $photo_failure = true; - } + // photo size + $img->scaleImageSquare(300); + $r = $img->save($p); + if ($r === false) { + $photo_failure = true; + } - } - else { - logger('Invalid image.'); - $photo_failure = true; - } + // thumb size + $img->scaleImage(80); + $p['imgscale'] = 5; + $r = $img->save($p); + if ($r === false) { + $photo_failure = true; + } - if ($photo_failure) { - return false; - } + // micro size + $img->scaleImage(48); + $p['imgscale'] = 6; + $r = $img->save($p); + if ($r === false) { + $photo_failure = true; + } + } else { + logger('Invalid image.'); + $photo_failure = true; + } - return $hash; + if ($photo_failure) { + return false; + } + + return $hash; } diff --git a/include/photos.php b/include/photos.php index 3da0cf6eb..0e31ec601 100644 --- a/include/photos.php +++ b/include/photos.php @@ -1,4 +1,5 @@ false ]; - $channel_id = $channel['channel_id']; - $account_id = $channel['channel_account_id']; - - if (! perm_is_allowed($channel_id, $observer['xchan_hash'], 'write_storage')) { - $ret['message'] = t('Permission denied.'); - return $ret; - } - - /* - * Determine the album to use - */ - - $album = $args['album']; - - $visible = ((intval($args['visible']) || $args['visible'] === 'true') ? 1 : 0); - $deliver = ((array_key_exists('deliver', $args)) ? intval($args['deliver']) : 1 ); - - - // Set to default channel permissions. If the parent directory (album) has permissions set, - // use those instead. If we have specific permissions supplied, they take precedence over - // all other settings. 'allow_cid' being passed from an external source takes priority over channel settings. - // ...messy... needs re-factoring once the photos/files integration stabilises - - $acl = new AccessControl($channel); - if (array_key_exists('directory',$args) && $args['directory']) { - $acl->set($args['directory']); - } - if (array_key_exists('allow_cid',$args)) { - $acl->set($args); - } - if ((array_key_exists('group_allow',$args)) - || (array_key_exists('contact_allow',$args)) - || (array_key_exists('group_deny',$args)) - || (array_key_exists('contact_deny',$args))) { - $acl->set_from_array($args); - } - - $ac = $acl->get(); - - $width = $height = 0; - - if($args['getimagesize']) { - $width = $args['getimagesize'][0]; - $height = $args['getimagesize'][1]; - } - - $os_storage = 0; - - $max_thumb = get_config('system','max_thumbnail',1600); - - if($args['os_syspath'] && $args['getimagesize']) { - if($args['getimagesize'][0] > $max_thumb || $args['getimagesize'][1] > $max_thumb) { - $imagick_path = get_config('system','imagick_convert_path'); - if($imagick_path && @file_exists($imagick_path)) { - $tmp_name = $args['os_syspath'] . '-001'; - $newsize = photo_calculate_scale(array_merge($args['getimagesize'],['max' => $max_thumb])); - $cmd = $imagick_path . ' ' . escapeshellarg(PROJECT_BASE . '/' . $args['os_syspath']) . ' -resize ' . $newsize . ' ' . escapeshellarg(PROJECT_BASE . '/' . $tmp_name); - logger('imagick thumbnail command: ' . $cmd); - for($x = 0; $x < 4; $x ++) { - exec($cmd); - if(file_exists($tmp_name)) { - break; - } - logger('imagick scale failed. Retrying.'); - continue; - } - if(! file_exists($tmp_name)) { - logger('imagick scale failed. Abort.'); - return $ret; - } - - $imagedata = @file_get_contents($tmp_name); - $filesize = @filesize($args['os_syspath']); - } - else { - $imagedata = @file_get_contents($args['os_syspath']); - $filesize = strlen($imagedata); - } - } - else { - $imagedata = @file_get_contents($args['os_syspath']); - $filesize = strlen($imagedata); - } - $filename = $args['filename']; - // this is going to be deleted if it exists - $src = '/tmp/deletemenow'; - $type = $args['getimagesize']['mime']; - $os_storage = 1; - } - elseif ($args['data'] || $args['content']) { - - // allow an import from a binary string representing the image. - // This bypasses the upload step and max size limit checking - - $imagedata = (($args['content']) ? $args['content'] : $args['data']); - $filename = $args['filename']; - $filesize = strlen($imagedata); - // this is going to be deleted if it exists - $src = '/tmp/deletemenow'; - $type = (($args['mimetype']) ? $args['mimetype'] : $args['type']); - } else { - $f = array('src' => '', 'filename' => '', 'filesize' => 0, 'type' => ''); - - if (x($f,'src') && x($f,'filesize')) { - $src = $f['src']; - $filename = $f['filename']; - $filesize = $f['filesize']; - $type = $f['type']; - } else { - $src = $_FILES['userfile']['tmp_name']; - $filename = basename($_FILES['userfile']['name']); - $filesize = intval($_FILES['userfile']['size']); - $type = $_FILES['userfile']['type']; - } - - if (! $type) - $type=guess_image_type($filename); - - logger('Received file: ' . $filename . ' as ' . $src . ' ('. $type . ') ' . $filesize . ' bytes', LOGGER_DEBUG); - - $maximagesize = get_config('system','maximagesize'); - - if (($maximagesize) && ($filesize > $maximagesize)) { - $ret['message'] = sprintf ( t('Image exceeds website size limit of %lu bytes'), $maximagesize); - @unlink($src); - /** - * @hooks photo_upload_end - * Called when a photo upload has been processed. - */ - call_hooks('photo_upload_end', $ret); - return $ret; - } - - if (! $filesize) { - $ret['message'] = t('Image file is empty.'); - @unlink($src); - /** - * @hooks photo_post_end - * Called after uploading a photo. - */ - call_hooks('photo_post_end', $ret); - return $ret; - } - - logger('Loading the contents of ' . $src , LOGGER_DEBUG); - $imagedata = @file_get_contents($src); - } - - $r = q("select sum(filesize) as total from photo where aid = %d and imgscale = 0 ", - intval($account_id) - ); - - $limit = engr_units_to_bytes(service_class_fetch($channel_id,'photo_upload_limit')); - - if (($r) && ($limit !== false) && (($r[0]['total'] + strlen($imagedata)) > $limit)) { - $ret['message'] = upgrade_message(); - @unlink($src); - /** - * @hooks photo_post_end - * Called after uploading a photo. - */ - call_hooks('photo_post_end', $ret); - return $ret; - } - - $ph = photo_factory($imagedata, $type); - - if (! ($ph && $ph->is_valid())) { - $ret['message'] = t('Unable to process image'); - logger('unable to process image'); - @unlink($src); - /** - * @hooks photo_upload_end - * Called when a photo upload has been processed. - */ - call_hooks('photo_upload_end', $ret); - return $ret; - } - - // obtain exif data from the source file if present - - $exif = $ph->exif(($args['os_syspath']) ? $args['os_syspath'] : $src); - - if($exif) { - $ph->orient($exif); - } - - $ph->clearexif(); - - if(get_pconfig($channel_id,'system','clearexif',false)) { - - // only do this on the original if it was uploaded by some method other than WebDAV. - // Otherwise Microsoft Windows will note the file size mismatch, erase the file and - // upload it again and again. - - } - - @unlink($src); - - $max_length = get_config('system','max_image_length', MAX_IMAGE_LENGTH); - if ($max_length > 0) { - $ph->scaleImage($max_length); - } - - if (! $width) { - $width = $ph->getWidth(); - } - if (! $height) { - $height = $ph->getHeight(); - } - $smallest = 0; - - $photo_hash = (($args['resource_id']) ? $args['resource_id'] : photo_new_resource()); - - $visitor = ''; - if ($channel['channel_hash'] !== $observer['xchan_hash']) - $visitor = $observer['xchan_hash']; - - $errors = false; - - $p = array('aid' => $account_id, 'uid' => $channel_id, 'xchan' => $visitor, 'resource_id' => $photo_hash, - 'filename' => $filename, 'album' => $album, 'imgscale' => 0, 'photo_usage' => PHOTO_NORMAL, - 'width' => $width, 'height' => $height, - 'allow_cid' => $ac['allow_cid'], 'allow_gid' => $ac['allow_gid'], - 'deny_cid' => $ac['deny_cid'], 'deny_gid' => $ac['deny_gid'], - 'os_storage' => $os_storage, 'os_syspath' => $args['os_syspath'], - 'os_path' => $args['os_path'], 'display_path' => $args['display_path'] - ); - if($args['created']) - $p['created'] = $args['created']; - if($args['edited']) - $p['edited'] = $args['edited']; - if($args['title']) - $p['title'] = $args['title']; - - if ($args['description']) { - $p['description'] = $args['description']; - } - - $alt_desc = ((isset($p['description']) && $p['description']) ? $p['description'] : $p['filename']); - - $url = []; - - $r0 = $ph->save($p); - $url[0] = [ - 'type' => 'Link', - 'rel' => 'alternate', - 'mediaType' => $type, - 'summary' => $alt_desc, - 'href' => z_root() . '/photo/' . $photo_hash . '-0.' . $ph->getExt(), - 'width' => $width, - 'height' => $height - ]; - if(! $r0) - $errors = true; - - unset($p['os_storage']); - unset($p['os_syspath']); - unset($p['width']); - unset($p['height']); - - if(($width > 1024 || $height > 1024) && (! $errors)) - $ph->scaleImage(1024); - - $p['imgscale'] = 1; - $r1 = $ph->storeThumbnail($p, PHOTO_RES_1024); - $url[1] = [ - 'type' => 'Link', - 'rel' => 'alternate', - 'mediaType' => $type, - 'summary' => $alt_desc, - 'href' => z_root() . '/photo/' . $photo_hash . '-1.' . $ph->getExt(), - 'width' => $ph->getWidth(), - 'height' => $ph->getHeight() - ]; - if(! $r1) - $errors = true; - - if(($width > 640 || $height > 640) && (! $errors)) - $ph->scaleImage(640); - - $p['imgscale'] = 2; - $r2 = $ph->storeThumbnail($p, PHOTO_RES_640); - $url[2] = [ - 'type' => 'Link', - 'rel' => 'alternate', - 'mediaType' => $type, - 'summary' => $alt_desc, - 'href' => z_root() . '/photo/' . $photo_hash . '-2.' . $ph->getExt(), - 'width' => $ph->getWidth(), - 'height' => $ph->getHeight() - ]; - if(! $r2) - $errors = true; - - if(($width > 320 || $height > 320) && (! $errors)) - $ph->scaleImage(320); - - $p['imgscale'] = 3; - $r3 = $ph->storeThumbnail($p, PHOTO_RES_320); - $url[3] = [ - 'type' => 'Link', - 'rel' => 'alternate', - 'mediaType' => $type, - 'summary' => $alt_desc, - 'href' => z_root() . '/photo/' . $photo_hash . '-3.' . $ph->getExt(), - 'width' => $ph->getWidth(), - 'height' => $ph->getHeight() - ]; - if(! $r3) - $errors = true; - - if($errors) { - q("delete from photo where resource_id = '%s' and uid = %d", - dbesc($photo_hash), - intval($channel_id) - ); - $ret['message'] = t('Photo storage failed.'); - logger('Photo store failed.'); - /** - * @hooks photo_upload_end - * Called when a photo upload has been processed. - */ - call_hooks('photo_upload_end', $ret); - return $ret; - } - - $url[] = [ - 'type' => 'Link', - 'rel' => 'about', - 'mediaType' => 'text/html', - 'href' => z_root() . '/photos/' . $channel['channel_address'] . '/image/' . $photo_hash - ]; - - $item_hidden = (($visible) ? 0 : 1 ); - - $lat = $lon = null; - - if($exif && Apps::system_app_installed($channel_id,'Photomap')) { - $gps = null; - if(array_key_exists('GPS',$exif)) { - $gps = $exif['GPS']; - } - elseif(array_key_exists('GPSLatitude',$exif)) { - $gps = $exif; - } - if($gps) { - $lat = getGps($gps['GPSLatitude'], $gps['GPSLatitudeRef']); - $lon = getGps($gps['GPSLongitude'], $gps['GPSLongitudeRef']); - } - } - - $title = ((isset($args['title']) && $args['title']) ? $args['title'] : $args['filename']); - - $desc = htmlspecialchars($alt_desc); - - $found_tags = linkify_tags($args['body'], $channel_id); - - $alt = ' alt="' . $desc . '"' ; - - $scale = 1; - $width = $url[1]['width']; - $height = $url[1]['height']; - $tag = (($r1) ? '[zmg width="' . $width . '" height="' . $height . '"' . $alt . ']' : '[zmg' . $alt . ']'); - - $author_link = '[zrl=' . z_root() . '/channel/' . $channel['channel_address'] . ']' . $channel['channel_name'] . '[/zrl]'; - - $photo_link = '[zrl=' . z_root() . '/photos/' . $channel['channel_address'] . '/image/' . $photo_hash . ']' . t('a new photo') . '[/zrl]'; - - $album_link = '[zrl=' . z_root() . '/photos/' . $channel['channel_address'] . '/album/' . $args['directory']['hash'] . ']' . ((strlen($album)) ? $album : '/') . '[/zrl]'; - - $activity_format = sprintf(t('%1$s posted %2$s to %3$s','photo_upload'), $author_link, $photo_link, $album_link); - - $body = (($args['body']) ? $args['body'] : '') . '[footer]' . $activity_format . '[/footer]'; - - // If uploaded into a post, this is the text that is returned to the webapp for inclusion in the post. - - $obj_body = '[zrl=' . z_root() . '/photos/' . $channel['channel_address'] . '/image/' . $photo_hash . ']' - . $tag . z_root() . "/photo/{$photo_hash}-{$scale}." . $ph->getExt() . '[/zmg]' - . '[/zrl]'; - - $attribution = (($visitor) ? $visitor['xchan_url'] : $channel['xchan_url']); - - // Create item object - $object = [ - 'type' => ACTIVITY_OBJ_PHOTO, - 'name' => $title, - 'summary' => $p['description'], - 'published' => datetime_convert('UTC','UTC',$p['created'],ATOM_TIME), - 'updated' => datetime_convert('UTC','UTC',$p['edited'],ATOM_TIME), - 'attributedTo' => $attribution, - // This is a placeholder and will get over-ridden by the item mid, which is critical for sharing as a conversational item over activitypub - 'id' => z_root() . '/photo/' . $photo_hash, - 'url' => $url, - 'source' => [ 'content' => $body, 'mediaType' => 'text/x-multicode' ], - 'content' => bbcode($body, [ 'export' => true ]) - ]; - - $public = (($ac['allow_cid'] || $ac['allow_gid'] || $ac['deny_cid'] || $ac['deny_gid']) ? false : true); - - if ($public) { - $object['to'] = [ ACTIVITY_PUBLIC_INBOX ]; - $object['cc'] = [ z_root() . '/followers/' . $channel['channel_address'] ]; +function photo_upload($channel, $observer, $args) +{ + + $ret = [ 'success' => false ]; + $channel_id = $channel['channel_id']; + $account_id = $channel['channel_account_id']; + + if (! perm_is_allowed($channel_id, $observer['xchan_hash'], 'write_storage')) { + $ret['message'] = t('Permission denied.'); + return $ret; + } + + /* + * Determine the album to use + */ + + $album = $args['album']; + + $visible = ((intval($args['visible']) || $args['visible'] === 'true') ? 1 : 0); + $deliver = ((array_key_exists('deliver', $args)) ? intval($args['deliver']) : 1 ); + + + // Set to default channel permissions. If the parent directory (album) has permissions set, + // use those instead. If we have specific permissions supplied, they take precedence over + // all other settings. 'allow_cid' being passed from an external source takes priority over channel settings. + // ...messy... needs re-factoring once the photos/files integration stabilises + + $acl = new AccessControl($channel); + if (array_key_exists('directory', $args) && $args['directory']) { + $acl->set($args['directory']); + } + if (array_key_exists('allow_cid', $args)) { + $acl->set($args); + } + if ( + (array_key_exists('group_allow', $args)) + || (array_key_exists('contact_allow', $args)) + || (array_key_exists('group_deny', $args)) + || (array_key_exists('contact_deny', $args)) + ) { + $acl->set_from_array($args); + } + + $ac = $acl->get(); + + $width = $height = 0; + + if ($args['getimagesize']) { + $width = $args['getimagesize'][0]; + $height = $args['getimagesize'][1]; + } + + $os_storage = 0; + + $max_thumb = get_config('system', 'max_thumbnail', 1600); + + if ($args['os_syspath'] && $args['getimagesize']) { + if ($args['getimagesize'][0] > $max_thumb || $args['getimagesize'][1] > $max_thumb) { + $imagick_path = get_config('system', 'imagick_convert_path'); + if ($imagick_path && @file_exists($imagick_path)) { + $tmp_name = $args['os_syspath'] . '-001'; + $newsize = photo_calculate_scale(array_merge($args['getimagesize'], ['max' => $max_thumb])); + $cmd = $imagick_path . ' ' . escapeshellarg(PROJECT_BASE . '/' . $args['os_syspath']) . ' -resize ' . $newsize . ' ' . escapeshellarg(PROJECT_BASE . '/' . $tmp_name); + logger('imagick thumbnail command: ' . $cmd); + for ($x = 0; $x < 4; $x++) { + exec($cmd); + if (file_exists($tmp_name)) { + break; + } + logger('imagick scale failed. Retrying.'); + continue; + } + if (! file_exists($tmp_name)) { + logger('imagick scale failed. Abort.'); + return $ret; + } + + $imagedata = @file_get_contents($tmp_name); + $filesize = @filesize($args['os_syspath']); + } else { + $imagedata = @file_get_contents($args['os_syspath']); + $filesize = strlen($imagedata); + } + } else { + $imagedata = @file_get_contents($args['os_syspath']); + $filesize = strlen($imagedata); + } + $filename = $args['filename']; + // this is going to be deleted if it exists + $src = '/tmp/deletemenow'; + $type = $args['getimagesize']['mime']; + $os_storage = 1; + } elseif ($args['data'] || $args['content']) { + // allow an import from a binary string representing the image. + // This bypasses the upload step and max size limit checking + + $imagedata = (($args['content']) ? $args['content'] : $args['data']); + $filename = $args['filename']; + $filesize = strlen($imagedata); + // this is going to be deleted if it exists + $src = '/tmp/deletemenow'; + $type = (($args['mimetype']) ? $args['mimetype'] : $args['type']); + } else { + $f = array('src' => '', 'filename' => '', 'filesize' => 0, 'type' => ''); + + if (x($f, 'src') && x($f, 'filesize')) { + $src = $f['src']; + $filename = $f['filename']; + $filesize = $f['filesize']; + $type = $f['type']; + } else { + $src = $_FILES['userfile']['tmp_name']; + $filename = basename($_FILES['userfile']['name']); + $filesize = intval($_FILES['userfile']['size']); + $type = $_FILES['userfile']['type']; + } + + if (! $type) { + $type = guess_image_type($filename); + } + + logger('Received file: ' . $filename . ' as ' . $src . ' (' . $type . ') ' . $filesize . ' bytes', LOGGER_DEBUG); + + $maximagesize = get_config('system', 'maximagesize'); + + if (($maximagesize) && ($filesize > $maximagesize)) { + $ret['message'] = sprintf(t('Image exceeds website size limit of %lu bytes'), $maximagesize); + @unlink($src); + /** + * @hooks photo_upload_end + * Called when a photo upload has been processed. + */ + call_hooks('photo_upload_end', $ret); + return $ret; + } + + if (! $filesize) { + $ret['message'] = t('Image file is empty.'); + @unlink($src); + /** + * @hooks photo_post_end + * Called after uploading a photo. + */ + call_hooks('photo_post_end', $ret); + return $ret; + } + + logger('Loading the contents of ' . $src, LOGGER_DEBUG); + $imagedata = @file_get_contents($src); + } + + $r = q( + "select sum(filesize) as total from photo where aid = %d and imgscale = 0 ", + intval($account_id) + ); + + $limit = engr_units_to_bytes(service_class_fetch($channel_id, 'photo_upload_limit')); + + if (($r) && ($limit !== false) && (($r[0]['total'] + strlen($imagedata)) > $limit)) { + $ret['message'] = upgrade_message(); + @unlink($src); + /** + * @hooks photo_post_end + * Called after uploading a photo. + */ + call_hooks('photo_post_end', $ret); + return $ret; + } + + $ph = photo_factory($imagedata, $type); + + if (! ($ph && $ph->is_valid())) { + $ret['message'] = t('Unable to process image'); + logger('unable to process image'); + @unlink($src); + /** + * @hooks photo_upload_end + * Called when a photo upload has been processed. + */ + call_hooks('photo_upload_end', $ret); + return $ret; + } + + // obtain exif data from the source file if present + + $exif = $ph->exif(($args['os_syspath']) ? $args['os_syspath'] : $src); + + if ($exif) { + $ph->orient($exif); + } + + $ph->clearexif(); + + if (get_pconfig($channel_id, 'system', 'clearexif', false)) { + // only do this on the original if it was uploaded by some method other than WebDAV. + // Otherwise Microsoft Windows will note the file size mismatch, erase the file and + // upload it again and again. + } + + @unlink($src); + + $max_length = get_config('system', 'max_image_length', MAX_IMAGE_LENGTH); + if ($max_length > 0) { + $ph->scaleImage($max_length); + } + + if (! $width) { + $width = $ph->getWidth(); + } + if (! $height) { + $height = $ph->getHeight(); + } + $smallest = 0; + + $photo_hash = (($args['resource_id']) ? $args['resource_id'] : photo_new_resource()); + + $visitor = ''; + if ($channel['channel_hash'] !== $observer['xchan_hash']) { + $visitor = $observer['xchan_hash']; + } + + $errors = false; + + $p = array('aid' => $account_id, 'uid' => $channel_id, 'xchan' => $visitor, 'resource_id' => $photo_hash, + 'filename' => $filename, 'album' => $album, 'imgscale' => 0, 'photo_usage' => PHOTO_NORMAL, + 'width' => $width, 'height' => $height, + 'allow_cid' => $ac['allow_cid'], 'allow_gid' => $ac['allow_gid'], + 'deny_cid' => $ac['deny_cid'], 'deny_gid' => $ac['deny_gid'], + 'os_storage' => $os_storage, 'os_syspath' => $args['os_syspath'], + 'os_path' => $args['os_path'], 'display_path' => $args['display_path'] + ); + if ($args['created']) { + $p['created'] = $args['created']; + } + if ($args['edited']) { + $p['edited'] = $args['edited']; + } + if ($args['title']) { + $p['title'] = $args['title']; + } + + if ($args['description']) { + $p['description'] = $args['description']; + } + + $alt_desc = ((isset($p['description']) && $p['description']) ? $p['description'] : $p['filename']); + + $url = []; + + $r0 = $ph->save($p); + $url[0] = [ + 'type' => 'Link', + 'rel' => 'alternate', + 'mediaType' => $type, + 'summary' => $alt_desc, + 'href' => z_root() . '/photo/' . $photo_hash . '-0.' . $ph->getExt(), + 'width' => $width, + 'height' => $height + ]; + if (! $r0) { + $errors = true; + } + + unset($p['os_storage']); + unset($p['os_syspath']); + unset($p['width']); + unset($p['height']); + + if (($width > 1024 || $height > 1024) && (! $errors)) { + $ph->scaleImage(1024); + } + + $p['imgscale'] = 1; + $r1 = $ph->storeThumbnail($p, PHOTO_RES_1024); + $url[1] = [ + 'type' => 'Link', + 'rel' => 'alternate', + 'mediaType' => $type, + 'summary' => $alt_desc, + 'href' => z_root() . '/photo/' . $photo_hash . '-1.' . $ph->getExt(), + 'width' => $ph->getWidth(), + 'height' => $ph->getHeight() + ]; + if (! $r1) { + $errors = true; + } + + if (($width > 640 || $height > 640) && (! $errors)) { + $ph->scaleImage(640); + } + + $p['imgscale'] = 2; + $r2 = $ph->storeThumbnail($p, PHOTO_RES_640); + $url[2] = [ + 'type' => 'Link', + 'rel' => 'alternate', + 'mediaType' => $type, + 'summary' => $alt_desc, + 'href' => z_root() . '/photo/' . $photo_hash . '-2.' . $ph->getExt(), + 'width' => $ph->getWidth(), + 'height' => $ph->getHeight() + ]; + if (! $r2) { + $errors = true; + } + + if (($width > 320 || $height > 320) && (! $errors)) { + $ph->scaleImage(320); + } + + $p['imgscale'] = 3; + $r3 = $ph->storeThumbnail($p, PHOTO_RES_320); + $url[3] = [ + 'type' => 'Link', + 'rel' => 'alternate', + 'mediaType' => $type, + 'summary' => $alt_desc, + 'href' => z_root() . '/photo/' . $photo_hash . '-3.' . $ph->getExt(), + 'width' => $ph->getWidth(), + 'height' => $ph->getHeight() + ]; + if (! $r3) { + $errors = true; + } + + if ($errors) { + q( + "delete from photo where resource_id = '%s' and uid = %d", + dbesc($photo_hash), + intval($channel_id) + ); + $ret['message'] = t('Photo storage failed.'); + logger('Photo store failed.'); + /** + * @hooks photo_upload_end + * Called when a photo upload has been processed. + */ + call_hooks('photo_upload_end', $ret); + return $ret; + } + + $url[] = [ + 'type' => 'Link', + 'rel' => 'about', + 'mediaType' => 'text/html', + 'href' => z_root() . '/photos/' . $channel['channel_address'] . '/image/' . $photo_hash + ]; + + $item_hidden = (($visible) ? 0 : 1 ); + + $lat = $lon = null; + + if ($exif && Apps::system_app_installed($channel_id, 'Photomap')) { + $gps = null; + if (array_key_exists('GPS', $exif)) { + $gps = $exif['GPS']; + } elseif (array_key_exists('GPSLatitude', $exif)) { + $gps = $exif; + } + if ($gps) { + $lat = getGps($gps['GPSLatitude'], $gps['GPSLatitudeRef']); + $lon = getGps($gps['GPSLongitude'], $gps['GPSLongitudeRef']); + } + } + + $title = ((isset($args['title']) && $args['title']) ? $args['title'] : $args['filename']); + + $desc = htmlspecialchars($alt_desc); + + $found_tags = linkify_tags($args['body'], $channel_id); + + $alt = ' alt="' . $desc . '"' ; + + $scale = 1; + $width = $url[1]['width']; + $height = $url[1]['height']; + $tag = (($r1) ? '[zmg width="' . $width . '" height="' . $height . '"' . $alt . ']' : '[zmg' . $alt . ']'); + + $author_link = '[zrl=' . z_root() . '/channel/' . $channel['channel_address'] . ']' . $channel['channel_name'] . '[/zrl]'; + + $photo_link = '[zrl=' . z_root() . '/photos/' . $channel['channel_address'] . '/image/' . $photo_hash . ']' . t('a new photo') . '[/zrl]'; + + if (array_path_exists('/directory/hash',$args)) { + $album_link = '[zrl=' . z_root() . '/photos/' . $channel['channel_address'] . '/album/' . $args['directory']['hash'] . ']' . ((strlen($album)) ? $album : '/') . '[/zrl]'; } else { - $object['to'] = Activity::map_acl(array_merge($ac, ['item_private' => 1 - intval($public) ])); + $album_link = EMPTY_STR; } + $activity_format = sprintf(t('%1$s posted %2$s to %3$s', 'photo_upload'), $author_link, $photo_link, $album_link); - $target = [ - 'type' => 'orderedCollection', - 'name' => ((strlen($album)) ? $album : '/'), - 'id' => z_root() . '/album/' . $channel['channel_address'] . ((isset($args['folder'])) ? '/' . $args['folder'] : EMPTY_STR) - ]; + $body = (($args['body']) ? $args['body'] : '') . '[footer]' . $activity_format . '[/footer]'; - $post_tags = []; + // If uploaded into a post, this is the text that is returned to the webapp for inclusion in the post. - if ($found_tags) { - foreach($found_tags as $result) { - $success = $result['success']; - if($success['replaced']) { - $post_tags[] = array( - 'uid' => $channel['channel_id'], - 'ttype' => $success['termtype'], - 'otype' => TERM_OBJ_POST, - 'term' => $success['term'], - 'url' => $success['url'] - ); - } - } - } + $obj_body = '[zrl=' . z_root() . '/photos/' . $channel['channel_address'] . '/image/' . $photo_hash . ']' + . $tag . z_root() . "/photo/{$photo_hash}-{$scale}." . $ph->getExt() . '[/zmg]' + . '[/zrl]'; - // Create item container - if ($args['item']) { - foreach ($args['item'] as $i) { + $attribution = (($visitor) ? $visitor['xchan_url'] : $channel['xchan_url']); - $item = get_item_elements($i); - $force = false; + // Create item object + $object = [ + 'type' => ACTIVITY_OBJ_PHOTO, + 'name' => $title, + 'summary' => $p['description'], + 'published' => datetime_convert('UTC', 'UTC', $p['created'], ATOM_TIME), + 'updated' => datetime_convert('UTC', 'UTC', $p['edited'], ATOM_TIME), + 'attributedTo' => $attribution, + // This is a placeholder and will get over-ridden by the item mid, which is critical for sharing as a conversational item over activitypub + 'id' => z_root() . '/photo/' . $photo_hash, + 'url' => $url, + 'source' => [ 'content' => $body, 'mediaType' => 'text/x-multicode' ], + 'content' => bbcode($body) + ]; - if($item['mid'] === $item['parent_mid']) { + $public = (($ac['allow_cid'] || $ac['allow_gid'] || $ac['deny_cid'] || $ac['deny_gid']) ? false : true); - $object['id'] = $item['mid']; - $item['summary'] = $summary; - $item['body'] = $body; - $item['mimetype'] = 'text/x-multicode'; - $item['obj_type'] = ACTIVITY_OBJ_PHOTO; - $item['obj'] = json_encode($object); + if ($public) { + $object['to'] = [ ACTIVITY_PUBLIC_INBOX ]; + $object['cc'] = [ z_root() . '/followers/' . $channel['channel_address'] ]; + } else { + $object['to'] = Activity::map_acl(array_merge($ac, ['item_private' => 1 - intval($public) ])); + } - $item['tgt_type'] = 'orderedCollection'; - $item['target'] = json_encode($target); - if ($post_tags) { - $arr['term'] = $post_tags; - } - $force = true; - } - $r = q("select id, edited from item where mid = '%s' and uid = %d limit 1", - dbesc($item['mid']), - intval($channel['channel_id']) - ); - if ($r) { - if (($item['edited'] > $r[0]['edited']) || $force) { - $item['id'] = $r[0]['id']; - $item['uid'] = $channel['channel_id']; - item_store_update($item,false,$deliver); - continue; - } - } - else { - $item['aid'] = $channel['channel_account_id']; - $item['uid'] = $channel['channel_id']; - $item_result = item_store($item,false,$deliver); - } - } - } - else { - - $uuid = new_uuid(); - $mid = z_root() . '/item/' . $uuid; + $target = [ + 'type' => 'orderedCollection', + 'name' => ((strlen($album)) ? $album : '/'), + 'id' => z_root() . '/album/' . $channel['channel_address'] . ((isset($args['folder'])) ? '/' . $args['folder'] : EMPTY_STR) + ]; - $object['id'] = $mid; + $post_tags = []; - $arr = [ - 'aid' => $account_id, - 'uid' => $channel_id, - 'uuid' => $uuid, - 'mid' => $mid, - 'parent_mid' => $mid, - 'created' => $p['created'], - 'edited' => $p['edited'], - 'item_hidden' => $item_hidden, - 'resource_type' => 'photo', - 'resource_id' => $photo_hash, - 'owner_xchan' => $channel['channel_hash'], - 'author_xchan' => $observer['xchan_hash'], - 'title' => $title, - 'allow_cid' => $ac['allow_cid'], - 'allow_gid' => $ac['allow_gid'], - 'deny_cid' => $ac['deny_cid'], - 'deny_gid' => $ac['deny_gid'], - 'verb' => ACTIVITY_POST, - 'obj_type' => ACTIVITY_OBJ_PHOTO, - 'obj' => json_encode($object), - 'tgt_type' => 'orderedCollection', - 'target' => json_encode($target), - 'item_wall' => 1, - 'item_origin' => 1, - 'item_thread_top' => 1, - 'item_private' => intval($acl->is_private()), - 'summary' => $summary, - 'body' => $body - ]; + if ($found_tags) { + foreach ($found_tags as $result) { + $success = $result['success']; + if ($success['replaced']) { + $post_tags[] = array( + 'uid' => $channel['channel_id'], + 'ttype' => $success['termtype'], + 'otype' => TERM_OBJ_POST, + 'term' => $success['term'], + 'url' => $success['url'] + ); + } + } + } - if ($post_tags) { - $arr['term'] = $post_tags; - } + // Create item container + if ($args['item']) { + foreach ($args['item'] as $i) { + $item = get_item_elements($i); + $force = false; - $arr['plink'] = z_root() . '/channel/' . $channel['channel_address'] . '/?f=&mid=' . urlencode($arr['mid']); + if ($item['mid'] === $item['parent_mid']) { + $object['id'] = $item['mid']; + $item['summary'] = $summary; + $item['body'] = $body; + $item['mimetype'] = 'text/x-multicode'; + $item['obj_type'] = ACTIVITY_OBJ_PHOTO; + $item['obj'] = json_encode($object); - if($lat && $lon) - $arr['coord'] = $lat . ' ' . $lon; + $item['tgt_type'] = 'orderedCollection'; + $item['target'] = json_encode($target); + if ($post_tags) { + $arr['term'] = $post_tags; + } + $force = true; + } + $r = q( + "select id, edited from item where mid = '%s' and uid = %d limit 1", + dbesc($item['mid']), + intval($channel['channel_id']) + ); + if ($r) { + if (($item['edited'] > $r[0]['edited']) || $force) { + $item['id'] = $r[0]['id']; + $item['uid'] = $channel['channel_id']; + item_store_update($item, false, $deliver); + continue; + } + } else { + $item['aid'] = $channel['channel_account_id']; + $item['uid'] = $channel['channel_id']; + $item_result = item_store($item, false, $deliver); + } + } + } else { + $uuid = new_uuid(); + $mid = z_root() . '/item/' . $uuid; - // this one is tricky because the item and the photo have the same permissions, those of the photo. - // Use the channel read_stream permissions to get the correct public_policy for the item and recalculate the - // private flag accordingly. This may cause subtle bugs due to custom permissions roles. We want to use - // public policy when federating items to other sites, but should probably ignore them when accessing the item - // in the photos pages - using the photos permissions instead. We need the public policy to keep the photo - // linked item from leaking into the feed when somebody has a channel with read_stream restrictions. + $object['id'] = $mid; - $arr['public_policy'] = map_scope(PermissionLimits::Get($channel['channel_id'],'view_stream'),true); - if($arr['public_policy']) - $arr['item_private'] = 1; + $arr = [ + 'aid' => $account_id, + 'uid' => $channel_id, + 'uuid' => $uuid, + 'mid' => $mid, + 'parent_mid' => $mid, + 'created' => $p['created'], + 'edited' => $p['edited'], + 'item_hidden' => $item_hidden, + 'resource_type' => 'photo', + 'resource_id' => $photo_hash, + 'owner_xchan' => $channel['channel_hash'], + 'author_xchan' => $observer['xchan_hash'], + 'title' => $title, + 'allow_cid' => $ac['allow_cid'], + 'allow_gid' => $ac['allow_gid'], + 'deny_cid' => $ac['deny_cid'], + 'deny_gid' => $ac['deny_gid'], + 'verb' => ACTIVITY_POST, + 'obj_type' => ACTIVITY_OBJ_PHOTO, + 'obj' => json_encode($object), + 'tgt_type' => 'orderedCollection', + 'target' => json_encode($target), + 'item_wall' => 1, + 'item_origin' => 1, + 'item_thread_top' => 1, + 'item_private' => intval($acl->is_private()), + 'summary' => $summary, + 'body' => $body + ]; + + if ($post_tags) { + $arr['term'] = $post_tags; + } + + $arr['plink'] = z_root() . '/channel/' . $channel['channel_address'] . '/?f=&mid=' . urlencode($arr['mid']); + + if ($lat && $lon) { + $arr['coord'] = $lat . ' ' . $lon; + } + + // this one is tricky because the item and the photo have the same permissions, those of the photo. + // Use the channel read_stream permissions to get the correct public_policy for the item and recalculate the + // private flag accordingly. This may cause subtle bugs due to custom permissions roles. We want to use + // public policy when federating items to other sites, but should probably ignore them when accessing the item + // in the photos pages - using the photos permissions instead. We need the public policy to keep the photo + // linked item from leaking into the feed when somebody has a channel with read_stream restrictions. + + $arr['public_policy'] = map_scope(PermissionLimits::Get($channel['channel_id'], 'view_stream'), true); + if ($arr['public_policy']) { + $arr['item_private'] = 1; + } - $result = item_store($arr,false,$deliver); - $item_id = $result['item_id']; + $result = item_store($arr, false, $deliver); + $item_id = $result['item_id']; - if($visible && $deliver) - Run::Summon( [ 'Notifier', 'wall-new', $item_id ] ); - } + if ($visible && $deliver) { + Run::Summon([ 'Notifier', 'wall-new', $item_id ]); + } + } - $ret['success'] = true; - $ret['item'] = $arr; - $ret['body'] = $obj_body; - $ret['resource_id'] = $photo_hash; - $ret['photoitem_id'] = $item_id; + $ret['success'] = true; + $ret['item'] = $arr; + $ret['body'] = $obj_body; + $ret['resource_id'] = $photo_hash; + $ret['photoitem_id'] = $item_id; - /** - * @hooks photo_upload_end - * Called when a photo upload has been processed. - */ - call_hooks('photo_upload_end', $ret); + /** + * @hooks photo_upload_end + * Called when a photo upload has been processed. + */ + call_hooks('photo_upload_end', $ret); - return $ret; + return $ret; } -function photo_calculate_scale($arr) { +function photo_calculate_scale($arr) +{ - $max = $arr['max']; - $width = $arr[0]; - $height = $arr[1]; + $max = $arr['max']; + $width = $arr[0]; + $height = $arr[1]; - $dest_width = $dest_height = 0; + $dest_width = $dest_height = 0; - if (! ($width && $height)) { - return false; - } + if (! ($width && $height)) { + return false; + } - if ($width > $max && $height > $max) { + if ($width > $max && $height > $max) { + // very tall image (greater than 16:9) + // constrain the width - let the height float. - // very tall image (greater than 16:9) - // constrain the width - let the height float. + if ((($height * 9) / 16) > $width) { + $dest_width = $max; + $dest_height = intval(( $height * $max ) / $width); + } - if ((($height * 9) / 16) > $width) { - $dest_width = $max; - $dest_height = intval(( $height * $max ) / $width); - } + // else constrain both dimensions - // else constrain both dimensions + elseif ($width > $height) { + $dest_width = $max; + $dest_height = intval(( $height * $max ) / $width); + } else { + $dest_width = intval(( $width * $max ) / $height); + $dest_height = $max; + } + } else { + if ($width > $max) { + $dest_width = $max; + $dest_height = intval(( $height * $max ) / $width); + } else { + if ($height > $max) { + // very tall image (greater than 16:9) + // but width is OK - don't do anything - elseif ($width > $height) { - $dest_width = $max; - $dest_height = intval(( $height * $max ) / $width); - } - else { - $dest_width = intval(( $width * $max ) / $height); - $dest_height = $max; - } - } - else { - if ( $width > $max ) { - $dest_width = $max; - $dest_height = intval(( $height * $max ) / $width); - } - else { - if ( $height > $max ) { + if ((($height * 9) / 16) > $width) { + $dest_width = $width; + $dest_height = $height; + } else { + $dest_width = intval(( $width * $max ) / $height); + $dest_height = $max; + } + } else { + $dest_width = $width; + $dest_height = $height; + } + } + } - // very tall image (greater than 16:9) - // but width is OK - don't do anything - - if ((($height * 9) / 16) > $width) { - $dest_width = $width; - $dest_height = $height; - } - else { - $dest_width = intval(( $width * $max ) / $height); - $dest_height = $max; - } - } - else { - $dest_width = $width; - $dest_height = $height; - } - } - } - - return $dest_width . 'x' . $dest_height; + return $dest_width . 'x' . $dest_height; } /** @@ -659,99 +666,102 @@ function photo_calculate_scale($arr) { * * \e boolean \b success * * \e array \b albums */ -function photos_albums_list($channel, $observer, $sort_key = 'display_path', $direction = 'asc') { +function photos_albums_list($channel, $observer, $sort_key = 'display_path', $direction = 'asc') +{ - $channel_id = $channel['channel_id']; - $observer_xchan = (($observer) ? $observer['xchan_hash'] : ''); + $channel_id = $channel['channel_id']; + $observer_xchan = (($observer) ? $observer['xchan_hash'] : ''); - if (! perm_is_allowed($channel_id, $observer_xchan, 'view_storage')) { - return false; - } + if (! perm_is_allowed($channel_id, $observer_xchan, 'view_storage')) { + return false; + } - $sql_extra = permissions_sql($channel_id,$observer_xchan); + $sql_extra = permissions_sql($channel_id, $observer_xchan); - $sort_key = dbesc($sort_key); - $direction = dbesc($direction); + $sort_key = dbesc($sort_key); + $direction = dbesc($direction); - $r = q("select display_path, hash from attach where is_dir = 1 and uid = %d $sql_extra order by $sort_key $direction", - intval($channel_id) - ); + $r = q( + "select display_path, hash from attach where is_dir = 1 and uid = %d $sql_extra order by $sort_key $direction", + intval($channel_id) + ); - // add a 'root directory' to the results - - array_unshift($r,[ 'display_path' => '/', 'hash' => '' ]); - $str = ids_to_querystr($r,'hash',true); + // add a 'root directory' to the results - $albums = []; + array_unshift($r, [ 'display_path' => '/', 'hash' => '' ]); + $str = ids_to_querystr($r, 'hash', true); - if ($str) { - $x = q("select count( distinct hash ) as total, folder from attach where is_photo = 1 and uid = %d and folder in ( $str ) $sql_extra group by folder ", - intval($channel_id) - ); - if ($x) { - foreach ($r as $rv) { - foreach ($x as $xv) { - if ($xv['folder'] === $rv['hash']) { - if ($xv['total'] != 0 && attach_can_view_folder($channel_id,$observer_xchan,$xv['folder'])) { - $albums[] = [ 'album' => $rv['display_path'], 'folder' => $xv['folder'], 'total' => $xv['total'] ]; - } - continue; - } - } - } - } - } + $albums = []; - // add various encodings to the array so we can just loop through and pick them out in a template + if ($str) { + $x = q( + "select count( distinct hash ) as total, folder from attach where is_photo = 1 and uid = %d and folder in ( $str ) $sql_extra group by folder ", + intval($channel_id) + ); + if ($x) { + foreach ($r as $rv) { + foreach ($x as $xv) { + if ($xv['folder'] === $rv['hash']) { + if ($xv['total'] != 0 && attach_can_view_folder($channel_id, $observer_xchan, $xv['folder'])) { + $albums[] = [ 'album' => $rv['display_path'], 'folder' => $xv['folder'], 'total' => $xv['total'] ]; + } + continue; + } + } + } + } + } - $ret = [ 'success' => false ]; + // add various encodings to the array so we can just loop through and pick them out in a template - if ($albums) { - $ret['success'] = true; - $ret['albums'] = []; - foreach ($albums as $k => $album) { - $entry = [ - 'text' => (($album['album']) ? $album['album'] : '/'), - 'shorttext' => (($album['album']) ? ellipsify($album['album'],28) : '/'), - 'jstext' => (($album['album']) ? addslashes($album['album']) : '/'), - 'total' => $album['total'], - 'url' => z_root() . '/photos/' . $channel['channel_address'] . '/album/' . $album['folder'], - 'urlencode' => urlencode($album['album']), - 'bin2hex' => $album['folder'] - ]; - $ret['albums'][] = $entry; - } - } + $ret = [ 'success' => false ]; - App::$data['albums'] = $ret; + if ($albums) { + $ret['success'] = true; + $ret['albums'] = []; + foreach ($albums as $k => $album) { + $entry = [ + 'text' => (($album['album']) ? $album['album'] : '/'), + 'shorttext' => (($album['album']) ? ellipsify($album['album'], 28) : '/'), + 'jstext' => (($album['album']) ? addslashes($album['album']) : '/'), + 'total' => $album['total'], + 'url' => z_root() . '/photos/' . $channel['channel_address'] . '/album/' . $album['folder'], + 'urlencode' => urlencode($album['album']), + 'bin2hex' => $album['folder'] + ]; + $ret['albums'][] = $entry; + } + } - return $ret; + App::$data['albums'] = $ret; + + return $ret; } -function photos_album_widget($channelx,$observer,$sortkey = 'display_path',$direction = 'asc') { +function photos_album_widget($channelx, $observer, $sortkey = 'display_path', $direction = 'asc') +{ - $o = EMPTY_STR; + $o = EMPTY_STR; - if (array_key_exists('albums', App::$data)) { - $albums = App::$data['albums']; - } - else { - $albums = photos_albums_list($channelx,$observer,$sortkey,$direction); - } - - if ($albums['success']) { - $o = replace_macros(get_markup_template('photo_albums.tpl'), [ - '$nick' => $channelx['channel_address'], - '$title' => t('Photo Albums'), - '$recent' => t('Recent Photos'), - '$albums' => $albums['albums'], - '$baseurl' => z_root(), - '$upload' => ((perm_is_allowed($channelx['channel_id'],(($observer) ? $observer['xchan_hash'] : ''),'write_storage')) - ? t('Upload New Photos') : '') - ]); - } + if (array_key_exists('albums', App::$data)) { + $albums = App::$data['albums']; + } else { + $albums = photos_albums_list($channelx, $observer, $sortkey, $direction); + } - return $o; + if ($albums['success']) { + $o = replace_macros(get_markup_template('photo_albums.tpl'), [ + '$nick' => $channelx['channel_address'], + '$title' => t('Photo Albums'), + '$recent' => t('Recent Photos'), + '$albums' => $albums['albums'], + '$baseurl' => z_root(), + '$upload' => ((perm_is_allowed($channelx['channel_id'], (($observer) ? $observer['xchan_hash'] : ''), 'write_storage')) + ? t('Upload New Photos') : '') + ]); + } + + return $o; } /** @@ -760,40 +770,42 @@ function photos_album_widget($channelx,$observer,$sortkey = 'display_path',$dire * @param array $channel * @param array $observer * @param string $album (optional) default empty - * @return boolean|array + * @return bool|array */ -function photos_list_photos($channel, $observer, $album = '') { +function photos_list_photos($channel, $observer, $album = '') +{ - $channel_id = $channel['channel_id']; - $observer_xchan = (($observer) ? $observer['xchan_hash'] : ''); + $channel_id = $channel['channel_id']; + $observer_xchan = (($observer) ? $observer['xchan_hash'] : ''); - if (! perm_is_allowed($channel_id,$observer_xchan,'view_storage')) { - return false; - } + if (! perm_is_allowed($channel_id, $observer_xchan, 'view_storage')) { + return false; + } - $sql_extra = permissions_sql($channel_id); + $sql_extra = permissions_sql($channel_id); - if ($album) { - $sql_extra .= " and album = '" . protect_sprintf(dbesc($album)) . "' "; - } - - $ret = [ 'success' => false ]; + if ($album) { + $sql_extra .= " and album = '" . protect_sprintf(dbesc($album)) . "' "; + } - $r = q("select resource_id, created, edited, title, description, album, filename, mimetype, height, width, filesize, imgscale, photo_usage, allow_cid, allow_gid, deny_cid, deny_gid from photo where uid = %d and photo_usage in ( %d, %d ) $sql_extra ", - intval($channel_id), - intval(PHOTO_NORMAL), - intval(PHOTO_PROFILE) - ); + $ret = [ 'success' => false ]; - if ($r) { - for ($x = 0; $x < count($r); $x ++) { - $r[$x]['src'] = z_root() . '/photo/' . $r[$x]['resource_id'] . '-' . $r[$x]['imgscale']; - } - $ret['success'] = true; - $ret['photos'] = $r; - } + $r = q( + "select resource_id, created, edited, title, description, album, filename, mimetype, height, width, filesize, imgscale, photo_usage, allow_cid, allow_gid, deny_cid, deny_gid from photo where uid = %d and photo_usage in ( %d, %d ) $sql_extra ", + intval($channel_id), + intval(PHOTO_NORMAL), + intval(PHOTO_PROFILE) + ); - return $ret; + if ($r) { + for ($x = 0; $x < count($r); $x++) { + $r[$x]['src'] = z_root() . '/photo/' . $r[$x]['resource_id'] . '-' . $r[$x]['imgscale']; + } + $ret['success'] = true; + $ret['photos'] = $r; + } + + return $ret; } /** @@ -802,18 +814,20 @@ function photos_list_photos($channel, $observer, $album = '') { * @param int $channel_id id of the channel * @param string $observer_hash * @param string $album name of the album - * @return boolean + * @return bool */ -function photos_album_exists($channel_id, $observer_hash, $album) { +function photos_album_exists($channel_id, $observer_hash, $album) +{ - $sql_extra = permissions_sql($channel_id, $observer_hash); + $sql_extra = permissions_sql($channel_id, $observer_hash); - $r = q("SELECT folder, hash, is_dir, filename, os_path, display_path FROM attach WHERE hash = '%s' AND is_dir = 1 AND uid = %d $sql_extra limit 1", - dbesc($album), - intval($channel_id) - ); + $r = q( + "SELECT folder, hash, is_dir, filename, os_path, display_path FROM attach WHERE hash = '%s' AND is_dir = 1 AND uid = %d $sql_extra limit 1", + dbesc($album), + intval($channel_id) + ); - return (($r) ? array_shift($r) : false); + return (($r) ? array_shift($r) : false); } /** @@ -826,12 +840,14 @@ function photos_album_exists($channel_id, $observer_hash, $album) { * @param string $newname The new name of the album * @return bool|array */ -function photos_album_rename($channel_id, $oldname, $newname) { - return q("UPDATE photo SET album = '%s' WHERE album = '%s' AND uid = %d", - dbesc($newname), - dbesc($oldname), - intval($channel_id) - ); +function photos_album_rename($channel_id, $oldname, $newname) +{ + return q( + "UPDATE photo SET album = '%s' WHERE album = '%s' AND uid = %d", + dbesc($newname), + dbesc($oldname), + intval($channel_id) + ); } /** @@ -842,45 +858,50 @@ function photos_album_rename($channel_id, $oldname, $newname) { * @param int $channel_id * @param string $album * @param string $remote_xchan (optional) default empty - * @return string|boolean + * @return string|bool */ -function photos_album_get_db_idstr($channel_id, $album, $remote_xchan = '') { +function photos_album_get_db_idstr($channel_id, $album, $remote_xchan = '') +{ - if($remote_xchan) { - $r = q("SELECT hash from attach where creator = '%s' and uid = %d and folder = '%s' ", - dbesc($remote_xchan), - intval($channel_id), - dbesc($album) - ); - } - else { - $r = q("SELECT hash from attach where uid = %d and folder = '%s' ", - intval($channel_id), - dbesc($album) - ); - } - if ($r) { - return ids_to_querystr($r,'hash',true); - } + if ($remote_xchan) { + $r = q( + "SELECT hash from attach where creator = '%s' and uid = %d and folder = '%s' ", + dbesc($remote_xchan), + intval($channel_id), + dbesc($album) + ); + } else { + $r = q( + "SELECT hash from attach where uid = %d and folder = '%s' ", + intval($channel_id), + dbesc($album) + ); + } + if ($r) { + return ids_to_querystr($r, 'hash', true); + } - return false; + return false; } -function photos_album_get_db_idstr_admin($channel_id, $album) { +function photos_album_get_db_idstr_admin($channel_id, $album) +{ - if(! is_site_admin()) - return false; + if (! is_site_admin()) { + return false; + } - $r = q("SELECT hash from attach where uid = %d and folder = '%s' ", - intval($channel_id), - dbesc($album) - ); + $r = q( + "SELECT hash from attach where uid = %d and folder = '%s' ", + intval($channel_id), + dbesc($album) + ); - if ($r) { - return ids_to_querystr($r,'hash',true); - } + if ($r) { + return ids_to_querystr($r, 'hash', true); + } - return false; + return false; } @@ -891,115 +912,127 @@ function photos_album_get_db_idstr_admin($channel_id, $album) { * @param array $channel * @param string $creator_hash * @param array $photo - * @param boolean $visible (optional) default false + * @param bool $visible (optional) default false * @return int item_id */ -function photos_create_item($channel, $creator_hash, $photo, $visible = false) { +function photos_create_item($channel, $creator_hash, $photo, $visible = false) +{ - // Create item container + // Create item container - $item_hidden = (($visible) ? 0 : 1 ); + $item_hidden = (($visible) ? 0 : 1 ); - $uuid = new_uuid(); - $mid = z_root() . '/item/' . $uuid; + $uuid = new_uuid(); + $mid = z_root() . '/item/' . $uuid; - $arr = []; + $arr = []; - $arr['aid'] = $channel['channel_account_id']; - $arr['uid'] = $channel['channel_id']; - $arr['uuid'] = $uuid; - $arr['mid'] = $mid; - $arr['parent_mid'] = $mid; - $arr['item_wall'] = 1; - $arr['item_origin'] = 1; - $arr['item_thread_top'] = 1; - $arr['item_hidden'] = $item_hidden; - $arr['resource_type'] = 'photo'; - $arr['resource_id'] = $photo['resource_id']; - $arr['owner_xchan'] = $channel['channel_hash']; - $arr['author_xchan'] = $creator_hash; + $arr['aid'] = $channel['channel_account_id']; + $arr['uid'] = $channel['channel_id']; + $arr['uuid'] = $uuid; + $arr['mid'] = $mid; + $arr['parent_mid'] = $mid; + $arr['item_wall'] = 1; + $arr['item_origin'] = 1; + $arr['item_thread_top'] = 1; + $arr['item_hidden'] = $item_hidden; + $arr['resource_type'] = 'photo'; + $arr['resource_id'] = $photo['resource_id']; + $arr['owner_xchan'] = $channel['channel_hash']; + $arr['author_xchan'] = $creator_hash; - $arr['allow_cid'] = $photo['allow_cid']; - $arr['allow_gid'] = $photo['allow_gid']; - $arr['deny_cid'] = $photo['deny_cid']; - $arr['deny_gid'] = $photo['deny_gid']; + $arr['allow_cid'] = $photo['allow_cid']; + $arr['allow_gid'] = $photo['allow_gid']; + $arr['deny_cid'] = $photo['deny_cid']; + $arr['deny_gid'] = $photo['deny_gid']; - $arr['plink'] = z_root() . '/channel/' . $channel['channel_address'] . '/?f=&mid=' . urlencode($arr['mid']); + $arr['plink'] = z_root() . '/channel/' . $channel['channel_address'] . '/?f=&mid=' . urlencode($arr['mid']); - $arr['body'] = '[zrl=' . z_root() . '/photos/' . $channel['channel_address'] . '/image/' . $photo['resource_id'] . ']' - . '[zmg]' . z_root() . '/photo/' . $photo['resource_id'] . '-' . $photo['imgscale'] . '[/zmg]' - . '[/zrl]'; + $arr['body'] = '[zrl=' . z_root() . '/photos/' . $channel['channel_address'] . '/image/' . $photo['resource_id'] . ']' + . '[zmg]' . z_root() . '/photo/' . $photo['resource_id'] . '-' . $photo['imgscale'] . '[/zmg]' + . '[/zrl]'; - $result = item_store($arr); - $item_id = $result['item_id']; + $result = item_store($arr); + $item_id = $result['item_id']; - return $item_id; + return $item_id; } -function getGps($exifCoord, $hemi) { +function getGps($exifCoord, $hemi) +{ - $degrees = count($exifCoord) > 0 ? gps2Num($exifCoord[0]) : 0; - $minutes = count($exifCoord) > 1 ? gps2Num($exifCoord[1]) : 0; - $seconds = count($exifCoord) > 2 ? gps2Num($exifCoord[2]) : 0; + $degrees = count($exifCoord) > 0 ? gps2Num($exifCoord[0]) : 0; + $minutes = count($exifCoord) > 1 ? gps2Num($exifCoord[1]) : 0; + $seconds = count($exifCoord) > 2 ? gps2Num($exifCoord[2]) : 0; - $flip = ($hemi == 'W' or $hemi == 'S') ? -1 : 1; + $flip = ($hemi == 'W' or $hemi == 'S') ? -1 : 1; - return floatval($flip * ($degrees + ($minutes / 60) + ($seconds / 3600))); + return floatval($flip * ($degrees + ($minutes / 60) + ($seconds / 3600))); } -function getGpstimestamp($exifCoord) { +function getGpstimestamp($exifCoord) +{ - $hours = count($exifCoord) > 0 ? gps2Num($exifCoord[0]) : 0; - $minutes = count($exifCoord) > 1 ? gps2Num($exifCoord[1]) : 0; - $seconds = count($exifCoord) > 2 ? gps2Num($exifCoord[2]) : 0; + $hours = count($exifCoord) > 0 ? gps2Num($exifCoord[0]) : 0; + $minutes = count($exifCoord) > 1 ? gps2Num($exifCoord[1]) : 0; + $seconds = count($exifCoord) > 2 ? gps2Num($exifCoord[2]) : 0; - return sprintf('%02d:%02d:%02d', $hours, $minutes, $seconds); + return sprintf('%02d:%02d:%02d', $hours, $minutes, $seconds); } -function gps2Num($coordPart) { +function gps2Num($coordPart) +{ - $parts = explode('/', $coordPart); + $parts = explode('/', $coordPart); - if (count($parts) <= 0) - return 0; + if (count($parts) <= 0) { + return 0; + } - if (count($parts) == 1) - return $parts[0]; + if (count($parts) == 1) { + return $parts[0]; + } - return floatval($parts[0]) / floatval($parts[1]); + return floatval($parts[0]) / floatval($parts[1]); } -function photo_profile_setperms($channel_id,$resource_id,$profile_id) { +function photo_profile_setperms($channel_id, $resource_id, $profile_id) +{ - if(! $profile_id) - return; + if (! $profile_id) { + return; + } - $r = q("select profile_guid, is_default from profile where id = %d and uid = %d limit 1", - dbesc($profile_id), - intval($channel_id) - ); + $r = q( + "select profile_guid, is_default from profile where id = %d and uid = %d limit 1", + dbesc($profile_id), + intval($channel_id) + ); - if(! $r) - return; + if (! $r) { + return; + } - $is_default = $r[0]['is_default']; - $profile_guid = $r[0]['profile_guid']; + $is_default = $r[0]['is_default']; + $profile_guid = $r[0]['profile_guid']; - if($is_default) { - $r = q("update photo set allow_cid = '', allow_gid = '', deny_cid = '', deny_gid = '' + if ($is_default) { + $r = q( + "update photo set allow_cid = '', allow_gid = '', deny_cid = '', deny_gid = '' where resource_id = '%s' and uid = %d", - dbesc($resource_id), - intval($channel_id) - ); - $r = q("update attach set allow_cid = '', allow_gid = '', deny_cid = '', deny_gid = '' + dbesc($resource_id), + intval($channel_id) + ); + $r = q( + "update attach set allow_cid = '', allow_gid = '', deny_cid = '', deny_gid = '' where hash = '%s' and uid = %d", - dbesc($resource_id), - intval($channel_id) - ); - } + dbesc($resource_id), + intval($channel_id) + ); + } } /** @@ -1008,85 +1041,93 @@ function photo_profile_setperms($channel_id,$resource_id,$profile_id) { * @param int $uid * @param int|string $profileid */ -function profile_photo_set_profile_perms($uid, $profileid = 0) { +function profile_photo_set_profile_perms($uid, $profileid = 0) +{ - $allowcid = ''; + $allowcid = ''; - if($profileid) { - $r = q("SELECT photo, profile_guid, id, is_default, uid + if ($profileid) { + $r = q( + "SELECT photo, profile_guid, id, is_default, uid FROM profile WHERE uid = %d and ( profile.id = %d OR profile.profile_guid = '%s') LIMIT 1", - intval($uid), - intval($profileid), - dbesc($profileid) - ); - } - else { - logger('Resetting permissions on default-profile-photo for user ' . $uid); + intval($uid), + intval($profileid), + dbesc($profileid) + ); + } else { + logger('Resetting permissions on default-profile-photo for user ' . $uid); - $r = q("SELECT photo, profile_guid, id, is_default, uid FROM profile + $r = q( + "SELECT photo, profile_guid, id, is_default, uid FROM profile WHERE profile.uid = %d AND is_default = 1 LIMIT 1", - intval($uid) - ); //If no profile is given, we update the default profile - } - if(! $r) - return; + intval($uid) + ); //If no profile is given, we update the default profile + } + if (! $r) { + return; + } - $profile = $r[0]; + $profile = $r[0]; - if($profile['id'] && $profile['photo']) { - preg_match("@\w*(?=-\d*$)@i", $profile['photo'], $resource_id); - $resource_id = $resource_id[0]; + if ($profile['id'] && $profile['photo']) { + preg_match("@\w*(?=-\d*$)@i", $profile['photo'], $resource_id); + $resource_id = $resource_id[0]; - if (! intval($profile['is_default'])) { - $r0 = q("SELECT channel_hash FROM channel WHERE channel_id = %d LIMIT 1", - intval($uid) - ); - //Should not be needed in future. Catches old int-profile-ids. - $r1 = q("SELECT abook.abook_xchan FROM abook WHERE abook_profile = '%d' ", - intval($profile['id']) - ); - $r2 = q("SELECT abook.abook_xchan FROM abook WHERE abook_profile = '%s'", - dbesc($profile['profile_guid']) - ); - $allowcid = "<" . $r0[0]['channel_hash'] . ">"; - foreach ($r1 as $entry) { - $allowcid .= "<" . $entry['abook_xchan'] . ">"; - } - foreach ($r2 as $entry) { - $allowcid .= "<" . $entry['abook_xchan'] . ">"; - } + if (! intval($profile['is_default'])) { + $r0 = q( + "SELECT channel_hash FROM channel WHERE channel_id = %d LIMIT 1", + intval($uid) + ); + //Should not be needed in future. Catches old int-profile-ids. + $r1 = q( + "SELECT abook.abook_xchan FROM abook WHERE abook_profile = '%d' ", + intval($profile['id']) + ); + $r2 = q( + "SELECT abook.abook_xchan FROM abook WHERE abook_profile = '%s'", + dbesc($profile['profile_guid']) + ); + $allowcid = "<" . $r0[0]['channel_hash'] . ">"; + foreach ($r1 as $entry) { + $allowcid .= "<" . $entry['abook_xchan'] . ">"; + } + foreach ($r2 as $entry) { + $allowcid .= "<" . $entry['abook_xchan'] . ">"; + } - q("UPDATE photo SET allow_cid = '%s' WHERE resource_id = '%s' AND uid = %d", - dbesc($allowcid), - dbesc($resource_id), - intval($uid) - ); - } - else { - // Reset permissions on default profile picture to public - q("UPDATE photo SET allow_cid = '' WHERE photo_usage = %d AND uid = %d", - intval(PHOTO_PROFILE), - intval($uid) - ); - } - } + q( + "UPDATE photo SET allow_cid = '%s' WHERE resource_id = '%s' AND uid = %d", + dbesc($allowcid), + dbesc($resource_id), + intval($uid) + ); + } else { + // Reset permissions on default profile picture to public + q( + "UPDATE photo SET allow_cid = '' WHERE photo_usage = %d AND uid = %d", + intval(PHOTO_PROFILE), + intval($uid) + ); + } + } } -function fetch_image_from_url($url,&$mimetype) { +function fetch_image_from_url($url, &$mimetype) +{ - $redirects = 0; - $x = z_fetch_url($url,true,$redirects,[ 'novalidate' => true ]); - if($x['success']) { - $ht = new HTTPHeaders($x['header']); - $hdrs = $ht->fetcharr(); - if ($hdrs && array_key_exists('content-type', $hdrs)) { - $mimetype = $hdrs['content-type']; - } + $redirects = 0; + $x = z_fetch_url($url, true, $redirects, [ 'novalidate' => true ]); + if ($x['success']) { + $ht = new HTTPHeaders($x['header']); + $hdrs = $ht->fetcharr(); + if ($hdrs && array_key_exists('content-type', $hdrs)) { + $mimetype = $hdrs['content-type']; + } - return $x['body']; - } + return $x['body']; + } - return EMPTY_STR; + return EMPTY_STR; } @@ -1128,4 +1169,3 @@ function isAnimatedGif($fileName) return $totalCount > 1; } - diff --git a/include/security.php b/include/security.php index 817b2cb86..bdf87c73e 100644 --- a/include/security.php +++ b/include/security.php @@ -1,4 +1,5 @@ $atoken['atoken_id'], - 'xchan_hash' => substr($c['channel_hash'],0,16) . '.' . $atoken['atoken_guid'], - 'xchan_name' => $atoken['atoken_name'], - 'xchan_addr' => 'guest:' . $atoken['atoken_name'] . '@' . App::get_hostname(), - 'xchan_network' => 'token', - 'xchan_url' => z_root() . '/guest/' . substr($c['channel_hash'],0,16) . '.' . $atoken['atoken_guid'], - 'xchan_hidden' => 1, - 'xchan_photo_mimetype' => 'image/png', - 'xchan_photo_l' => z_root() . '/' . get_default_profile_photo(300), - 'xchan_photo_m' => z_root() . '/' . get_default_profile_photo(80), - 'xchan_photo_s' => z_root() . '/' . get_default_profile_photo(48) - ]; - } + $c = channelx_by_n($atoken['atoken_uid']); + if ($c) { + return [ + 'atoken_id' => $atoken['atoken_id'], + 'xchan_hash' => substr($c['channel_hash'], 0, 16) . '.' . $atoken['atoken_guid'], + 'xchan_name' => $atoken['atoken_name'], + 'xchan_addr' => 'guest:' . $atoken['atoken_name'] . '@' . App::get_hostname(), + 'xchan_network' => 'token', + 'xchan_url' => z_root() . '/guest/' . substr($c['channel_hash'], 0, 16) . '.' . $atoken['atoken_guid'], + 'xchan_hidden' => 1, + 'xchan_photo_mimetype' => 'image/png', + 'xchan_photo_l' => z_root() . '/' . get_default_profile_photo(300), + 'xchan_photo_m' => z_root() . '/' . get_default_profile_photo(80), + 'xchan_photo_s' => z_root() . '/' . get_default_profile_photo(48) + ]; + } - return null; + return null; } -function atoken_delete($atoken_id) { +function atoken_delete($atoken_id) +{ - $r = q("select * from atoken where atoken_id = %d", - intval($atoken_id) - ); - if (! $r) { - return; - } + $r = q( + "select * from atoken where atoken_id = %d", + intval($atoken_id) + ); + if (! $r) { + return; + } - $c = q("select channel_id, channel_hash from channel where channel_id = %d", - intval($r[0]['atoken_uid']) - ); - if (! $c) { - return; - } + $c = q( + "select channel_id, channel_hash from channel where channel_id = %d", + intval($r[0]['atoken_uid']) + ); + if (! $c) { + return; + } - $atoken_xchan = substr($c[0]['channel_hash'],0,16) . '.' . $r[0]['atoken_guid']; + $atoken_xchan = substr($c[0]['channel_hash'], 0, 16) . '.' . $r[0]['atoken_guid']; - q("delete from atoken where atoken_id = %d", - intval($atoken_id) - ); - q("delete from abook where abook_channel = %d and abook_xchan = '%s'", - intval($c[0]['channel_id']), - dbesc($atoken_xchan) - ); - - q("delete from abconfig where chan = %d and xchan = '%s'", - intval($c[0]['channel_id']), - dbesc($atoken_xchan) - ); + q( + "delete from atoken where atoken_id = %d", + intval($atoken_id) + ); + q( + "delete from abook where abook_channel = %d and abook_xchan = '%s'", + intval($c[0]['channel_id']), + dbesc($atoken_xchan) + ); + + q( + "delete from abconfig where chan = %d and xchan = '%s'", + intval($c[0]['channel_id']), + dbesc($atoken_xchan) + ); } /** @@ -180,72 +193,81 @@ function atoken_delete($atoken_id) { * @fixme we should set xchan_deleted if it's expired or removed * * @param array $xchan - * @return void|boolean + * @return void|bool */ -function atoken_create_xchan($xchan) { +function atoken_create_xchan($xchan) +{ - $r = q("select xchan_hash from xchan where xchan_hash = '%s'", - dbesc($xchan['xchan_hash']) - ); - if ($r) { - return; - } - - $xchan['xchan_guid'] = $xchan['xchan_hash']; + $r = q( + "select xchan_hash from xchan where xchan_hash = '%s'", + dbesc($xchan['xchan_hash']) + ); + if ($r) { + return; + } - $store = []; - foreach ($xchan as $k => $v) { - if (strpos($k,'xchan_') === 0) { - $store[$k] = $v; - } - } - - $r = xchan_store_lowlevel($store); + $xchan['xchan_guid'] = $xchan['xchan_hash']; - return true; + $store = []; + foreach ($xchan as $k => $v) { + if (strpos($k, 'xchan_') === 0) { + $store[$k] = $v; + } + } + + $r = xchan_store_lowlevel($store); + + return true; } -function atoken_abook($uid,$xchan_hash) { +function atoken_abook($uid, $xchan_hash) +{ - if(substr($xchan_hash,16,1) != '.') - return false; + if (substr($xchan_hash, 16, 1) != '.') { + return false; + } - $r = q("select channel_hash from channel where channel_id = %d limit 1", - intval($uid) - ); + $r = q( + "select channel_hash from channel where channel_id = %d limit 1", + intval($uid) + ); - if(! $r) - return false; + if (! $r) { + return false; + } - $x = q("select * from atoken where atoken_uid = %d and atoken_guid = '%s'", - intval($uid), - dbesc(substr($xchan_hash,17)) - ); + $x = q( + "select * from atoken where atoken_uid = %d and atoken_guid = '%s'", + intval($uid), + dbesc(substr($xchan_hash, 17)) + ); - if($x) { - $xchan = atoken_xchan($x[0]); - $xchan['abook_blocked'] = 0; - $xchan['abook_ignored'] = 0; - $xchan['abook_pending'] = 0; - return $xchan; - } + if ($x) { + $xchan = atoken_xchan($x[0]); + $xchan['abook_blocked'] = 0; + $xchan['abook_ignored'] = 0; + $xchan['abook_pending'] = 0; + return $xchan; + } - return false; + return false; } -function pseudo_abook($xchan) { - if(! $xchan) - return false; +function pseudo_abook($xchan) +{ + if (! $xchan) { + return false; + } - // set abook_pseudo to flag that we aren't really connected. + // set abook_pseudo to flag that we aren't really connected. - $xchan['abook_pseudo'] = 1; - $xchan['abook_blocked'] = 0; - $xchan['abook_ignored'] = 0; - $xchan['abook_pending'] = 0; + $xchan['abook_pseudo'] = 1; + $xchan['abook_blocked'] = 0; + $xchan['abook_ignored'] = 0; + $xchan['abook_pending'] = 0; - return $xchan; + return $xchan; } @@ -256,61 +278,65 @@ function pseudo_abook($xchan) { * * @return bool|array false or channel record of the new channel */ -function change_channel($change_channel) { +function change_channel($change_channel) +{ - $ret = false; + $ret = false; - if($change_channel) { + if ($change_channel) { + $r = q( + "select channel.*, xchan.* from channel left join xchan on channel.channel_hash = xchan.xchan_hash where channel_id = %d and channel_account_id = %d and channel_removed = 0 limit 1", + intval($change_channel), + intval(get_account_id()) + ); - $r = q("select channel.*, xchan.* from channel left join xchan on channel.channel_hash = xchan.xchan_hash where channel_id = %d and channel_account_id = %d and channel_removed = 0 limit 1", - intval($change_channel), - intval(get_account_id()) - ); + // It's not there. Is this an administrator, and is this the sys channel? + if (! $r) { + if (is_developer() || is_site_admin()) { + $r = q( + "select channel.*, xchan.* from channel left join xchan on channel.channel_hash = xchan.xchan_hash where channel_id = %d and channel_system = 1 and channel_removed = 0 limit 1", + intval($change_channel) + ); + } + } - // It's not there. Is this an administrator, and is this the sys channel? - if (! $r) { - if (is_developer() || is_site_admin()) { - $r = q("select channel.*, xchan.* from channel left join xchan on channel.channel_hash = xchan.xchan_hash where channel_id = %d and channel_system = 1 and channel_removed = 0 limit 1", - intval($change_channel) - ); - } - } + if ($r) { + $hash = $r[0]['channel_hash']; + $_SESSION['uid'] = intval($r[0]['channel_id']); + App::set_channel($r[0]); + $_SESSION['theme'] = $r[0]['channel_theme']; + $_SESSION['mobile_theme'] = get_pconfig(local_channel(), 'system', 'mobile_theme'); + $_SESSION['cloud_tiles'] = get_pconfig(local_channel(), 'system', 'cloud_tiles'); + date_default_timezone_set($r[0]['channel_timezone']); - if($r) { - $hash = $r[0]['channel_hash']; - $_SESSION['uid'] = intval($r[0]['channel_id']); - App::set_channel($r[0]); - $_SESSION['theme'] = $r[0]['channel_theme']; - $_SESSION['mobile_theme'] = get_pconfig(local_channel(),'system', 'mobile_theme'); - $_SESSION['cloud_tiles'] = get_pconfig(local_channel(),'system', 'cloud_tiles'); - date_default_timezone_set($r[0]['channel_timezone']); + // Update the active timestamp at most once a day - // Update the active timestamp at most once a day + if (substr($r[0]['channel_active'], 0, 10) !== substr(datetime_convert(), 0, 10) && (! (isset($_SESSION['sudo']) && $_SESSION['sudo']))) { + $z = q( + "UPDATE channel SET channel_active = '%s' WHERE channel_id = %d", + dbesc(datetime_convert()), + intval($r[0]['channel_id']) + ); + } + $ret = $r[0]; + } + $x = xchan_match([ 'xchan_hash' => $hash ]); + if ($x) { + $_SESSION['my_url'] = $x['xchan_url']; + $_SESSION['my_address'] = channel_reddress($r[0]); - if(substr($r[0]['channel_active'],0,10) !== substr(datetime_convert(),0,10) && (! (isset($_SESSION['sudo']) && $_SESSION['sudo']))) { - $z = q("UPDATE channel SET channel_active = '%s' WHERE channel_id = %d", - dbesc(datetime_convert()), - intval($r[0]['channel_id']) - ); - } - $ret = $r[0]; - } - $x = xchan_match([ 'xchan_hash' => $hash ]); - if($x) { - $_SESSION['my_url'] = $x['xchan_url']; - $_SESSION['my_address'] = channel_reddress($r[0]); + App::set_observer($x); + App::set_perms(get_all_perms(local_channel(), $hash)); + } + if (! is_dir('store/' . $r[0]['channel_address'])) { + @os_mkdir('store/' . $r[0]['channel_address'], STORAGE_DEFAULT_PERMISSIONS, true); + } - App::set_observer($x); - App::set_perms(get_all_perms(local_channel(), $hash)); - } - if(! is_dir('store/' . $r[0]['channel_address'])) - @os_mkdir('store/' . $r[0]['channel_address'], STORAGE_DEFAULT_PERMISSIONS,true); + $arr = [ 'channel_id' => $change_channel, 'chanx' => $ret ]; + call_hooks('change_channel', $arr); + } - $arr = [ 'channel_id' => $change_channel, 'chanx' => $ret ]; - call_hooks('change_channel', $arr); - } - - return $ret; + return $ret; } /** @@ -324,110 +350,107 @@ function change_channel($change_channel) { */ -function permissions_sql($owner_id, $remote_observer = null, $table = '', $token = EMPTY_STR) { +function permissions_sql($owner_id, $remote_observer = null, $table = '', $token = EMPTY_STR) +{ - $local_channel = local_channel(); + $local_channel = local_channel(); - /** - * Construct permissions - * - * default permissions - anonymous user - */ + /** + * Construct permissions + * + * default permissions - anonymous user + */ - if ($table) - $table .= '.'; + if ($table) { + $table .= '.'; + } - $sql = " AND {$table}allow_cid = '' + $sql = " AND {$table}allow_cid = '' AND {$table}allow_gid = '' AND {$table}deny_cid = '' AND {$table}deny_gid = '' "; - /** - * Profile owner - everything is visible - */ + /** + * Profile owner - everything is visible + */ - if (($local_channel) && ($local_channel == $owner_id)) { - return EMPTY_STR; - } + if (($local_channel) && ($local_channel == $owner_id)) { + return EMPTY_STR; + } - /** - * Authenticated visitor. - */ + /** + * Authenticated visitor. + */ - else { + else { + $observer = ((! is_null($remote_observer)) ? $remote_observer : get_observer_hash()); - $observer = ((! is_null($remote_observer)) ? $remote_observer : get_observer_hash()); + if ($observer) { + $sec = get_security_ids($owner_id, $observer); - if ($observer) { + if ($token) { + if (! array_key_exists('allow_cid', $sec)) { + $sec['allow_cid'] = []; + } + $sec['allow_cid'][] = 'token:' . $token; + } - $sec = get_security_ids($owner_id,$observer); + // always allow the channel owner, even if authenticated as a visitor - if ($token) { - if (! array_key_exists('allow_cid',$sec)) { - $sec['allow_cid'] = []; - } - $sec['allow_cid'][] = 'token:' . $token; - } + if ($sec['channel_id']) { + foreach ($sec['channel_id'] as $ch) { + if ($observer === $ch) { + return EMPTY_STR; + } + } + } - // always allow the channel owner, even if authenticated as a visitor + if (is_array($sec['allow_cid']) && count($sec['allow_cid'])) { + $ca = []; + foreach ($sec['allow_cid'] as $c) { + $ca[] = '<' . $c . '>'; + } + $cs = implode('|', $ca); + } else { + $cs = '<<>>'; // should be impossible to match + } - if ($sec['channel_id']) { - foreach ($sec['channel_id'] as $ch) { - if ($observer === $ch) { - return EMPTY_STR; - } - } - } + if (is_array($sec['allow_gid']) && count($sec['allow_gid'])) { + $ga = []; + foreach ($sec['allow_gid'] as $g) { + $ga[] = '<' . $g . '>'; + } + $gs = implode('|', $ga); + } else { + $gs = '<<>>'; // should be impossible to match + } - if (is_array($sec['allow_cid']) && count($sec['allow_cid'])) { - $ca = []; - foreach ($sec['allow_cid'] as $c) { - $ca[] = '<' . $c . '>'; - } - $cs = implode('|',$ca); - } - else { - $cs = '<<>>'; // should be impossible to match - } - - if (is_array($sec['allow_gid']) && count($sec['allow_gid'])) { - $ga = []; - foreach ($sec['allow_gid'] as $g) { - $ga[] = '<' . $g . '>'; - } - $gs = implode('|',$ga); - } - else { - $gs = '<<>>'; // should be impossible to match - } - - $regexop = db_getfunc('REGEXP'); - $sql = sprintf( - " AND ( NOT ({$table}deny_cid $regexop '%s' OR {$table}deny_gid $regexop '%s') + $regexop = db_getfunc('REGEXP'); + $sql = sprintf( + " AND ( NOT ({$table}deny_cid $regexop '%s' OR {$table}deny_gid $regexop '%s') AND ( {$table}allow_cid $regexop '%s' OR {$table}allow_gid $regexop '%s' OR ( {$table}allow_cid = '' AND {$table}allow_gid = '') ) ) ", - dbesc($cs), - dbesc($gs), - dbesc($cs), - dbesc($gs) - ); - } + dbesc($cs), + dbesc($gs), + dbesc($cs), + dbesc($gs) + ); + } - /* - * OCAP token access - */ - - elseif ($token) { - $sql = " and ( {$table}allow_cid like '" . protect_sprintf('%%') . - "' OR ( {$table}allow_cid = '' AND {$table}allow_gid = '' AND {$table}deny_cid = '' AND {$table}deny_gid = '' ) )"; - } + /* + * OCAP token access + */ - } + elseif ($token) { + $sql = " and ( {$table}allow_cid like '" . protect_sprintf('%%') . + "' OR ( {$table}allow_cid = '' AND {$table}allow_gid = '' AND {$table}deny_cid = '' AND {$table}deny_gid = '' ) )"; + } + } - return $sql; + return $sql; } /** @@ -439,92 +462,89 @@ function permissions_sql($owner_id, $remote_observer = null, $table = '', $token * @return string additional SQL where statement */ -function item_permissions_sql($owner_id, $remote_observer = null) { +function item_permissions_sql($owner_id, $remote_observer = null) +{ - $local_channel = local_channel(); + $local_channel = local_channel(); - /** - * Construct permissions - * - * default permissions - anonymous user - */ + /** + * Construct permissions + * + * default permissions - anonymous user + */ - $sql = " AND item_private = 0 "; + $sql = " AND item_private = 0 "; - /** - * Profile owner - everything is visible - */ + /** + * Profile owner - everything is visible + */ - if(($local_channel) && ($local_channel == $owner_id)) { - $sql = ''; - } + if (($local_channel) && ($local_channel == $owner_id)) { + $sql = ''; + } - /** - * Authenticated visitor. - */ - - else { + /** + * Authenticated visitor. + */ + else { $observer = (($remote_observer) ? $remote_observer : get_observer_hash()); - if($observer) { + if ($observer) { + $sec = get_security_ids($owner_id, $observer); - $sec = get_security_ids($owner_id,$observer); + // always allow the channel owner, even if authenticated as a visitor - // always allow the channel owner, even if authenticated as a visitor + if ($sec['channel_id']) { + foreach ($sec['channel_id'] as $ch) { + if ($observer === $ch) { + return EMPTY_STR; + } + } + } - if($sec['channel_id']) { - foreach($sec['channel_id'] as $ch) { - if($observer === $ch) { - return EMPTY_STR; - } - } - } + if (is_array($sec['allow_cid']) && count($sec['allow_cid'])) { + $ca = []; + foreach ($sec['allow_cid'] as $c) { + $ca[] = '<' . $c . '>'; + } + $cs = implode('|', $ca); + } else { + $cs = '<<>>'; // should be impossible to match + } - if (is_array($sec['allow_cid']) && count($sec['allow_cid'])) { - $ca = []; - foreach ($sec['allow_cid'] as $c) { - $ca[] = '<' . $c . '>'; - } - $cs = implode('|',$ca); - } - else { - $cs = '<<>>'; // should be impossible to match - } + if (is_array($sec['allow_gid']) && count($sec['allow_gid'])) { + $ga = []; + foreach ($sec['allow_gid'] as $g) { + $ga[] = '<' . $g . '>'; + } + $gs = implode('|', $ga); + } else { + $gs = '<<>>'; // should be impossible to match + } - if (is_array($sec['allow_gid']) && count($sec['allow_gid'])) { - $ga = []; - foreach ($sec['allow_gid'] as $g) { - $ga[] = '<' . $g . '>'; - } - $gs = implode('|',$ga); - } - else { - $gs = '<<>>'; // should be impossible to match - } + // This function is often called without an $owner_id in places where this could not be + // determined in advance. The ACL fields will usually not contain the original author or owner + // so we will also check for author_xchan and owner_xchan to account for this ACL deficiency. - // This function is often called without an $owner_id in places where this could not be - // determined in advance. The ACL fields will usually not contain the original author or owner - // so we will also check for author_xchan and owner_xchan to account for this ACL deficiency. - - $regexop = db_getfunc('REGEXP'); - $sql = sprintf( - " AND ( author_xchan = '%s' OR owner_xchan = '%s' OR + $regexop = db_getfunc('REGEXP'); + $sql = sprintf( + " AND ( author_xchan = '%s' OR owner_xchan = '%s' OR (( NOT (deny_cid $regexop '%s' OR deny_gid $regexop '%s') AND ( allow_cid $regexop '%s' OR allow_gid $regexop '%s' OR ( allow_cid = '' AND allow_gid = '' AND item_private = 0 )) ))) ", - dbesc($observer), - dbesc($observer), - dbesc($cs), - dbesc($gs), - dbesc($cs), - dbesc($gs) - ); - } - } + dbesc($observer), + dbesc($observer), + dbesc($cs), + dbesc($gs), + dbesc($cs), + dbesc($gs) + ); + } + } - return $sql; + return $sql; } /** @@ -533,53 +553,50 @@ function item_permissions_sql($owner_id, $remote_observer = null) { * @return string additional SQL where statement */ -function public_permissions_sql($observer_hash) { +function public_permissions_sql($observer_hash) +{ - $owner_id = 0; + $owner_id = 0; - if ($observer_hash) { + if ($observer_hash) { + $sec = get_security_ids($owner_id, $observer_hash); - $sec = get_security_ids($owner_id,$observer_hash); + if (is_array($sec['allow_cid']) && count($sec['allow_cid'])) { + $ca = []; + foreach ($sec['allow_cid'] as $c) { + $ca[] = '<' . $c . '>'; + } + $cs = implode('|', $ca); + } else { + $cs = '<<>>'; // should be impossible to match + } - if (is_array($sec['allow_cid']) && count($sec['allow_cid'])) { - $ca = []; - foreach ($sec['allow_cid'] as $c) { - $ca[] = '<' . $c . '>'; - } - $cs = implode('|',$ca); - } - else { - $cs = '<<>>'; // should be impossible to match - } + if (is_array($sec['allow_gid']) && count($sec['allow_gid'])) { + $ga = []; + foreach ($sec['allow_gid'] as $g) { + $ga[] = '<' . $g . '>'; + } + $gs = implode('|', $ga); + } else { + $gs = '<<>>'; // should be impossible to match + } - if (is_array($sec['allow_gid']) && count($sec['allow_gid'])) { - $ga = []; - foreach ($sec['allow_gid'] as $g) { - $ga[] = '<' . $g . '>'; - } - $gs = implode('|',$ga); - } - else { - $gs = '<<>>'; // should be impossible to match - } - - $regexop = db_getfunc('REGEXP'); - $sql = sprintf( - " AND ( NOT (deny_cid $regexop '%s' OR deny_gid $regexop '%s') + $regexop = db_getfunc('REGEXP'); + $sql = sprintf( + " AND ( NOT (deny_cid $regexop '%s' OR deny_gid $regexop '%s') AND ( allow_cid $regexop '%s' OR allow_gid $regexop '%s' OR ( allow_cid = '' AND allow_gid = '' AND item_private = 0) ) ) ", - dbesc($cs), - dbesc($gs), - dbesc($cs), - dbesc($gs) - ); - } - else { - $sql = " and item_private = 0 "; - } + dbesc($cs), + dbesc($gs), + dbesc($cs), + dbesc($gs) + ); + } else { + $sql = " and item_private = 0 "; + } - return $sql; + return $sql; } /* @@ -593,133 +610,147 @@ function public_permissions_sql($observer_hash) { * Actually, important actions should not be triggered by Links / GET-Requests at all, but somethimes they still are, * so this mechanism brings in some damage control (the attacker would be able to forge a request to a form of this type, but not to forms of other types). */ -function get_form_security_token($typename = '') { +function get_form_security_token($typename = '') +{ - $timestamp = time(); - $sec_hash = hash('whirlpool', App::$observer['xchan_guid'] . ((local_channel()) ? App::$channel['channel_prvkey'] : '') . session_id() . $timestamp . $typename); + $timestamp = time(); + $sec_hash = hash('whirlpool', App::$observer['xchan_guid'] . ((local_channel()) ? App::$channel['channel_prvkey'] : '') . session_id() . $timestamp . $typename); - return $timestamp . '.' . $sec_hash; + return $timestamp . '.' . $sec_hash; } -function check_form_security_token($typename = '', $formname = 'form_security_token') { - if (!x($_REQUEST, $formname)) return false; - $hash = $_REQUEST[$formname]; +function check_form_security_token($typename = '', $formname = 'form_security_token') +{ + if (!x($_REQUEST, $formname)) { + return false; + } + $hash = $_REQUEST[$formname]; - $max_livetime = 10800; // 3 hours + $max_livetime = 10800; // 3 hours - $x = explode('.', $hash); - if (time() > (IntVal($x[0]) + $max_livetime)) return false; + $x = explode('.', $hash); + if (time() > (IntVal($x[0]) + $max_livetime)) { + return false; + } - $sec_hash = hash('whirlpool', App::$observer['xchan_guid'] . ((local_channel()) ? App::$channel['channel_prvkey'] : '') . session_id() . $x[0] . $typename); + $sec_hash = hash('whirlpool', App::$observer['xchan_guid'] . ((local_channel()) ? App::$channel['channel_prvkey'] : '') . session_id() . $x[0] . $typename); - return ($sec_hash == $x[1]); + return ($sec_hash == $x[1]); } -function check_form_security_std_err_msg() { - return t('The form security token was not correct. This probably happened because the form has been opened for too long (>3 hours) before submitting it.') . EOL; +function check_form_security_std_err_msg() +{ + return t('The form security token was not correct. This probably happened because the form has been opened for too long (>3 hours) before submitting it.') . EOL; } -function check_form_security_token_redirectOnErr($err_redirect, $typename = '', $formname = 'form_security_token') { - if (!check_form_security_token($typename, $formname)) { - logger('check_form_security_token failed: user ' . App::$observer['xchan_name'] . ' - form element ' . $typename); - logger('check_form_security_token failed: _REQUEST data: ' . print_r($_REQUEST, true), LOGGER_DATA); - notice( check_form_security_std_err_msg() ); - goaway(z_root() . $err_redirect ); - } +function check_form_security_token_redirectOnErr($err_redirect, $typename = '', $formname = 'form_security_token') +{ + if (!check_form_security_token($typename, $formname)) { + logger('check_form_security_token failed: user ' . App::$observer['xchan_name'] . ' - form element ' . $typename); + logger('check_form_security_token failed: _REQUEST data: ' . print_r($_REQUEST, true), LOGGER_DATA); + notice(check_form_security_std_err_msg()); + goaway(z_root() . $err_redirect); + } } -function check_form_security_token_ForbiddenOnErr($typename = '', $formname = 'form_security_token') { - if (!check_form_security_token($typename, $formname)) { - logger('check_form_security_token failed: user ' . App::$observer['xchan_name'] . ' - form element ' . $typename); - logger('check_form_security_token failed: _REQUEST data: ' . print_r($_REQUEST, true), LOGGER_DATA); - header('HTTP/1.1 403 Forbidden'); - killme(); - } +function check_form_security_token_ForbiddenOnErr($typename = '', $formname = 'form_security_token') +{ + if (!check_form_security_token($typename, $formname)) { + logger('check_form_security_token failed: user ' . App::$observer['xchan_name'] . ' - form element ' . $typename); + logger('check_form_security_token failed: _REQUEST data: ' . print_r($_REQUEST, true), LOGGER_DATA); + header('HTTP/1.1 403 Forbidden'); + killme(); + } } // Returns an array of group hash id's on this entire site (across all channels) that this connection is a member of. // var $contact_id = xchan_hash of connection -function init_groups_visitor($contact_id) { - $groups = []; +function init_groups_visitor($contact_id) +{ + $groups = []; - // physical groups this channel is a member of + // physical groups this channel is a member of - $r = q("SELECT hash FROM pgrp left join pgrp_member on pgrp.id = pgrp_member.gid WHERE xchan = '%s' ", - dbesc($contact_id) - ); - if($r) { - foreach($r as $rr) - $groups[] = $rr['hash']; - } - return $groups; + $r = q( + "SELECT hash FROM pgrp left join pgrp_member on pgrp.id = pgrp_member.gid WHERE xchan = '%s' ", + dbesc($contact_id) + ); + if ($r) { + foreach ($r as $rr) { + $groups[] = $rr['hash']; + } + } + return $groups; } -function get_security_ids($channel_id, $ob_hash) { +function get_security_ids($channel_id, $ob_hash) +{ - $ret = [ - 'channel_id' => [], - 'allow_cid' => [], - 'allow_gid' => [] - ]; + $ret = [ + 'channel_id' => [], + 'allow_cid' => [], + 'allow_gid' => [] + ]; - if($channel_id) { - $ch = q("select channel_hash from channel where channel_id = %d", - intval($channel_id) - ); - if($ch) { - $ret['channel_id'][] = $ch[0]['channel_hash']; - } - } + if ($channel_id) { + $ch = q( + "select channel_hash from channel where channel_id = %d", + intval($channel_id) + ); + if ($ch) { + $ret['channel_id'][] = $ch[0]['channel_hash']; + } + } - $groups = []; + $groups = []; - $x = q("select * from xchan where xchan_hash = '%s'", - dbesc($ob_hash) - ); + $x = q( + "select * from xchan where xchan_hash = '%s'", + dbesc($ob_hash) + ); - if ($x) { + if ($x) { + // include xchans for all zot-like networks - // include xchans for all zot-like networks + $xchans = q( + "select xchan_hash, xchan_network from xchan where xchan_hash = '%s' OR ( xchan_guid = '%s' AND xchan_pubkey = '%s' ) ", + dbesc($ob_hash), + dbesc($x[0]['xchan_guid']), + dbesc($x[0]['xchan_pubkey']) + ); - $xchans = q("select xchan_hash, xchan_network from xchan where xchan_hash = '%s' OR ( xchan_guid = '%s' AND xchan_pubkey = '%s' ) ", - dbesc($ob_hash), - dbesc($x[0]['xchan_guid']), - dbesc($x[0]['xchan_pubkey']) - ); + if ($xchans) { + $ret['allow_cid'] = ids_to_array($xchans, 'xchan_hash'); + $hashes = ids_to_querystr($xchans, 'xchan_hash', true); - if ($xchans) { - $ret['allow_cid'] = ids_to_array($xchans,'xchan_hash'); - $hashes = ids_to_querystr($xchans,'xchan_hash',true); + // physical groups this identity is a member of - // physical groups this identity is a member of + $r = q("SELECT hash FROM pgrp left join pgrp_member on pgrp.id = pgrp_member.gid WHERE xchan in ( " . protect_sprintf($hashes) . " ) "); + if ($r) { + foreach ($r as $rv) { + $groups[] = $rv['hash']; + } + } - $r = q("SELECT hash FROM pgrp left join pgrp_member on pgrp.id = pgrp_member.gid WHERE xchan in ( " . protect_sprintf($hashes) . " ) "); - if($r) { - foreach ($r as $rv) { - $groups[] = $rv['hash']; - } - } + // virtual groups this identity is a member of - // virtual groups this identity is a member of - - $r = q("select channel_hash from channel left join abook on channel_id = abook_channel where abook_xchan in ( " . protect_sprintf($hashes) . " ) and abook_self = 0 and abook_pending = 0 and abook_archived = 0 "); - if ($r) { - foreach ($r as $rv) { - $groups[] = 'connections:' . $rv['channel_hash']; + $r = q("select channel_hash from channel left join abook on channel_id = abook_channel where abook_xchan in ( " . protect_sprintf($hashes) . " ) and abook_self = 0 and abook_pending = 0 and abook_archived = 0 "); + if ($r) { + foreach ($r as $rv) { + $groups[] = 'connections:' . $rv['channel_hash']; if (in_array($xchans[0]['xchan_network'],['nomad','zot6'])) { - $groups[] = 'zot:' . $rv['channel_hash']; - } - if ($xchans[0]['xchan_network'] === 'activitypub') { - $groups[] = 'activitypub:' . $rv['channel_hash']; - } - } - } + $groups[] = 'zot:' . $rv['channel_hash']; + } + if ($xchans[0]['xchan_network'] === 'activitypub') { + $groups[] = 'activitypub:' . $rv['channel_hash']; + } + } + } - $ret['allow_gid'] = $groups; - } - } + $ret['allow_gid'] = $groups; + } + } - return $ret; + return $ret; } - diff --git a/include/sharedwithme.php b/include/sharedwithme.php index b342f51d5..570b201e4 100644 --- a/include/sharedwithme.php +++ b/include/sharedwithme.php @@ -1,32 +1,30 @@ $max) { - break; - } + if (! $r) { + q( + "insert into xlink ( xlink_xchan, xlink_link, xlink_rating, xlink_rating_text, xlink_sig, xlink_updated, xlink_static ) values ( '%s', '%s', %d, '%s', '%s', '%s', 0 ) ", + dbesc($xchan), + dbesc($hash), + intval(0), + dbesc(''), + dbesc(''), + dbesc(datetime_convert()) + ); + } else { + q( + "update xlink set xlink_updated = '%s' where xlink_id = %d", + dbesc(datetime_convert()), + intval($r[0]['xlink_id']) + ); + } - } - logger("poco_load: loaded $total entries",LOGGER_DEBUG); - - q("delete from xlink where xlink_xchan = '%s' and xlink_updated < %s - INTERVAL %s and xlink_static = 0", - dbesc($xchan), - db_utcnow(), db_quoteinterval('7 DAY') - ); + $total++; + if ($total > $max) { + break; + } + } + logger("poco_load: loaded $total entries", LOGGER_DEBUG); + q( + "delete from xlink where xlink_xchan = '%s' and xlink_updated < %s - INTERVAL %s and xlink_static = 0", + dbesc($xchan), + db_utcnow(), + db_quoteinterval('7 DAY') + ); } -function ap_poco_load($xchan) { +function ap_poco_load($xchan) +{ - $max = intval(get_config('system','max_imported_follow', MAX_IMPORTED_FOLLOW)); - if (! intval($max)) { - return; - } - - - if($xchan) { - $cl = get_xconfig($xchan,'activitypub','collections'); - if (is_array($cl) && $cl) { - $url = ((array_key_exists('following',$cl)) ? $cl['following'] : ''); - } - else { - return false; - } - } - - if (! $url) { - logger('ap_poco_load: no url'); - return; - } - - $obj = new ASCollection($url, '', 0, $max); - - $friends = $obj->get(); - - if (! $friends) { - return; - } - - foreach($friends as $entry) { - - $hash = EMPTY_STR; - - $x = q("select xchan_hash from xchan where (xchan_hash = '%s' or xchan_url = '%s') order by xchan_network desc limit 1", - dbesc($entry), - dbesc($entry) - ); + $max = intval(get_config('system', 'max_imported_follow', MAX_IMPORTED_FOLLOW)); + if (! intval($max)) { + return; + } - if ($x) { - $hash = $x[0]['xchan_hash']; - } - else { + if ($xchan) { + $cl = get_xconfig($xchan, 'activitypub', 'collections'); + if (is_array($cl) && $cl) { + $url = ((array_key_exists('following', $cl)) ? $cl['following'] : ''); + } else { + return false; + } + } - // We've never seen this person before. Import them. + if (! $url) { + logger('ap_poco_load: no url'); + return; + } - $wf = discover_by_webbie($entry); - if ($wf) { - $x = q("select xchan_hash from xchan where (xchan_hash = '%s' or xchan_url = '%s') order by xchan_network desc limit 1", - dbesc($wf), - dbesc($wf) - ); - if ($x) { - $hash = $x[0]['xchan_hash']; - } - } - } + $obj = new ASCollection($url, '', 0, $max); - if (! $hash) { - continue; - } + $friends = $obj->get(); - $total ++; + if (! $friends) { + return; + } - $r = q("select * from xlink where xlink_xchan = '%s' and xlink_link = '%s' and xlink_static = 0 limit 1", - dbesc($xchan), - dbesc($hash) - ); + foreach ($friends as $entry) { + $hash = EMPTY_STR; - if(! $r) { - q("insert into xlink ( xlink_xchan, xlink_link, xlink_rating, xlink_rating_text, xlink_sig, xlink_updated, xlink_static ) values ( '%s', '%s', %d, '%s', '%s', '%s', 0 ) ", - dbesc($xchan), - dbesc($hash), - intval(0), - dbesc(''), - dbesc(''), - dbesc(datetime_convert()) - ); - } - else { - q("update xlink set xlink_updated = '%s' where xlink_id = %d", - dbesc(datetime_convert()), - intval($r[0]['xlink_id']) - ); - } - } + $x = q( + "select xchan_hash from xchan where (xchan_hash = '%s' or xchan_url = '%s') order by xchan_network desc limit 1", + dbesc($entry), + dbesc($entry) + ); - logger("ap_poco_load: loaded $total entries", LOGGER_DEBUG); - q("delete from xlink where xlink_xchan = '%s' and xlink_updated < %s - INTERVAL %s and xlink_static = 0", - dbesc($xchan), - db_utcnow(), - db_quoteinterval('7 DAY') - ); + if ($x) { + $hash = $x[0]['xchan_hash']; + } else { + // We've never seen this person before. Import them. - return true; + $wf = discover_by_webbie($entry); + if ($wf) { + $x = q( + "select xchan_hash from xchan where (xchan_hash = '%s' or xchan_url = '%s') order by xchan_network desc limit 1", + dbesc($wf), + dbesc($wf) + ); + if ($x) { + $hash = $x[0]['xchan_hash']; + } + } + } + + if (! $hash) { + continue; + } + + $total++; + + $r = q( + "select * from xlink where xlink_xchan = '%s' and xlink_link = '%s' and xlink_static = 0 limit 1", + dbesc($xchan), + dbesc($hash) + ); + + if (! $r) { + q( + "insert into xlink ( xlink_xchan, xlink_link, xlink_rating, xlink_rating_text, xlink_sig, xlink_updated, xlink_static ) values ( '%s', '%s', %d, '%s', '%s', '%s', 0 ) ", + dbesc($xchan), + dbesc($hash), + intval(0), + dbesc(''), + dbesc(''), + dbesc(datetime_convert()) + ); + } else { + q( + "update xlink set xlink_updated = '%s' where xlink_id = %d", + dbesc(datetime_convert()), + intval($r[0]['xlink_id']) + ); + } + } + + logger("ap_poco_load: loaded $total entries", LOGGER_DEBUG); + + q( + "delete from xlink where xlink_xchan = '%s' and xlink_updated < %s - INTERVAL %s and xlink_static = 0", + dbesc($xchan), + db_utcnow(), + db_quoteinterval('7 DAY') + ); + + return true; } -function count_common_friends($uid,$xchan) { +function count_common_friends($uid, $xchan) +{ - $r = q("SELECT count(xlink_id) as total from xlink where xlink_xchan = '%s' and xlink_static = 0 and xlink_link in + $r = q( + "SELECT count(xlink_id) as total from xlink where xlink_xchan = '%s' and xlink_static = 0 and xlink_link in (select abook_xchan from abook where abook_xchan != '%s' and abook_channel = %d and abook_self = 0 )", - dbesc($xchan), - dbesc($xchan), - intval($uid) - ); + dbesc($xchan), + dbesc($xchan), + intval($uid) + ); - if($r) - return $r[0]['total']; - return 0; + if ($r) { + return $r[0]['total']; + } + return 0; } -function common_friends($uid,$xchan,$start = 0,$limit=100000000,$shuffle = false) { +function common_friends($uid, $xchan, $start = 0, $limit = 100000000, $shuffle = false) +{ - $rand = db_getfunc('rand'); - if($shuffle) - $sql_extra = " order by $rand "; - else - $sql_extra = " order by xchan_name asc "; + $rand = db_getfunc('rand'); + if ($shuffle) { + $sql_extra = " order by $rand "; + } else { + $sql_extra = " order by xchan_name asc "; + } - $r = q("SELECT * from xchan left join xlink on xlink_link = xchan_hash where xlink_xchan = '%s' and xlink_static = 0 and xlink_link in + $r = q( + "SELECT * from xchan left join xlink on xlink_link = xchan_hash where xlink_xchan = '%s' and xlink_static = 0 and xlink_link in (select abook_xchan from abook where abook_xchan != '%s' and abook_channel = %d and abook_self = 0 ) $sql_extra limit %d offset %d", - dbesc($xchan), - dbesc($xchan), - intval($uid), - intval($limit), - intval($start) - ); + dbesc($xchan), + dbesc($xchan), + intval($uid), + intval($limit), + intval($start) + ); - return $r; + return $r; } -function suggestion_query($uid, $myxchan, $start = 0, $limit = 120) { +function suggestion_query($uid, $myxchan, $start = 0, $limit = 120) +{ - if ((! $uid) || (! $myxchan)) { - return []; - } + if ((! $uid) || (! $myxchan)) { + return []; + } - $r1 = q("SELECT count(xlink_xchan) as total, xchan.* from xchan + $r1 = q( + "SELECT count(xlink_xchan) as total, xchan.* from xchan left join xlink on xlink_link = xchan_hash where xlink_xchan in ( select abook_xchan from abook where abook_channel = %d ) and not xlink_link in ( select abook_xchan from abook where abook_channel = %d ) @@ -388,18 +410,19 @@ function suggestion_query($uid, $myxchan, $start = 0, $limit = 120) { and xchan_deleted = 0 and xlink_static = 0 group by xchan_hash order by total desc limit %d offset %d ", - intval($uid), - intval($uid), - intval($uid), - intval($limit), - intval($start) - ); + intval($uid), + intval($uid), + intval($uid), + intval($limit), + intval($start) + ); - if (! $r1) { - $r1 = []; - } + if (! $r1) { + $r1 = []; + } - $r2 = q("SELECT count(xtag_hash) as total, xchan.* from xchan + $r2 = q( + "SELECT count(xtag_hash) as total, xchan.* from xchan left join xtag on xtag_hash = xchan_hash where xtag_hash != '%s' and not xtag_hash in ( select abook_xchan from abook where abook_channel = %d ) @@ -408,238 +431,261 @@ function suggestion_query($uid, $myxchan, $start = 0, $limit = 120) { and xchan_hidden = 0 and xchan_deleted = 0 group by xchan_hash order by total desc limit %d offset %d ", - dbesc($myxchan), - intval($uid), - dbesc($myxchan), - intval($uid), - intval($limit), - intval($start) - ); + dbesc($myxchan), + intval($uid), + dbesc($myxchan), + intval($uid), + intval($limit), + intval($start) + ); - if (! $r2) { - $r2 = []; - } + if (! $r2) { + $r2 = []; + } - foreach ($r2 as $r) { - $found = false; - for ($x = 0; $x < count($r1); $x ++) { - if ($r['xchan_hash'] === $r1[$x]['xchan_hash']) { - $r1[$x]['total'] = intval($r1[$x]['total']) + intval($r['total']); - $found = true; - continue; - } - } - if (! $found) { - $r1[] = $r; - } - } + foreach ($r2 as $r) { + $found = false; + for ($x = 0; $x < count($r1); $x++) { + if ($r['xchan_hash'] === $r1[$x]['xchan_hash']) { + $r1[$x]['total'] = intval($r1[$x]['total']) + intval($r['total']); + $found = true; + continue; + } + } + if (! $found) { + $r1[] = $r; + } + } - usort($r1,'socgraph_total_sort'); - return ($r1); + usort($r1, 'socgraph_total_sort'); + return ($r1); } -function socgraph_total_sort($a,$b) { - if ($a['total'] === $b['total']) { - return 0; - } - - return((intval($a['total']) < intval($b['total'])) ? 1 : -1 ); +function socgraph_total_sort($a, $b) +{ + if ($a['total'] === $b['total']) { + return 0; + } + + return((intval($a['total']) < intval($b['total'])) ? 1 : -1 ); } -function poco() { +function poco() +{ - $system_mode = false; + $system_mode = false; - if(observer_prohibited()) { - logger('mod_poco: block_public'); - http_status_exit(401); - } + if (observer_prohibited()) { + logger('mod_poco: block_public'); + http_status_exit(401); + } - $observer = App::get_observer(); + $observer = App::get_observer(); - if(argc() > 1) { - $user = notags(trim(argv(1))); - } - if(! (isset($user) && $user)) { - $c = q("select * from pconfig where cat = 'system' and k = 'suggestme' and v = '1'"); - if(! $c) { - logger('mod_poco: system mode. No candidates.', LOGGER_DEBUG); - http_status_exit(404); - } - $system_mode = true; - } + if (argc() > 1) { + $user = notags(trim(argv(1))); + } + if (! (isset($user) && $user)) { + $c = q("select * from pconfig where cat = 'system' and k = 'suggestme' and v = '1'"); + if (! $c) { + logger('mod_poco: system mode. No candidates.', LOGGER_DEBUG); + http_status_exit(404); + } + $system_mode = true; + } - $format = ((isset($_REQUEST['format']) && $_REQUEST['format']) ? $_REQUEST['format'] : 'json'); + $format = ((isset($_REQUEST['format']) && $_REQUEST['format']) ? $_REQUEST['format'] : 'json'); - $justme = false; + $justme = false; - if(argc() > 2 && argv(2) === '@me') - $justme = true; - if(argc() > 3) { - if(argv(3) === '@all') - $justme = false; - elseif(argv(3) === '@self') - $justme = true; - } - if(argc() > 4 && intval(argv(4)) && $justme == false) - $cid = intval(argv(4)); + if (argc() > 2 && argv(2) === '@me') { + $justme = true; + } + if (argc() > 3) { + if (argv(3) === '@all') { + $justme = false; + } elseif (argv(3) === '@self') { + $justme = true; + } + } + if (argc() > 4 && intval(argv(4)) && $justme == false) { + $cid = intval(argv(4)); + } - if(! $system_mode) { + if (! $system_mode) { + $r = q( + "SELECT channel_id from channel where channel_address = '%s' limit 1", + dbesc($user) + ); + if (! $r) { + logger('mod_poco: user mode. Account not found. ' . $user); + http_status_exit(404); + } - $r = q("SELECT channel_id from channel where channel_address = '%s' limit 1", - dbesc($user) - ); - if(! $r) { - logger('mod_poco: user mode. Account not found. ' . $user); - http_status_exit(404); - } + $channel_id = $r[0]['channel_id']; + $ohash = (($observer) ? $observer['xchan_hash'] : ''); - $channel_id = $r[0]['channel_id']; - $ohash = (($observer) ? $observer['xchan_hash'] : ''); + if (! perm_is_allowed($channel_id, $ohash, 'view_contacts')) { + logger('mod_poco: user mode. Permission denied for ' . $ohash . ' user: ' . $user); + http_status_exit(401); + } + } - if(! perm_is_allowed($channel_id,$ohash,'view_contacts')) { - logger('mod_poco: user mode. Permission denied for ' . $ohash . ' user: ' . $user); - http_status_exit(401); - } + if (isset($justme) && $justme) { + $sql_extra = " and abook_self = 1 "; + } else { + $sql_extra = " and abook_self = 0 "; + } - } + if (isset($cid) && $cid) { + $sql_extra = sprintf(" and abook_id = %d and abook_archived = 0 and abook_hidden = 0 and abook_pending = 0 ", intval($cid)); + } - if(isset($justme) && $justme) - $sql_extra = " and abook_self = 1 "; - else - $sql_extra = " and abook_self = 0 "; - - if(isset($cid) && $cid) - $sql_extra = sprintf(" and abook_id = %d and abook_archived = 0 and abook_hidden = 0 and abook_pending = 0 ",intval($cid)); - - if(isset($system_mode) && $system_mode) { - $r = q("SELECT count(*) as total from abook where abook_self = 1 + if (isset($system_mode) && $system_mode) { + $r = q("SELECT count(*) as total from abook where abook_self = 1 and abook_channel in (select uid from pconfig where cat = 'system' and k = 'suggestme' and v = '1') "); - } - else { - $r = q("SELECT count(*) as total from abook where abook_channel = %d + } else { + $r = q( + "SELECT count(*) as total from abook where abook_channel = %d $sql_extra ", - intval($channel_id) - ); - $rooms = q("select * from menu_item where ( mitem_flags & " . intval(MENU_ITEM_CHATROOM) . " ) > 0 and allow_cid = '' and allow_gid = '' and deny_cid = '' and deny_gid = '' and mitem_channel_id = %d", - intval($channel_id) - ); - } - if($r) - $totalResults = intval($r[0]['total']); - else - $totalResults = 0; + intval($channel_id) + ); + $rooms = q( + "select * from menu_item where ( mitem_flags & " . intval(MENU_ITEM_CHATROOM) . " ) > 0 and allow_cid = '' and allow_gid = '' and deny_cid = '' and deny_gid = '' and mitem_channel_id = %d", + intval($channel_id) + ); + } + if ($r) { + $totalResults = intval($r[0]['total']); + } else { + $totalResults = 0; + } - $startIndex = ((isset($_GET['startIndex'])) ? intval($_GET['startIndex']) : 0); - if($startIndex < 0) - $startIndex = 0; + $startIndex = ((isset($_GET['startIndex'])) ? intval($_GET['startIndex']) : 0); + if ($startIndex < 0) { + $startIndex = 0; + } - $itemsPerPage = ((isset($_GET['count']) && intval($_GET['count'])) ? intval($_GET['count']) : $totalResults); + $itemsPerPage = ((isset($_GET['count']) && intval($_GET['count'])) ? intval($_GET['count']) : $totalResults); - if($system_mode) { - $r = q("SELECT abook.*, xchan.* from abook left join xchan on abook_xchan = xchan_hash where abook_self = 1 + if ($system_mode) { + $r = q( + "SELECT abook.*, xchan.* from abook left join xchan on abook_xchan = xchan_hash where abook_self = 1 and abook_channel in (select uid from pconfig where cat = 'system' and k = 'suggestme' and v = '1') limit %d offset %d ", - intval($itemsPerPage), - intval($startIndex) - ); - } else { - $r = q("SELECT abook.*, xchan.* from abook left join xchan on abook_xchan = xchan_hash where abook_channel = %d + intval($itemsPerPage), + intval($startIndex) + ); + } else { + $r = q( + "SELECT abook.*, xchan.* from abook left join xchan on abook_xchan = xchan_hash where abook_channel = %d $sql_extra LIMIT %d OFFSET %d", - intval($channel_id), - intval($itemsPerPage), - intval($startIndex) - ); - } + intval($channel_id), + intval($itemsPerPage), + intval($startIndex) + ); + } - $ret = []; - if(x($_GET,'sorted')) - $ret['sorted'] = 'false'; - if(x($_GET,'filtered')) - $ret['filtered'] = 'false'; - if(x($_GET,'updatedSince')) - $ret['updateSince'] = 'false'; + $ret = []; + if (x($_GET, 'sorted')) { + $ret['sorted'] = 'false'; + } + if (x($_GET, 'filtered')) { + $ret['filtered'] = 'false'; + } + if (x($_GET, 'updatedSince')) { + $ret['updateSince'] = 'false'; + } - $ret['startIndex'] = (string) $startIndex; - $ret['itemsPerPage'] = (string) $itemsPerPage; - $ret['totalResults'] = (string) $totalResults; + $ret['startIndex'] = (string) $startIndex; + $ret['itemsPerPage'] = (string) $itemsPerPage; + $ret['totalResults'] = (string) $totalResults; - if($rooms) { - $ret['chatrooms'] = []; - foreach($rooms as $room) { - $ret['chatrooms'][] = array('url' => $room['mitem_link'], 'desc' => $room['mitem_desc']); - } - } + if ($rooms) { + $ret['chatrooms'] = []; + foreach ($rooms as $room) { + $ret['chatrooms'][] = array('url' => $room['mitem_link'], 'desc' => $room['mitem_desc']); + } + } - $ret['entry'] = []; + $ret['entry'] = []; - $fields_ret = array( - 'id' => false, - 'guid' => false, - 'guid_sig' => false, - 'hash' => false, - 'displayName' => false, - 'urls' => false, - 'preferredUsername' => false, - 'photos' => false, - 'rating' => false - ); + $fields_ret = array( + 'id' => false, + 'guid' => false, + 'guid_sig' => false, + 'hash' => false, + 'displayName' => false, + 'urls' => false, + 'preferredUsername' => false, + 'photos' => false, + 'rating' => false + ); - if((! x($_GET,'fields')) || ($_GET['fields'] === '@all')) { - foreach($fields_ret as $k => $v) - $fields_ret[$k] = true; - } else { - $fields_req = explode(',',$_GET['fields']); - foreach($fields_req as $f) - $fields_ret[trim($f)] = true; - } + if ((! x($_GET, 'fields')) || ($_GET['fields'] === '@all')) { + foreach ($fields_ret as $k => $v) { + $fields_ret[$k] = true; + } + } else { + $fields_req = explode(',', $_GET['fields']); + foreach ($fields_req as $f) { + $fields_ret[trim($f)] = true; + } + } - if(is_array($r)) { - if(count($r)) { - foreach($r as $rr) { - $entry = []; - if($fields_ret['id']) - $entry['id'] = $rr['abook_id']; - if($fields_ret['guid']) - $entry['guid'] = $rr['xchan_guid']; - if($fields_ret['guid_sig']) - $entry['guid_sig'] = $rr['xchan_guid_sig']; - if($fields_ret['hash']) - $entry['hash'] = $rr['xchan_hash']; + if (is_array($r)) { + if (count($r)) { + foreach ($r as $rr) { + $entry = []; + if ($fields_ret['id']) { + $entry['id'] = $rr['abook_id']; + } + if ($fields_ret['guid']) { + $entry['guid'] = $rr['xchan_guid']; + } + if ($fields_ret['guid_sig']) { + $entry['guid_sig'] = $rr['xchan_guid_sig']; + } + if ($fields_ret['hash']) { + $entry['hash'] = $rr['xchan_hash']; + } - if($fields_ret['displayName']) - $entry['displayName'] = $rr['xchan_name']; - if($fields_ret['urls']) { - $entry['urls'] = array(array('value' => $rr['xchan_url'], 'type' => 'profile')); - $network = $rr['xchan_network']; - if($rr['xchan_addr']) - $entry['urls'][] = array('value' => 'acct:' . $rr['xchan_addr'], 'type' => $network); - } - if($fields_ret['preferredUsername']) - $entry['preferredUsername'] = substr($rr['xchan_addr'],0,strpos($rr['xchan_addr'],'@')); - if($fields_ret['photos']) - $entry['photos'] = array(array('value' => $rr['xchan_photo_l'], 'mimetype' => $rr['xchan_photo_mimetype'], 'type' => 'profile')); - $ret['entry'][] = $entry; - } - } - else - $ret['entry'][] = []; - } - else - http_status_exit(500); - - if($format === 'xml') { - header('Content-type: text/xml'); - echo replace_macros(get_markup_template('poco_xml.tpl'),array_xmlify(array('$response' => $ret))); - http_status_exit(500); - } - if($format === 'json') { - header('Content-type: application/json'); - echo json_encode($ret); - killme(); - } - else - http_status_exit(500); + if ($fields_ret['displayName']) { + $entry['displayName'] = $rr['xchan_name']; + } + if ($fields_ret['urls']) { + $entry['urls'] = array(array('value' => $rr['xchan_url'], 'type' => 'profile')); + $network = $rr['xchan_network']; + if ($rr['xchan_addr']) { + $entry['urls'][] = array('value' => 'acct:' . $rr['xchan_addr'], 'type' => $network); + } + } + if ($fields_ret['preferredUsername']) { + $entry['preferredUsername'] = substr($rr['xchan_addr'], 0, strpos($rr['xchan_addr'], '@')); + } + if ($fields_ret['photos']) { + $entry['photos'] = array(array('value' => $rr['xchan_photo_l'], 'mimetype' => $rr['xchan_photo_mimetype'], 'type' => 'profile')); + } + $ret['entry'][] = $entry; + } + } else { + $ret['entry'][] = []; + } + } else { + http_status_exit(500); + } + if ($format === 'xml') { + header('Content-type: text/xml'); + echo replace_macros(get_markup_template('poco_xml.tpl'), array_xmlify(array('$response' => $ret))); + http_status_exit(500); + } + if ($format === 'json') { + header('Content-type: application/json'); + echo json_encode($ret); + killme(); + } else { + http_status_exit(500); + } } diff --git a/include/statistics_fns.php b/include/statistics_fns.php index f3ae6a71e..363d08268 100644 --- a/include/statistics_fns.php +++ b/include/statistics_fns.php @@ -1,58 +1,66 @@ %s - INTERVAL %s", - db_utcnow(), db_quoteinterval('6 MONTH') - ); - if($r) { - set_config('system','channels_active_halfyear_stat',count($r)); - } - else { - set_config('system','channels_active_halfyear_stat','0'); - } + db_utcnow(), + db_quoteinterval('6 MONTH') + ); + if ($r) { + set_config('system', 'channels_active_halfyear_stat', count($r)); + } else { + set_config('system', 'channels_active_halfyear_stat', '0'); + } } -function update_channels_active_monthly_stat() { - $r = q("select channel_id from channel left join account on account_id = channel_account_id +function update_channels_active_monthly_stat() +{ + $r = q( + "select channel_id from channel left join account on account_id = channel_account_id where account_flags = 0 and channel_active > %s - INTERVAL %s", - db_utcnow(), db_quoteinterval('1 MONTH') - ); - if($r) { - set_config('system','channels_active_monthly_stat',count($r)); - } - else { - set_config('system','channels_active_monthly_stat','0'); - } + db_utcnow(), + db_quoteinterval('1 MONTH') + ); + if ($r) { + set_config('system', 'channels_active_monthly_stat', count($r)); + } else { + set_config('system', 'channels_active_monthly_stat', '0'); + } } -function update_local_posts_stat() { - $posts = q("SELECT COUNT(*) AS local_posts FROM item WHERE item_wall = 1 and id = parent"); - if (is_array($posts)) { - $local_posts_stat = intval($posts[0]["local_posts"]); - set_config('system','local_posts_stat',$local_posts_stat); - } else { - set_config('system','local_posts_stat',0); - } +function update_local_posts_stat() +{ + $posts = q("SELECT COUNT(*) AS local_posts FROM item WHERE item_wall = 1 and id = parent"); + if (is_array($posts)) { + $local_posts_stat = intval($posts[0]["local_posts"]); + set_config('system', 'local_posts_stat', $local_posts_stat); + } else { + set_config('system', 'local_posts_stat', 0); + } } -function update_local_comments_stat() { - $posts = q("SELECT COUNT(*) AS local_posts FROM item WHERE item_wall = 1 and id != parent"); - if (!is_array($posts)) +function update_local_comments_stat() +{ + $posts = q("SELECT COUNT(*) AS local_posts FROM item WHERE item_wall = 1 and id != parent"); + if (!is_array($posts)) { $local_posts = 0; - else + } else { $local_posts = $posts[0]["local_posts"]; + } - set_config('system','local_comments_stat', $local_posts); -} \ No newline at end of file + set_config('system', 'local_comments_stat', $local_posts); +} diff --git a/include/system_unavailable.php b/include/system_unavailable.php index 9b92eae36..8c99a33ef 100644 --- a/include/system_unavailable.php +++ b/include/system_unavailable.php @@ -2,9 +2,10 @@ require_once("include/network.php"); -function system_down() { - http_status(503, 'Service Unavailable'); - echo <<< EOT +function system_down() +{ + http_status(503, 'Service Unavailable'); + echo <<< EOT System Unavailable @@ -13,5 +14,4 @@ Apologies but this site is unavailable at the moment. Please try again later. EOT; - -} \ No newline at end of file +} diff --git a/include/taxonomy.php b/include/taxonomy.php index 230e4cfcb..549e021a2 100644 --- a/include/taxonomy.php +++ b/include/taxonomy.php @@ -1,689 +1,755 @@ -','[',']'),array('%3c','%3e','%5b','%5d'),$s); +function file_tag_encode($s) +{ + return str_replace(array('<','>','[',']'), array('%3c','%3e','%5b','%5d'), $s); } -function file_tag_decode($s) { - return str_replace(array('%3c','%3e','%5b','%5d'),array('<','>','[',']'),$s); +function file_tag_decode($s) +{ + return str_replace(array('%3c','%3e','%5b','%5d'), array('<','>','[',']'), $s); } -function file_tag_file_query($table,$s,$type = 'file') { +function file_tag_file_query($table, $s, $type = 'file') +{ - if($type == 'file') - $termtype = TERM_FILE; - else - $termtype = TERM_CATEGORY; + if ($type == 'file') { + $termtype = TERM_FILE; + } else { + $termtype = TERM_CATEGORY; + } - return sprintf(" AND " . (($table) ? dbesc($table) . '.' : '') . "id in (select term.oid from term where term.ttype = %d and term.term = '%s' and term.uid = " . (($table) ? dbesc($table) . '.' : '') . "uid ) ", - intval($termtype), - protect_sprintf(dbesc($s)) - ); + return sprintf( + " AND " . (($table) ? dbesc($table) . '.' : '') . "id in (select term.oid from term where term.ttype = %d and term.term = '%s' and term.uid = " . (($table) ? dbesc($table) . '.' : '') . "uid ) ", + intval($termtype), + protect_sprintf(dbesc($s)) + ); } -function term_query($table,$s,$type = TERM_UNKNOWN, $type2 = '') { +function term_query($table, $s, $type = TERM_UNKNOWN, $type2 = '') +{ - if($type2) { - return sprintf(" AND " . (($table) ? dbesc($table) . '.' : '') . "id in (select term.oid from term where term.ttype in (%d, %d) and term.term = '%s' and term.uid = " . (($table) ? dbesc($table) . '.' : '') . "uid ) ", - intval($type), - intval($type2), - protect_sprintf(dbesc($s)) - ); - } - else { - return sprintf(" AND " . (($table) ? dbesc($table) . '.' : '') . "id in (select term.oid from term where term.ttype = %d and term.term = '%s' and term.uid = " . (($table) ? dbesc($table) . '.' : '') . "uid ) ", - intval($type), - protect_sprintf(dbesc($s)) - ); - } + if ($type2) { + return sprintf( + " AND " . (($table) ? dbesc($table) . '.' : '') . "id in (select term.oid from term where term.ttype in (%d, %d) and term.term = '%s' and term.uid = " . (($table) ? dbesc($table) . '.' : '') . "uid ) ", + intval($type), + intval($type2), + protect_sprintf(dbesc($s)) + ); + } else { + return sprintf( + " AND " . (($table) ? dbesc($table) . '.' : '') . "id in (select term.oid from term where term.ttype = %d and term.term = '%s' and term.uid = " . (($table) ? dbesc($table) . '.' : '') . "uid ) ", + intval($type), + protect_sprintf(dbesc($s)) + ); + } } -function term_item_parent_query($uid,$table,$s,$type = TERM_UNKNOWN, $type2 = '') { +function term_item_parent_query($uid, $table, $s, $type = TERM_UNKNOWN, $type2 = '') +{ - // Allow asterisks for wildcard search - // In theory this means '%' will also do a wildcard search, but there appear to be multiple escape - // issues with '%' in term names and trying to fix this with '\\%' here did not help. - // Ideally I think we want '*' to indicate wildcards and allow '%' literally in names, but that is being - // left for another developer on another day. + // Allow asterisks for wildcard search + // In theory this means '%' will also do a wildcard search, but there appear to be multiple escape + // issues with '%' in term names and trying to fix this with '\\%' here did not help. + // Ideally I think we want '*' to indicate wildcards and allow '%' literally in names, but that is being + // left for another developer on another day. - $s = str_replace('*','%',$s); + $s = str_replace('*', '%', $s); - if($type2) { - $r = q("select parent from item left join term on term.oid = item.id where term.ttype in (%d, %d) and term.term like '%s' and term.uid = %d and term.otype = 1 and item.verb != '%s'", - intval($type), - intval($type2), - dbesc($s), - intval($uid), - dbesc(ACTIVITY_UPDATE) - ); - } - else { - $r = q("select parent from item left join term on term.oid = item.id where term.ttype = %d and term.term like '%s' and term.uid = %d and term.otype = 1 and item.verb != '%s'", - intval($type), - dbesc($s), - intval($uid), - dbesc(ACTIVITY_UPDATE) - ); - } + if ($type2) { + $r = q( + "select parent from item left join term on term.oid = item.id where term.ttype in (%d, %d) and term.term like '%s' and term.uid = %d and term.otype = 1 and item.verb != '%s'", + intval($type), + intval($type2), + dbesc($s), + intval($uid), + dbesc(ACTIVITY_UPDATE) + ); + } else { + $r = q( + "select parent from item left join term on term.oid = item.id where term.ttype = %d and term.term like '%s' and term.uid = %d and term.otype = 1 and item.verb != '%s'", + intval($type), + dbesc($s), + intval($uid), + dbesc(ACTIVITY_UPDATE) + ); + } - if($r) { - $str = ''; - foreach($r as $rv) { - if($str) - $str .= ','; - $str .= intval($rv['parent']); - } - return " AND " . (($table) ? dbesc($table) . '.' : '') . "id in ( $str ) "; - } - return " AND false "; + if ($r) { + $str = ''; + foreach ($r as $rv) { + if ($str) { + $str .= ','; + } + $str .= intval($rv['parent']); + } + return " AND " . (($table) ? dbesc($table) . '.' : '') . "id in ( $str ) "; + } + return " AND false "; } -function store_item_tag($uid,$iid,$otype,$type,$term,$url = '') { - if(! $term) - return false; +function store_item_tag($uid, $iid, $otype, $type, $term, $url = '') +{ + if (! $term) { + return false; + } - $r = q("select * from term + $r = q( + "select * from term where uid = %d and oid = %d and otype = %d and ttype = %d and term = '%s' and url = '%s' ", - intval($uid), - intval($iid), - intval($otype), - intval($type), - dbesc($term), - dbesc($url) - ); - if($r) - return false; + intval($uid), + intval($iid), + intval($otype), + intval($type), + dbesc($term), + dbesc($url) + ); + if ($r) { + return false; + } - $r = q("insert into term (uid, oid, otype, ttype, term, url) + $r = q( + "insert into term (uid, oid, otype, ttype, term, url) values( %d, %d, %d, %d, '%s', '%s') ", - intval($uid), - intval($iid), - intval($otype), - intval($type), - dbesc($term), - dbesc($url) - ); + intval($uid), + intval($iid), + intval($otype), + intval($type), + dbesc($term), + dbesc($url) + ); - return $r; + return $r; } -function get_terms_oftype($arr,$type) { - $ret = []; - if(! (is_array($arr) && count($arr))) - return $ret; +function get_terms_oftype($arr, $type) +{ + $ret = []; + if (! (is_array($arr) && count($arr))) { + return $ret; + } - if(! is_array($type)) - $type = array($type); + if (! is_array($type)) { + $type = array($type); + } - foreach($type as $t) - foreach($arr as $x) - if($x['ttype'] == $t) - $ret[] = $x; + foreach ($type as $t) { + foreach ($arr as $x) { + if ($x['ttype'] == $t) { + $ret[] = $x; + } + } + } - return $ret; + return $ret; } -function format_term_for_display($term) { - $s = ''; - if(($term['ttype'] == TERM_HASHTAG) || ($term['ttype'] == TERM_COMMUNITYTAG)) - $s .= '#'; - elseif($term['ttype'] == TERM_FORUM) - $s .= '!'; - elseif($term['ttype'] == TERM_MENTION) - $s .= '@'; - else - return $s; +function format_term_for_display($term) +{ + $s = ''; + if (($term['ttype'] == TERM_HASHTAG) || ($term['ttype'] == TERM_COMMUNITYTAG)) { + $s .= '#'; + } elseif ($term['ttype'] == TERM_FORUM) { + $s .= '!'; + } elseif ($term['ttype'] == TERM_MENTION) { + $s .= '@'; + } else { + return $s; + } - if($term['url']) - $s .= '' . htmlspecialchars($term['term'], ENT_COMPAT,'UTF-8') . ''; - else - $s .= htmlspecialchars($term['term'], ENT_COMPAT,'UTF-8'); - return $s; + if ($term['url']) { + $s .= '' . htmlspecialchars($term['term'], ENT_COMPAT, 'UTF-8') . ''; + } else { + $s .= htmlspecialchars($term['term'], ENT_COMPAT, 'UTF-8'); + } + return $s; } // Tag cloud functions - need to be adpated to this database format -function tagadelic($uid, $count = 0, $authors = '', $owner = '', $flags = 0, $restrict = 0, $type = TERM_HASHTAG) { +function tagadelic($uid, $count = 0, $authors = '', $owner = '', $flags = 0, $restrict = 0, $type = TERM_HASHTAG) +{ - require_once('include/security.php'); + require_once('include/security.php'); - if(! perm_is_allowed($uid,get_observer_hash(),'view_stream')) - return []; - - - $item_normal = item_normal(); - $sql_options = item_permissions_sql($uid); - $count = intval($count); - - if($flags) { - if($flags === 'wall') - $sql_options .= " and item_wall = 1 "; - } - - if($authors) { - if(! is_array($authors)) - $authors = array($authors); - - $sql_options .= " and author_xchan in (" . stringify_array($authors,true) . ") "; - } - - if($owner) { - $sql_options .= " and owner_xchan = '" . dbesc($owner) . "' "; - } - - // Fetch tags - $r = q("select term, count(term) as total from term left join item on term.oid = item.id - where term.uid = %d and term.ttype = %d - and otype = %d and item_type = %d - $sql_options $item_normal - group by term order by total desc %s", - intval($uid), - intval($type), - intval(TERM_OBJ_POST), - intval($restrict), - ((intval($count)) ? "limit $count" : '') - ); - - - if(! $r) - return []; - - return Zotlabs\Text\Tagadelic::calc($r); - -} - - - -function card_tagadelic($uid, $count = 0, $authors = '', $owner = '', $flags = 0, $restrict = 0, $type = TERM_CATEGORY) { - - require_once('include/security.php'); - - if(! perm_is_allowed($uid,get_observer_hash(),'view_pages')) - return []; - - $item_normal = " and item.item_hidden = 0 and item.item_deleted = 0 and item.item_unpublished = 0 and item.item_delayed = 0 and item.item_pending_remove = 0 - and item.item_blocked = 0 and item.obj_type != 'http://purl.org/zot/activity/file' "; - - $sql_options = item_permissions_sql($uid); - $count = intval($count); - - if($flags) { - if($flags === 'wall') - $sql_options .= " and item_wall = 1 "; - } - - if($authors) { - if(! is_array($authors)) - $authors = array($authors); - - $sql_options .= " and author_xchan in (" . stringify_array($authors,true) . ") "; - } - - if($owner) { - $sql_options .= " and owner_xchan = '" . dbesc($owner) . "' "; - } - - - // Fetch tags - - $r = q("select term, count(term) as total from term left join item on term.oid = item.id - where term.uid = %d and term.ttype = %d - and otype = %d and item_type = %d - $sql_options $item_normal - group by term order by total desc %s", - intval($uid), - intval($type), - intval(TERM_OBJ_POST), - intval(ITEM_TYPE_CARD), - ((intval($count)) ? "limit $count" : '') - ); - - if(! $r) - return []; - - return Zotlabs\Text\Tagadelic::calc($r); - -} - -function article_tagadelic($uid, $count = 0, $authors = '', $owner = '', $flags = 0, $restrict = 0, $type = TERM_CATEGORY) { - - require_once('include/security.php'); - - if(! perm_is_allowed($uid,get_observer_hash(),'view_pages')) - return []; - - - $item_normal = " and item.item_hidden = 0 and item.item_deleted = 0 and item.item_unpublished = 0 and item.item_delayed = 0 and item.item_pending_remove = 0 - and item.item_blocked = 0 and item.obj_type != 'http://purl.org/zot/activity/file' "; - - $sql_options = item_permissions_sql($uid); - $count = intval($count); - - if($flags) { - if($flags === 'wall') - $sql_options .= " and item_wall = 1 "; - } - - if($authors) { - if(! is_array($authors)) - $authors = array($authors); - - $sql_options .= " and author_xchan in (" . stringify_array($authors,true) . ") "; - } - - if($owner) { - $sql_options .= " and owner_xchan = '" . dbesc($owner) . "' "; - } - - - // Fetch tags - $r = q("select term, count(term) as total from term left join item on term.oid = item.id - where term.uid = %d and term.ttype = %d - and otype = %d and item_type = %d - $sql_options $item_normal - group by term order by total desc %s", - intval($uid), - intval($type), - intval(TERM_OBJ_POST), - intval(ITEM_TYPE_ARTICLE), - ((intval($count)) ? "limit $count" : '') - ); - - if(! $r) - return []; - - return Zotlabs\Text\Tagadelic::calc($r); - -} - - - - -function pubtagblock($mode,$limit,$recent = 0,$safemode = 1, $type = TERM_HASHTAG) { - $o = ''; - - $r = pub_tagadelic($mode,$limit,$recent,$safemode,$type); - - $link = z_root() . '/pubstream'; - - if($r) { - $o = '

                    ' . (($recent) ? t('Popular Tags') : t('Tags')) . '

                    '; - foreach($r as $rr) { - $o .= '#'.$rr[0].' ' . "\r\n"; - } - $o .= '
                    '; - } - - return $o; -} - -function pub_tagadelic($mode,$limit,$recent,$safemode,$type) { - - - $item_normal = item_normal(); - $count = intval($limit); - - if (intval($mode) === PUBLIC_STREAM_SITE) { - $uids = " and item_private = 0 and item_wall = 1 "; - } - else { - $sys = get_sys_channel(); - $uids = " and item.uid = " . intval($sys['channel_id']) . " "; - $sql_extra = " and item_private = 0 "; + if (! perm_is_allowed($uid, get_observer_hash(), 'view_stream')) { + return []; } - if ($recent) { - $sql_extra .= " and item.created > '" . datetime_convert('UTC','UTC', 'now - ' . intval($recent) . ' days ') . "' "; - } - if ($safemode) { - $unsafetags = get_config('system','unsafepubtags', [ 'boobs', 'bot', 'rss', 'girl','girls', 'nsfw', 'sexy', 'nude' ]); - if($unsafetags) { - $sql_extra .= " and not term.term in ( " . stringify_array($unsafetags,true) . ") "; - } - } - + $item_normal = item_normal(); + $sql_options = item_permissions_sql($uid); + $count = intval($count); - // Fetch tags - $r = q("select term, count(term) as total from term left join item on term.oid = item.id + if ($flags) { + if ($flags === 'wall') { + $sql_options .= " and item_wall = 1 "; + } + } + + if ($authors) { + if (! is_array($authors)) { + $authors = array($authors); + } + + $sql_options .= " and author_xchan in (" . stringify_array($authors, true) . ") "; + } + + if ($owner) { + $sql_options .= " and owner_xchan = '" . dbesc($owner) . "' "; + } + + // Fetch tags + $r = q( + "select term, count(term) as total from term left join item on term.oid = item.id + where term.uid = %d and term.ttype = %d + and otype = %d and item_type = %d + $sql_options $item_normal + group by term order by total desc %s", + intval($uid), + intval($type), + intval(TERM_OBJ_POST), + intval($restrict), + ((intval($count)) ? "limit $count" : '') + ); + + + if (! $r) { + return []; + } + + return Zotlabs\Text\Tagadelic::calc($r); +} + + + +function card_tagadelic($uid, $count = 0, $authors = '', $owner = '', $flags = 0, $restrict = 0, $type = TERM_CATEGORY) +{ + + require_once('include/security.php'); + + if (! perm_is_allowed($uid, get_observer_hash(), 'view_pages')) { + return []; + } + + $item_normal = " and item.item_hidden = 0 and item.item_deleted = 0 and item.item_unpublished = 0 and item.item_delayed = 0 and item.item_pending_remove = 0 + and item.item_blocked = 0 and item.obj_type != 'http://purl.org/zot/activity/file' "; + + $sql_options = item_permissions_sql($uid); + $count = intval($count); + + if ($flags) { + if ($flags === 'wall') { + $sql_options .= " and item_wall = 1 "; + } + } + + if ($authors) { + if (! is_array($authors)) { + $authors = array($authors); + } + + $sql_options .= " and author_xchan in (" . stringify_array($authors, true) . ") "; + } + + if ($owner) { + $sql_options .= " and owner_xchan = '" . dbesc($owner) . "' "; + } + + + // Fetch tags + + $r = q( + "select term, count(term) as total from term left join item on term.oid = item.id + where term.uid = %d and term.ttype = %d + and otype = %d and item_type = %d + $sql_options $item_normal + group by term order by total desc %s", + intval($uid), + intval($type), + intval(TERM_OBJ_POST), + intval(ITEM_TYPE_CARD), + ((intval($count)) ? "limit $count" : '') + ); + + if (! $r) { + return []; + } + + return Zotlabs\Text\Tagadelic::calc($r); +} + +function article_tagadelic($uid, $count = 0, $authors = '', $owner = '', $flags = 0, $restrict = 0, $type = TERM_CATEGORY) +{ + + require_once('include/security.php'); + + if (! perm_is_allowed($uid, get_observer_hash(), 'view_pages')) { + return []; + } + + + $item_normal = " and item.item_hidden = 0 and item.item_deleted = 0 and item.item_unpublished = 0 and item.item_delayed = 0 and item.item_pending_remove = 0 + and item.item_blocked = 0 and item.obj_type != 'http://purl.org/zot/activity/file' "; + + $sql_options = item_permissions_sql($uid); + $count = intval($count); + + if ($flags) { + if ($flags === 'wall') { + $sql_options .= " and item_wall = 1 "; + } + } + + if ($authors) { + if (! is_array($authors)) { + $authors = array($authors); + } + + $sql_options .= " and author_xchan in (" . stringify_array($authors, true) . ") "; + } + + if ($owner) { + $sql_options .= " and owner_xchan = '" . dbesc($owner) . "' "; + } + + + // Fetch tags + $r = q( + "select term, count(term) as total from term left join item on term.oid = item.id + where term.uid = %d and term.ttype = %d + and otype = %d and item_type = %d + $sql_options $item_normal + group by term order by total desc %s", + intval($uid), + intval($type), + intval(TERM_OBJ_POST), + intval(ITEM_TYPE_ARTICLE), + ((intval($count)) ? "limit $count" : '') + ); + + if (! $r) { + return []; + } + + return Zotlabs\Text\Tagadelic::calc($r); +} + + + + +function pubtagblock($mode, $limit, $recent = 0, $safemode = 1, $type = TERM_HASHTAG) +{ + $o = ''; + + $r = pub_tagadelic($mode, $limit, $recent, $safemode, $type); + + $link = z_root() . '/pubstream'; + + if ($r) { + $o = '

                    ' . (($recent) ? t('Popular Tags') : t('Tags')) . '

                    '; + foreach ($r as $rr) { + $o .= '#' . $rr[0] . ' ' . "\r\n"; + } + $o .= '
                    '; + } + + return $o; +} + +function pub_tagadelic($mode, $limit, $recent, $safemode, $type) +{ + + + $item_normal = item_normal(); + $count = intval($limit); + + if (intval($mode) === PUBLIC_STREAM_SITE) { + $uids = " and item_private = 0 and item_wall = 1 "; + } else { + $sys = get_sys_channel(); + $uids = " and item.uid = " . intval($sys['channel_id']) . " "; + $sql_extra = " and item_private = 0 "; + } + + if ($recent) { + $sql_extra .= " and item.created > '" . datetime_convert('UTC', 'UTC', 'now - ' . intval($recent) . ' days ') . "' "; + } + + if ($safemode) { + $unsafetags = get_config('system', 'unsafepubtags', [ 'boobs', 'bot', 'rss', 'girl','girls', 'nsfw', 'sexy', 'nude' ]); + if ($unsafetags) { + $sql_extra .= " and not term.term in ( " . stringify_array($unsafetags, true) . ") "; + } + } + + + // Fetch tags + $r = q( + "select term, count(term) as total from term left join item on term.oid = item.id where term.ttype = %d and otype = %d and item_type = %d $sql_extra $uids $item_normal group by term order by total desc %s", - intval($type), - intval(TERM_OBJ_POST), - intval(ITEM_TYPE_POST), - ((intval($count)) ? "limit $count" : '') - ); - - if (! $r) { - return []; - } - return Zotlabs\Text\Tagadelic::calc($r); + intval($type), + intval(TERM_OBJ_POST), + intval(ITEM_TYPE_POST), + ((intval($count)) ? "limit $count" : '') + ); + if (! $r) { + return []; + } + return Zotlabs\Text\Tagadelic::calc($r); } -function dir_tagadelic($count = 0, $hub = '', $type = 0, $safe = '') { +function dir_tagadelic($count = 0, $hub = '', $type = 0, $safe = '') +{ - $count = intval($count); + $count = intval($count); - $sql_extra = EMPTY_STR; + $sql_extra = EMPTY_STR; - if($type) { - return []; - } + if ($type) { + return []; + } - if($hub) { - $r = q("select xtag_term as term, count(xtag_term) as total from xtag + if ($hub) { + $r = q( + "select xtag_term as term, count(xtag_term) as total from xtag left join hubloc on xtag_hash = hubloc_hash where xtag_flags = 0 and xtag_hash in (select hubloc_hash from hubloc where hubloc_host = '%s' ) $sql_extra group by xtag_term order by total desc %s", - dbesc($hub), - ((intval($count)) ? "limit $count" : '') - ); - } - else { - $r = q("select xtag_term as term, count(xtag_term) as total from xtag left join xchan on xtag_hash = xchan_hash where xtag_flags = 0 + dbesc($hub), + ((intval($count)) ? "limit $count" : '') + ); + } else { + $r = q( + "select xtag_term as term, count(xtag_term) as total from xtag left join xchan on xtag_hash = xchan_hash where xtag_flags = 0 $sql_extra $safe group by xtag_term order by total desc %s", - ((intval($count)) ? "limit $count" : '') - ); - } - if(! $r) - return []; + ((intval($count)) ? "limit $count" : '') + ); + } + if (! $r) { + return []; + } - return Zotlabs\Text\Tagadelic::calc($r); + return Zotlabs\Text\Tagadelic::calc($r); } -function app_tagblock($link,$count = 0) { - $o = ''; +function app_tagblock($link, $count = 0) +{ + $o = ''; - $r = app_tagadelic($count); + $r = app_tagadelic($count); - if($r) { - $o = '

                    ' . t('Categories') . '

                    '; - foreach($r as $rr) { - $o .= ''.$rr[0].' ' . "\r\n"; - } - $o .= '
                    '; - } + if ($r) { + $o = '

                    ' . t('Categories') . '

                    '; + foreach ($r as $rr) { + $o .= '' . $rr[0] . ' ' . "\r\n"; + } + $o .= '
                    '; + } - return $o; + return $o; } -function app_tagadelic($count = 0) { +function app_tagadelic($count = 0) +{ - if(! local_channel()) - return ''; + if (! local_channel()) { + return ''; + } - $count = intval($count); + $count = intval($count); - // Fetch tags - $r = q("select term, count(term) as total from term left join app on term.uid = app_channel where term.uid = %d + // Fetch tags + $r = q( + "select term, count(term) as total from term left join app on term.uid = app_channel where term.uid = %d and term.otype = %d group by term order by total desc %s", - intval(local_channel()), - intval(TERM_OBJ_APP), - ((intval($count)) ? "limit $count" : '') - ); + intval(local_channel()), + intval(TERM_OBJ_APP), + ((intval($count)) ? "limit $count" : '') + ); - if(! $r) - return []; - - return Zotlabs\Text\Tagadelic::calc($r); + if (! $r) { + return []; + } + return Zotlabs\Text\Tagadelic::calc($r); } -function tagblock($link,$uid,$count = 0,$authors = '',$owner = '', $flags = 0,$restrict = 0,$type = TERM_HASHTAG) { - $o = ''; +function tagblock($link, $uid, $count = 0, $authors = '', $owner = '', $flags = 0, $restrict = 0, $type = TERM_HASHTAG) +{ + $o = ''; - $r = tagadelic($uid,$count,$authors,$owner, $flags,$restrict,$type); + $r = tagadelic($uid, $count, $authors, $owner, $flags, $restrict, $type); - if($r) { - $o = '

                    ' . t('Tags') . '

                    '; - foreach($r as $rr) { - $o .= '#'.$rr[0].' ' . "\r\n"; - } - $o .= '
                    '; - } + if ($r) { + $o = '

                    ' . t('Tags') . '

                    '; + foreach ($r as $rr) { + $o .= '#' . $rr[0] . ' ' . "\r\n"; + } + $o .= '
                    '; + } - return $o; + return $o; } -function wtagblock($uid,$count = 0,$authors = '',$owner = '', $flags = 0,$restrict = 0,$type = TERM_HASHTAG) { - $o = ''; +function wtagblock($uid, $count = 0, $authors = '', $owner = '', $flags = 0, $restrict = 0, $type = TERM_HASHTAG) +{ + $o = ''; - $r = tagadelic($uid,$count,$authors,$owner, $flags,$restrict,$type); + $r = tagadelic($uid, $count, $authors, $owner, $flags, $restrict, $type); - if($r) { - $c = q("select channel_address from channel where channel_id = %d limit 1", - intval($uid) - ); - - $o = '

                    ' . t('Tags') . '

                    '; - foreach($r as $rr) { - $o .= '#'.$rr[0].' ' . "\r\n"; - } - $o .= '
                    '; - } + if ($r) { + $c = q( + "select channel_address from channel where channel_id = %d limit 1", + intval($uid) + ); - return $o; + $o = '

                    ' . t('Tags') . '

                    '; + foreach ($r as $rr) { + $o .= '#' . $rr[0] . ' ' . "\r\n"; + } + $o .= '
                    '; + } + + return $o; } -function catblock($uid,$count = 0,$authors = '',$owner = '', $flags = 0,$restrict = 0,$type = TERM_CATEGORY) { - $o = ''; +function catblock($uid, $count = 0, $authors = '', $owner = '', $flags = 0, $restrict = 0, $type = TERM_CATEGORY) +{ + $o = ''; - if(! Apps::system_app_installed($uid,'Categories')) { - return $o; - } + if (! Apps::system_app_installed($uid, 'Categories')) { + return $o; + } - $r = tagadelic($uid,$count,$authors,$owner,$flags,$restrict,$type); + $r = tagadelic($uid, $count, $authors, $owner, $flags, $restrict, $type); - if($r) { - $c = q("select channel_address from channel where channel_id = %d limit 1", - intval($uid) - ); - - $o = '

                    ' . t('Categories') . '

                    '; - foreach($r as $rr) { - $o .= ''.$rr[0].' ' . "\r\n"; - } - $o .= '
                    '; - } + if ($r) { + $c = q( + "select channel_address from channel where channel_id = %d limit 1", + intval($uid) + ); - return $o; + $o = '

                    ' . t('Categories') . '

                    '; + foreach ($r as $rr) { + $o .= '' . $rr[0] . ' ' . "\r\n"; + } + $o .= '
                    '; + } + + return $o; } -function card_catblock($uid,$count = 0,$authors = '',$owner = '', $flags = 0,$restrict = 0,$type = TERM_CATEGORY) { - $o = ''; +function card_catblock($uid, $count = 0, $authors = '', $owner = '', $flags = 0, $restrict = 0, $type = TERM_CATEGORY) +{ + $o = ''; - $r = card_tagadelic($uid,$count,$authors,$owner,$flags,$restrict,$type); + $r = card_tagadelic($uid, $count, $authors, $owner, $flags, $restrict, $type); - if($r) { - $c = q("select channel_address from channel where channel_id = %d limit 1", - intval($uid) - ); - - $o = '

                    ' . t('Categories') . '

                    '; - foreach($r as $rr) { - $o .= ''.$rr[0].' ' . "\r\n"; - } - $o .= '
                    '; - } + if ($r) { + $c = q( + "select channel_address from channel where channel_id = %d limit 1", + intval($uid) + ); - return $o; + $o = '

                    ' . t('Categories') . '

                    '; + foreach ($r as $rr) { + $o .= '' . $rr[0] . ' ' . "\r\n"; + } + $o .= '
                    '; + } + + return $o; } -function article_catblock($uid,$count = 0,$authors = '',$owner = '', $flags = 0,$restrict = 0,$type = TERM_CATEGORY) { - $o = ''; +function article_catblock($uid, $count = 0, $authors = '', $owner = '', $flags = 0, $restrict = 0, $type = TERM_CATEGORY) +{ + $o = ''; - $r = article_tagadelic($uid,$count,$authors,$owner,$flags,$restrict,$type); + $r = article_tagadelic($uid, $count, $authors, $owner, $flags, $restrict, $type); - if($r) { - $c = q("select channel_address from channel where channel_id = %d limit 1", - intval($uid) - ); - - $o = '

                    ' . t('Categories') . '

                    '; - foreach($r as $rr) { - $o .= ''.$rr[0].' ' . "\r\n"; - } - $o .= '
                    '; - } + if ($r) { + $c = q( + "select channel_address from channel where channel_id = %d limit 1", + intval($uid) + ); - return $o; + $o = '

                    ' . t('Categories') . '

                    '; + foreach ($r as $rr) { + $o .= '' . $rr[0] . ' ' . "\r\n"; + } + $o .= '
                    '; + } + + return $o; } -function dir_tagblock($link,$r) { - $o = ''; +function dir_tagblock($link, $r) +{ + $o = ''; - $observer = get_observer_hash(); + $observer = get_observer_hash(); - if(! $r) - $r = App::$data['directory_keywords']; + if (! $r) { + $r = App::$data['directory_keywords']; + } - if($r) { - $o = '

                    ' . t('Keywords') . '

                    '; - foreach($r as $rr) { - $o .= ''.$rr['term'].' ' . "\r\n"; - } - $o .= '
                    '; - } + if ($r) { + $o = '

                    ' . t('Keywords') . '

                    '; + foreach ($r as $rr) { + $o .= '' . $rr['term'] . ' ' . "\r\n"; + } + $o .= '
                    '; + } - return $o; + return $o; } - /** - * verbs: [0] = first person singular, e.g. "I want", [1] = 3rd person singular, e.g. "Bill wants" - * We use the first person form when creating an activity, but the third person for use in activities - * FIXME: There is no accounting for verb gender for languages where this is significant. We may eventually - * require obj_verbs() to provide full conjugations and specify which form to use in the $_REQUEST params to this module. - */ + /** + * verbs: [0] = first person singular, e.g. "I want", [1] = 3rd person singular, e.g. "Bill wants" + * We use the first person form when creating an activity, but the third person for use in activities + * FIXME: There is no accounting for verb gender for languages where this is significant. We may eventually + * require obj_verbs() to provide full conjugations and specify which form to use in the $_REQUEST params to this module. + */ -function obj_verbs() { - $verbs = array( - 'has' => array( t('have'), t('has')), - 'wants' => array( t('want'), t('wants')), - 'likes' => array( t('like'), t('likes')), - 'dislikes' => array( t('dislike'), t('dislikes')), - ); +function obj_verbs() +{ + $verbs = array( + 'has' => array( t('have'), t('has')), + 'wants' => array( t('want'), t('wants')), + 'likes' => array( t('like'), t('likes')), + 'dislikes' => array( t('dislike'), t('dislikes')), + ); - $arr = array('verbs' => $verbs); - call_hooks('obj_verbs', $arr); + $arr = array('verbs' => $verbs); + call_hooks('obj_verbs', $arr); - return $arr['verbs']; + return $arr['verbs']; } -function obj_verb_selector($current = '') { - $verbs = obj_verbs(); - $o = ''; +function obj_verb_selector($current = '') +{ + $verbs = obj_verbs(); + $o = ''; - return $o; + return $o; } -function get_things($profile_hash,$uid) { +function get_things($profile_hash, $uid) +{ - $sql_extra = (($profile_hash) ? " and obj_page = '" . $profile_hash . "' " : ''); + $sql_extra = (($profile_hash) ? " and obj_page = '" . $profile_hash . "' " : ''); - $r = q("select * from obj where obj_channel = %d and obj_type = %d $sql_extra order by obj_verb, obj_term", - intval($uid), - intval(TERM_OBJ_THING) - ); + $r = q( + "select * from obj where obj_channel = %d and obj_type = %d $sql_extra order by obj_verb, obj_term", + intval($uid), + intval(TERM_OBJ_THING) + ); - $things = $sorted_things = null; + $things = $sorted_things = null; - $profile_hashes = []; + $profile_hashes = []; - if($r) { + if ($r) { + // if no profile_hash was specified (display on profile page mode), match each of the things to a profile name + // (list all my things mode). This is harder than it sounds. - // if no profile_hash was specified (display on profile page mode), match each of the things to a profile name - // (list all my things mode). This is harder than it sounds. + foreach ($r as $rr) { + $rr['profile_name'] = ''; + if (! in_array($rr['obj_obj'], $profile_hashes)) { + $profile_hashes[] = $rr['obj_obj']; + } + } + if (! $profile_hash) { + $exp = stringify_array($profile_hashes, true); + $p = q("select profile_guid as hash, profile_name as name from profile where profile_guid in ( $exp ) "); + if ($p) { + foreach ($r as $rr) { + foreach ($p as $pp) { + if ($rr['obj_page'] == $pp['hash']) { + $rr['profile_name'] == $pp['name']; + } + } + } + } + } - foreach($r as $rr) { - $rr['profile_name'] = ''; - if(! in_array($rr['obj_obj'],$profile_hashes)) - $profile_hashes[] = $rr['obj_obj']; - } - if(! $profile_hash) { - $exp = stringify_array($profile_hashes,true); - $p = q("select profile_guid as hash, profile_name as name from profile where profile_guid in ( $exp ) "); - if($p) { - foreach($r as $rr) { - foreach($p as $pp) { - if($rr['obj_page'] == $pp['hash']) { - $rr['profile_name'] == $pp['name']; - } - } - } - } - } + $things = []; - $things = []; + // Use the system obj_verbs array as a sort key, since we don't really + // want an alphabetic sort. To change the order, use a plugin to + // alter the obj_verbs() array or alter it in code. Unknown verbs come + // after the known ones - in no particular order. - // Use the system obj_verbs array as a sort key, since we don't really - // want an alphabetic sort. To change the order, use a plugin to - // alter the obj_verbs() array or alter it in code. Unknown verbs come - // after the known ones - in no particular order. - - $v = obj_verbs(); - foreach($v as $k => $foo) - $things[$k] = null; - foreach($r as $rr) { - - $l = q("select xchan_name, xchan_photo_s, xchan_url from likes left join xchan on liker = xchan_hash where + $v = obj_verbs(); + foreach ($v as $k => $foo) { + $things[$k] = null; + } + foreach ($r as $rr) { + $l = q( + "select xchan_name, xchan_photo_s, xchan_url from likes left join xchan on liker = xchan_hash where target_type = '%s' and target_id = '%s' and channel_id = %d", - dbesc(ACTIVITY_OBJ_THING), - dbesc($rr['obj_obj']), - intval($uid) - ); + dbesc(ACTIVITY_OBJ_THING), + dbesc($rr['obj_obj']), + intval($uid) + ); - for($x = 0; $x < count($l); $x ++) { - $l[$x]['xchan_url'] = zid($l[$x]['xchan_url']); - $l[$x]['xchan_photo_s'] = zid($l[$x]['xchan_photo_s']); - } - if(! $things[$rr['obj_verb']]) - $things[$rr['obj_verb']] = []; + for ($x = 0; $x < count($l); $x++) { + $l[$x]['xchan_url'] = zid($l[$x]['xchan_url']); + $l[$x]['xchan_photo_s'] = zid($l[$x]['xchan_photo_s']); + } + if (! $things[$rr['obj_verb']]) { + $things[$rr['obj_verb']] = []; + } - $things[$rr['obj_verb']][] = array('term' => $rr['obj_term'],'url' => $rr['obj_url'],'img' => $rr['obj_imgurl'], 'editurl' => z_root() . '/thing/' . $rr['obj_obj'], 'profile' => $rr['profile_name'],'term_hash' => $rr['obj_obj'], 'likes' => $l,'like_count' => count($l),'like_label' => tt('Like','Likes',count($l),'noun')); - } - $sorted_things = []; - if($things) { - foreach($things as $k => $v) { - if(is_array($things[$k])) { - $sorted_things[$k] = $v; - } - } - } - } + $things[$rr['obj_verb']][] = array('term' => $rr['obj_term'],'url' => $rr['obj_url'],'img' => $rr['obj_imgurl'], 'editurl' => z_root() . '/thing/' . $rr['obj_obj'], 'profile' => $rr['profile_name'],'term_hash' => $rr['obj_obj'], 'likes' => $l,'like_count' => count($l),'like_label' => tt('Like', 'Likes', count($l), 'noun')); + } + $sorted_things = []; + if ($things) { + foreach ($things as $k => $v) { + if (is_array($things[$k])) { + $sorted_things[$k] = $v; + } + } + } + } //logger('things: ' . print_r($sorted_things,true)); - return $sorted_things; + return $sorted_things; } diff --git a/include/text.php b/include/text.php index 067b841e4..02a82abc1 100644 --- a/include/text.php +++ b/include/text.php @@ -1,8 +1,9 @@ $s, - 'params' => $r - ]; +function replace_macros($s, $r) +{ + $arr = [ + 'template' => $s, + 'params' => $r + ]; - /** - * @hooks replace_macros - * * \e string \b template - * * \e array \b params - */ + /** + * @hooks replace_macros + * * \e string \b template + * * \e array \b params + */ - call_hooks('replace_macros', $arr); + call_hooks('replace_macros', $arr); - $t = App::template_engine(); + $t = App::template_engine(); - try { - $output = $t->replace_macros($arr['template'], $arr['params']); - } - catch (Exception $e) { - btlogger("Unable to render template: " . $e->getMessage()); - $output = "

                    ERROR: there was an error creating the output.

                    "; - } + try { + $output = $t->replace_macros($arr['template'], $arr['params']); + } catch (Exception $e) { + btlogger("Unable to render template: " . $e->getMessage()); + $output = "

                    ERROR: there was an error creating the output.

                    "; + } - return $output; + return $output; } /** @@ -67,15 +66,16 @@ function replace_macros($s, $r) { */ -define('RANDOM_STRING_HEX', 0x00 ); -define('RANDOM_STRING_TEXT', 0x01 ); +define('RANDOM_STRING_HEX', 0x00); +define('RANDOM_STRING_TEXT', 0x01); -function random_string($size = 64, $type = RANDOM_STRING_HEX) { - // generate a bit of entropy and run it through the whirlpool - $s = hash('whirlpool', (string) rand() . uniqid(rand(),true) . (string) rand(),(($type == RANDOM_STRING_TEXT) ? true : false)); - $s = (($type == RANDOM_STRING_TEXT) ? str_replace("\n","",base64url_encode($s,true)) : $s); +function random_string($size = 64, $type = RANDOM_STRING_HEX) +{ + // generate a bit of entropy and run it through the whirlpool + $s = hash('whirlpool', (string) rand() . uniqid(rand(), true) . (string) rand(), (($type == RANDOM_STRING_TEXT) ? true : false)); + $s = (($type == RANDOM_STRING_TEXT) ? str_replace("\n", "", base64url_encode($s, true)) : $s); - return(substr($s, 0, $size)); + return(substr($s, 0, $size)); } /** @@ -86,8 +86,9 @@ function random_string($size = 64, $type = RANDOM_STRING_HEX) { * @return string Filtered string * */ -function notags($string) { - return(str_replace(array("<",">"), array('[',']'), $string)); +function notags($string) +{ + return(str_replace(array("<",">"), array('[',']'), $string)); } @@ -99,40 +100,46 @@ function notags($string) { * * @return string */ -function escape_tags($string) { - return(htmlspecialchars($string, ENT_COMPAT, 'UTF-8', false)); +function escape_tags($string) +{ + return(htmlspecialchars($string, ENT_COMPAT, 'UTF-8', false)); } -function z_input_filter($s, $type = 'text/x-multicode', $allow_code = false) { +function z_input_filter($s, $type = 'text/x-multicode', $allow_code = false) +{ - if (in_array($type, [ 'text/bbcode', 'text/x-multicode' ])) { - return (multicode_purify($s)); - } - if($type == 'text/plain') - return escape_tags($s); - if($type == 'application/x-pdl') - return escape_tags($s); + if (in_array($type, [ 'text/bbcode', 'text/x-multicode' ])) { + return (multicode_purify($s)); + } + if ($type == 'text/plain') { + return escape_tags($s); + } + if ($type == 'application/x-pdl') { + return escape_tags($s); + } - if(App::$is_sys) { - return $s; - } + if (App::$is_sys) { + return $s; + } - if($allow_code) { - if($type === 'text/markdown') - return htmlspecialchars($s,ENT_QUOTES); - return $s; - } + if ($allow_code) { + if ($type === 'text/markdown') { + return htmlspecialchars($s, ENT_QUOTES); + } + return $s; + } - if($type === 'text/markdown') { - $x = new MarkdownSoap($s); - return $x->clean(); - } + if ($type === 'text/markdown') { + $x = new MarkdownSoap($s); + return $x->clean(); + } - if($type === 'text/html') - return purify_html($s); + if ($type === 'text/html') { + return purify_html($s); + } - return escape_tags($s); + return escape_tags($s); } @@ -145,180 +152,181 @@ function z_input_filter($s, $type = 'text/x-multicode', $allow_code = false) { * @see HTMLPurifier * * @param string $s raw HTML - * @param boolean $allow_position allow CSS position + * @param bool $allow_position allow CSS position * @return string standards compliant filtered HTML */ -function purify_html($s, $opts = []) { +function purify_html($s, $opts = []) +{ - $config = HTMLPurifier_Config::createDefault(); - $config->set('Cache.DefinitionImpl', null); - $config->set('Attr.EnableID', true); + $config = HTMLPurifier_Config::createDefault(); + $config->set('Cache.DefinitionImpl', null); + $config->set('Attr.EnableID', true); - // disable Unicode version of RTL over-ride - $s = str_replace([ '‮', '‮', html_entity_decode('‮', ENT_QUOTES,'UTF-8') ],[ '','','' ],$s); + // disable Unicode version of RTL over-ride + $s = str_replace([ '‮', '‮', html_entity_decode('‮', ENT_QUOTES, 'UTF-8') ], [ '','','' ], $s); - // This will escape invalid tags in the output instead of removing. - // This is necessary for mixed format (text+bbcode+html+markdown) messages or - // some angle brackets in plaintext may get stripped if they look like an HTML tag - - if (in_array('escape',$opts)) { - $config->set('Core.EscapeInvalidChildren', true); - $config->set('Core.EscapeInvalidTags', true); - } + // This will escape invalid tags in the output instead of removing. + // This is necessary for mixed format (text+bbcode+html+markdown) messages or + // some angle brackets in plaintext may get stripped if they look like an HTML tag - // If enabled, target=blank attributes are added to all links. - //$config->set('HTML.TargetBlank', true); - //$config->set('Attr.AllowedFrameTargets', ['_blank', '_self', '_parent', '_top']); - // restore old behavior of HTMLPurifier < 4.8, only used when targets allowed at all - // do not add rel="noreferrer" to all links with target attributes - //$config->set('HTML.TargetNoreferrer', false); - // do not add noopener rel attributes to links which have a target attribute associated with them - //$config->set('HTML.TargetNoopener', false); + if (in_array('escape', $opts)) { + $config->set('Core.EscapeInvalidChildren', true); + $config->set('Core.EscapeInvalidTags', true); + } - //Allow some custom data- attributes used by built-in libs. - //In this way members which do not have allowcode set can still use the built-in js libs in webpages to some extent. + // If enabled, target=blank attributes are added to all links. + //$config->set('HTML.TargetBlank', true); + //$config->set('Attr.AllowedFrameTargets', ['_blank', '_self', '_parent', '_top']); + // restore old behavior of HTMLPurifier < 4.8, only used when targets allowed at all + // do not add rel="noreferrer" to all links with target attributes + //$config->set('HTML.TargetNoreferrer', false); + // do not add noopener rel attributes to links which have a target attribute associated with them + //$config->set('HTML.TargetNoopener', false); - $def = $config->getHTMLDefinition(true); + //Allow some custom data- attributes used by built-in libs. + //In this way members which do not have allowcode set can still use the built-in js libs in webpages to some extent. - //data- attributes used by the foundation library + $def = $config->getHTMLDefinition(true); - // f6 navigation + //data- attributes used by the foundation library - //dropdown menu - $def->info_global_attr['data-dropdown-menu'] = new HTMLPurifier_AttrDef_Text; - //drilldown menu - $def->info_global_attr['data-drilldown'] = new HTMLPurifier_AttrDef_Text; - //accordion menu - $def->info_global_attr['data-accordion-menu'] = new HTMLPurifier_AttrDef_Text; - //responsive navigation - $def->info_global_attr['data-responsive-menu'] = new HTMLPurifier_AttrDef_Text; - $def->info_global_attr['data-responsive-toggle'] = new HTMLPurifier_AttrDef_Text; - //magellan - $def->info_global_attr['data-magellan'] = new HTMLPurifier_AttrDef_Text; - $def->info_global_attr['data-magellan-target'] = new HTMLPurifier_AttrDef_Text; + // f6 navigation - // f6 containers + //dropdown menu + $def->info_global_attr['data-dropdown-menu'] = new HTMLPurifier_AttrDef_Text(); + //drilldown menu + $def->info_global_attr['data-drilldown'] = new HTMLPurifier_AttrDef_Text(); + //accordion menu + $def->info_global_attr['data-accordion-menu'] = new HTMLPurifier_AttrDef_Text(); + //responsive navigation + $def->info_global_attr['data-responsive-menu'] = new HTMLPurifier_AttrDef_Text(); + $def->info_global_attr['data-responsive-toggle'] = new HTMLPurifier_AttrDef_Text(); + //magellan + $def->info_global_attr['data-magellan'] = new HTMLPurifier_AttrDef_Text(); + $def->info_global_attr['data-magellan-target'] = new HTMLPurifier_AttrDef_Text(); - //accordion - $def->info_global_attr['data-accordion'] = new HTMLPurifier_AttrDef_Text; - $def->info_global_attr['data-accordion-item'] = new HTMLPurifier_AttrDef_Text; - $def->info_global_attr['data-tab-content'] = new HTMLPurifier_AttrDef_Text; - //dropdown - $def->info_global_attr['data-dropdown'] = new HTMLPurifier_AttrDef_Text; - //off-canvas - $def->info_global_attr['data-off-canvas-wrapper'] = new HTMLPurifier_AttrDef_Text; - $def->info_global_attr['data-off-canvas'] = new HTMLPurifier_AttrDef_Text; - $def->info_global_attr['data-off-canvas-content'] = new HTMLPurifier_AttrDef_Text; - //reveal - $def->info_global_attr['data-reveal'] = new HTMLPurifier_AttrDef_Text; - //tabs - $def->info_global_attr['data-tabs'] = new HTMLPurifier_AttrDef_Text; - $def->info_global_attr['data-tabs-content'] = new HTMLPurifier_AttrDef_Text; + // f6 containers - // f6 media + //accordion + $def->info_global_attr['data-accordion'] = new HTMLPurifier_AttrDef_Text(); + $def->info_global_attr['data-accordion-item'] = new HTMLPurifier_AttrDef_Text(); + $def->info_global_attr['data-tab-content'] = new HTMLPurifier_AttrDef_Text(); + //dropdown + $def->info_global_attr['data-dropdown'] = new HTMLPurifier_AttrDef_Text(); + //off-canvas + $def->info_global_attr['data-off-canvas-wrapper'] = new HTMLPurifier_AttrDef_Text(); + $def->info_global_attr['data-off-canvas'] = new HTMLPurifier_AttrDef_Text(); + $def->info_global_attr['data-off-canvas-content'] = new HTMLPurifier_AttrDef_Text(); + //reveal + $def->info_global_attr['data-reveal'] = new HTMLPurifier_AttrDef_Text(); + //tabs + $def->info_global_attr['data-tabs'] = new HTMLPurifier_AttrDef_Text(); + $def->info_global_attr['data-tabs-content'] = new HTMLPurifier_AttrDef_Text(); - //orbit - $def->info_global_attr['data-orbit'] = new HTMLPurifier_AttrDef_Text; - $def->info_global_attr['data-slide'] = new HTMLPurifier_AttrDef_Text; - //tooltip - $def->info_global_attr['data-tooltip'] = new HTMLPurifier_AttrDef_Text; + // f6 media - // f6 plugins + //orbit + $def->info_global_attr['data-orbit'] = new HTMLPurifier_AttrDef_Text(); + $def->info_global_attr['data-slide'] = new HTMLPurifier_AttrDef_Text(); + //tooltip + $def->info_global_attr['data-tooltip'] = new HTMLPurifier_AttrDef_Text(); - //abide - the use is pointless since we can't do anything with forms + // f6 plugins - //equalizer - $def->info_global_attr['data-equalizer'] = new HTMLPurifier_AttrDef_Text; - $def->info_global_attr['data-equalizer-watch'] = new HTMLPurifier_AttrDef_Text; + //abide - the use is pointless since we can't do anything with forms - //interchange - potentially dangerous since it can load content + //equalizer + $def->info_global_attr['data-equalizer'] = new HTMLPurifier_AttrDef_Text(); + $def->info_global_attr['data-equalizer-watch'] = new HTMLPurifier_AttrDef_Text(); - //toggler - $def->info_global_attr['data-toggler'] = new HTMLPurifier_AttrDef_Text; + //interchange - potentially dangerous since it can load content - //sticky - $def->info_global_attr['data-sticky'] = new HTMLPurifier_AttrDef_Text; - $def->info_global_attr['data-sticky-container'] = new HTMLPurifier_AttrDef_Text; + //toggler + $def->info_global_attr['data-toggler'] = new HTMLPurifier_AttrDef_Text(); - // f6 common + //sticky + $def->info_global_attr['data-sticky'] = new HTMLPurifier_AttrDef_Text(); + $def->info_global_attr['data-sticky-container'] = new HTMLPurifier_AttrDef_Text(); - $def->info_global_attr['data-options'] = new HTMLPurifier_AttrDef_Text; - $def->info_global_attr['data-toggle'] = new HTMLPurifier_AttrDef_Text; - $def->info_global_attr['data-close'] = new HTMLPurifier_AttrDef_Text; - $def->info_global_attr['data-open'] = new HTMLPurifier_AttrDef_Text; - $def->info_global_attr['data-position'] = new HTMLPurifier_AttrDef_Text; + // f6 common + + $def->info_global_attr['data-options'] = new HTMLPurifier_AttrDef_Text(); + $def->info_global_attr['data-toggle'] = new HTMLPurifier_AttrDef_Text(); + $def->info_global_attr['data-close'] = new HTMLPurifier_AttrDef_Text(); + $def->info_global_attr['data-open'] = new HTMLPurifier_AttrDef_Text(); + $def->info_global_attr['data-position'] = new HTMLPurifier_AttrDef_Text(); - //data- attributes used by the bootstrap library - $def->info_global_attr['data-dismiss'] = new HTMLPurifier_AttrDef_Text; - $def->info_global_attr['data-target'] = new HTMLPurifier_AttrDef_Text; - $def->info_global_attr['data-toggle'] = new HTMLPurifier_AttrDef_Text; - $def->info_global_attr['data-backdrop'] = new HTMLPurifier_AttrDef_Text; - $def->info_global_attr['data-keyboard'] = new HTMLPurifier_AttrDef_Text; - $def->info_global_attr['data-show'] = new HTMLPurifier_AttrDef_Text; - $def->info_global_attr['data-spy'] = new HTMLPurifier_AttrDef_Text; - $def->info_global_attr['data-offset'] = new HTMLPurifier_AttrDef_Text; - $def->info_global_attr['data-animation'] = new HTMLPurifier_AttrDef_Text; - $def->info_global_attr['data-container'] = new HTMLPurifier_AttrDef_Text; - $def->info_global_attr['data-delay'] = new HTMLPurifier_AttrDef_Text; - $def->info_global_attr['data-placement'] = new HTMLPurifier_AttrDef_Text; - $def->info_global_attr['data-title'] = new HTMLPurifier_AttrDef_Text; - $def->info_global_attr['data-trigger'] = new HTMLPurifier_AttrDef_Text; - $def->info_global_attr['data-content'] = new HTMLPurifier_AttrDef_Text; - $def->info_global_attr['data-trigger'] = new HTMLPurifier_AttrDef_Text; - $def->info_global_attr['data-parent'] = new HTMLPurifier_AttrDef_Text; - $def->info_global_attr['data-ride'] = new HTMLPurifier_AttrDef_Text; - $def->info_global_attr['data-slide-to'] = new HTMLPurifier_AttrDef_Text; - $def->info_global_attr['data-slide'] = new HTMLPurifier_AttrDef_Text; - $def->info_global_attr['data-interval'] = new HTMLPurifier_AttrDef_Text; - $def->info_global_attr['data-pause'] = new HTMLPurifier_AttrDef_Text; - $def->info_global_attr['data-wrap'] = new HTMLPurifier_AttrDef_Text; - $def->info_global_attr['data-offset-top'] = new HTMLPurifier_AttrDef_Text; - $def->info_global_attr['data-offset-bottom'] = new HTMLPurifier_AttrDef_Text; + //data- attributes used by the bootstrap library + $def->info_global_attr['data-dismiss'] = new HTMLPurifier_AttrDef_Text(); + $def->info_global_attr['data-target'] = new HTMLPurifier_AttrDef_Text(); + $def->info_global_attr['data-toggle'] = new HTMLPurifier_AttrDef_Text(); + $def->info_global_attr['data-backdrop'] = new HTMLPurifier_AttrDef_Text(); + $def->info_global_attr['data-keyboard'] = new HTMLPurifier_AttrDef_Text(); + $def->info_global_attr['data-show'] = new HTMLPurifier_AttrDef_Text(); + $def->info_global_attr['data-spy'] = new HTMLPurifier_AttrDef_Text(); + $def->info_global_attr['data-offset'] = new HTMLPurifier_AttrDef_Text(); + $def->info_global_attr['data-animation'] = new HTMLPurifier_AttrDef_Text(); + $def->info_global_attr['data-container'] = new HTMLPurifier_AttrDef_Text(); + $def->info_global_attr['data-delay'] = new HTMLPurifier_AttrDef_Text(); + $def->info_global_attr['data-placement'] = new HTMLPurifier_AttrDef_Text(); + $def->info_global_attr['data-title'] = new HTMLPurifier_AttrDef_Text(); + $def->info_global_attr['data-trigger'] = new HTMLPurifier_AttrDef_Text(); + $def->info_global_attr['data-content'] = new HTMLPurifier_AttrDef_Text(); + $def->info_global_attr['data-trigger'] = new HTMLPurifier_AttrDef_Text(); + $def->info_global_attr['data-parent'] = new HTMLPurifier_AttrDef_Text(); + $def->info_global_attr['data-ride'] = new HTMLPurifier_AttrDef_Text(); + $def->info_global_attr['data-slide-to'] = new HTMLPurifier_AttrDef_Text(); + $def->info_global_attr['data-slide'] = new HTMLPurifier_AttrDef_Text(); + $def->info_global_attr['data-interval'] = new HTMLPurifier_AttrDef_Text(); + $def->info_global_attr['data-pause'] = new HTMLPurifier_AttrDef_Text(); + $def->info_global_attr['data-wrap'] = new HTMLPurifier_AttrDef_Text(); + $def->info_global_attr['data-offset-top'] = new HTMLPurifier_AttrDef_Text(); + $def->info_global_attr['data-offset-bottom'] = new HTMLPurifier_AttrDef_Text(); - //some html5 elements - //Block - $def->addElement('section', 'Block', 'Flow', 'Common'); - $def->addElement('nav', 'Block', 'Flow', 'Common'); - $def->addElement('article', 'Block', 'Flow', 'Common'); - $def->addElement('aside', 'Block', 'Flow', 'Common'); - $def->addElement('header', 'Block', 'Flow', 'Common'); - $def->addElement('footer', 'Block', 'Flow', 'Common'); - //Inline - $def->addElement('button', 'Inline', 'Inline', 'Common'); - $def->addElement('mark', 'Inline', 'Inline', 'Common'); + //some html5 elements + //Block + $def->addElement('section', 'Block', 'Flow', 'Common'); + $def->addElement('nav', 'Block', 'Flow', 'Common'); + $def->addElement('article', 'Block', 'Flow', 'Common'); + $def->addElement('aside', 'Block', 'Flow', 'Common'); + $def->addElement('header', 'Block', 'Flow', 'Common'); + $def->addElement('footer', 'Block', 'Flow', 'Common'); + //Inline + $def->addElement('button', 'Inline', 'Inline', 'Common'); + $def->addElement('mark', 'Inline', 'Inline', 'Common'); - if(in_array('allow_position', $opts)) { - $cssDefinition = $config->getCSSDefinition(); + if (in_array('allow_position', $opts)) { + $cssDefinition = $config->getCSSDefinition(); - $cssDefinition->info['position'] = new HTMLPurifier_AttrDef_Enum(array('absolute', 'fixed', 'relative', 'static', 'inherit'), false); + $cssDefinition->info['position'] = new HTMLPurifier_AttrDef_Enum(array('absolute', 'fixed', 'relative', 'static', 'inherit'), false); - $cssDefinition->info['left'] = new HTMLPurifier_AttrDef_CSS_Composite(array( - new HTMLPurifier_AttrDef_CSS_Length(), - new HTMLPurifier_AttrDef_CSS_Percentage() - )); + $cssDefinition->info['left'] = new HTMLPurifier_AttrDef_CSS_Composite(array( + new HTMLPurifier_AttrDef_CSS_Length(), + new HTMLPurifier_AttrDef_CSS_Percentage() + )); - $cssDefinition->info['right'] = new HTMLPurifier_AttrDef_CSS_Composite(array( - new HTMLPurifier_AttrDef_CSS_Length(), - new HTMLPurifier_AttrDef_CSS_Percentage() - )); + $cssDefinition->info['right'] = new HTMLPurifier_AttrDef_CSS_Composite(array( + new HTMLPurifier_AttrDef_CSS_Length(), + new HTMLPurifier_AttrDef_CSS_Percentage() + )); - $cssDefinition->info['top'] = new HTMLPurifier_AttrDef_CSS_Composite(array( - new HTMLPurifier_AttrDef_CSS_Length(), - new HTMLPurifier_AttrDef_CSS_Percentage() - )); + $cssDefinition->info['top'] = new HTMLPurifier_AttrDef_CSS_Composite(array( + new HTMLPurifier_AttrDef_CSS_Length(), + new HTMLPurifier_AttrDef_CSS_Percentage() + )); - $cssDefinition->info['bottom'] = new HTMLPurifier_AttrDef_CSS_Composite(array( - new HTMLPurifier_AttrDef_CSS_Length(), - new HTMLPurifier_AttrDef_CSS_Percentage() - )); - } + $cssDefinition->info['bottom'] = new HTMLPurifier_AttrDef_CSS_Composite(array( + new HTMLPurifier_AttrDef_CSS_Length(), + new HTMLPurifier_AttrDef_CSS_Percentage() + )); + } - $purifier = new HTMLPurifier($config); + $purifier = new HTMLPurifier($config); - return $purifier->purify($s); + return $purifier->purify($s); } @@ -334,76 +342,82 @@ function purify_html($s, $opts = []) { * @param int $len max length of generated string * @return string Genereated random, but usually pronounceable string */ -function autoname($len) { +function autoname($len) +{ - if ($len <= 0) - return ''; + if ($len <= 0) { + return ''; + } - $vowels = array('a','a','ai','au','e','e','e','ee','ea','i','ie','o','ou','u'); - if (mt_rand(0, 5) == 4) - $vowels[] = 'y'; + $vowels = array('a','a','ai','au','e','e','e','ee','ea','i','ie','o','ou','u'); + if (mt_rand(0, 5) == 4) { + $vowels[] = 'y'; + } - $cons = array( - 'b','bl','br', - 'c','ch','cl','cr', - 'd','dr', - 'f','fl','fr', - 'g','gh','gl','gr', - 'h', - 'j', - 'k','kh','kl','kr', - 'l', - 'm', - 'n', - 'p','ph','pl','pr', - 'qu', - 'r','rh', - 's','sc','sh','sm','sp','st', - 't','th','tr', - 'v', - 'w','wh', - 'x', - 'z','zh' - ); + $cons = array( + 'b','bl','br', + 'c','ch','cl','cr', + 'd','dr', + 'f','fl','fr', + 'g','gh','gl','gr', + 'h', + 'j', + 'k','kh','kl','kr', + 'l', + 'm', + 'n', + 'p','ph','pl','pr', + 'qu', + 'r','rh', + 's','sc','sh','sm','sp','st', + 't','th','tr', + 'v', + 'w','wh', + 'x', + 'z','zh' + ); - $midcons = array('ck','ct','gn','ld','lf','lm','lt','mb','mm', 'mn','mp', - 'nd','ng','nk','nt','rn','rp','rt'); + $midcons = array('ck','ct','gn','ld','lf','lm','lt','mb','mm', 'mn','mp', + 'nd','ng','nk','nt','rn','rp','rt'); - // avoid these consonant pairs at the end of the string - $noend = array('bl', 'br', 'cl','cr','dr','fl','fr','gl','gr', - 'kh', 'kl','kr','mn','pl','pr','rh','tr','qu','wh'); + // avoid these consonant pairs at the end of the string + $noend = array('bl', 'br', 'cl','cr','dr','fl','fr','gl','gr', + 'kh', 'kl','kr','mn','pl','pr','rh','tr','qu','wh'); - $start = mt_rand(0, 2); - if ($start == 0) - $table = $vowels; - else - $table = $cons; + $start = mt_rand(0, 2); + if ($start == 0) { + $table = $vowels; + } else { + $table = $cons; + } - $word = ''; + $word = ''; - for ($x = 0; $x < $len; $x ++) { - $r = mt_rand(0, count($table) - 1); - $word .= $table[$r]; + for ($x = 0; $x < $len; $x++) { + $r = mt_rand(0, count($table) - 1); + $word .= $table[$r]; - if ($table == $vowels) - $table = array_merge($cons, $midcons); - else - $table = $vowels; - } + if ($table == $vowels) { + $table = array_merge($cons, $midcons); + } else { + $table = $vowels; + } + } - $word = substr($word, 0, $len); + $word = substr($word, 0, $len); - foreach ($noend as $noe) { - if ((strlen($word) > 2) && (substr($word, -2) == $noe)) { - $word = substr($word, 0, -1); - break; - } - } - // avoid the letter 'q' as it does not make a very good word ending - if (substr($word, -1) == 'q') - $word = substr($word, 0, -1); + foreach ($noend as $noe) { + if ((strlen($word) > 2) && (substr($word, -2) == $noe)) { + $word = substr($word, 0, -1); + break; + } + } + // avoid the letter 'q' as it does not make a very good word ending + if (substr($word, -1) == 'q') { + $word = substr($word, 0, -1); + } - return $word; + return $word; } @@ -413,50 +427,50 @@ function autoname($len) { * @param string $str * @return string Escaped text. */ -function xmlify($str) { - $buffer = ''; +function xmlify($str) +{ + $buffer = ''; - if(is_array($str)) { + if (is_array($str)) { + // allow to fall through so we ge a PHP error, as the log statement will + // probably get lost in the noise unless we're specifically looking for it. - // allow to fall through so we ge a PHP error, as the log statement will - // probably get lost in the noise unless we're specifically looking for it. + btlogger('xmlify called with array: ' . print_r($str, true), LOGGER_NORMAL, LOG_WARNING); + } - btlogger('xmlify called with array: ' . print_r($str,true), LOGGER_NORMAL, LOG_WARNING); - } + $len = mb_strlen($str); + for ($x = 0; $x < $len; $x++) { + $char = mb_substr($str, $x, 1); - $len = mb_strlen($str); - for($x = 0; $x < $len; $x ++) { - $char = mb_substr($str,$x,1); + switch ($char) { + case "\r": + break; + case "&": + $buffer .= '&'; + break; + case "'": + $buffer .= '''; + break; + case "\"": + $buffer .= '"'; + break; + case '<': + $buffer .= '<'; + break; + case '>': + $buffer .= '>'; + break; + case "\n": + $buffer .= "\n"; + break; + default: + $buffer .= $char; + break; + } + } + $buffer = trim($buffer); - switch( $char ) { - case "\r" : - break; - case "&" : - $buffer .= '&'; - break; - case "'" : - $buffer .= '''; - break; - case "\"" : - $buffer .= '"'; - break; - case '<' : - $buffer .= '<'; - break; - case '>' : - $buffer .= '>'; - break; - case "\n" : - $buffer .= "\n"; - break; - default : - $buffer .= $char; - break; - } - } - $buffer = trim($buffer); - - return($buffer); + return($buffer); } /** @@ -468,11 +482,12 @@ function xmlify($str) { * * @return string */ -function unxmlify($s) { - $ret = str_replace('&', '&', $s); - $ret = str_replace(array('<', '>', '"', '''), array('<', '>', '"', "'"), $ret); +function unxmlify($s) +{ + $ret = str_replace('&', '&', $s); + $ret = str_replace(array('<', '>', '"', '''), array('<', '>', '"', "'"), $ret); - return $ret; + return $ret; } /** @@ -489,85 +504,92 @@ function unxmlify($s) { * * @param App &$a */ -function paginate(&$a) { - $o = ''; - $stripped = preg_replace('/(&page=[0-9]*)/','',App::$query_string); +function paginate(&$a) +{ + $o = ''; + $stripped = preg_replace('/(&page=[0-9]*)/', '', App::$query_string); -// $stripped = preg_replace('/&zid=(.*?)([\?&]|$)/ism','',$stripped); +// $stripped = preg_replace('/&zid=(.*?)([\?&]|$)/ism','',$stripped); - $stripped = str_replace('q=','',$stripped); - $stripped = trim($stripped,'/'); - $pagenum = App::$pager['page']; - $url = z_root() . '/' . $stripped; + $stripped = str_replace('q=', '', $stripped); + $stripped = trim($stripped, '/'); + $pagenum = App::$pager['page']; + $url = z_root() . '/' . $stripped; - if(App::$pager['total'] > App::$pager['itemspage']) { - $o .= '
                    '; - if(App::$pager['page'] != 1) - $o .= ''."' . t('prev') . ' '; + if (App::$pager['total'] > App::$pager['itemspage']) { + $o .= '
                    '; + if (App::$pager['page'] != 1) { + $o .= '' . "' . t('prev') . ' '; + } - $o .= "" . t('first') . " "; + $o .= "" . t('first') . " "; - $numpages = App::$pager['total'] / App::$pager['itemspage']; + $numpages = App::$pager['total'] / App::$pager['itemspage']; - $numstart = 1; - $numstop = $numpages; + $numstart = 1; + $numstop = $numpages; - if($numpages > 14) { - $numstart = (($pagenum > 7) ? ($pagenum - 7) : 1); - $numstop = (($pagenum > ($numpages - 7)) ? $numpages : ($numstart + 14)); - } + if ($numpages > 14) { + $numstart = (($pagenum > 7) ? ($pagenum - 7) : 1); + $numstop = (($pagenum > ($numpages - 7)) ? $numpages : ($numstart + 14)); + } - for($i = $numstart; $i <= $numstop; $i++){ - if($i == App::$pager['page']) - $o .= ''.(($i < 10) ? ' '.$i : $i); - else - $o .= "".(($i < 10) ? ' '.$i : $i).""; - $o .= ' '; - } + for ($i = $numstart; $i <= $numstop; $i++) { + if ($i == App::$pager['page']) { + $o .= '' . (($i < 10) ? ' ' . $i : $i); + } else { + $o .= "" . (($i < 10) ? ' ' . $i : $i) . ""; + } + $o .= ' '; + } - if((App::$pager['total'] % App::$pager['itemspage']) != 0) { - if($i == App::$pager['page']) - $o .= ''.(($i < 10) ? ' '.$i : $i); - else - $o .= "".(($i < 10) ? ' '.$i : $i).""; - $o .= ' '; - } + if ((App::$pager['total'] % App::$pager['itemspage']) != 0) { + if ($i == App::$pager['page']) { + $o .= '' . (($i < 10) ? ' ' . $i : $i); + } else { + $o .= "" . (($i < 10) ? ' ' . $i : $i) . ""; + } + $o .= ' '; + } - $lastpage = (($numpages > intval($numpages)) ? intval($numpages)+1 : $numpages); - $o .= "" . t('last') . " "; + $lastpage = (($numpages > intval($numpages)) ? intval($numpages) + 1 : $numpages); + $o .= "" . t('last') . " "; - if((App::$pager['total'] - (App::$pager['itemspage'] * App::$pager['page'])) > 0) - $o .= ''."' . t('next') . ''; - $o .= '
                    '."\r\n"; - } + if ((App::$pager['total'] - (App::$pager['itemspage'] * App::$pager['page'])) > 0) { + $o .= '' . "' . t('next') . ''; + } + $o .= '
                    ' . "\r\n"; + } - return $o; + return $o; } -function alt_pager($i, $more = '', $less = '') { +function alt_pager($i, $more = '', $less = '') +{ - if(! $more) - $more = t('older'); - if(! $less) - $less = t('newer'); + if (! $more) { + $more = t('older'); + } + if (! $less) { + $less = t('newer'); + } - $stripped = preg_replace('/(&page=[0-9]*)/','',App::$query_string); - $stripped = str_replace('q=','',$stripped); - $stripped = trim($stripped,'/'); - //$pagenum = App::$pager['page']; - $url = z_root() . '/' . $stripped; - - return replace_macros(get_markup_template('alt_pager.tpl'), array( - '$has_less' => ((App::$pager['page'] > 1) ? true : false), - '$has_more' => (($i > 0 && $i >= App::$pager['itemspage']) ? true : false), - '$less' => $less, - '$more' => $more, - '$url' => $url, - '$prevpage' => App::$pager['page'] - 1, - '$nextpage' => App::$pager['page'] + 1, - )); + $stripped = preg_replace('/(&page=[0-9]*)/', '', App::$query_string); + $stripped = str_replace('q=', '', $stripped); + $stripped = trim($stripped, '/'); + //$pagenum = App::$pager['page']; + $url = z_root() . '/' . $stripped; + return replace_macros(get_markup_template('alt_pager.tpl'), array( + '$has_less' => ((App::$pager['page'] > 1) ? true : false), + '$has_more' => (($i > 0 && $i >= App::$pager['itemspage']) ? true : false), + '$less' => $less, + '$more' => $more, + '$url' => $url, + '$prevpage' => App::$pager['page'] - 1, + '$nextpage' => App::$pager['page'] + 1, + )); } @@ -578,17 +600,18 @@ function alt_pager($i, $more = '', $less = '') { * * @return string a unique id */ -function item_message_id() { +function item_message_id() +{ - try { - $hash = Uuid::uuid4()->toString(); - } catch (UnsatisfiedDependencyException $e) { - $hash = random_string(48); - } + try { + $hash = Uuid::uuid4()->toString(); + } catch (UnsatisfiedDependencyException $e) { + $hash = random_string(48); + } - $mid = z_root() . '/item/' . $hash; + $mid = z_root() . '/item/' . $hash; - return $mid; + return $mid; } /** @@ -598,32 +621,34 @@ function item_message_id() { * * @return string a unique hash */ -function photo_new_resource() { +function photo_new_resource() +{ - try { - $hash = Uuid::uuid4()->toString(); - } catch (UnsatisfiedDependencyException $e) { - $hash = random_string(48); - } + try { + $hash = Uuid::uuid4()->toString(); + } catch (UnsatisfiedDependencyException $e) { + $hash = random_string(48); + } - return $hash; + return $hash; } // provide psuedo random token (string) consisting entirely of US-ASCII letters/numbers // and with possibly variable length -function new_token($minlen = 36,$maxlen = 48) { +function new_token($minlen = 36, $maxlen = 48) +{ - $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890'; - $str = EMPTY_STR; + $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890'; + $str = EMPTY_STR; - $len = (($minlen === $maxlen) ? $minlen : mt_rand($minlen,$maxlen)); + $len = (($minlen === $maxlen) ? $minlen : mt_rand($minlen, $maxlen)); - for($a = 0; $a < $len; $a ++) { - $str .= $chars[mt_rand(0,62)]; - } - return $str; + for ($a = 0; $a < $len; $a++) { + $str .= $chars[mt_rand(0, 62)]; + } + return $str; } /** @@ -631,15 +656,16 @@ function new_token($minlen = 36,$maxlen = 48) { * * @return string */ -function new_uuid() { +function new_uuid() +{ - try { - $hash = Uuid::uuid4()->toString(); - } catch (UnsatisfiedDependencyException $e) { - $hash = random_string(48); - } + try { + $hash = Uuid::uuid4()->toString(); + } catch (UnsatisfiedDependencyException $e) { + $hash = random_string(48); + } - return $hash; + return $hash; } /** @@ -657,16 +683,18 @@ function new_uuid() { * * @param string $attr attribute string * @param string $s attribute you are looking for - * @return boolean true if found + * @return bool true if found */ -function attribute_contains($attr, $s) { - // remove quotes - $attr = str_replace([ '"',"'" ],['',''],$attr); - $a = explode(' ', $attr); - if($a && in_array($s, $a)) - return true; +function attribute_contains($attr, $s) +{ + // remove quotes + $attr = str_replace([ '"',"'" ], ['',''], $attr); + $a = explode(' ', $attr); + if ($a && in_array($s, $a)) { + return true; + } - return false; + return false; } /** @@ -686,42 +714,47 @@ function attribute_contains($attr, $s) { * @param int $level A log level * @param int $priority - compatible with syslog */ -function logger($msg, $level = LOGGER_NORMAL, $priority = LOG_INFO) { +function logger($msg, $level = LOGGER_NORMAL, $priority = LOG_INFO) +{ - if(App::$module == 'setup' && is_writable('install.log')) { - $debugging = true; - $logfile = 'install.log'; - $loglevel = LOGGER_ALL; - } - else { - $debugging = get_config('system', 'debugging'); - $loglevel = intval(get_config('system', 'loglevel')); - $logfile = get_config('system', 'logfile'); - } + if (App::$module == 'setup' && is_writable('install.log')) { + $debugging = true; + $logfile = 'install.log'; + $loglevel = LOGGER_ALL; + } else { + $debugging = get_config('system', 'debugging'); + $loglevel = intval(get_config('system', 'loglevel')); + $logfile = get_config('system', 'logfile'); + } - if((! $debugging) || (! $logfile) || ($level > $loglevel)) - return; + if ((! $debugging) || (! $logfile) || ($level > $loglevel)) { + return; + } - $where = ''; + $where = ''; - $stack = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2); - $where = basename($stack[0]['file']) . ':' . $stack[0]['line'] . ':' . $stack[1]['function'] . ': '; + $stack = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2); + $where = basename($stack[0]['file']) . ':' . $stack[0]['line'] . ':' . $stack[1]['function'] . ': '; - $s = datetime_convert('UTC','UTC', 'now', ATOM_TIME) . ':' . log_priority_str($priority) . ':' . logid() . ':' . $where . $msg . PHP_EOL; - $pluginfo = array('filename' => $logfile, 'loglevel' => $level, 'message' => $s,'priority' => $priority, 'logged' => false); + $s = datetime_convert('UTC', 'UTC', 'now', ATOM_TIME) . ':' . log_priority_str($priority) . ':' . logid() . ':' . $where . $msg . PHP_EOL; + $pluginfo = array('filename' => $logfile, 'loglevel' => $level, 'message' => $s,'priority' => $priority, 'logged' => false); - if(! (App::$module == 'setup')) - call_hooks('logger',$pluginfo); + if (! (App::$module == 'setup')) { + call_hooks('logger', $pluginfo); + } - if(! $pluginfo['logged']) - @file_put_contents($pluginfo['filename'], $pluginfo['message'], FILE_APPEND); + if (! $pluginfo['logged']) { + @file_put_contents($pluginfo['filename'], $pluginfo['message'], FILE_APPEND); + } } -function logid() { - $x = session_id(); - if(! $x) - $x = getmypid(); - return substr(hash('whirlpool',$x),0,10); +function logid() +{ + $x = session_id(); + if (! $x) { + $x = getmypid(); + } + return substr(hash('whirlpool', $x), 0, 10); } /** @@ -732,48 +765,52 @@ function logid() { * @param int $level A log level * @param int $priority - compatible with syslog */ -function btlogger($msg, $level = LOGGER_NORMAL, $priority = LOG_INFO) { +function btlogger($msg, $level = LOGGER_NORMAL, $priority = LOG_INFO) +{ - if(! defined('BTLOGGER_DEBUG_FILE')) - define('BTLOGGER_DEBUG_FILE','btlogger.out'); + if (! defined('BTLOGGER_DEBUG_FILE')) { + define('BTLOGGER_DEBUG_FILE', 'btlogger.out'); + } - logger($msg, $level, $priority); + logger($msg, $level, $priority); - if(file_exists(BTLOGGER_DEBUG_FILE) && is_writable(BTLOGGER_DEBUG_FILE)) { - $stack = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2); - $where = basename($stack[0]['file']) . ':' . $stack[0]['line'] . ':' . $stack[1]['function'] . ': '; - $s = datetime_convert('UTC','UTC', 'now', ATOM_TIME) . ':' . log_priority_str($priority) . ':' . logid() . ':' . $where . $msg . PHP_EOL; - @file_put_contents(BTLOGGER_DEBUG_FILE, $s, FILE_APPEND); - } + if (file_exists(BTLOGGER_DEBUG_FILE) && is_writable(BTLOGGER_DEBUG_FILE)) { + $stack = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2); + $where = basename($stack[0]['file']) . ':' . $stack[0]['line'] . ':' . $stack[1]['function'] . ': '; + $s = datetime_convert('UTC', 'UTC', 'now', ATOM_TIME) . ':' . log_priority_str($priority) . ':' . logid() . ':' . $where . $msg . PHP_EOL; + @file_put_contents(BTLOGGER_DEBUG_FILE, $s, FILE_APPEND); + } - $stack = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); - if($stack) { - for($x = 1; $x < count($stack); $x ++) { - $s = 'stack: ' . basename($stack[$x]['file']) . ':' . $stack[$x]['line'] . ':' . $stack[$x]['function'] . '()'; - logger($s,$level, $priority); + $stack = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); + if ($stack) { + for ($x = 1; $x < count($stack); $x++) { + $s = 'stack: ' . basename($stack[$x]['file']) . ':' . $stack[$x]['line'] . ':' . $stack[$x]['function'] . '()'; + logger($s, $level, $priority); - if(file_exists(BTLOGGER_DEBUG_FILE) && is_writable(BTLOGGER_DEBUG_FILE)) { - @file_put_contents(BTLOGGER_DEBUG_FILE, $s . PHP_EOL, FILE_APPEND); - } - } - } + if (file_exists(BTLOGGER_DEBUG_FILE) && is_writable(BTLOGGER_DEBUG_FILE)) { + @file_put_contents(BTLOGGER_DEBUG_FILE, $s . PHP_EOL, FILE_APPEND); + } + } + } } -function log_priority_str($priority) { - $parr = array( - LOG_EMERG => 'LOG_EMERG', - LOG_ALERT => 'LOG_ALERT', - LOG_CRIT => 'LOG_CRIT', - LOG_ERR => 'LOG_ERR', - LOG_WARNING => 'LOG_WARNING', - LOG_NOTICE => 'LOG_NOTICE', - LOG_INFO => 'LOG_INFO', - LOG_DEBUG => 'LOG_DEBUG' - ); +function log_priority_str($priority) +{ + $parr = array( + LOG_EMERG => 'LOG_EMERG', + LOG_ALERT => 'LOG_ALERT', + LOG_CRIT => 'LOG_CRIT', + LOG_ERR => 'LOG_ERR', + LOG_WARNING => 'LOG_WARNING', + LOG_NOTICE => 'LOG_NOTICE', + LOG_INFO => 'LOG_INFO', + LOG_DEBUG => 'LOG_DEBUG' + ); - if($parr[$priority]) - return $parr[$priority]; - return 'LOG_UNDEFINED'; + if ($parr[$priority]) { + return $parr[$priority]; + } + return 'LOG_UNDEFINED'; } /** @@ -791,49 +828,56 @@ function log_priority_str($priority) { * @param string $msg Message to log * @param int $level A log level. */ -function dlogger($msg, $level = 0) { +function dlogger($msg, $level = 0) +{ - // turn off logger in install mode + // turn off logger in install mode - if(App::$module == 'setup') - return; + if (App::$module == 'setup') { + return; + } - $debugging = get_config('system','debugging'); - $loglevel = intval(get_config('system','loglevel')); - $logfile = get_config('system','dlogfile'); + $debugging = get_config('system', 'debugging'); + $loglevel = intval(get_config('system', 'loglevel')); + $logfile = get_config('system', 'dlogfile'); - if((! $debugging) || (! $logfile) || ($level > $loglevel)) - return; + if ((! $debugging) || (! $logfile) || ($level > $loglevel)) { + return; + } - $where = ''; + $where = ''; - $stack = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2); - $where = basename($stack[0]['file']) . ':' . $stack[0]['line'] . ':' . $stack[1]['function'] . ': '; + $stack = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2); + $where = basename($stack[0]['file']) . ':' . $stack[0]['line'] . ':' . $stack[1]['function'] . ': '; - @file_put_contents($logfile, datetime_convert('UTC','UTC', 'now', ATOM_TIME) . ':' . logid() . ' ' . $where . $msg . PHP_EOL, FILE_APPEND); + @file_put_contents($logfile, datetime_convert('UTC', 'UTC', 'now', ATOM_TIME) . ':' . logid() . ' ' . $where . $msg . PHP_EOL, FILE_APPEND); } -function profiler($t1,$t2,$label) { - if(file_exists('profiler.out') && $t1 && t2) - @file_put_contents('profiler.out', sprintf('%01.4f %s',$t2 - $t1,$label) . PHP_EOL, FILE_APPEND); +function profiler($t1, $t2, $label) +{ + if (file_exists('profiler.out') && $t1 && t2) { + @file_put_contents('profiler.out', sprintf('%01.4f %s', $t2 - $t1, $label) . PHP_EOL, FILE_APPEND); + } } -function activity_match($haystack,$needle) { +function activity_match($haystack, $needle) +{ - if(! is_array($needle)) - $needle = [ $needle ]; + if (! is_array($needle)) { + $needle = [ $needle ]; + } - if($needle) { - foreach($needle as $n) { - if(($haystack === $n) || (strtolower(basename($n)) === strtolower(basename($haystack)))) { - return true; - } - } - } - return false; + if ($needle) { + foreach ($needle as $n) { + if (($haystack === $n) || (strtolower(basename($n)) === strtolower(basename($haystack)))) { + return true; + } + } + } + return false; } /** @@ -847,97 +891,105 @@ function activity_match($haystack,$needle) { * @param string $s * @return Returns array of tags found, or empty array. */ -function get_tags($s) { - $ret = []; - $match = []; +function get_tags($s) +{ + $ret = []; + $match = []; - // ignore anything in a code or svg block or HTML tag + // ignore anything in a code or svg block or HTML tag - $s = preg_replace('/\[code(.*?)\](.*?)\[\/code\]/sm','',$s); - $s = preg_replace('/\<(.*?)\>/sm','',$s); - $s = preg_replace('/\[svg(.*?)\](.*?)\[\/svg\]/sm','',$s); + $s = preg_replace('/\[code(.*?)\](.*?)\[\/code\]/sm', '', $s); + $s = preg_replace('/\<(.*?)\>/sm', '', $s); + $s = preg_replace('/\[svg(.*?)\](.*?)\[\/svg\]/sm', '', $s); - // ignore anything in [style= ] - $s = preg_replace('/\[style=(.*?)\]/sm','',$s); + // ignore anything in [style= ] + $s = preg_replace('/\[style=(.*?)\]/sm', '', $s); - // ignore anything in [color= ], because it may contain color codes which are mistaken for tags - $s = preg_replace('/\[color=(.*?)\]/sm','',$s); + // ignore anything in [color= ], because it may contain color codes which are mistaken for tags + $s = preg_replace('/\[color=(.*?)\]/sm', '', $s); - // skip anchors in URL - $s = preg_replace('/\[url=(.*?)\]/sm','',$s); + // skip anchors in URL + $s = preg_replace('/\[url=(.*?)\]/sm', '', $s); - // match any double quoted tags + // match any double quoted tags - if(preg_match_all('/([@#\!]\"\;.*?\"\;)/',$s,$match)) { - foreach($match[1] as $mtch) { - $ret[] = $mtch; - } - } + if (preg_match_all('/([@#\!]\"\;.*?\"\;)/', $s, $match)) { + foreach ($match[1] as $mtch) { + $ret[] = $mtch; + } + } - // match any unescaped double quoted tags (rare) - - if(preg_match_all('/([@#\!]\".*?\")/',$s,$match)) { - foreach($match[1] as $mtch) { - $ret[] = $mtch; - } - } + // match any unescaped double quoted tags (rare) - // match bracket mentions + if (preg_match_all('/([@#\!]\".*?\")/', $s, $match)) { + foreach ($match[1] as $mtch) { + $ret[] = $mtch; + } + } - if(preg_match_all('/([@!]\!?\{.*?\})/',$s,$match)) { - foreach($match[1] as $mtch) { - $ret[] = $mtch; - } - } + // match bracket mentions - // Pull out single word tags. These can be @nickname, @first_last - // and #hash tags. + if (preg_match_all('/([@!]\!?\{.*?\})/', $s, $match)) { + foreach ($match[1] as $mtch) { + $ret[] = $mtch; + } + } - if(preg_match_all('/(? $a['total']) ? 1 : (-1)); +function total_sort($a, $b) +{ + if ($a['total'] == $b['total']) { + return 0; + } + return(($b['total'] > $a['total']) ? 1 : (-1)); } @@ -947,74 +999,82 @@ function total_sort($a,$b) { * @param string $s * @return string */ -function qp($s) { - return str_replace ("%", "=", rawurlencode($s)); +function qp($s) +{ + return str_replace("%", "=", rawurlencode($s)); } -function chanlink_hash($s) { - return z_root() . '/chanview?f=&hash=' . urlencode($s); +function chanlink_hash($s) +{ + return z_root() . '/chanview?f=&hash=' . urlencode($s); } -function chanlink_url($s) { - return z_root() . '/chanview?f=&url=' . urlencode($s); +function chanlink_url($s) +{ + return z_root() . '/chanview?f=&url=' . urlencode($s); } -function chanlink_cid($d) { - return z_root() . '/chanview?f=&cid=' . intval($d); +function chanlink_cid($d) +{ + return z_root() . '/chanview?f=&cid=' . intval($d); } -function magiclink_url($observer,$myaddr,$url) { - return (($observer) - ? z_root() . '/magic?f=&owa=1&bdest=' . bin2hex($url) . '&addr=' . $myaddr - : $url - ); +function magiclink_url($observer, $myaddr, $url) +{ + return (($observer) + ? z_root() . '/magic?f=&owa=1&bdest=' . bin2hex($url) . '&addr=' . $myaddr + : $url + ); } -function search($s,$id='search-box',$url='/search',$save = false) { +function search($s, $id = 'search-box', $url = '/search', $save = false) +{ - return replace_macros(get_markup_template('searchbox.tpl'),array( - '$s' => $s, - '$id' => $id, - '$action_url' => z_root() . $url, - '$search_label' => t('Search'), - '$save_label' => t('Save'), - '$savedsearch' => feature_enabled(local_channel(),'savedsearch') - )); + return replace_macros(get_markup_template('searchbox.tpl'), array( + '$s' => $s, + '$id' => $id, + '$action_url' => z_root() . $url, + '$search_label' => t('Search'), + '$save_label' => t('Save'), + '$savedsearch' => feature_enabled(local_channel(), 'savedsearch') + )); } -function searchbox($s,$id='search-box',$url='/search',$save = false) { - return replace_macros(get_markup_template('searchbox.tpl'),array( - '$s' => $s, - '$id' => $id, - '$action_url' => z_root() . '/' . $url, - '$search_label' => t('Search'), - '$save_label' => t('Save'), - '$savedsearch' => ($save && feature_enabled(local_channel(),'savedsearch')) - )); +function searchbox($s, $id = 'search-box', $url = '/search', $save = false) +{ + return replace_macros(get_markup_template('searchbox.tpl'), array( + '$s' => $s, + '$id' => $id, + '$action_url' => z_root() . '/' . $url, + '$search_label' => t('Search'), + '$save_label' => t('Save'), + '$savedsearch' => ($save && feature_enabled(local_channel(), 'savedsearch')) + )); } /** * @brief Replace naked text hyperlink with HTML formatted hyperlink. * * @param string $s - * @param boolean $me (optional) default false + * @param bool $me (optional) default false * @return string */ -function linkify($s, $me = false) { - $rel = 'nofollow noopener'; - if ($me) { - $rel .= ' me'; - } - $s = preg_replace("/(https?\:\/\/[a-zA-Z0-9\pL\:\/\-\?\&\;\.\=\_\@\~\#\'\%\$\!\+\,\@]*)/u", '$1', $s); - $s = preg_replace("/\<(.*?)(src|href)=(.*?)\&\;(.*?)\>/ism",'<$1$2=$3&$4>',$s); +function linkify($s, $me = false) +{ + $rel = 'nofollow noopener'; + if ($me) { + $rel .= ' me'; + } + $s = preg_replace("/(https?\:\/\/[a-zA-Z0-9\pL\:\/\-\?\&\;\.\=\_\@\~\#\'\%\$\!\+\,\@]*)/u", '$1', $s); + $s = preg_replace("/\<(.*?)(src|href)=(.*?)\&\;(.*?)\>/ism", '<$1$2=$3&$4>', $s); - return($s); + return($s); } /** @@ -1027,83 +1087,83 @@ function linkify($s, $me = false) { * @param string $s * @returns string */ -function sslify($s, $cache_enable = true) { +function sslify($s, $cache_enable = true) +{ - if (! $cache_enable) { + if (! $cache_enable) { + // we're fetching an old item and we are no longer + // caching photos for it. Remove any existing cached photos. + // Cron_weekly tasks will also remove these, but if the cache + // entry was updated recently they might not get removed for + // another couple of months. - // we're fetching an old item and we are no longer - // caching photos for it. Remove any existing cached photos. - // Cron_weekly tasks will also remove these, but if the cache - // entry was updated recently they might not get removed for - // another couple of months. - - uncache($s); - } + uncache($s); + } - if ((! $cache_enable) || (! intval(get_config('system','cache_images', 1)))) { + if ((! $cache_enable) || (! intval(get_config('system', 'cache_images', 1)))) { + // if caching is prevented for whatever reason, proxy any non-SSL photos - // if caching is prevented for whatever reason, proxy any non-SSL photos + if (strpos(z_root(), 'https:') === false) { + return $s; + } - if (strpos(z_root(),'https:') === false) { - return $s; - } + // we'll only sslify img tags because media files will probably choke. - // we'll only sslify img tags because media files will probably choke. + $pattern = "/\/"; - $pattern = "/\/"; + $matches = null; + $cnt = preg_match_all($pattern, $s, $matches, PREG_SET_ORDER); + if ($cnt) { + foreach ($matches as $match) { + $filename = basename(parse_url($match[2], PHP_URL_PATH)); + $s = str_replace($match[2], z_root() . '/sslify/' . $filename . '?f=&url=' . urlencode($match[2]), $s); + } + } + return $s; + } - $matches = null; - $cnt = preg_match_all($pattern,$s,$matches,PREG_SET_ORDER); - if ($cnt) { - foreach ($matches as $match) { - $filename = basename( parse_url($match[2], PHP_URL_PATH) ); - $s = str_replace($match[2],z_root() . '/sslify/' . $filename . '?f=&url=' . urlencode($match[2]),$s); - } - } - return $s; - } + $pattern = "/\/ism"; - $pattern = "/\/ism"; - - $matches = null; - $cnt = preg_match_all($pattern,$s,$matches,PREG_SET_ORDER); - if ($cnt) { - foreach ($matches as $match) { - // For access controlled photos using OpenWebAuth, remove any zid attributes. - // This will cache a publicly available image but will not cache a protected one. - $clean = strip_zids(strip_query_param($match[2],'f')); - $cached = Img_cache::check($clean,'cache/img'); - if ($cached) { - // $file = Img_cache::get_filename($clean,'cache/img'); - // @fixme getimagesize and replace height/width/alt in image tag - $s = str_replace($match[2],z_root() . '/ca/' . basename(Img_cache::get_filename($clean,'cache/img')) . '?url=' . urlencode($clean),$s); - } - } - } + $matches = null; + $cnt = preg_match_all($pattern, $s, $matches, PREG_SET_ORDER); + if ($cnt) { + foreach ($matches as $match) { + // For access controlled photos using OpenWebAuth, remove any zid attributes. + // This will cache a publicly available image but will not cache a protected one. + $clean = strip_zids(strip_query_param($match[2], 'f')); + $cached = Img_cache::check($clean, 'cache/img'); + if ($cached) { + // $file = Img_cache::get_filename($clean,'cache/img'); + // @fixme getimagesize and replace height/width/alt in image tag + $s = str_replace($match[2], z_root() . '/ca/' . basename(Img_cache::get_filename($clean, 'cache/img')) . '?url=' . urlencode($clean), $s); + } + } + } - return $s; + return $s; } // clean out the image cache -function uncache($s) { +function uncache($s) +{ - $pattern = "/\/ism"; - - $matches = null; - $cnt = preg_match_all($pattern,$s,$matches,PREG_SET_ORDER); - if ($cnt) { - foreach ($matches as $match) { - // repeat the filename generation procedure we used when creating the cache entry - $clean = strip_zids(strip_query_param($match[2],'f')); - $file = Img_cache::get_filename($clean,'cache/img'); - if (file_exists($file)) { - unlink($file); - } - } - } + $pattern = "/\/ism"; - return $s; + $matches = null; + $cnt = preg_match_all($pattern, $s, $matches, PREG_SET_ORDER); + if ($cnt) { + foreach ($matches as $match) { + // repeat the filename generation procedure we used when creating the cache entry + $clean = strip_zids(strip_query_param($match[2], 'f')); + $file = Img_cache::get_filename($clean, 'cache/img'); + if (file_exists($file)) { + unlink($file); + } + } + } + + return $s; } @@ -1115,29 +1175,30 @@ function uncache($s) { * * \e index is present tense verb * * \e value is array containing past tense verb, translation of present, translation of past */ -function get_poke_verbs() { - if (get_config('system', 'poke_basic')) { - $arr = array( - 'poke' => array('poked', t('poke'), t('poked')), - ); - } else { - $arr = array( - 'poke' => array( 'poked', t('poke'), t('poked')), - 'ping' => array( 'pinged', t('ping'), t('pinged')), - 'prod' => array( 'prodded', t('prod'), t('prodded')), - 'slap' => array( 'slapped', t('slap'), t('slapped')), - 'finger' => array( 'fingered', t('finger'), t('fingered')), - 'rebuff' => array( 'rebuffed', t('rebuff'), t('rebuffed')), - ); +function get_poke_verbs() +{ + if (get_config('system', 'poke_basic')) { + $arr = array( + 'poke' => array('poked', t('poke'), t('poked')), + ); + } else { + $arr = array( + 'poke' => array( 'poked', t('poke'), t('poked')), + 'ping' => array( 'pinged', t('ping'), t('pinged')), + 'prod' => array( 'prodded', t('prod'), t('prodded')), + 'slap' => array( 'slapped', t('slap'), t('slapped')), + 'finger' => array( 'fingered', t('finger'), t('fingered')), + 'rebuff' => array( 'rebuffed', t('rebuff'), t('rebuffed')), + ); - /** - * @hooks poke_verbs - * * \e array associative array with another array as value - */ - call_hooks('poke_verbs', $arr); - } + /** + * @hooks poke_verbs + * * \e array associative array with another array as value + */ + call_hooks('poke_verbs', $arr); + } - return $arr; + return $arr; } /** @@ -1147,39 +1208,40 @@ function get_poke_verbs() { * * \e index is the verb * * \e value is the translated verb */ -function get_mood_verbs() { +function get_mood_verbs() +{ - $arr = [ - 'happy' => t('happy'), - 'sad' => t('sad'), - 'mellow' => t('mellow'), - 'tired' => t('tired'), - 'perky' => t('perky'), - 'angry' => t('angry'), - 'stupefied' => t('stupefied'), - 'puzzled' => t('puzzled'), - 'interested' => t('interested'), - 'bitter' => t('bitter'), - 'cheerful' => t('cheerful'), - 'alive' => t('alive'), - 'annoyed' => t('annoyed'), - 'anxious' => t('anxious'), - 'cranky' => t('cranky'), - 'disturbed' => t('disturbed'), - 'frustrated' => t('frustrated'), - 'depressed' => t('depressed'), - 'motivated' => t('motivated'), - 'relaxed' => t('relaxed'), - 'surprised' => t('surprised'), - ]; + $arr = [ + 'happy' => t('happy'), + 'sad' => t('sad'), + 'mellow' => t('mellow'), + 'tired' => t('tired'), + 'perky' => t('perky'), + 'angry' => t('angry'), + 'stupefied' => t('stupefied'), + 'puzzled' => t('puzzled'), + 'interested' => t('interested'), + 'bitter' => t('bitter'), + 'cheerful' => t('cheerful'), + 'alive' => t('alive'), + 'annoyed' => t('annoyed'), + 'anxious' => t('anxious'), + 'cranky' => t('cranky'), + 'disturbed' => t('disturbed'), + 'frustrated' => t('frustrated'), + 'depressed' => t('depressed'), + 'motivated' => t('motivated'), + 'relaxed' => t('relaxed'), + 'surprised' => t('surprised'), + ]; - /** - * @hooks mood_verbs - * * \e array associative array with mood verbs - */ - call_hooks('mood_verbs', $arr); + /** + * @hooks mood_verbs + * * \e array associative array with mood verbs + */ + call_hooks('mood_verbs', $arr); - return $arr; + return $arr; } /** @@ -1187,85 +1249,87 @@ function get_mood_verbs() { * * @return Returns array with keys 'texts' and 'icons' */ -function list_smilies($default_only = false) { +function list_smilies($default_only = false) +{ - $texts = array( - '<3', - '</3', - ':-)', - ';-)', - ':-(', - ':-P', - ':-p', - ':-"', - ':-"', - ':-x', - ':-X', - ':-D', - '8-|', - '8-O', - ':-O', - '\\o/', - 'o.O', - 'O.o', - 'o_O', - 'O_o', - ":'(", - ":-!", - ":-/", - ":-[", - "8-)", - ':beer', - ':homebrew', - ':coffee', - ':facepalm', - ':like', - ':dislike' - ); + $texts = array( + '<3', + '</3', + ':-)', + ';-)', + ':-(', + ':-P', + ':-p', + ':-"', + ':-"', + ':-x', + ':-X', + ':-D', + '8-|', + '8-O', + ':-O', + '\\o/', + 'o.O', + 'O.o', + 'o_O', + 'O_o', + ":'(", + ":-!", + ":-/", + ":-[", + "8-)", + ':beer', + ':homebrew', + ':coffee', + ':facepalm', + ':like', + ':dislike' + ); - $icons = array( - '<3', - '</3', - ':-)', - ';-)', - ':-(', - ':-P', - ':-p', - ':-\', - ':-\', - ':-x', - ':-X', - ':-D', - '8-|', - '8-O', - ':-O', - '\\o/', - 'o.O', - 'O.o', - 'o_O', - 'O_o', - ':\'(', - ':-!', - ':-/', - ':-[', - '8-)', - ':beer', - ':homebrew', - ':coffee', - ':facepalm', - ':like', - ':dislike' + $icons = array( + '<3', + '</3', + ':-)', + ';-)', + ':-(', + ':-P', + ':-p', + ':-\', + ':-\', + ':-x', + ':-X', + ':-D', + '8-|', + '8-O', + ':-O', + '\\o/', + 'o.O', + 'O.o', + 'o_O', + 'O_o', + ':\'(', + ':-!', + ':-/', + ':-[', + '8-)', + ':beer', + ':homebrew', + ':coffee', + ':facepalm', + ':like', + ':dislike' - ); + ); - $params = array('texts' => $texts, 'icons' => $icons); + $params = array('texts' => $texts, 'icons' => $icons); - if($default_only) - return $params; + if ($default_only) { + return $params; + } - call_hooks('smilie', $params); + call_hooks('smilie', $params); - return $params; + return $params; } /** @@ -1280,36 +1344,40 @@ function list_smilies($default_only = false) { * bbcode source for HTML display. * * @param string $s - * @param boolean $sample (optional) default false + * @param bool $sample (optional) default false * @return string */ -function smilies($s, $sample = false) { +function smilies($s, $sample = false) +{ - if(intval(get_config('system', 'no_smilies')) - || (local_channel() && intval(get_pconfig(local_channel(), 'system', 'no_smilies')))) - return $s; + if ( + intval(get_config('system', 'no_smilies')) + || (local_channel() && intval(get_pconfig(local_channel(), 'system', 'no_smilies'))) + ) { + return $s; + } - $s = preg_replace_callback('{<(pre|code)>.*?}ism', 'smile_shield', $s); - $s = preg_replace_callback('/<[a-z]+ .*?>/ism', 'smile_shield', $s); + $s = preg_replace_callback('{<(pre|code)>.*?}ism', 'smile_shield', $s); + $s = preg_replace_callback('/<[a-z]+ .*?>/ism', 'smile_shield', $s); - $params = list_smilies(); - $params['string'] = $s; + $params = list_smilies(); + $params['string'] = $s; - if ($sample) { - $s = '
                    '; - for ($x = 0; $x < count($params['texts']); $x ++) { - $s .= '
                    ' . $params['texts'][$x] . '
                    ' . $params['icons'][$x] . '
                    '; - } - } else { - $params['string'] = preg_replace_callback('/<(3+)/','preg_heart',$params['string']); - $s = str_replace($params['texts'],$params['icons'],$params['string']); - } + if ($sample) { + $s = '
                    '; + for ($x = 0; $x < count($params['texts']); $x++) { + $s .= '
                    ' . $params['texts'][$x] . '
                    ' . $params['icons'][$x] . '
                    '; + } + } else { + $params['string'] = preg_replace_callback('/<(3+)/', 'preg_heart', $params['string']); + $s = str_replace($params['texts'], $params['icons'], $params['string']); + } - $s = preg_replace_callback('//ism', 'smile_unshield', $s); + $s = preg_replace_callback('//ism', 'smile_unshield', $s); - return $s; + return $s; } /** @@ -1318,12 +1386,14 @@ function smilies($s, $sample = false) { * @param array $m * @return string */ -function smile_shield($m) { - return ''; +function smile_shield($m) +{ + return ''; } -function smile_unshield($m) { - return base64special_decode($m[1]); +function smile_unshield($m) +{ + return base64special_decode($m[1]); } /** @@ -1331,32 +1401,40 @@ function smile_unshield($m) { * * @param array $x */ -function preg_heart($x) { +function preg_heart($x) +{ - if (strlen($x[1]) == 1) - return $x[0]; + if (strlen($x[1]) == 1) { + return $x[0]; + } - $t = ''; - for($cnt = 0; $cnt < strlen($x[1]); $cnt ++) - $t .= '<​3'; + $t = ''; + for ($cnt = 0; $cnt < strlen($x[1]); $cnt++) { + $t .= '<​3'; + } - $r = str_replace($x[0],$t,$x[0]); + $r = str_replace($x[0], $t, $x[0]); - return $r; + return $r; } -function day_translate($s) { - $ret = str_replace(array('Monday','Tuesday','Wednesday','Thursday','Friday','Saturday','Sunday'), - array( t('Monday'), t('Tuesday'), t('Wednesday'), t('Thursday'), t('Friday'), t('Saturday'), t('Sunday')), - $s); +function day_translate($s) +{ + $ret = str_replace( + array('Monday','Tuesday','Wednesday','Thursday','Friday','Saturday','Sunday'), + array( t('Monday'), t('Tuesday'), t('Wednesday'), t('Thursday'), t('Friday'), t('Saturday'), t('Sunday')), + $s + ); - $ret = str_replace(array('January','February','March','April','May','June','July','August','September','October','November','December'), - array( t('January'), t('February'), t('March'), t('April'), t('May'), t('June'), t('July'), t('August'), t('September'), t('October'), t('November'), t('December')), - $ret); + $ret = str_replace( + array('January','February','March','April','May','June','July','August','September','October','November','December'), + array( t('January'), t('February'), t('March'), t('April'), t('May'), t('June'), t('July'), t('August'), t('September'), t('October'), t('November'), t('December')), + $ret + ); - return $ret; + return $ret; } /** @@ -1365,10 +1443,11 @@ function day_translate($s) { * @param string $url * @return string */ -function normalise_link($url) { - $ret = str_replace(array('https:', '//www.'), array('http:', '//'), $url); +function normalise_link($url) +{ + $ret = str_replace(array('https:', '//www.'), array('http:', '//'), $url); - return(rtrim($ret, '/')); + return(rtrim($ret, '/')); } /** @@ -1384,127 +1463,134 @@ function normalise_link($url) { * @param string $b * @return true if the URLs match, otherwise false */ -function link_compare($a, $b) { - if (strcasecmp(normalise_link($a), normalise_link($b)) === 0) - return true; +function link_compare($a, $b) +{ + if (strcasecmp(normalise_link($a), normalise_link($b)) === 0) { + return true; + } - return false; + return false; } // Given an item array, convert the body element from bbcode to html and add smilie icons. // If attach is true, also add icons for item attachments -function unobscure(&$item) { - return; +function unobscure(&$item) +{ + return; } -function unobscure_mail(&$item) { - if(array_key_exists('mail_obscured',$item) && intval($item['mail_obscured'])) { - if($item['title']) - $item['title'] = base64url_decode(str_rot47($item['title'])); - if($item['body']) - $item['body'] = base64url_decode(str_rot47($item['body'])); - } +function unobscure_mail(&$item) +{ + if (array_key_exists('mail_obscured', $item) && intval($item['mail_obscured'])) { + if ($item['title']) { + $item['title'] = base64url_decode(str_rot47($item['title'])); + } + if ($item['body']) { + $item['body'] = base64url_decode(str_rot47($item['body'])); + } + } } -function theme_attachments(&$item) { +function theme_attachments(&$item) +{ - $s = EMPTY_STR; + $s = EMPTY_STR; - $arr = json_decode($item['attach'],true); + $arr = json_decode($item['attach'], true); - if (is_array($arr) && count($arr)) { - $attaches = []; - foreach ($arr as $r) { + if (is_array($arr) && count($arr)) { + $attaches = []; + foreach ($arr as $r) { + $label = EMPTY_STR; + $icon = getIconFromType($r['type']); - $label = EMPTY_STR; - $icon = getIconFromType($r['type']); - - if (isset($r['title']) && $r['title']) { - $label = urldecode(htmlspecialchars($r['title'], ENT_COMPAT, 'UTF-8')); - } + if (isset($r['title']) && $r['title']) { + $label = urldecode(htmlspecialchars($r['title'], ENT_COMPAT, 'UTF-8')); + } - if (isset($r['name']) && $r['name']) { - $label = urldecode(htmlspecialchars($r['name'], ENT_COMPAT, 'UTF-8')); - } + if (isset($r['name']) && $r['name']) { + $label = urldecode(htmlspecialchars($r['name'], ENT_COMPAT, 'UTF-8')); + } - if (isset($r['href']) && $r['href']) { - $m = parse_url($r['href']); - } - if (! $label) { - if (isset($r['href']) && $r['href']) { - if (isset($m) && $m && $m['path']) { - $label = basename($m['path']); - } - } - } + if (isset($r['href']) && $r['href']) { + $m = parse_url($r['href']); + } + if (! $label) { + if (isset($r['href']) && $r['href']) { + if (isset($m) && $m && $m['path']) { + $label = basename($m['path']); + } + } + } - // some feeds provide an attachment where title is an empty space - if (! trim($label)) { - $label = t('Unknown Attachment'); - } + // some feeds provide an attachment where title is an empty space + if (! trim($label)) { + $label = t('Unknown Attachment'); + } - $title = t('Size') . ' ' . ((isset($r['length']) && $r['length']) ? userReadableSize($r['length']) : t('unknown')); + $title = t('Size') . ' ' . ((isset($r['length']) && $r['length']) ? userReadableSize($r['length']) : t('unknown')); - if (! (isset($r['href']))) { - continue; - } + if (! (isset($r['href']))) { + continue; + } - if (isset($m) && $m && $m['scheme'] === 'data') { - continue; - } + if (isset($m) && $m && $m['scheme'] === 'data') { + continue; + } - if (is_foreigner($item['author_xchan'])) { - $url = $r['href']; - } - else { - $url = z_root() . '/magic?f=&owa=1&hash=' . $item['author_xchan'] . '&bdest=' . bin2hex($r['href'] . ((isset($r['revision']) ? '/' . $r['revision'] : ''))); - } - $attaches[] = [ - 'label' => $label, - 'url' => $url, - 'icon' => $icon, - 'title' => $title - ]; - } + if (is_foreigner($item['author_xchan'])) { + $url = $r['href']; + } else { + $url = z_root() . '/magic?f=&owa=1&hash=' . $item['author_xchan'] . '&bdest=' . bin2hex($r['href'] . ((isset($r['revision']) ? '/' . $r['revision'] : ''))); + } + $attaches[] = [ + 'label' => $label, + 'url' => $url, + 'icon' => $icon, + 'title' => $title + ]; + } - $s = replace_macros(get_markup_template('item_attach.tpl'), [ - '$attaches' => $attaches - ]); - } + $s = replace_macros(get_markup_template('item_attach.tpl'), [ + '$attaches' => $attaches + ]); + } - return $s; + return $s; } -function format_categories(&$item,$writeable) { +function format_categories(&$item, $writeable) +{ - $s = EMPTY_STR; + $s = EMPTY_STR; - if (! (isset($item['term']) && $item['term'])) { - return $s; - } + if (! (isset($item['term']) && $item['term'])) { + return $s; + } - $terms = get_terms_oftype($item['term'],TERM_CATEGORY); - if($terms) { - $categories = []; - foreach($terms as $t) { - $term = htmlspecialchars($t['term'],ENT_COMPAT,'UTF-8',false) ; - if(! trim($term)) - continue; - $removelink = (($writeable) ? z_root() . '/filerm/' . $item['id'] . '?f=&cat=' . urlencode($t['term']) : ''); - $categories[] = array('term' => $term, 'writeable' => $writeable, 'removelink' => $removelink, 'url' => zid($t['url'])); - } + $terms = get_terms_oftype($item['term'], TERM_CATEGORY); + if ($terms) { + $categories = []; + foreach ($terms as $t) { + $term = htmlspecialchars($t['term'], ENT_COMPAT, 'UTF-8', false) ; + if (! trim($term)) { + continue; + } + $removelink = (($writeable) ? z_root() . '/filerm/' . $item['id'] . '?f=&cat=' . urlencode($t['term']) : ''); + $categories[] = array('term' => $term, 'writeable' => $writeable, 'removelink' => $removelink, 'url' => zid($t['url'])); + } - $s = replace_macros(get_markup_template('item_categories.tpl'),array( - '$remove' => t('remove category'), - '$categories' => $categories - )); - } + $s = replace_macros(get_markup_template('item_categories.tpl'), array( + '$remove' => t('remove category'), + '$categories' => $categories + )); + } - return $s; + return $s; } /** @@ -1514,464 +1600,468 @@ function format_categories(&$item,$writeable) { * @return string HTML link of hashtag */ -function format_hashtags(&$item) { - $s = ''; +function format_hashtags(&$item) +{ + $s = ''; - if (! isset($item['term'])) { - return $s; - } + if (! isset($item['term'])) { + return $s; + } - $terms = get_terms_oftype($item['term'], array(TERM_HASHTAG,TERM_COMMUNITYTAG)); - if($terms) { - foreach($terms as $t) { - $term = htmlspecialchars($t['term'], ENT_COMPAT, 'UTF-8', false) ; - if(! trim($term)) - continue; + $terms = get_terms_oftype($item['term'], array(TERM_HASHTAG,TERM_COMMUNITYTAG)); + if ($terms) { + foreach ($terms as $t) { + $term = htmlspecialchars($t['term'], ENT_COMPAT, 'UTF-8', false) ; + if (! trim($term)) { + continue; + } - // Pleroma uses example.com/tags/xxx in the taglist but example.com/tag/xxx in the body. Possibly a bug because one of these leads to - // an error page, but it messes up our detection of whether the tag was already present in the body. - - if($t['url'] && ((stripos($item['body'], $t['url']) !== false) || (stripos($item['body'], str_replace('/tags/','/tag/',$t['url']))))) { - continue; - } - if($s) - $s .= ' '; + // Pleroma uses example.com/tags/xxx in the taglist but example.com/tag/xxx in the body. Possibly a bug because one of these leads to + // an error page, but it messes up our detection of whether the tag was already present in the body. - $s .= ' ' . $term . ''; - } - } + if ($t['url'] && ((stripos($item['body'], $t['url']) !== false) || (stripos($item['body'], str_replace('/tags/', '/tag/', $t['url']))))) { + continue; + } + if ($s) { + $s .= ' '; + } - return $s; + $s .= ' ' . $term . ''; + } + } + + return $s; } -function format_mentions(&$item) { - $s = EMPTY_STR; +function format_mentions(&$item) +{ + $s = EMPTY_STR; - $pref = intval(PConfig::Get($item['uid'],'system','tag_username',Config::Get('system','tag_username',false))); + $pref = intval(PConfig::Get($item['uid'], 'system', 'tag_username', Config::Get('system', 'tag_username', false))); - // hide "auto mentions" by default - this hidden pref let's you display them. + // hide "auto mentions" by default - this hidden pref let's you display them. - $show = intval(PConfig::Get($item['uid'],'system','show_auto_mentions',false)); - if ((! $show) && (! $item['resource_type'])) { - return $s; - } + $show = intval(PConfig::Get($item['uid'], 'system', 'show_auto_mentions', false)); + if ((! $show) && (! $item['resource_type'])) { + return $s; + } - if ($pref === 127) { - return $s; - } + if ($pref === 127) { + return $s; + } - if (! (isset($item['term']) && is_array($item['term']) && $item['term'])) { - return $s; - } - $terms = get_terms_oftype($item['term'],TERM_MENTION); - if($terms) { - foreach($terms as $t) { + if (! (isset($item['term']) && is_array($item['term']) && $item['term'])) { + return $s; + } + $terms = get_terms_oftype($item['term'], TERM_MENTION); + if ($terms) { + foreach ($terms as $t) { + $term = htmlspecialchars($t['term'], ENT_COMPAT, 'UTF-8', false) ; + if (! trim($term)) { + continue; + } - $term = htmlspecialchars($t['term'],ENT_COMPAT,'UTF-8',false) ; - if(! trim($term)) - continue; + if ($t['url'] && stripos($item['body'], $t['url']) !== false) { + continue; + } - if($t['url'] && stripos($item['body'], $t['url']) !== false) - continue; + // some platforms put the identity url into href rather than the profile url. Accept either form. + $x = q( + "select * from xchan where xchan_url = '%s' or xchan_hash = '%s' limit 1", + dbesc($t['url']), + dbesc($t['url']) + ); + if ($x) { + switch ($pref) { + case 0: + $txt = $x[0]['xchan_name']; + break; + case 1: + $txt = (($x[0]['xchan_addr']) ? $x[0]['xchan_addr'] : $x[0]['xchan_name']); + break; + case 2: + default; + if ($x[0]['xchan_addr']) { + $txt = sprintf(t('%1$s (%2$s)'), $x[0]['xchan_name'], $x[0]['xchan_addr']); + } else { + $txt = $x[0]['xchan_name']; + } + break; + } + } - // some platforms put the identity url into href rather than the profile url. Accept either form. - $x = q("select * from xchan where xchan_url = '%s' or xchan_hash = '%s' limit 1", - dbesc($t['url']), - dbesc($t['url']) - ); - if ($x) { - switch ($pref) { - case 0: - $txt = $x[0]['xchan_name']; - break; - case 1: - $txt = (($x[0]['xchan_addr']) ? $x[0]['xchan_addr'] : $x[0]['xchan_name']); - break; - case 2: - default; - if ($x[0]['xchan_addr']) { - $txt = sprintf( t('%1$s (%2$s)'), $x[0]['xchan_name'], $x[0]['xchan_addr']); - } - else { - $txt = $x[0]['xchan_name']; - } - break; - } - } - - if ($s) { - $s .= ' '; - } - $s .= ' ' . $txt . ''; - } - } + if ($s) { + $s .= ' '; + } + $s .= ' ' . $txt . ''; + } + } - return $s; + return $s; } -function format_filer(&$item) { - $s = EMPTY_STR; +function format_filer(&$item) +{ + $s = EMPTY_STR; - if (! (isset($item['term']) && $item['term'])) { - return $s; - } + if (! (isset($item['term']) && $item['term'])) { + return $s; + } - $terms = get_terms_oftype($item['term'],TERM_FILE); - if($terms) { - $categories = []; - foreach($terms as $t) { - $term = htmlspecialchars($t['term'],ENT_COMPAT,'UTF-8',false) ; - if(! trim($term)) - continue; - $removelink = z_root() . '/filerm/' . $item['id'] . '?f=&term=' . urlencode($t['term']); - $categories[] = array('term' => $term, 'removelink' => $removelink); - } + $terms = get_terms_oftype($item['term'], TERM_FILE); + if ($terms) { + $categories = []; + foreach ($terms as $t) { + $term = htmlspecialchars($t['term'], ENT_COMPAT, 'UTF-8', false) ; + if (! trim($term)) { + continue; + } + $removelink = z_root() . '/filerm/' . $item['id'] . '?f=&term=' . urlencode($t['term']); + $categories[] = array('term' => $term, 'removelink' => $removelink); + } - $s = replace_macros(get_markup_template('item_filer.tpl'),array( - '$remove' => t('remove from file'), - '$categories' => $categories - )); - } + $s = replace_macros(get_markup_template('item_filer.tpl'), array( + '$remove' => t('remove from file'), + '$categories' => $categories + )); + } - return $s; + return $s; } -function generate_map($coord) { +function generate_map($coord) +{ - $coord = str_replace(array(',','/',' '),array(' ',' ',' '),trim($coord)); + $coord = str_replace(array(',','/',' '), array(' ',' ',' '), trim($coord)); - - $zoom = ((strpos($coord,'?z=') !== false) ? substr($coord,strpos($coord,'?z=')+3) : 0); - if ($zoom) { - $coord = substr($coord,0,strpos($coord,'?')); - } - else { - $zoom = 16; - } + $zoom = ((strpos($coord, '?z=') !== false) ? substr($coord, strpos($coord, '?z=') + 3) : 0); - $arr = [ - 'lat' => trim(substr($coord, 0, strpos($coord, ' '))), - 'lon' => trim(substr($coord, strpos($coord, ' ')+1)), - 'zoom' => $zoom, - 'html' => '' - ]; + if ($zoom) { + $coord = substr($coord, 0, strpos($coord, '?')); + } else { + $zoom = 16; + } - /** - * @hooks generate_map - * * \e string \b lat - * * \e string \b lon - * * \e string \b html the parsed HTML to return - */ - call_hooks('generate_map', $arr); + $arr = [ + 'lat' => trim(substr($coord, 0, strpos($coord, ' '))), + 'lon' => trim(substr($coord, strpos($coord, ' ') + 1)), + 'zoom' => $zoom, + 'html' => '' + ]; - return (($arr['html']) ? $arr['html'] : $coord); + /** + * @hooks generate_map + * * \e string \b lat + * * \e string \b lon + * * \e string \b html the parsed HTML to return + */ + call_hooks('generate_map', $arr); + + return (($arr['html']) ? $arr['html'] : $coord); } -function generate_named_map($location) { - $arr = [ - 'location' => $location, - 'html' => '' - ]; +function generate_named_map($location) +{ + $arr = [ + 'location' => $location, + 'html' => '' + ]; - /** - * @hooks generate_named_map - * * \e string \b location - * * \e string \b html the parsed HTML to return - */ - call_hooks('generate_named_map', $arr); + /** + * @hooks generate_named_map + * * \e string \b location + * * \e string \b html the parsed HTML to return + */ + call_hooks('generate_named_map', $arr); - return (($arr['html']) ? $arr['html'] : $location); + return (($arr['html']) ? $arr['html'] : $location); } -function prepare_body(&$item,$attach = false,$opts = false) { +function prepare_body(&$item, $attach = false, $opts = false) +{ - call_hooks('prepare_body_init', $item); + call_hooks('prepare_body_init', $item); - $censored = ((($item['author']['abook_censor'] || $item['owner']['abook_censor'] || $item['author']['xchan_selfcensored'] || $item['owner']['xchan_selfcensored'] || $item['author']['xchan_censored'] || $item['owner']['xchan_censored'] || intval($item['item_nsfw'])) && (get_safemode())) - ? true - : false - ); + $censored = ((($item['author']['abook_censor'] || $item['owner']['abook_censor'] || $item['author']['xchan_selfcensored'] || $item['owner']['xchan_selfcensored'] || $item['author']['xchan_censored'] || $item['owner']['xchan_censored'] || intval($item['item_nsfw'])) && (get_safemode())) + ? true + : false + ); - if ($censored) { - if (! $opts) { - $opts = []; - } - $opts['censored'] = true; - } - - $s = ''; - $photo = ''; + if ($censored) { + if (! $opts) { + $opts = []; + } + $opts['censored'] = true; + } - $is_photo = ((($item['verb'] === ACTIVITY_POST) && ($item['obj_type'] === ACTIVITY_OBJ_PHOTO)) ? true : false); + $s = ''; + $photo = ''; - if ($is_photo && ! $censored) { + $is_photo = ((($item['verb'] === ACTIVITY_POST) && ($item['obj_type'] === ACTIVITY_OBJ_PHOTO)) ? true : false); - $object = json_decode($item['obj'],true); - $ptr = null; + if ($is_photo && ! $censored) { + $object = json_decode($item['obj'], true); + $ptr = null; - if (is_array($object) && array_key_exists('url',$object) && is_array($object['url'])) { - if (array_key_exists(0,$object['url'])) { - foreach ($object['url'] as $link) { - if(array_key_exists('width',$link) && $link['width'] >= 640 && $link['width'] <= 1024) { - $ptr = $link; - } - } - if (! $ptr) { - $ptr = $object['url'][0]; - } - } - else { - $ptr = $object['url']; - } + if (is_array($object) && array_key_exists('url', $object) && is_array($object['url'])) { + if (array_key_exists(0, $object['url'])) { + foreach ($object['url'] as $link) { + if (array_key_exists('width', $link) && $link['width'] >= 640 && $link['width'] <= 1024) { + $ptr = $link; + } + } + if (! $ptr) { + $ptr = $object['url'][0]; + } + } else { + $ptr = $object['url']; + } - // if original photo width is > 640px make it a cover photo - if ($ptr) { - $alt_text = ' alt="' . ((isset($ptr['summary']) && $ptr['summary']) ? htmlspecialchars($ptr['summary'], ENT_QUOTES, 'UTF-8') : t('Image/photo')) . '"'; - $title_text = ' title="' . ((isset($ptr['summary']) && $ptr['summary']) ? htmlspecialchars($ptr['summary'], ENT_QUOTES, 'UTF-8') : t('Image/photo')) . '"'; + // if original photo width is > 640px make it a cover photo + if ($ptr) { + $alt_text = ' alt="' . ((isset($ptr['summary']) && $ptr['summary']) ? htmlspecialchars($ptr['summary'], ENT_QUOTES, 'UTF-8') : t('Image/photo')) . '"'; + $title_text = ' title="' . ((isset($ptr['summary']) && $ptr['summary']) ? htmlspecialchars($ptr['summary'], ENT_QUOTES, 'UTF-8') : t('Image/photo')) . '"'; - if (array_key_exists('width',$ptr) && $ptr['width'] > 640) { - $photo = ''; - } - else { - $item['body'] = '[zmg' . $alt_text . ']' . $ptr['href'] . '[/zmg]' . "\n\n" . $item['body']; - } - } - } - } + if (array_key_exists('width', $ptr) && $ptr['width'] > 640) { + $photo = ''; + } else { + $item['body'] = '[zmg' . $alt_text . ']' . $ptr['href'] . '[/zmg]' . "\n\n" . $item['body']; + } + } + } + } - if($item['item_obscured']) { - $s .= prepare_binary($item); - } - else { - if($item['summary']) { - // 8203 is a zero-width space so as not to trigger a markdown link if the summary starts with parentheses - $s .= prepare_text('[summary]​' . $item['summary'] . '[/summary]​' . $item['body'],$item['mimetype'],$opts); - } - else { - if ($item['html']) { - $s .= smilies($item['html']); - } - else { - $s .= prepare_text($item['body'],$item['mimetype'], $opts); - } - } - } + if ($item['item_obscured']) { + $s .= prepare_binary($item); + } else { + if ($item['summary']) { + // 8203 is a zero-width space so as not to trigger a markdown link if the summary starts with parentheses + $s .= prepare_text('[summary]​' . $item['summary'] . '[/summary]​' . $item['body'], $item['mimetype'], $opts); + } else { + if ($item['html']) { + $s .= smilies($item['html']); + } else { + $s .= prepare_text($item['body'], $item['mimetype'], $opts); + } + } + } - $poll = (($item['obj_type'] === 'Question' && in_array($item['verb'],[ 'Create','Update' ])) ? format_poll($item, $s, $opts) : false); - if ($poll) { - $s = $poll; - } + $poll = (($item['obj_type'] === 'Question' && in_array($item['verb'], [ 'Create','Update' ])) ? format_poll($item, $s, $opts) : false); + if ($poll) { + $s = $poll; + } - $e = trim($item['body']); - $em = Emoji\is_single_emoji($e) || mb_strlen($e) === 1; - if ($em) { - $s = '' . trim($item['body']) . ''; - } + $e = trim($item['body']); + $em = Emoji\is_single_emoji($e) || mb_strlen($e) === 1; + if ($em) { + $s = '' . trim($item['body']) . ''; + } - $event = (($item['obj_type'] === ACTIVITY_OBJ_EVENT) ? format_event_obj($item['obj']) : false); + $event = (($item['obj_type'] === ACTIVITY_OBJ_EVENT) ? format_event_obj($item['obj']) : false); - // This is not the most pleasant UI element possible, but this is difficult to add to one of the templates. - // Eventually we may wish to add/remove to/from calendar in the message title area but it will take a chunk - // of code re-factoring to make that happen. + // This is not the most pleasant UI element possible, but this is difficult to add to one of the templates. + // Eventually we may wish to add/remove to/from calendar in the message title area but it will take a chunk + // of code re-factoring to make that happen. - if(is_array($event) && $event['header'] && $item['resource_id']) { - $event['header'] .= '' . ' ' . t('Added to your calendar'); - } + if (is_array($event) && $event['header'] && $item['resource_id']) { + $event['header'] .= '' . ' ' . t('Added to your calendar'); + } - $prep_arr = array( - 'item' => $item, - 'html' => $event ? $event['content'] : $s, - 'event' => ((is_array($event)) ? $event['header'] : EMPTY_STR), - 'photo' => $photo - ); + $prep_arr = array( + 'item' => $item, + 'html' => $event ? $event['content'] : $s, + 'event' => ((is_array($event)) ? $event['header'] : EMPTY_STR), + 'photo' => $photo + ); - call_hooks('prepare_body', $prep_arr); + call_hooks('prepare_body', $prep_arr); - $s = $prep_arr['html']; - $photo = $prep_arr['photo']; - $event = $prep_arr['event']; + $s = $prep_arr['html']; + $photo = $prep_arr['photo']; + $event = $prep_arr['event']; - if(! $attach) { - return $s; - } + if (! $attach) { + return $s; + } - if(strpos($s,'
                    ') !== false && $item['coord']) { - $x = generate_map(trim($item['coord'])); - if($x) { - $s = preg_replace('/\
                    /','$0' . $x,$s); - } - } + if (strpos($s, '
                    ') !== false && $item['coord']) { + $x = generate_map(trim($item['coord'])); + if ($x) { + $s = preg_replace('/\
                    /', '$0' . $x, $s); + } + } - $attachments = theme_attachments($item); + $attachments = theme_attachments($item); - $writeable = ((get_observer_hash() == $item['owner_xchan']) ? true : false); + $writeable = ((get_observer_hash() == $item['owner_xchan']) ? true : false); - $tags = format_hashtags($item); + $tags = format_hashtags($item); - $mentions = format_mentions($item); + $mentions = format_mentions($item); - $categories = format_categories($item,$writeable); + $categories = format_categories($item, $writeable); - if(local_channel() == $item['uid']) - $filer = format_filer($item); + if (local_channel() == $item['uid']) { + $filer = format_filer($item); + } - // Caching photos can use absurd amounts of space. - // Don't cache photos if somebody is just browsing the stream to the - // beginning of time. This optimises performance for viewing things created - // recently. Also catch an explicit expiration of 0 or "no cache". + // Caching photos can use absurd amounts of space. + // Don't cache photos if somebody is just browsing the stream to the + // beginning of time. This optimises performance for viewing things created + // recently. Also catch an explicit expiration of 0 or "no cache". - $cache_expire = intval(get_config('system', 'default_expire_days')); - if ($cache_expire <= 0) { - $cache_expire = 60; - } - $cache_enable = ((($cache_expire) && ($item['created'] < datetime_convert('UTC','UTC', 'now - ' . $cache_expire . ' days'))) ? false : true); + $cache_expire = intval(get_config('system', 'default_expire_days')); + if ($cache_expire <= 0) { + $cache_expire = 60; + } + $cache_enable = ((($cache_expire) && ($item['created'] < datetime_convert('UTC', 'UTC', 'now - ' . $cache_expire . ' days'))) ? false : true); - // disable Unicode RTL over-ride since it can destroy presentation in some cases, use HTML or CSS instead - $s = str_replace([ '‮', '‮', html_entity_decode('‮', ENT_QUOTES,'UTF-8') ],[ '','','' ],$s); + // disable Unicode RTL over-ride since it can destroy presentation in some cases, use HTML or CSS instead + $s = str_replace([ '‮', '‮', html_entity_decode('‮', ENT_QUOTES, 'UTF-8') ], [ '','','' ], $s); - if($s) - $s = sslify($s, $cache_enable); - if($photo) - $photo = sslify($photo, $cache_enable); - if($event) - $event = sslify($event, $cache_enable); - - $prep_arr = array( - 'item' => $item, - 'photo' => $photo, - 'html' => $s, - 'event' => $event, - 'categories' => $categories, - 'folders' => $filer, - 'tags' => $tags, - 'mentions' => $mentions, - 'attachments' => $attachments - ); + if ($s) { + $s = sslify($s, $cache_enable); + } + if ($photo) { + $photo = sslify($photo, $cache_enable); + } + if ($event) { + $event = sslify($event, $cache_enable); + } - call_hooks('prepare_body_final', $prep_arr); + $prep_arr = array( + 'item' => $item, + 'photo' => $photo, + 'html' => $s, + 'event' => $event, + 'categories' => $categories, + 'folders' => $filer, + 'tags' => $tags, + 'mentions' => $mentions, + 'attachments' => $attachments + ); - unset($prep_arr['item']); + call_hooks('prepare_body_final', $prep_arr); - return $prep_arr; + unset($prep_arr['item']); + + return $prep_arr; } -function separate_img_links($s) { - $x = preg_replace('/\\\<\/a\>/ism', - '
                    ' . t('Link') . '
                    ',$s); +function separate_img_links($s) +{ + $x = preg_replace( + '/\\\<\/a\>/ism', + '
                    ' . t('Link') . '
                    ', + $s + ); - return $x; -} + return $x; +} -function format_poll($item,$s,$opts) { +function format_poll($item, $s, $opts) +{ - if (! is_array($item['obj'])) { - $act = json_decode($item['obj'],true); - } - else { - $act = $item['obj']; - } + if (! is_array($item['obj'])) { + $act = json_decode($item['obj'], true); + } else { + $act = $item['obj']; + } - if (! is_array($act)) { - return EMPTY_STR; - } + if (! is_array($act)) { + return EMPTY_STR; + } - $commentable = can_comment_on_post(((local_channel()) ? get_observer_hash() : EMPTY_STR),$item); + $commentable = can_comment_on_post(((local_channel()) ? get_observer_hash() : EMPTY_STR), $item); - //logger('format_poll: ' . print_r($item,true)); - $activated = ((local_channel() && local_channel() == $item['uid']) ? true : false); - $output = $s . EOL. EOL; + //logger('format_poll: ' . print_r($item,true)); + $activated = ((local_channel() && local_channel() == $item['uid']) ? true : false); + $output = $s . EOL . EOL; - $closed = false; - $closing = false; + $closed = false; + $closing = false; - if ($item['comments_closed'] > NULL_DATE) { - $closing = true; - $t = datetime_convert('UTC',date_default_timezone_get(), $item['comments_closed'], 'Y-m-d H:i'); - $closed = ((datetime_convert() > $item['comments_closed']) ? true : false); - if ($closed) { - $commentable = false; - } - } + if ($item['comments_closed'] > NULL_DATE) { + $closing = true; + $t = datetime_convert('UTC', date_default_timezone_get(), $item['comments_closed'], 'Y-m-d H:i'); + $closed = ((datetime_convert() > $item['comments_closed']) ? true : false); + if ($closed) { + $commentable = false; + } + } - if ($act['type'] === 'Question') { - if ($activated and $commentable) { - $output .= '
                    '; - } - if (array_key_exists('anyOf',$act) && is_array($act['anyOf'])) { - foreach ($act['anyOf'] as $poll) { - if (array_key_exists('name',$poll) && $poll['name']) { - $text = html2plain(purify_html($poll['name']),256); - if (array_path_exists('replies/totalItems',$poll)) { - $total = $poll['replies']['totalItems']; - } - else { - $total = 0; - } - if ($activated && $commentable) { - $output .= ' ' . $text . '' . ' (' . $total . ')' . EOL; - } - else { - $output .= $text . ' (' . $total . ')' . EOL; - } - } - } - } - if (array_key_exists('oneOf',$act) && is_array($act['oneOf'])) { - $totalResponses = 0; - foreach ($act['oneOf'] as $poll) { - if (array_path_exists('replies/totalItems',$poll)) { - $totalResponses += intval($poll['replies']['totalItems']); - } - } - foreach ($act['oneOf'] as $poll) { - if (is_array($poll) && array_key_exists('name',$poll) && $poll['name']) { - $text = html2plain(purify_html($poll['name']),256); - if (array_path_exists('replies/totalItems',$poll)) { - $total = $poll['replies']['totalItems']; - } - else { - $total = 0; - } - if ($activated && $commentable) { - $output .= ' ' . $text . '' . ' (' . $total . ')' . (($totalResponses) ? ' ' . intval($total / $totalResponses * 100) . '%' : '') . EOL; - } - else { - $output .= $text . ' (' . $total . ')' . (($totalResponses) ? ' ' . intval($total / $totalResponses * 100) . '%' : '') . EOL; - } - - } - } - } - if ($closed) { - $message = t('Poll has ended.'); - } - elseif ($closing) { - $message = sprintf(t('Poll ends: %1$s (%2$s)'),relative_date($t),$t); - } - $output .= EOL . '
                    ' . $message . '
                    '; + if ($act['type'] === 'Question') { + if ($activated and $commentable) { + $output .= ''; + } + if (array_key_exists('anyOf', $act) && is_array($act['anyOf'])) { + foreach ($act['anyOf'] as $poll) { + if (array_key_exists('name', $poll) && $poll['name']) { + $text = html2plain(purify_html($poll['name']), 256); + if (array_path_exists('replies/totalItems', $poll)) { + $total = $poll['replies']['totalItems']; + } else { + $total = 0; + } + if ($activated && $commentable) { + $output .= ' ' . $text . '' . ' (' . $total . ')' . EOL; + } else { + $output .= $text . ' (' . $total . ')' . EOL; + } + } + } + } + if (array_key_exists('oneOf', $act) && is_array($act['oneOf'])) { + $totalResponses = 0; + foreach ($act['oneOf'] as $poll) { + if (array_path_exists('replies/totalItems', $poll)) { + $totalResponses += intval($poll['replies']['totalItems']); + } + } + foreach ($act['oneOf'] as $poll) { + if (is_array($poll) && array_key_exists('name', $poll) && $poll['name']) { + $text = html2plain(purify_html($poll['name']), 256); + if (array_path_exists('replies/totalItems', $poll)) { + $total = $poll['replies']['totalItems']; + } else { + $total = 0; + } + if ($activated && $commentable) { + $output .= ' ' . $text . '' . ' (' . $total . ')' . (($totalResponses) ? ' ' . intval($total / $totalResponses * 100) . '%' : '') . EOL; + } else { + $output .= $text . ' (' . $total . ')' . (($totalResponses) ? ' ' . intval($total / $totalResponses * 100) . '%' : '') . EOL; + } + } + } + } + if ($closed) { + $message = t('Poll has ended.'); + } elseif ($closing) { + $message = sprintf(t('Poll ends: %1$s (%2$s)'), relative_date($t), $t); + } + $output .= EOL . '
                    ' . $message . '
                    '; - if ($activated and $commentable) { - $output .= EOL . ''. '
                    '; - } - - - } - return $output; + if ($activated and $commentable) { + $output .= EOL . '' . ''; + } + } + return $output; } -function prepare_binary($item) { - return replace_macros(get_markup_template('item_binary.tpl'), [ - '$download' => t('Download binary/encrypted content'), - '$url' => z_root() . '/viewsrc/' . $item['id'] . '/download' - ]); +function prepare_binary($item) +{ + return replace_macros(get_markup_template('item_binary.tpl'), [ + '$download' => t('Download binary/encrypted content'), + '$url' => z_root() . '/viewsrc/' . $item['id'] . '/download' + ]); } @@ -1980,202 +2070,225 @@ function prepare_binary($item) { * * @param string $text * @param string $content_type (optional) default text/bbcode - * @param boolean $cache (optional) default false + * @param bool $cache (optional) default false * * @return string */ -function prepare_text($text, $content_type = 'text/x-multicode', $opts = false) { +function prepare_text($text, $content_type = 'text/x-multicode', $opts = false) +{ - switch($content_type) { - case 'text/plain': - $s = escape_tags($text); - break; + switch ($content_type) { + case 'text/plain': + $s = escape_tags($text); + break; - case 'text/html': - $s = $text; - break; + case 'text/html': + $s = $text; + break; - case 'text/markdown': - $text = MarkdownSoap::unescape($text); - $s = MarkdownExtra::defaultTransform($text); - break; + case 'text/markdown': + $text = MarkdownSoap::unescape($text); + $s = MarkdownExtra::defaultTransform($text); + break; - case 'application/x-pdl'; - $s = escape_tags($text); - break; + case 'application/x-pdl'; + $s = escape_tags($text); + break; - // No security checking is done here at display time - so we need to verify - // that the author is allowed to use PHP before storing. We also cannot allow - // importation of PHP text bodies from other sites. Therefore this content - // type is only valid for web pages (and profile details). + // No security checking is done here at display time - so we need to verify + // that the author is allowed to use PHP before storing. We also cannot allow + // importation of PHP text bodies from other sites. Therefore this content + // type is only valid for web pages (and profile details). - // It may be possible to provide a PHP message body which is evaluated on the - // sender's site before sending it elsewhere. In that case we will have a - // different content-type here. + // It may be possible to provide a PHP message body which is evaluated on the + // sender's site before sending it elsewhere. In that case we will have a + // different content-type here. - case 'application/x-php': - ob_start(); - eval($text); - $s = ob_get_contents(); - ob_end_clean(); - break; + case 'application/x-php': + ob_start(); + eval($text); + $s = ob_get_contents(); + ob_end_clean(); + break; - case 'text/bbcode': - case 'text/x-multicode': - case '': - default: - require_once('include/bbcode.php'); + case 'text/bbcode': + case 'text/x-multicode': + case '': + default: + require_once('include/bbcode.php'); - if(stristr($text,'[nosmile]')) - $s = bbcode($text, [ 'cache' => $cache ]); - else - $s = smilies(bbcode($text, ((is_array($opts)) ? $opts : [] ))); + if (stristr($text, '[nosmile]')) { + $s = bbcode($text, [ 'cache' => $cache ]); + } else { + $s = smilies(bbcode($text, ((is_array($opts)) ? $opts : [] ))); + } - $s = zidify_links($s); + $s = zidify_links($s); - break; - } + break; + } //logger('prepare_text: ' . $s); - return $s; + return $s; } -function create_export_photo_body(&$item) { - if(($item['verb'] === ACTIVITY_POST) && ($item['obj_type'] === ACTIVITY_OBJ_PHOTO)) { - $j = json_decode($item['obj'],true); - if($j) { - $item['body'] .= "\n\n" . (($j['source']['content']) ? $j['source']['content'] : $item['content']); - $item['sig'] = ''; - } - } +function create_export_photo_body(&$item) +{ + if (($item['verb'] === ACTIVITY_POST) && ($item['obj_type'] === ACTIVITY_OBJ_PHOTO)) { + $j = json_decode($item['obj'], true); + if ($j) { + $item['body'] .= "\n\n" . (($j['source']['content']) ? $j['source']['content'] : $item['content']); + $item['sig'] = ''; + } + } } -function get_plink($item,$conversation_mode = true) { - if($conversation_mode) - $key = 'plink'; - else - $key = 'llink'; +function get_plink($item, $conversation_mode = true) +{ + if ($conversation_mode) { + $key = 'plink'; + } else { + $key = 'llink'; + } - $zidify = true; + $zidify = true; - if(array_key_exists('author',$item) && ! in_array($item['author']['xchan_network'],['nomad','zot6'])) + + if (array_key_exists('author', $item) && ! in_array($item['author']['xchan_network'], [ 'nomad', 'zot6' ])) { $zidify = false; + } - if(x($item,$key)) { - return [ - 'href' => (($zidify) ? zid($item[$key]) : $item[$key]), - 'title' => t('Link to Source'), - ]; - } - else { - return false; - } + if (x($item, $key)) { + return [ + 'href' => (($zidify) ? zid($item[$key]) : $item[$key]), + 'title' => t('Link to Source'), + ]; + } else { + return false; + } } -function layout_select($channel_id, $current = '') { - $r = q("select mid, v from item left join iconfig on iconfig.iid = item.id +function layout_select($channel_id, $current = '') +{ + $r = q( + "select mid, v from item left join iconfig on iconfig.iid = item.id where iconfig.cat = 'system' and iconfig.k = 'PDL' and item.uid = %d and item_type = %d ", - intval($channel_id), - intval(ITEM_TYPE_PDL) - ); + intval($channel_id), + intval(ITEM_TYPE_PDL) + ); - if($r) { - $empty_selected = (($current === false) ? ' selected="selected" ' : ''); - $options .= ''; - foreach($r as $rr) { - $selected = (($rr['mid'] == $current) ? ' selected="selected" ' : ''); - $options .= ''; - } - } + if ($r) { + $empty_selected = (($current === false) ? ' selected="selected" ' : ''); + $options .= ''; + foreach ($r as $rr) { + $selected = (($rr['mid'] == $current) ? ' selected="selected" ' : ''); + $options .= ''; + } + } - $o = replace_macros(get_markup_template('field_select_raw.tpl'), array( - '$field' => array('layout_mid', t('Page layout'), $selected, t('You can create your own with the layouts tool'), $options) - )); + $o = replace_macros(get_markup_template('field_select_raw.tpl'), array( + '$field' => array('layout_mid', t('Page layout'), $selected, t('You can create your own with the layouts tool'), $options) + )); - return $o; + return $o; } -function mimetype_select($channel_id, $current = 'text/x-multicode', $choices = null, $element = 'mimetype') { +function mimetype_select($channel_id, $current = 'text/x-multicode', $choices = null, $element = 'mimetype') +{ - $x = (($choices) ? $choices : [ - 'text/bbcode' => t('BBcode'), + $x = (($choices) ? $choices : [ + 'text/bbcode' => t('BBcode'), 'text/x-multicode' => t('Multicode'), - 'text/html' => t('HTML'), - 'text/markdown' => t('Markdown'), - 'text/plain' => t('Text'), - 'application/x-pdl' => t('Comanche Layout') - ]); + 'text/html' => t('HTML'), + 'text/markdown' => t('Markdown'), + 'text/plain' => t('Text'), + 'application/x-pdl' => t('Comanche Layout') + ]); + if ((App::$is_sys) || (channel_codeallowed($channel_id) && $channel_id == local_channel())) { + $x['application/x-php'] = t('PHP'); + } - if((App::$is_sys) || (channel_codeallowed($channel_id) && $channel_id == local_channel())){ - $x['application/x-php'] = t('PHP'); - } + foreach ($x as $y => $z) { + $selected = (($y == $current) ? ' selected="selected" ' : ''); + $options .= ''; + } - foreach($x as $y => $z) { - $selected = (($y == $current) ? ' selected="selected" ' : ''); - $options .= ''; - } + $o = replace_macros(get_markup_template('field_select_raw.tpl'), array( + '$field' => array( $element, t('Page content type'), $selected, '', $options) + )); - $o = replace_macros(get_markup_template('field_select_raw.tpl'), array( - '$field' => array( $element, t('Page content type'), $selected, '', $options) - )); - - return $o; + return $o; } -function engr_units_to_bytes ($size_str) { - if(! $size_str) - return $size_str; - switch (substr(trim($size_str), -1)) { - case 'M': case 'm': return (int)$size_str * 1048576; - case 'K': case 'k': return (int)$size_str * 1024; - case 'G': case 'g': return (int)$size_str * 1073741824; - default: return $size_str; - } +function engr_units_to_bytes($size_str) +{ + if (! $size_str) { + return $size_str; + } + switch (substr(trim($size_str), -1)) { + case 'M': + case 'm': + return (int)$size_str * 1048576; + case 'K': + case 'k': + return (int)$size_str * 1024; + case 'G': + case 'g': + return (int)$size_str * 1073741824; + default: + return $size_str; + } } -function base64url_encode($s, $strip_padding = true) { +function base64url_encode($s, $strip_padding = true) +{ - $s = strtr(base64_encode($s),'+/','-_'); + $s = strtr(base64_encode($s), '+/', '-_'); - if($strip_padding) - $s = str_replace('=','',$s); + if ($strip_padding) { + $s = str_replace('=', '', $s); + } - return $s; + return $s; } -function base64url_decode($s) { - if(is_array($s)) { - logger('base64url_decode: illegal input: ' . print_r(debug_backtrace(), true)); - return $s; - } - return base64_decode(strtr($s,'-_','+/')); +function base64url_decode($s) +{ + if (is_array($s)) { + logger('base64url_decode: illegal input: ' . print_r(debug_backtrace(), true)); + return $s; + } + return base64_decode(strtr($s, '-_', '+/')); } -function base64special_encode($s, $strip_padding = true) { +function base64special_encode($s, $strip_padding = true) +{ - $s = strtr(base64_encode($s),'+/',',.'); + $s = strtr(base64_encode($s), '+/', ',.'); - if($strip_padding) - $s = str_replace('=','',$s); + if ($strip_padding) { + $s = str_replace('=', '', $s); + } - return $s; + return $s; } -function base64special_decode($s) { - if(is_array($s)) { - logger('base64url_decode: illegal input: ' . print_r(debug_backtrace(), true)); - return $s; - } - return base64_decode(strtr($s,',.','+/')); +function base64special_decode($s) +{ + if (is_array($s)) { + logger('base64url_decode: illegal input: ' . print_r(debug_backtrace(), true)); + return $s; + } + return base64_decode(strtr($s, ',.', '+/')); } /** @@ -2183,269 +2296,295 @@ function base64special_decode($s) { * * @return string */ -function cleardiv() { - return '
                    '; +function cleardiv() +{ + return '
                    '; } -function bb_translate_video($s) { - $arr = array('string' => $s); - call_hooks('bb_translate_video',$arr); - return $arr['string']; +function bb_translate_video($s) +{ + $arr = array('string' => $s); + call_hooks('bb_translate_video', $arr); + return $arr['string']; } -function html2bb_video($s) { - $arr = array('string' => $s); - call_hooks('html2bb_video',$arr); - return $arr['string']; +function html2bb_video($s) +{ + $arr = array('string' => $s); + call_hooks('html2bb_video', $arr); + return $arr['string']; } /** * apply xmlify() to all values of array $val, recursively */ -function array_xmlify($val) { - if (is_bool($val)) return $val?"true":"false"; - if (is_array($val)) return array_map('array_xmlify', $val); - return xmlify((string) $val); +function array_xmlify($val) +{ + if (is_bool($val)) { + return $val ? "true" : "false"; + } + if (is_array($val)) { + return array_map('array_xmlify', $val); + } + return xmlify((string) $val); } -function reltoabs($text, $base) { - if (empty($base)) - return $text; +function reltoabs($text, $base) +{ + if (empty($base)) { + return $text; + } - $base = rtrim($base,'/'); + $base = rtrim($base, '/'); - $base2 = $base . "/"; + $base2 = $base . "/"; - // Replace links - $pattern = "/]*) href=\"(?!http|https|\/)([^\"]*)\"/"; - $replace = " 1900) { - $y = date('Y'); - if($i <= $y+1 && strpos($s,'-') == 4) { - $m = intval(substr($s,5)); - if($m > 0 && $m <= 12) - return true; - } - } +function is_a_date_arg($s) +{ + $i = intval($s); + if ($i > 1900) { + $y = date('Y'); + if ($i <= $y + 1 && strpos($s, '-') == 4) { + $m = intval(substr($s, 5)); + if ($m > 0 && $m <= 12) { + return true; + } + } + } - return false; + return false; } -function legal_webbie($s) { - if(! $s) - return ''; +function legal_webbie($s) +{ + if (! $s) { + return ''; + } - // WARNING: This regex may not work in a federated environment. - // You will probably want something like - // preg_replace('/([^a-z0-9\_])/','',strtolower($s)); + // WARNING: This regex may not work in a federated environment. + // You will probably want something like + // preg_replace('/([^a-z0-9\_])/','',strtolower($s)); - $r = preg_replace('/([^a-z0-9\-\_])/','',strtolower($s)); - - $x = [ 'input' => $s, 'output' => $r ]; - call_hooks('legal_webbie',$x); - return $x['output']; + $r = preg_replace('/([^a-z0-9\-\_])/', '', strtolower($s)); + $x = [ 'input' => $s, 'output' => $r ]; + call_hooks('legal_webbie', $x); + return $x['output']; } -function legal_webbie_text() { +function legal_webbie_text() +{ - // WARNING: This will not work in a federated environment. + // WARNING: This will not work in a federated environment. - $s = t('a-z, 0-9, -, and _ only'); - - $x = [ 'text' => $s ]; - call_hooks('legal_webbie_text',$x); - return $x['text']; + $s = t('a-z, 0-9, -, and _ only'); + $x = [ 'text' => $s ]; + call_hooks('legal_webbie_text', $x); + return $x['text']; } -function check_webbie($arr) { +function check_webbie($arr) +{ - // These names conflict with the CalDAV server - $taken = [ 'principals', 'addressbooks', 'calendars' ]; + // These names conflict with the CalDAV server + $taken = [ 'principals', 'addressbooks', 'calendars' ]; - $reservechan = get_config('system','reserved_channels'); - if(strlen($reservechan)) { - $taken = array_merge($taken,explode(',', $reservechan)); - } + $reservechan = get_config('system', 'reserved_channels'); + if (strlen($reservechan)) { + $taken = array_merge($taken, explode(',', $reservechan)); + } - $str = ''; - if(count($arr)) { - foreach($arr as $x) { - $y = legal_webbie($x); - if(strlen($y)) { - if($str) - $str .= ','; - $str .= "'" . dbesc($y) . "'"; - } - } + $str = ''; + if (count($arr)) { + foreach ($arr as $x) { + $y = legal_webbie($x); + if (strlen($y)) { + if ($str) { + $str .= ','; + } + $str .= "'" . dbesc($y) . "'"; + } + } - if(strlen($str)) { - $r = q("select channel_address from channel where channel_address in ( $str ) "); - if(count($r)) { - foreach($r as $rr) { - $taken[] = $rr['channel_address']; - } - } - foreach($arr as $x) { - $y = legal_webbie($x); - if(! in_array($y,$taken)) { - return $y; - } - } - } - } + if (strlen($str)) { + $r = q("select channel_address from channel where channel_address in ( $str ) "); + if (count($r)) { + foreach ($r as $rr) { + $taken[] = $rr['channel_address']; + } + } + foreach ($arr as $x) { + $y = legal_webbie($x); + if (! in_array($y, $taken)) { + return $y; + } + } + } + } - return ''; + return ''; } -function ids_to_array($arr,$idx = 'id') { - $t = []; - if($arr) { - foreach($arr as $x) { - if(array_key_exists($idx,$x) && strlen($x[$idx]) && (! in_array($x[$idx],$t))) { - $t[] = $x[$idx]; - } - } - } - return($t); +function ids_to_array($arr, $idx = 'id') +{ + $t = []; + if ($arr) { + foreach ($arr as $x) { + if (array_key_exists($idx, $x) && strlen($x[$idx]) && (! in_array($x[$idx], $t))) { + $t[] = $x[$idx]; + } + } + } + return($t); } -function ids_to_querystr($arr,$idx = 'id',$quote = false) { - $t = []; - if($arr) { - foreach($arr as $x) { - if(! in_array($x[$idx],$t)) { - if($quote) - $t[] = "'" . dbesc($x[$idx]) . "'"; - else - $t[] = $x[$idx]; - } - } - } - return(implode(',', $t)); +function ids_to_querystr($arr, $idx = 'id', $quote = false) +{ + $t = []; + if ($arr) { + foreach ($arr as $x) { + if (! in_array($x[$idx], $t)) { + if ($quote) { + $t[] = "'" . dbesc($x[$idx]) . "'"; + } else { + $t[] = $x[$idx]; + } + } + } + } + return(implode(',', $t)); } /** * @brief array_elm_to_str($arr,$elm,$delim = ',') extract unique individual elements from an array of arrays and return them as a string separated by a delimiter - * similar to ids_to_querystr, but allows a different delimiter instead of a db-quote option + * similar to ids_to_querystr, but allows a different delimiter instead of a db-quote option * empty elements (evaluated after trim()) are ignored. * @param $arr array * @param $elm array key to extract from sub-array @@ -2454,24 +2593,26 @@ function ids_to_querystr($arr,$idx = 'id',$quote = false) { * @returns string */ -function array_elm_to_str($arr,$elm,$delim = ',',$each = 'trim') { +function array_elm_to_str($arr, $elm, $delim = ',', $each = 'trim') +{ - $tmp = []; - if($arr && is_array($arr)) { - foreach($arr as $x) { - if(is_array($x) && array_key_exists($elm,$x)) { - $z = $each($x[$elm]); - if(($z) && (! in_array($z,$tmp))) { - $tmp[] = $z; - } - } - } - } - return implode($delim,$tmp); + $tmp = []; + if ($arr && is_array($arr)) { + foreach ($arr as $x) { + if (is_array($x) && array_key_exists($elm, $x)) { + $z = $each($x[$elm]); + if (($z) && (! in_array($z, $tmp))) { + $tmp[] = $z; + } + } + } + } + return implode($delim, $tmp); } -function trim_and_unpunify($s) { - return unpunify(trim($s)); +function trim_and_unpunify($s) +{ + return unpunify(trim($s)); } @@ -2484,111 +2625,125 @@ function trim_and_unpunify($s) { * save extra per item lookups there. * * @param[in,out] array &$items - * @param boolean $abook If true also include the abook info + * @param bool $abook If true also include the abook info * @param number $effective_uid */ -function xchan_query(&$items, $abook = true, $effective_uid = 0) { - $arr = []; +function xchan_query(&$items, $abook = true, $effective_uid = 0) +{ + $arr = []; - if($items && count($items)) { + if ($items && count($items)) { + if ($effective_uid) { + for ($x = 0; $x < count($items); $x++) { + $items[$x]['real_uid'] = $items[$x]['uid']; + $items[$x]['uid'] = $effective_uid; + } + } - if($effective_uid) { - for($x = 0; $x < count($items); $x ++) { - $items[$x]['real_uid'] = $items[$x]['uid']; - $items[$x]['uid'] = $effective_uid; - } - } - - foreach($items as $item) { - if($item['owner_xchan'] && (! in_array("'" . dbesc($item['owner_xchan']) . "'",$arr))) - $arr[] = "'" . dbesc($item['owner_xchan']) . "'"; - if($item['author_xchan'] && (! in_array("'" . dbesc($item['author_xchan']) . "'",$arr))) - $arr[] = "'" . dbesc($item['author_xchan']) . "'"; - } - } - if(count($arr)) { - if($abook) { - $chans = q("select * from xchan left join hubloc on hubloc_hash = xchan_hash left join abook on abook_xchan = xchan_hash and abook_channel = %d + foreach ($items as $item) { + if ($item['owner_xchan'] && (! in_array("'" . dbesc($item['owner_xchan']) . "'", $arr))) { + $arr[] = "'" . dbesc($item['owner_xchan']) . "'"; + } + if ($item['author_xchan'] && (! in_array("'" . dbesc($item['author_xchan']) . "'", $arr))) { + $arr[] = "'" . dbesc($item['author_xchan']) . "'"; + } + } + } + if (count($arr)) { + if ($abook) { + $chans = q( + "select * from xchan left join hubloc on hubloc_hash = xchan_hash left join abook on abook_xchan = xchan_hash and abook_channel = %d where xchan_hash in (" . protect_sprintf(implode(',', $arr)) . ") order by hubloc_primary desc", - intval($item['uid']) - ); - } - else { - $chans = q("select xchan.*,hubloc.* from xchan left join hubloc on hubloc_hash = xchan_hash + intval($item['uid']) + ); + } else { + $chans = q("select xchan.*,hubloc.* from xchan left join hubloc on hubloc_hash = xchan_hash where xchan_hash in (" . protect_sprintf(implode(',', $arr)) . ") order by hubloc_primary desc"); - } - $xchans = q("select * from xchan where xchan_hash in (" . protect_sprintf(implode(',',$arr)) . ") and xchan_network in ('rss','unknown', 'anon')"); - if(! $chans) - $chans = $xchans; - else - $chans = array_merge($xchans,$chans); - } + } + $xchans = q("select * from xchan where xchan_hash in (" . protect_sprintf(implode(',', $arr)) . ") and xchan_network in ('rss','unknown', 'anon')"); + if (! $chans) { + $chans = $xchans; + } else { + $chans = array_merge($xchans, $chans); + } + } - if($items && count($items) && $chans && count($chans)) { - for($x = 0; $x < count($items); $x ++) { - $items[$x]['owner'] = find_xchan_in_array($items[$x]['owner_xchan'],$chans); - $items[$x]['author'] = find_xchan_in_array($items[$x]['author_xchan'],$chans); - } - } + if ($items && count($items) && $chans && count($chans)) { + for ($x = 0; $x < count($items); $x++) { + $items[$x]['owner'] = find_xchan_in_array($items[$x]['owner_xchan'], $chans); + $items[$x]['author'] = find_xchan_in_array($items[$x]['author_xchan'], $chans); + } + } } -function xchan_mail_query(&$item) { - $arr = []; - $chans = null; - if($item) { - if($item['from_xchan'] && (! in_array("'" . dbesc($item['from_xchan']) . "'",$arr))) - $arr[] = "'" . dbesc($item['from_xchan']) . "'"; - if($item['to_xchan'] && (! in_array("'" . dbesc($item['to_xchan']) . "'",$arr))) - $arr[] = "'" . dbesc($item['to_xchan']) . "'"; - } +function xchan_mail_query(&$item) +{ + $arr = []; + $chans = null; + if ($item) { + if ($item['from_xchan'] && (! in_array("'" . dbesc($item['from_xchan']) . "'", $arr))) { + $arr[] = "'" . dbesc($item['from_xchan']) . "'"; + } + if ($item['to_xchan'] && (! in_array("'" . dbesc($item['to_xchan']) . "'", $arr))) { + $arr[] = "'" . dbesc($item['to_xchan']) . "'"; + } + } - if(count($arr)) { - $chans = q("select xchan.*,hubloc.* from xchan left join hubloc on hubloc_hash = xchan_hash + if (count($arr)) { + $chans = q("select xchan.*,hubloc.* from xchan left join hubloc on hubloc_hash = xchan_hash where xchan_hash in (" . protect_sprintf(implode(',', $arr)) . ") and hubloc_primary = 1"); - } - if($chans) { - $item['from'] = find_xchan_in_array($item['from_xchan'],$chans); - $item['to'] = find_xchan_in_array($item['to_xchan'],$chans); - } + } + if ($chans) { + $item['from'] = find_xchan_in_array($item['from_xchan'], $chans); + $item['to'] = find_xchan_in_array($item['to_xchan'], $chans); + } } -function find_xchan_in_array($xchan,$arr) { - if(count($arr)) { - foreach($arr as $x) { - if($x['xchan_hash'] === $xchan) { - return $x; - } - } - } - return []; +function find_xchan_in_array($xchan, $arr) +{ + if (count($arr)) { + foreach ($arr as $x) { + if ($x['xchan_hash'] === $xchan) { + return $x; + } + } + } + return []; } -function get_rel_link($j,$rel) { - if(is_array($j) && ($j)) - foreach($j as $l) - if(is_array($l) && array_key_exists('rel',$l) && $l['rel'] === $rel && array_key_exists('href',$l)) - return $l['href']; +function get_rel_link($j, $rel) +{ + if (is_array($j) && ($j)) { + foreach ($j as $l) { + if (is_array($l) && array_key_exists('rel', $l) && $l['rel'] === $rel && array_key_exists('href', $l)) { + return $l['href']; + } + } + } - return ''; + return ''; } // Lots of code to write here -function magic_link($s) { - return $s; +function magic_link($s) +{ + return $s; } /** * @brief If $escape is true, dbesc() each element before adding quotes. * * @param[in,out] array &$arr - * @param boolean $escape (optional) default false + * @param bool $escape (optional) default false */ -function stringify_array_elms(&$arr, $escape = false) { - for($x = 0; $x < count($arr); $x ++) - $arr[$x] = "'" . (($escape) ? dbesc($arr[$x]) : $arr[$x]) . "'"; +function stringify_array_elms(&$arr, $escape = false) +{ + for ($x = 0; $x < count($arr); $x++) { + $arr[$x] = "'" . (($escape) ? dbesc($arr[$x]) : $arr[$x]) . "'"; + } } @@ -2596,15 +2751,16 @@ function stringify_array_elms(&$arr, $escape = false) { * @brief Similar to stringify_array_elms but returns a string. If $escape is true, dbesc() each element before adding quotes. * * @param array $arr - * @param boolean $escape (optional) default false + * @param bool $escape (optional) default false * @return string */ -function stringify_array($arr, $escape = false) { - if($arr) { - stringify_array_elms($arr, $escape); - return(implode(',',$arr)); - } - return EMPTY_STR; +function stringify_array($arr, $escape = false) +{ + if ($arr) { + stringify_array_elms($arr, $escape); + return(implode(',', $arr)); + } + return EMPTY_STR; } @@ -2614,58 +2770,59 @@ function stringify_array($arr, $escape = false) { * @param string $json The original JSON string to process. * @return string Indented version of the original JSON string. */ -function jindent($json) { +function jindent($json) +{ - $result = ''; - $pos = 0; - $strLen = strlen($json); - $indentStr = ' '; - $newLine = "\n"; - $prevChar = ''; - $outOfQuotes = true; + $result = ''; + $pos = 0; + $strLen = strlen($json); + $indentStr = ' '; + $newLine = "\n"; + $prevChar = ''; + $outOfQuotes = true; - if (is_array($json)) { - btlogger('is an array', LOGGER_DATA); - $json = json_encode($json,JSON_UNESCAPED_SLASHES); - } + if (is_array($json)) { + btlogger('is an array', LOGGER_DATA); + $json = json_encode($json, JSON_UNESCAPED_SLASHES); + } - for ($i = 0; $i <= $strLen; $i++) { - // Grab the next character in the string. - $char = substr($json, $i, 1); + for ($i = 0; $i <= $strLen; $i++) { + // Grab the next character in the string. + $char = substr($json, $i, 1); - // Are we inside a quoted string? - if ($char == '"' && $prevChar != '\\') { - $outOfQuotes = !$outOfQuotes; - } - // If this character is the end of an element, - // output a new line and indent the next line. - elseif(($char == '}' || $char == ']') && $outOfQuotes) { - $result .= $newLine; - $pos --; - for ($j = 0; $j < $pos; $j++) { - $result .= $indentStr; - } - } + // Are we inside a quoted string? + if ($char == '"' && $prevChar != '\\') { + $outOfQuotes = !$outOfQuotes; + } + // If this character is the end of an element, + // output a new line and indent the next line. + elseif (($char == '}' || $char == ']') && $outOfQuotes) { + $result .= $newLine; + $pos--; + for ($j = 0; $j < $pos; $j++) { + $result .= $indentStr; + } + } - // Add the character to the result string. - $result .= $char; + // Add the character to the result string. + $result .= $char; - // If the last character was the beginning of an element, - // output a new line and indent the next line. - if (($char == ',' || $char == '{' || $char == '[') && $outOfQuotes) { - $result .= $newLine; - if ($char == '{' || $char == '[') { - $pos ++; - } + // If the last character was the beginning of an element, + // output a new line and indent the next line. + if (($char == ',' || $char == '{' || $char == '[') && $outOfQuotes) { + $result .= $newLine; + if ($char == '{' || $char == '[') { + $pos++; + } - for ($j = 0; $j < $pos; $j++) { - $result .= $indentStr; - } - } - $prevChar = $char; - } + for ($j = 0; $j < $pos; $j++) { + $result .= $indentStr; + } + } + $prevChar = $char; + } - return $result; + return $result; } /** @@ -2673,28 +2830,29 @@ function jindent($json) { * * @return string with parsed HTML */ -function design_tools() { +function design_tools() +{ - $channel = channelx_by_n(App::$profile['profile_uid']); - $sys = false; + $channel = channelx_by_n(App::$profile['profile_uid']); + $sys = false; - if(App::$is_sys && is_site_admin()) { - require_once('include/channel.php'); - $channel = get_sys_channel(); - $sys = true; - } + if (App::$is_sys && is_site_admin()) { + require_once('include/channel.php'); + $channel = get_sys_channel(); + $sys = true; + } - $who = $channel['channel_address']; + $who = $channel['channel_address']; - return replace_macros(get_markup_template('design_tools.tpl'), array( - '$title' => t('Design Tools'), - '$who' => $who, - '$sys' => $sys, - '$blocks' => t('Blocks'), - '$menus' => t('Menus'), - '$layout' => t('Layouts'), - '$pages' => t('Pages') - )); + return replace_macros(get_markup_template('design_tools.tpl'), array( + '$title' => t('Design Tools'), + '$who' => $who, + '$sys' => $sys, + '$blocks' => t('Blocks'), + '$menus' => t('Menus'), + '$layout' => t('Layouts'), + '$pages' => t('Pages') + )); } /** @@ -2702,35 +2860,36 @@ function design_tools() { * * @return string */ -function website_portation_tools() { +function website_portation_tools() +{ - $channel = App::get_channel(); - $sys = false; + $channel = App::get_channel(); + $sys = false; - if(App::$is_sys && is_site_admin()) { - require_once('include/channel.php'); - $channel = get_sys_channel(); - $sys = true; - } + if (App::$is_sys && is_site_admin()) { + require_once('include/channel.php'); + $channel = get_sys_channel(); + $sys = true; + } - return replace_macros(get_markup_template('website_portation_tools.tpl'), array( - '$title' => t('Import'), - '$import_label' => t('Import website...'), - '$import_placeholder' => t('Select folder to import'), - '$file_upload_text' => t('Import from a zipped folder:'), - '$file_import_text' => t('Import from cloud files:'), - '$desc' => t('/cloud/channel/path/to/folder'), - '$hint' => t('Enter path to website files'), - '$select' => t('Select folder'), - '$export_label' => t('Export website...'), - '$file_download_text' => t('Export to a zip file'), - '$filename_desc' => t('website.zip'), - '$filename_hint' => t('Enter a name for the zip file.'), - '$cloud_export_text' => t('Export to cloud files'), - '$cloud_export_desc' => t('/path/to/export/folder'), - '$cloud_export_hint' => t('Enter a path to a cloud files destination.'), - '$cloud_export_select' => t('Specify folder'), - )); + return replace_macros(get_markup_template('website_portation_tools.tpl'), array( + '$title' => t('Import'), + '$import_label' => t('Import website...'), + '$import_placeholder' => t('Select folder to import'), + '$file_upload_text' => t('Import from a zipped folder:'), + '$file_import_text' => t('Import from cloud files:'), + '$desc' => t('/cloud/channel/path/to/folder'), + '$hint' => t('Enter path to website files'), + '$select' => t('Select folder'), + '$export_label' => t('Export website...'), + '$file_download_text' => t('Export to a zip file'), + '$filename_desc' => t('website.zip'), + '$filename_hint' => t('Enter a name for the zip file.'), + '$cloud_export_text' => t('Export to cloud files'), + '$cloud_export_desc' => t('/path/to/export/folder'), + '$cloud_export_hint' => t('Enter a path to a cloud files destination.'), + '$cloud_export_select' => t('Specify folder'), + )); } /** @@ -2738,14 +2897,16 @@ function website_portation_tools() { * * @param string $needle * @param array $haystack - * @return boolean + * @return bool */ -function in_arrayi($needle, $haystack) { - return in_array(strtolower($needle), array_map('strtolower', $haystack)); +function in_arrayi($needle, $haystack) +{ + return in_array(strtolower($needle), array_map('strtolower', $haystack)); } -function normalise_openid($s) { - return trim(str_replace(array('http://','https://'),array('',''),$s),'/'); +function normalise_openid($s) +{ + return trim(str_replace(array('http://','https://'), array('',''), $s), '/'); } /** @@ -2755,26 +2916,27 @@ function normalise_openid($s) { * * @return string with additional URL parameters */ -function extra_query_args() { - $s = ''; - if(count($_GET)) { - foreach($_GET as $k => $v) { - // these are request vars we don't want to duplicate - if(! in_array($k, array('req','f','zid','page','PHPSESSID'))) { - $s .= '&' . $k . '=' . urlencode($v); - } - } - } - if(count($_POST)) { - foreach($_POST as $k => $v) { - // these are request vars we don't want to duplicate - if(! in_array($k, array('req','f','zid','page','PHPSESSID'))) { - $s .= '&' . $k . '=' . urlencode($v); - } - } - } +function extra_query_args() +{ + $s = ''; + if (count($_GET)) { + foreach ($_GET as $k => $v) { + // these are request vars we don't want to duplicate + if (! in_array($k, array('req','f','zid','page','PHPSESSID'))) { + $s .= '&' . $k . '=' . urlencode($v); + } + } + } + if (count($_POST)) { + foreach ($_POST as $k => $v) { + // these are request vars we don't want to duplicate + if (! in_array($k, array('req','f','zid','page','PHPSESSID'))) { + $s .= '&' . $k . '=' . urlencode($v); + } + } + } - return $s; + return $s; } /** @@ -2787,198 +2949,198 @@ function extra_query_args() { * @param[in,out] string &$str_tags string to add the tag to * @param int $profile_uid * @param string $tag the tag to replace - * @param boolean $in_network default true - * @return boolean true if replaced, false if not replaced + * @param bool $in_network default true + * @return bool true if replaced, false if not replaced */ -function handle_tag(&$body, &$str_tags, $profile_uid, $tag, $in_network = true) { +function handle_tag(&$body, &$str_tags, $profile_uid, $tag, $in_network = true) +{ - $channel = App::get_channel(); - $replaced = false; - $r = null; - $match = []; + $channel = App::get_channel(); + $replaced = false; + $r = null; + $match = []; - $termtype = ((strpos($tag,'#') === 0) ? TERM_HASHTAG : TERM_UNKNOWN); - $termtype = ((strpos($tag,'@') === 0) ? TERM_MENTION : $termtype); + $termtype = ((strpos($tag, '#') === 0) ? TERM_HASHTAG : TERM_UNKNOWN); + $termtype = ((strpos($tag, '@') === 0) ? TERM_MENTION : $termtype); - // Is it a hashtag of some kind? + // Is it a hashtag of some kind? - if ( in_array($termtype, [ TERM_HASHTAG ] )) { - // if the tag is already replaced... - if((strpos($tag,'[zrl=')) || (strpos($tag,'[url='))) { - // ...do nothing - return $replaced; - } + if (in_array($termtype, [ TERM_HASHTAG ])) { + // if the tag is already replaced... + if ((strpos($tag, '[zrl=')) || (strpos($tag, '[url='))) { + // ...do nothing + return $replaced; + } - if(! $replaced) { + if (! $replaced) { + // double-quoted hashtags: base tag has the htmlentity name only - // double-quoted hashtags: base tag has the htmlentity name only + if ((substr($tag, 0, 7) === '#"') && (substr($tag, -6, 6) === '"')) { + $basetag = substr($tag, 7); + $basetag = substr($basetag, 0, -6); + } elseif ((substr($tag, 0, 2) === '#"') && (substr($tag, -1, 1) === '"')) { + $basetag = substr($tag, 2); + $basetag = substr($basetag, 0, -1); + } else { + $basetag = substr($tag, 1); + } - if((substr($tag,0,7) === '#"') && (substr($tag,-6,6) === '"')) { - $basetag = substr($tag,7); - $basetag = substr($basetag,0,-6); - } - elseif((substr($tag,0,2) === '#"') && (substr($tag,-1,1) === '"')) { - $basetag = substr($tag,2); - $basetag = substr($basetag,0,-1); - } - else - $basetag = substr($tag,1); + // create text for link - // create text for link + $url = z_root() . '/search?tag=' . rawurlencode($basetag); + $newtag = '#[zrl=' . z_root() . '/search?tag=' . rawurlencode($basetag) . ']' . $basetag . '[/zrl]'; - $url = z_root() . '/search?tag=' . rawurlencode($basetag); - $newtag = '#[zrl=' . z_root() . '/search?tag=' . rawurlencode($basetag) . ']' . $basetag . '[/zrl]'; + // replace tag by the link. Make sure to not replace something in the middle of a word - // replace tag by the link. Make sure to not replace something in the middle of a word + $body = preg_replace('/(? $replaced, + 'termtype' => $termtype, + 'term' => $basetag, + 'url' => $url, + 'contact' => [], + 'access_tag' => '', + ]]; + } - $str_tags .= $newtag; - } - return [ [ - 'replaced' => $replaced, - 'termtype' => $termtype, - 'term' => $basetag, - 'url' => $url, - 'contact' => [], - 'access_tag' => '', - ]]; - } + // END hashtags - // END hashtags + // BEGIN mentions - // BEGIN mentions + if (in_array($termtype, [ TERM_MENTION ])) { + // The @! tag will alter permissions - if ( in_array($termtype, [ TERM_MENTION ] )) { + // $in_network is set to false to avoid false positives on posts originating + // on a network which does not implement privacy tags or implements them differently. - // The @! tag will alter permissions + $exclusive = (((strpos(substr($tag, 1), '!') === 0) && $in_network) ? true : false); - // $in_network is set to false to avoid false positives on posts originating - // on a network which does not implement privacy tags or implements them differently. + // is it already replaced? + if (strpos($tag, "[zrl=") || strpos($tag, "[url=")) { + return $replaced; + } - $exclusive = (((strpos(substr($tag,1), '!') === 0) && $in_network) ? true : false); + // get the channel name + // First extract the name or name fragment we are going to replace - // is it already replaced? - if(strpos($tag,"[zrl=") || strpos($tag,"[url=")) - return $replaced; + $name = substr($tag, (($exclusive) ? 2 : 1)); + $newname = $name; // make a copy that we can mess with + $tagcid = 0; - // get the channel name - // First extract the name or name fragment we are going to replace + $r = null; - $name = substr($tag,(($exclusive) ? 2 : 1)); - $newname = $name; // make a copy that we can mess with - $tagcid = 0; + // is it some generated (autocompleted) name? - $r = null; + if (substr($name, 0, 1) === '{' && substr($name, -1, 1) === '}') { + $newname = substr($name, 1); + $newname = substr($newname, 0, -1); - // is it some generated (autocompleted) name? + $r = q( + "select * from xchan where xchan_addr = '%s' or xchan_hash = '%s' or xchan_url = '%s'", + dbesc($newname), + dbesc($newname), + dbesc($newname) + ); + } - if(substr($name,0,1) === '{' && substr($name,-1,1) === '}') { - $newname = substr($name,1); - $newname = substr($newname,0,-1); + if (! $r) { + // look for matching names in the address book - $r = q("select * from xchan where xchan_addr = '%s' or xchan_hash = '%s' or xchan_url = '%s'", - dbesc($newname), - dbesc($newname), - dbesc($newname) - ); - } + // Double quote the entire mentioned term to include special characters + // such as spaces and some punctuation. - if(! $r) { + // We see this after input filtering so quotes have been html entity encoded - // look for matching names in the address book + if ((substr($name, 0, 6) === '"') && (substr($name, -6, 6) === '"')) { + $newname = substr($name, 6); + $newname = substr($newname, 0, -6); + } elseif ((substr($name, 0, 1) === '"') && (substr($name, -1, 1) === '"')) { + $newname = substr($name, 1); + $newname = substr($newname, 0, -1); + } - // Double quote the entire mentioned term to include special characters - // such as spaces and some punctuation. + // select someone from this user's contacts by name - // We see this after input filtering so quotes have been html entity encoded - - if((substr($name,0,6) === '"') && (substr($name,-6,6) === '"')) { - $newname = substr($name,6); - $newname = substr($newname,0,-6); - } - elseif((substr($name,0,1) === '"') && (substr($name,-1,1) === '"')) { - $newname = substr($name,1); - $newname = substr($newname,0,-1); - } - - // select someone from this user's contacts by name - - $r = q("SELECT * FROM abook left join xchan on abook_xchan = xchan_hash + $r = q( + "SELECT * FROM abook left join xchan on abook_xchan = xchan_hash WHERE xchan_name = '%s' AND abook_channel = %d ", - dbesc($newname), - intval($profile_uid) - ); + dbesc($newname), + intval($profile_uid) + ); - // select anybody by full hubloc_addr + // select anybody by full hubloc_addr - if((! $r) && strpos($newname,'@')) { - $r = q("SELECT * FROM xchan left join hubloc on xchan_hash = hubloc_hash + if ((! $r) && strpos($newname, '@')) { + $r = q( + "SELECT * FROM xchan left join hubloc on xchan_hash = hubloc_hash WHERE hubloc_addr = '%s' ", - dbesc($newname) - ); - } + dbesc($newname) + ); + } - // select someone by attag or nick and the name passed in + // select someone by attag or nick and the name passed in - if(! $r) { - // strip user-supplied wildcards before running a wildcard search - $newname = str_replace('%','',$newname); - $r = q("SELECT * FROM abook left join xchan on abook_xchan = xchan_hash + if (! $r) { + // strip user-supplied wildcards before running a wildcard search + $newname = str_replace('%', '', $newname); + $r = q( + "SELECT * FROM abook left join xchan on abook_xchan = xchan_hash WHERE xchan_addr like ('%s') AND abook_channel = %d ", - dbesc(((strpos($newname,'@')) ? $newname : $newname . '@%')), - intval($profile_uid) - ); - } - - } + dbesc(((strpos($newname, '@')) ? $newname : $newname . '@%')), + intval($profile_uid) + ); + } + } $fn_results = []; $access_tag = EMPTY_STR; - // some platforms prefer to mention people by username rather than display name. - // make this a personal choice by the publisher - - $tagpref = intval(PConfig::Get($profile_uid,'system','tag_username',Config::Get('system','tag_username',false))); - + // some platforms prefer to mention people by username rather than display name. + // make this a personal choice by the publisher + + $tagpref = intval(PConfig::Get($profile_uid, 'system', 'tag_username', Config::Get('system', 'tag_username', false))); + // $r is set if we found something - if ($r) { - if (array_key_exists('hubloc_network',$r)) { - $r = [ Libzot::zot_record_preferred($r) ]; - } - else { - $r = [ Libzot::zot_record_preferred($r, 'xchan_network') ]; - } - } + if ($r) { + if (array_key_exists('hubloc_network', $r)) { + $r = [ Libzot::zot_record_preferred($r) ]; + } else { + $r = [ Libzot::zot_record_preferred($r, 'xchan_network') ]; + } + } if ($r) { foreach ($r as $xc) { $profile = $xc['xchan_url']; - // $tagpref - // 0 use display name - // 1 use username@host - // 2 use 'display name (username@host)' - // 127 use display name outbound and don't change inbound - - $newname = $xc['xchan_name']; + // $tagpref + // 0 use display name + // 1 use username@host + // 2 use 'display name (username@host)' + // 127 use display name outbound and don't change inbound - if ($tagpref === 1 && $xc['xchan_addr']) { - $newname = $xc['xchan_addr']; - } - if ($tagpref === 2 && $xc['xchan_addr']) { - $newname = sprintf( t('%1$s (%2$s)'), $xc['xchan_name'], $newname); - } + $newname = $xc['xchan_name']; + + if ($tagpref === 1 && $xc['xchan_addr']) { + $newname = $xc['xchan_addr']; + } + if ($tagpref === 2 && $xc['xchan_addr']) { + $newname = sprintf(t('%1$s (%2$s)'), $xc['xchan_name'], $newname); + } // add the channel's xchan_hash to $access_tag if exclusive if ($exclusive) { $access_tag = 'cid:' . $xc['xchan_hash']; @@ -2986,20 +3148,21 @@ function handle_tag(&$body, &$str_tags, $profile_uid, $tag, $in_network = true) // if there is a url for this channel - if(isset($profile)) { + if (isset($profile)) { $replaced = true; //create profile link - $profile = str_replace(',','%2c',$profile); + $profile = str_replace(',', '%2c', $profile); $url = $profile; - $zrl = (in_array($xc['xchan_network'],['nomad','zot6']) ? 'zrl' : 'url'); + + $zrl = (in_array($xc['xchan_network'], [ 'nomad', 'zot6' ]) ? 'zrl' : 'url'); $newtag = '@' . (($exclusive) ? '!' : '') . '[' . $zrl . '=' . $profile . ']' . $newname . '[/' . $zrl . ']'; $body = str_replace('@' . (($exclusive) ? '!' : '') . $name, $newtag, $body); - // append tag to str_tags - if(! stristr($str_tags,$newtag)) { - if(strlen($str_tags)) + if (! stristr($str_tags, $newtag)) { + if (strlen($str_tags)) { $str_tags .= ','; + } $str_tags .= $newtag; } } @@ -3013,32 +3176,29 @@ function handle_tag(&$body, &$str_tags, $profile_uid, $tag, $in_network = true) 'access_tag' => $access_tag, 'contact' => (($r) ? $xc : []), ]; - - } - - } - else { - + } + } else { // check for a group/collection exclusion tag // note that we aren't setting $replaced even though we're replacing text. // This tag isn't going to get a term attached to it. It's only used for - // access control. + // access control. - if(local_channel() && local_channel() == $profile_uid) { - $grp = AccessList::byname($profile_uid,$name); - if($grp) { - $g = q("select * from pgrp where id = %d and visible = 1 limit 1", + if (local_channel() && local_channel() == $profile_uid) { + $grp = AccessList::byname($profile_uid, $name); + if ($grp) { + $g = q( + "select * from pgrp where id = %d and visible = 1 limit 1", intval($grp) ); - if($g && $exclusive) { - $access_tag .= 'gid:' . $g[0]['hash']; + if ($g && $exclusive) { + $access_tag .= 'gid:' . $g[0]['hash']; } $channel = App::get_channel(); - if($channel) { - $replaced = true; - $newname = $channel['channel_name'] . ' (' . $g[0]['gname'] . ')'; - $newtag = '@' . (($exclusive) ? '!' : '') . '[zrl=' . z_root() . '/lists/view/' . $g[0]['hash'] . ']' . $newname . '[/zrl]'; + if ($channel) { + $replaced = true; + $newname = $channel['channel_name'] . ' (' . $g[0]['gname'] . ')'; + $newtag = '@' . (($exclusive) ? '!' : '') . '[zrl=' . z_root() . '/lists/view/' . $g[0]['hash'] . ']' . $newname . '[/zrl]'; $body = str_replace('@' . (($exclusive) ? '!' : '') . $name, $newtag, $body); } } @@ -3046,19 +3206,20 @@ function handle_tag(&$body, &$str_tags, $profile_uid, $tag, $in_network = true) // if there is a url for this channel - if(isset($profile)) { + if (isset($profile)) { $replaced = true; //create profile link - $profile = str_replace(',','%2c',$profile); + $profile = str_replace(',', '%2c', $profile); $url = $profile; $newtag = '@' . (($exclusive) ? '!' : '') . '[zrl=' . $profile . ']' . $newname . '[/zrl]'; $body = str_replace('@' . (($exclusive) ? '!' : '') . $name, $newtag, $body); // append tag to str_tags - if(! stristr($str_tags,$newtag)) { - if(strlen($str_tags)) + if (! stristr($str_tags, $newtag)) { + if (strlen($str_tags)) { $str_tags .= ','; + } $str_tags .= $newtag; } } @@ -3075,27 +3236,26 @@ function handle_tag(&$body, &$str_tags, $profile_uid, $tag, $in_network = true) } return $fn_results; - } -function linkify_tags(&$body, $uid, $in_network = true) { - $str_tags = EMPTY_STR; - $results = []; +function linkify_tags(&$body, $uid, $in_network = true) +{ + $str_tags = EMPTY_STR; + $results = []; - $tags = get_tags($body); + $tags = get_tags($body); - if(is_array($tags) && count($tags)) { - foreach($tags as $tag) { + if (is_array($tags) && count($tags)) { + foreach ($tags as $tag) { + $success = handle_tag($body, $str_tags, ($uid) ? $uid : App::$profile_uid, $tag, $in_network); - $success = handle_tag($body, $str_tags, ($uid) ? $uid : App::$profile_uid , $tag, $in_network); + foreach ($success as $handled_tag) { + $results[] = [ 'success' => $handled_tag ]; + } + } + } - foreach($success as $handled_tag) { - $results[] = [ 'success' => $handled_tag ]; - } - } - } - - return $results; + return $results; } /** @@ -3108,77 +3268,77 @@ function linkify_tags(&$body, $uid, $in_network = true) { * @return string * @todo rename to get_icon_from_type() */ -function getIconFromType($type) { - $iconMap = array( - //Folder - t('Collection') => 'fa-folder-o', - 'multipart/mixed' => 'fa-folder-o', //dirs in attach use this mime type - //Common file - 'application/octet-stream' => 'fa-file-o', - //Text - 'text/plain' => 'fa-file-text-o', - 'text/markdown' => 'fa-file-text-o', - 'text/bbcode' => 'fa-file-text-o', - 'text/x-multicode' => 'fa-file-text-o', - 'text/html' => 'fa-file-text-o', - 'application/msword' => 'fa-file-word-o', - 'application/pdf' => 'fa-file-pdf-o', - 'application/vnd.oasis.opendocument.text' => 'fa-file-word-o', - 'application/epub+zip' => 'fa-book', - //Spreadsheet - 'application/vnd.oasis.opendocument.spreadsheet' => 'fa-file-excel-o', - 'application/vnd.ms-excel' => 'fa-file-excel-o', - //Image - 'image/jpeg' => 'fa-picture-o', - 'image/png' => 'fa-picture-o', - 'image/gif' => 'fa-picture-o', - 'image/svg+xml' => 'fa-picture-o', - //Archive - 'application/zip' => 'fa-file-archive-o', - 'application/x-rar-compressed' => 'fa-file-archive-o', - //Audio - 'audio/mpeg' => 'fa-file-audio-o', - 'audio/wav' => 'fa-file-audio-o', - 'application/ogg' => 'fa-file-audio-o', - 'audio/ogg' => 'fa-file-audio-o', - 'audio/webm' => 'fa-file-audio-o', - 'audio/mp4' => 'fa-file-audio-o', - //Video - 'video/quicktime' => 'fa-file-video-o', - 'video/webm' => 'fa-file-video-o', - 'video/mp4' => 'fa-file-video-o', - 'video/x-matroska' => 'fa-file-video-o' - ); +function getIconFromType($type) +{ + $iconMap = array( + //Folder + t('Collection') => 'fa-folder-o', + 'multipart/mixed' => 'fa-folder-o', //dirs in attach use this mime type + //Common file + 'application/octet-stream' => 'fa-file-o', + //Text + 'text/plain' => 'fa-file-text-o', + 'text/markdown' => 'fa-file-text-o', + 'text/bbcode' => 'fa-file-text-o', + 'text/x-multicode' => 'fa-file-text-o', + 'text/html' => 'fa-file-text-o', + 'application/msword' => 'fa-file-word-o', + 'application/pdf' => 'fa-file-pdf-o', + 'application/vnd.oasis.opendocument.text' => 'fa-file-word-o', + 'application/epub+zip' => 'fa-book', + //Spreadsheet + 'application/vnd.oasis.opendocument.spreadsheet' => 'fa-file-excel-o', + 'application/vnd.ms-excel' => 'fa-file-excel-o', + //Image + 'image/jpeg' => 'fa-picture-o', + 'image/png' => 'fa-picture-o', + 'image/gif' => 'fa-picture-o', + 'image/svg+xml' => 'fa-picture-o', + //Archive + 'application/zip' => 'fa-file-archive-o', + 'application/x-rar-compressed' => 'fa-file-archive-o', + //Audio + 'audio/mpeg' => 'fa-file-audio-o', + 'audio/wav' => 'fa-file-audio-o', + 'application/ogg' => 'fa-file-audio-o', + 'audio/ogg' => 'fa-file-audio-o', + 'audio/webm' => 'fa-file-audio-o', + 'audio/mp4' => 'fa-file-audio-o', + //Video + 'video/quicktime' => 'fa-file-video-o', + 'video/webm' => 'fa-file-video-o', + 'video/mp4' => 'fa-file-video-o', + 'video/x-matroska' => 'fa-file-video-o' + ); - $catMap = [ - 'application' => 'fa-file-code-o', - 'multipart' => 'fa-folder', - 'audio' => 'fa-file-audio-o', - 'video' => 'fa-file-video-o', - 'text' => 'fa-file-text-o', - 'image' => 'fa=file-picture-o', - 'message' => 'fa-file-text-o' - ]; + $catMap = [ + 'application' => 'fa-file-code-o', + 'multipart' => 'fa-folder', + 'audio' => 'fa-file-audio-o', + 'video' => 'fa-file-video-o', + 'text' => 'fa-file-text-o', + 'image' => 'fa=file-picture-o', + 'message' => 'fa-file-text-o' + ]; - $iconFromType = ''; + $iconFromType = ''; - if (array_key_exists($type, $iconMap)) { - $iconFromType = $iconMap[$type]; - } - else { - $parts = explode('/',$type); - if($parts[0] && $catMap[$parts[0]]) { - $iconFromType = $catMap[$parts[0]]; - } - } + if (array_key_exists($type, $iconMap)) { + $iconFromType = $iconMap[$type]; + } else { + $parts = explode('/', $type); + if ($parts[0] && $catMap[$parts[0]]) { + $iconFromType = $catMap[$parts[0]]; + } + } - if(! $iconFromType) { - $iconFromType = 'fa-file-o'; - } + if (! $iconFromType) { + $iconFromType = 'fa-file-o'; + } - return $iconFromType; + return $iconFromType; } /** @@ -3188,100 +3348,111 @@ function getIconFromType($type) { * @return string human readable formatted filesize * @todo rename to user_readable_size() */ -function userReadableSize($size) { - $ret = ''; - if (is_numeric($size)) { - $incr = 0; - $k = 1024; - $unit = array('bytes', 'KB', 'MB', 'GB', 'TB', 'PB'); - while (($size / $k) >= 1){ - $incr++; - $size = round($size / $k, 2); - } - $ret = $size . ' ' . $unit[$incr]; - } +function userReadableSize($size) +{ + $ret = ''; + if (is_numeric($size)) { + $incr = 0; + $k = 1024; + $unit = array('bytes', 'KB', 'MB', 'GB', 'TB', 'PB'); + while (($size / $k) >= 1) { + $incr++; + $size = round($size / $k, 2); + } + $ret = $size . ' ' . $unit[$incr]; + } - return $ret; + return $ret; } -function str_rot47($str) { - return strtr($str, - '!"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~', - 'PQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNO'); +function str_rot47($str) +{ + return strtr( + $str, + '!"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~', + 'PQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNO' + ); } -function string_replace($old,$new,&$s) { +function string_replace($old, $new, &$s) +{ - $x = str_replace($old,$new,$s); - $replaced = false; - if($x !== $s) { - $replaced = true; - } - $s = $x; - return $replaced; + $x = str_replace($old, $new, $s); + $replaced = false; + if ($x !== $s) { + $replaced = true; + } + $s = $x; + return $replaced; } -function json_url_replace($old,$new,&$s) { +function json_url_replace($old, $new, &$s) +{ - $old = str_replace('/','\\/',$old); - $new = str_replace('/','\\/',$new); + $old = str_replace('/', '\\/', $old); + $new = str_replace('/', '\\/', $new); - $x = str_replace($old,$new,$s); - $replaced = false; - if($x !== $s) { - $replaced = true; - } - $s = $x; - return $replaced; + $x = str_replace($old, $new, $s); + $replaced = false; + if ($x !== $s) { + $replaced = true; + } + $s = $x; + return $replaced; } -function item_url_replace($channel,&$item,$old,$new,$oldnick = '') { +function item_url_replace($channel, &$item, $old, $new, $oldnick = '') +{ - if($item['attach']) { - json_url_replace($old,$new,$item['attach']); - if($oldnick && ($oldnick !== $channel['channel_address'])) - json_url_replace('/' . $oldnick . '/' ,'/' . $channel['channel_address'] . '/' ,$item['attach']); - } - if($item['object']) { - json_url_replace($old,$new,$item['object']); - if($oldnick && ($oldnick !== $channel['channel_address'])) - json_url_replace('/' . $oldnick . '/' ,'/' . $channel['channel_address'] . '/' ,$item['object']); - } - if($item['target']) { - json_url_replace($old,$new,$item['target']); - if($oldnick && ($oldnick !== $channel['channel_address'])) - json_url_replace('/' . $oldnick . '/' ,'/' . $channel['channel_address'] . '/' ,$item['target']); - } + if ($item['attach']) { + json_url_replace($old, $new, $item['attach']); + if ($oldnick && ($oldnick !== $channel['channel_address'])) { + json_url_replace('/' . $oldnick . '/', '/' . $channel['channel_address'] . '/', $item['attach']); + } + } + if ($item['object']) { + json_url_replace($old, $new, $item['object']); + if ($oldnick && ($oldnick !== $channel['channel_address'])) { + json_url_replace('/' . $oldnick . '/', '/' . $channel['channel_address'] . '/', $item['object']); + } + } + if ($item['target']) { + json_url_replace($old, $new, $item['target']); + if ($oldnick && ($oldnick !== $channel['channel_address'])) { + json_url_replace('/' . $oldnick . '/', '/' . $channel['channel_address'] . '/', $item['target']); + } + } - $item['body'] = str_replace($old, $new, $item['body']); + $item['body'] = str_replace($old, $new, $item['body']); - if($oldnick && ($oldnick !== $channel['channel_address'])) { - $item['body'] = str_replace('/' . $oldnick . '/', '/' . $channel['channel_address'] . '/', $item['body']); - } - - $item['sig'] = Libzot::sign($item['body'],$channel['channel_prvkey']); - $item['item_verified'] = 1; - - $item['plink'] = str_replace($old,$new,$item['plink']); - if($oldnick && ($oldnick !== $channel['channel_address'])) - $item['plink'] = str_replace('/' . $oldnick . '/' ,'/' . $channel['channel_address'] . '/' ,$item['plink']); + if ($oldnick && ($oldnick !== $channel['channel_address'])) { + $item['body'] = str_replace('/' . $oldnick . '/', '/' . $channel['channel_address'] . '/', $item['body']); + } - $item['llink'] = str_replace($old,$new,$item['llink']); - if($oldnick && ($oldnick !== $channel['channel_address'])) - $item['llink'] = str_replace('/' . $oldnick . '/' ,'/' . $channel['channel_address'] . '/' ,$item['llink']); + $item['sig'] = Libzot::sign($item['body'], $channel['channel_prvkey']); + $item['item_verified'] = 1; - if($item['term']) { - for($x = 0; $x < count($item['term']); $x ++) { - $item['term'][$x]['url'] = str_replace($old,$new,$item['term'][$x]['url']); - if($oldnick && ($oldnick !== $channel['channel_address'])) { - $item['term'][$x]['url'] = str_replace('/' . $oldnick . '/' ,'/' . $channel['channel_address'] . '/' ,$item['term'][$x]['url']); - } - } - } + $item['plink'] = str_replace($old, $new, $item['plink']); + if ($oldnick && ($oldnick !== $channel['channel_address'])) { + $item['plink'] = str_replace('/' . $oldnick . '/', '/' . $channel['channel_address'] . '/', $item['plink']); + } + $item['llink'] = str_replace($old, $new, $item['llink']); + if ($oldnick && ($oldnick !== $channel['channel_address'])) { + $item['llink'] = str_replace('/' . $oldnick . '/', '/' . $channel['channel_address'] . '/', $item['llink']); + } + + if ($item['term']) { + for ($x = 0; $x < count($item['term']); $x++) { + $item['term'][$x]['url'] = str_replace($old, $new, $item['term'][$x]['url']); + if ($oldnick && ($oldnick !== $channel['channel_address'])) { + $item['term'][$x]['url'] = str_replace('/' . $oldnick . '/', '/' . $channel['channel_address'] . '/', $item['term'][$x]['url']); + } + } + } } @@ -3290,11 +3461,13 @@ function item_url_replace($channel,&$item,$old,$new,$oldnick = '') { * * @param[in,out] array &$item */ -function sanitise_acl(&$item) { - if (strlen($item)) - $item = '<' . notags(trim(urldecode($item))) . '>'; - else - unset($item); +function sanitise_acl(&$item) +{ + if (strlen($item)) { + $item = '<' . notags(trim(urldecode($item))) . '>'; + } else { + unset($item); + } } /** @@ -3303,20 +3476,22 @@ function sanitise_acl(&$item) { * @param array $p * @return array */ -function perms2str($p) { - $ret = ''; +function perms2str($p) +{ + $ret = ''; - if (is_array($p)) - $tmp = $p; - else - $tmp = explode(',', $p); + if (is_array($p)) { + $tmp = $p; + } else { + $tmp = explode(',', $p); + } - if (is_array($tmp)) { - array_walk($tmp, 'sanitise_acl'); - $ret = implode('', $tmp); - } + if (is_array($tmp)) { + array_walk($tmp, 'sanitise_acl'); + $ret = implode('', $tmp); + } - return $ret; + return $ret; } /** @@ -3328,26 +3503,29 @@ function perms2str($p) { * @param string $s * @return array */ -function expand_acl($s) { - $ret = []; +function expand_acl($s) +{ + $ret = []; - if(strlen($s)) { - $t = str_replace('<','',$s); - $a = explode('>',$t); - foreach($a as $aa) { - if($aa) - $ret[] = $aa; - } - } + if (strlen($s)) { + $t = str_replace('<', '', $s); + $a = explode('>', $t); + foreach ($a as $aa) { + if ($aa) { + $ret[] = $aa; + } + } + } - return $ret; + return $ret; } -function acl2json($s) { - $s = expand_acl($s); - $s = json_encode($s); +function acl2json($s) +{ + $s = expand_acl($s); + $s = json_encode($s); - return $s; + return $s; } /** @@ -3369,31 +3547,33 @@ function acl2json($s) { * @param string $current * @return string HTML code for dropdown */ -function pdl_selector($uid, $current='') { - $o = ''; +function pdl_selector($uid, $current = '') +{ + $o = ''; - $sql_extra = item_permissions_sql($uid); + $sql_extra = item_permissions_sql($uid); - $r = q("select iconfig.*, mid from iconfig left join item on iconfig.iid = item.id + $r = q( + "select iconfig.*, mid from iconfig left join item on iconfig.iid = item.id where item.uid = %d and iconfig.cat = 'system' and iconfig.k = 'PDL' $sql_extra order by v asc", - intval($uid) - ); + intval($uid) + ); - $arr = array('channel_id' => $uid, 'current' => $current, 'entries' => $r); - call_hooks('pdl_selector', $arr); + $arr = array('channel_id' => $uid, 'current' => $current, 'entries' => $r); + call_hooks('pdl_selector', $arr); - $entries = $arr['entries']; - $current = $arr['current']; + $entries = $arr['entries']; + $current = $arr['current']; - $o .= ''; + $entries[] = array('title' => t('Default'), 'mid' => ''); + foreach ($entries as $selection) { + $selected = (($selection == $current) ? ' selected="selected" ' : ''); + $o .= ""; + } - $o .= ''; - return $o; + $o .= ''; + return $o; } /** @@ -3407,27 +3587,27 @@ function pdl_selector($uid, $current='') { * @param array $arr multi-dimensional array * @return one-dimensional array */ -function flatten_array_recursive($arr) { +function flatten_array_recursive($arr) +{ - $ret = []; + $ret = []; - if(! ($arr && is_array($arr))) { - return $ret; - } + if (! ($arr && is_array($arr))) { + return $ret; + } - foreach($arr as $a) { - if(is_array($a)) { - $tmp = flatten_array_recursive($a); - if($tmp) { - $ret = array_merge($ret, $tmp); - } - } - elseif(isset($a)) { - $ret[] = $a; - } - } + foreach ($arr as $a) { + if (is_array($a)) { + $tmp = flatten_array_recursive($a); + if ($tmp) { + $ret = array_merge($ret, $tmp); + } + } elseif (isset($a)) { + $ret[] = $a; + } + } - return($ret); + return($ret); } /** @@ -3437,43 +3617,47 @@ function flatten_array_recursive($arr) { * @param string $lang Which language should be highlighted * @return string * Important: The returned text has the text pattern 'http' translated to '%eY9-!' which should be converted back - * after further processing. This was done to prevent oembed links from occurring inside code blocks. + * after further processing. This was done to prevent oembed links from occurring inside code blocks. * See include/bbcode.php */ -function text_highlight($s, $lang, $options) { +function text_highlight($s, $lang, $options) +{ - if($lang === 'js') - $lang = 'javascript'; + if ($lang === 'js') { + $lang = 'javascript'; + } - if($lang === 'json') { - $lang = 'javascript'; - if(! strpos(trim($s), "\n")) - $s = jindent($s); - } + if ($lang === 'json') { + $lang = 'javascript'; + if (! strpos(trim($s), "\n")) { + $s = jindent($s); + } + } - $arr = [ - 'text' => $s, - 'language' => $lang, - 'options' => $options, - 'success' => false - ]; + $arr = [ + 'text' => $s, + 'language' => $lang, + 'options' => $options, + 'success' => false + ]; - /** - * @hooks text_highlight - * * \e string \b text - * * \e string \b language - * * \e boolean \b success default false - */ - call_hooks('text_highlight', $arr); + /** + * @hooks text_highlight + * * \e string \b text + * * \e string \b language + * * \e boolean \b success default false + */ + call_hooks('text_highlight', $arr); - if($arr['success']) - $o = $arr['text']; - else - $o = $s; + if ($arr['success']) { + $o = $arr['text']; + } else { + $o = $s; + } - $o = str_replace('http','%eY9-!',$o); + $o = str_replace('http', '%eY9-!', $o); - return('' . $o . ''); + return('' . $o . ''); } // function to convert multi-dimensional array to xml @@ -3487,26 +3671,28 @@ function text_highlight($s, $lang, $options) { // save as xml file // echo (($xml->asXML('data.xml')) ? 'Your XML file has been generated successfully!' : 'Error generating XML file!'); -function arrtoxml($root_elem,$arr) { - $xml = new SimpleXMLElement('<' . $root_elem . '>', null, false); - array2XML($xml,$arr); +function arrtoxml($root_elem, $arr) +{ + $xml = new SimpleXMLElement('<' . $root_elem . '>', null, false); + array2XML($xml, $arr); - return $xml->asXML(); + return $xml->asXML(); } -function array2XML($obj, $array) { - foreach ($array as $key => $value) { - if(is_numeric($key)) - $key = 'item' . $key; +function array2XML($obj, $array) +{ + foreach ($array as $key => $value) { + if (is_numeric($key)) { + $key = 'item' . $key; + } - if(is_array($value)) { - $node = $obj->addChild($key); - array2XML($node, $value); - } - else { - $obj->addChild($key, htmlspecialchars($value)); - } - } + if (is_array($value)) { + $node = $obj->addChild($key); + array2XML($node, $value); + } else { + $obj->addChild($key, htmlspecialchars($value)); + } + } } /** @@ -3517,179 +3703,195 @@ function array2XML($obj, $array) { * @param string $table * @param array $arr * @param array $binary_fields - fields which will be cleansed with dbescbin rather than dbesc; this is critical for postgres - * @return boolean|PDOStatement + * @return bool|PDOStatement */ -function create_table_from_array($table, $arr, $binary_fields = []) { +function create_table_from_array($table, $arr, $binary_fields = []) +{ - if(! ($arr && $table)) - return false; + if (! ($arr && $table)) { + return false; + } - $columns = db_columns($table); + $columns = db_columns($table); - $clean = []; - foreach($arr as $k => $v) { + $clean = []; + foreach ($arr as $k => $v) { + if (! in_array($k, $columns)) { + continue; + } - if(! in_array($k,$columns)) { - continue; - } + $matches = false; + if (preg_match('/([^a-zA-Z0-9\-\_\.])/', $k, $matches)) { + return false; + } + if (in_array($k, $binary_fields)) { + $clean[$k] = dbescbin($v); + } else { + $clean[$k] = dbesc($v); + } + } + $r = dbq("INSERT INTO " . TQUOT . $table . TQUOT . " (" . TQUOT + . implode(TQUOT . ', ' . TQUOT, array_keys($clean)) + . TQUOT . ") VALUES ('" + . implode("', '", array_values($clean)) + . "')"); - $matches = false; - if(preg_match('/([^a-zA-Z0-9\-\_\.])/',$k,$matches)) { - return false; - } - if(in_array($k,$binary_fields)) { - $clean[$k] = dbescbin($v); - } - else { - $clean[$k] = dbesc($v); - } - } - $r = dbq("INSERT INTO " . TQUOT . $table . TQUOT . " (" . TQUOT - . implode(TQUOT . ', ' . TQUOT, array_keys($clean)) - . TQUOT . ") VALUES ('" - . implode("', '", array_values($clean)) - . "')" - ); - - return $r; + return $r; } -function share_shield($m) { - return str_replace($m[1],'!=+=+=!' . base64url_encode($m[1]) . '=+!=+!=',$m[0]); +function share_shield($m) +{ + return str_replace($m[1], '!=+=+=!' . base64url_encode($m[1]) . '=+!=+!=', $m[0]); } -function share_unshield($m) { - $x = str_replace(array('!=+=+=!','=+!=+!='),array('',''),$m[1]); - return str_replace($m[1], base64url_decode($x), $m[0]); +function share_unshield($m) +{ + $x = str_replace(array('!=+=+=!','=+!=+!='), array('',''), $m[1]); + return str_replace($m[1], base64url_decode($x), $m[0]); } -function cleanup_bbcode($body) { +function cleanup_bbcode($body) +{ - /** - * fix naked links by passing through a callback to see if this is a zot site of some kind - * (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. - */ + /** + * fix naked links by passing through a callback to see if this is a zot site of some kind + * (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. + */ - // markdown code blocks are slightly more complicated - - $body = preg_replace_callback('#(^|\n)([`~]{3,})(?: *\.?([a-zA-Z0-9\-.]+))?\n+([\s\S]+?)\n+\2(\n|$)#', function ($match) { - return $match[1] . $match[2] . "\n" . bb_code_protect($match[4]) . "\n" . $match[2] . (($match[5]) ? $match[5] : "\n"); - }, $body); + // markdown code blocks are slightly more complicated - $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('/\[svg(.*?)\[\/(svg)\]/ism','\red_escape_codeblock',$body); - $body = preg_replace_callback('/\[img(.*?)\[\/(img)\]/ism','\red_escape_codeblock',$body); - $body = preg_replace_callback('/\[zmg(.*?)\[\/(zmg)\]/ism','\red_escape_codeblock',$body); + $body = preg_replace_callback('#(^|\n)([`~]{3,})(?: *\.?([a-zA-Z0-9\-.]+))?\n+([\s\S]+?)\n+\2(\n|$)#', function ($match) { + return $match[1] . $match[2] . "\n" . bb_code_protect($match[4]) . "\n" . $match[2] . (($match[5]) ? $match[5] : "\n"); + }, $body); - $body = preg_replace_callback("/([^\]\='".'"'."\;\/\{\(]|^|\#\^)(https?\:\/\/[a-zA-Z0-9\pL\:\/\-\?\&\;\.\=\@\_\~\#\%\$\!\\+\,\(\)]+)/ismu", '\nakedoembed', $body); + $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('/\[svg(.*?)\[\/(svg)\]/ism', '\red_escape_codeblock', $body); + $body = preg_replace_callback('/\[img(.*?)\[\/(img)\]/ism', '\red_escape_codeblock', $body); + $body = preg_replace_callback('/\[zmg(.*?)\[\/(zmg)\]/ism', '\red_escape_codeblock', $body); - $body = preg_replace_callback("/([^\]\='".'"'."\;\/\{\(]|^|\#\^)(https?\:\/\/[a-zA-Z0-9\pL\:\/\-\?\&\;\.\=\@\_\~\#\%\$\!\\+\,\(\)]+)/ismu", '\red_zrl_callback', $body); + $body = preg_replace_callback("/([^\]\='" . '"' . "\;\/\{\(]|^|\#\^)(https?\:\/\/[a-zA-Z0-9\pL\:\/\-\?\&\;\.\=\@\_\~\#\%\$\!\\+\,\(\)]+)/ismu", '\nakedoembed', $body); + + $body = preg_replace_callback("/([^\]\='" . '"' . "\;\/\{\(]|^|\#\^)(https?\:\/\/[a-zA-Z0-9\pL\:\/\-\?\&\;\.\=\@\_\~\#\%\$\!\\+\,\(\)]+)/ismu", '\red_zrl_callback', $body); - $body = preg_replace_callback('/\[\$b64code(.*?)\[\/(code)\]/ism','\red_unescape_codeblock',$body); - $body = preg_replace_callback('/\[\$b64url(.*?)\[\/(url)\]/ism','\red_unescape_codeblock',$body); - $body = preg_replace_callback('/\[\$b64zrl(.*?)\[\/(zrl)\]/ism','\red_unescape_codeblock',$body); - $body = preg_replace_callback('/\[\$b64svg(.*?)\[\/(svg)\]/ism','\red_unescape_codeblock',$body); - $body = preg_replace_callback('/\[\$b64img(.*?)\[\/(img)\]/ism','\red_unescape_codeblock',$body); - $body = preg_replace_callback('/\[\$b64zmg(.*?)\[\/(zmg)\]/ism','\red_unescape_codeblock',$body); + $body = preg_replace_callback('/\[\$b64code(.*?)\[\/(code)\]/ism', '\red_unescape_codeblock', $body); + $body = preg_replace_callback('/\[\$b64url(.*?)\[\/(url)\]/ism', '\red_unescape_codeblock', $body); + $body = preg_replace_callback('/\[\$b64zrl(.*?)\[\/(zrl)\]/ism', '\red_unescape_codeblock', $body); + $body = preg_replace_callback('/\[\$b64svg(.*?)\[\/(svg)\]/ism', '\red_unescape_codeblock', $body); + $body = preg_replace_callback('/\[\$b64img(.*?)\[\/(img)\]/ism', '\red_unescape_codeblock', $body); + $body = preg_replace_callback('/\[\$b64zmg(.*?)\[\/(zmg)\]/ism', '\red_unescape_codeblock', $body); - $body = bb_code_unprotect($body); + $body = bb_code_unprotect($body); - // fix any img tags that should be zmg + // fix any img tags that should be zmg - $body = preg_replace_callback('/\[img(.*?)\](.*?)\[\/img\]/ism','\red_zrlify_img_callback',$body); + $body = preg_replace_callback('/\[img(.*?)\](.*?)\[\/img\]/ism', '\red_zrlify_img_callback', $body); - $body = bb_translate_video($body); + $body = bb_translate_video($body); - /** - * Fold multi-line [code] sequences - */ + /** + * Fold multi-line [code] sequences + */ - $body = preg_replace('/\[\/code\]\s*\[code\]/ism',"\n",$body); + $body = preg_replace('/\[\/code\]\s*\[code\]/ism', "\n", $body); - return $body; + return $body; } -function gen_link_id($mid) { - if (strpbrk($mid,':/&?<>"\'') !== false) { - return 'b64.' . base64url_encode($mid); - } - return $mid; +function gen_link_id($mid) +{ + if (strpbrk($mid, ':/&?<>"\'') !== false) { + return 'b64.' . base64url_encode($mid); + } + return $mid; } -function unpack_link_id($mid) { - if (strpos($mid,'b64.') === 0) { - $mid = base64url_decode(preg_replace('/[^A-Za-z0-9\-_].*/','',substr($mid,4))); - } - return $mid; +function unpack_link_id($mid) +{ + if (strpos($mid, 'b64.') === 0) { + $mid = base64url_decode(preg_replace('/[^A-Za-z0-9\-_].*/', '', substr($mid, 4))); + } + return $mid; } // callback for array_walk -function array_trim(&$v,$k) { - $v = trim($v); +function array_trim(&$v, $k) +{ + $v = trim($v); } -function array_escape_tags(&$v,$k) { - $v = escape_tags($v); +function array_escape_tags(&$v, $k) +{ + $v = escape_tags($v); } -function ellipsify($s,$maxlen) { - if($maxlen & 1) - $maxlen --; - if($maxlen < 4) - $maxlen = 4; +function ellipsify($s, $maxlen) +{ + if ($maxlen & 1) { + $maxlen--; + } + if ($maxlen < 4) { + $maxlen = 4; + } - if(mb_strlen($s) < $maxlen) - return $s; + if (mb_strlen($s) < $maxlen) { + return $s; + } - return mb_substr($s,0,$maxlen / 2) . '...' . mb_substr($s,mb_strlen($s) - ($maxlen / 2)); + return mb_substr($s, 0, $maxlen / 2) . '...' . mb_substr($s, mb_strlen($s) - ($maxlen / 2)); } -function purify_filename($s) { - if(($s[0] === '.') || strpos($s,'/') !== false) - return ''; - return $s; +function purify_filename($s) +{ + if (($s[0] === '.') || strpos($s, '/') !== false) { + return ''; + } + return $s; } // callback for sorting the settings/featured entries. -function featured_sort($a,$b) { - $s1 = substr($a,strpos($a,'id='),20); - $s2 = substr($b,strpos($b,'id='),20); - return(strcmp($s1,$s2)); +function featured_sort($a, $b) +{ + $s1 = substr($a, strpos($a, 'id='), 20); + $s2 = substr($b, strpos($b, 'id='), 20); + return(strcmp($s1, $s2)); } -function unpunify($s) { - if (function_exists('idn_to_utf8')) { - return idn_to_utf8($s); - } - return $s; +function unpunify($s) +{ + if (function_exists('idn_to_utf8')) { + return idn_to_utf8($s); + } + return $s; } -function punify($s) { - if (function_exists('idn_to_ascii')) { - return idn_to_ascii($s); - } - return $s; +function punify($s) +{ + if (function_exists('idn_to_ascii')) { + return idn_to_ascii($s); + } + return $s; } -function unique_multidim_array($array, $key) { +function unique_multidim_array($array, $key) +{ $temp_array = []; $i = 0; $key_array = []; - - foreach($array as $val) { + + foreach ($array as $val) { if (!in_array($val[$key], $key_array)) { $key_array[$i] = $val[$key]; $temp_array[$i] = $val; @@ -3697,161 +3899,167 @@ function unique_multidim_array($array, $key) { $i++; } return $temp_array; - -} +} // Much prettier formatting than print_r() // This assumes the output will be a web page and escapes angle-chars appropriately by default. -function print_array($arr, $escape = true, $level = 0) { +function print_array($arr, $escape = true, $level = 0) +{ - $o = EMPTY_STR; - $tabs = EMPTY_STR; + $o = EMPTY_STR; + $tabs = EMPTY_STR; - if(is_array($arr)) { - for($x = 0; $x <= $level; $x ++) { - $tabs .= "\t"; - } - $o .= '[' . "\n"; - if(count($arr)) { - foreach($arr as $k => $v) { - if(is_array($v)) { - $o .= $tabs . '[' . (($escape) ? escape_tags($k) : $k) . '] => ' . print_array($v, $escape, $level + 1) . "\n"; - } - else { - $o .= $tabs . '[' . (($escape) ? escape_tags($k) : $k) . '] => ' . print_val($v, $escape) . ",\n"; - } - } - } - $o .= substr($tabs,0,-1) . ']' . (($level) ? ',' : ';' ). "\n"; - return $o; - } - + if (is_array($arr)) { + for ($x = 0; $x <= $level; $x++) { + $tabs .= "\t"; + } + $o .= '[' . "\n"; + if (count($arr)) { + foreach ($arr as $k => $v) { + if (is_array($v)) { + $o .= $tabs . '[' . (($escape) ? escape_tags($k) : $k) . '] => ' . print_array($v, $escape, $level + 1) . "\n"; + } else { + $o .= $tabs . '[' . (($escape) ? escape_tags($k) : $k) . '] => ' . print_val($v, $escape) . ",\n"; + } + } + } + $o .= substr($tabs, 0, -1) . ']' . (($level) ? ',' : ';' ) . "\n"; + return $o; + } } -function print_val($v, $escape = true) { - if (is_bool($v)) { - if ($v) { - return 'true'; - } - return 'false'; - } - if (is_string($v)) { - return "'" . (($escape) ? escape_tags($v) : $v) . "'"; - } - return $v; +function print_val($v, $escape = true) +{ + if (is_bool($v)) { + if ($v) { + return 'true'; + } + return 'false'; + } + if (is_string($v)) { + return "'" . (($escape) ? escape_tags($v) : $v) . "'"; + } + return $v; } -function array_path_exists($str,$arr) { +function array_path_exists($str, $arr) +{ - if (! ($arr && is_array($arr))) { - return false; - } + if (! ($arr && is_array($arr))) { + return false; + } - $ptr = $arr; - $search = explode('/', $str); + $ptr = $arr; + $search = explode('/', $str); - if ($search) { - foreach ($search as $s) { - if ($ptr && is_array($ptr) && array_key_exists($s,$ptr)) { - $ptr = $ptr[$s]; - } - else { - return false; - } - } - return true; - } - return false; + if ($search) { + foreach ($search as $s) { + if ($ptr && is_array($ptr) && array_key_exists($s, $ptr)) { + $ptr = $ptr[$s]; + } else { + return false; + } + } + return true; + } + return false; } -function get_forum_channels($uid,$collections = 0) { +function get_forum_channels($uid, $collections = 0) +{ - if (! $uid) - return; + if (! $uid) { + return; + } - if ($collections) { - $pagetype = $collections; - } - else { - $pagetype = 1; - } + if ($collections) { + $pagetype = $collections; + } else { + $pagetype = 1; + } - $r = q("select abook_id, xchan_hash, xchan_network, xchan_name, xchan_url, xchan_photo_s from abook left join xchan on abook_xchan = xchan_hash where xchan_deleted = 0 and abook_channel = %d and abook_pending = 0 and abook_ignored = 0 and abook_blocked = 0 and abook_archived = 0 and abook_self = 0 and xchan_type = %d order by xchan_name", - intval($uid), - intval($pagetype) - ); - - return $r; + $r = q( + "select abook_id, xchan_hash, xchan_network, xchan_name, xchan_url, xchan_photo_s from abook left join xchan on abook_xchan = xchan_hash where xchan_deleted = 0 and abook_channel = %d and abook_pending = 0 and abook_ignored = 0 and abook_blocked = 0 and abook_archived = 0 and abook_self = 0 and xchan_type = %d order by xchan_name", + intval($uid), + intval($pagetype) + ); + return $r; } -function serialise($x) { - return ((is_array($x)) ? 'json:' . json_encode($x) : $x); +function serialise($x) +{ + return ((is_array($x)) ? 'json:' . json_encode($x) : $x); } -function unserialise($x) { - if (is_array($x)) { - return $x; - } - $y = ((substr($x,0,5) === 'json:') ? json_decode(substr($x,5),true) : ''); - return ((is_array($y)) ? $y : $x); +function unserialise($x) +{ + if (is_array($x)) { + return $x; + } + $y = ((substr($x, 0, 5) === 'json:') ? json_decode(substr($x, 5), true) : ''); + return ((is_array($y)) ? $y : $x); } -function obscurify($s) { - return str_rot47(base64url_encode($s)); +function obscurify($s) +{ + return str_rot47(base64url_encode($s)); } -function unobscurify($s) { - return base64url_decode(str_rot47($s)); +function unobscurify($s) +{ + return base64url_decode(str_rot47($s)); } -function svg2bb($s) { +function svg2bb($s) +{ - $s = preg_replace("/\(.*?)\<(.*?)\<\/text\>/", '$2<$3', $s); - $s = preg_replace("/\(.*?)\>(.*?)\<\/text\>/", '$2>$3', $s); - $s = preg_replace("/\(.*?)\[(.*?)\<\/text\>/", '$2[$3', $s); - $s = preg_replace("/\(.*?)\](.*?)\<\/text\>/", '$2]$3', $s); - $s = utf8_encode($s); - $purify = new SvgSanitizer(); - if ($purify->loadXML($s)) { - $purify->sanitize(); - $output = $purify->saveSVG(); - $output = preg_replace("/\<\?xml(.*?)\>/",'',$output); - $output = preg_replace("/\<\!\-\-(.*?)\-\-\>/",'',$output); - $output = str_replace(['<','>'],['[',']'],$output); - return $output; - } - return EMPTY_STR; + $s = preg_replace("/\(.*?)\<(.*?)\<\/text\>/", '$2<$3', $s); + $s = preg_replace("/\(.*?)\>(.*?)\<\/text\>/", '$2>$3', $s); + $s = preg_replace("/\(.*?)\[(.*?)\<\/text\>/", '$2[$3', $s); + $s = preg_replace("/\(.*?)\](.*?)\<\/text\>/", '$2]$3', $s); + $s = utf8_encode($s); + $purify = new SvgSanitizer(); + if ($purify->loadXML($s)) { + $purify->sanitize(); + $output = $purify->saveSVG(); + $output = preg_replace("/\<\?xml(.*?)\>/", '', $output); + $output = preg_replace("/\<\!\-\-(.*?)\-\-\>/", '', $output); + $output = str_replace(['<','>'], ['[',']'], $output); + return $output; + } + return EMPTY_STR; } // Takes something that looks like a phone number and returns a string suitable for tel: protocol or false. -function is_phone_number($s) { - $ext = substr($s,strpos($s,'x')+1); - if (! $ext) { - $ext = substr($s,strpos($s,'X')+1); - } - if ($ext && ctype_digit($ext)) { - $rext = ';ext=' . $ext; - $s = str_replace(['x' . $ext, 'X' . $ext],['',''],$s); - } - else { - $ext = EMPTY_STR; - } - $s = str_replace(['(',')',' ','-','+'],['','','','',''],$s); - return ((ctype_digit($s)) ? $s . $rext : false); +function is_phone_number($s) +{ + $ext = substr($s, strpos($s, 'x') + 1); + if (! $ext) { + $ext = substr($s, strpos($s, 'X') + 1); + } + if ($ext && ctype_digit($ext)) { + $rext = ';ext=' . $ext; + $s = str_replace(['x' . $ext, 'X' . $ext], ['',''], $s); + } else { + $ext = EMPTY_STR; + } + $s = str_replace(['(',')',' ','-','+'], ['','','','',''], $s); + return ((ctype_digit($s)) ? $s . $rext : false); } /** * fnmatch seems a bit unpredictable, so use this instead. */ -function wildmat($pattern,$string) { - return preg_match("#^".strtr(preg_quote($pattern, '#'), [ '\*' => '.*', '\?' => '.', '\[' => '[', '\]' => ']' ] ) . "$#i", $string); +function wildmat($pattern, $string) +{ + return preg_match("#^" . strtr(preg_quote($pattern, '#'), [ '\*' => '.*', '\?' => '.', '\[' => '[', '\]' => ']' ]) . "$#i", $string); } diff --git a/include/xchan.php b/include/xchan.php index 14e3156d9..f995562a4 100644 --- a/include/xchan.php +++ b/include/xchan.php @@ -3,372 +3,399 @@ use Zotlabs\Lib\Libzot; use Zotlabs\Web\HTTPSig; -function xchan_store_lowlevel($arr) { +function xchan_store_lowlevel($arr) +{ - $store = [ - 'xchan_hash' => ((array_key_exists('xchan_hash',$arr)) ? $arr['xchan_hash'] : ''), - 'xchan_guid' => ((array_key_exists('xchan_guid',$arr)) ? $arr['xchan_guid'] : ''), - 'xchan_guid_sig' => ((array_key_exists('xchan_guid_sig',$arr)) ? $arr['xchan_guid_sig'] : ''), - 'xchan_pubkey' => ((array_key_exists('xchan_pubkey',$arr)) ? $arr['xchan_pubkey'] : ''), - 'xchan_photo_mimetype' => ((array_key_exists('xchan_photo_mimetype',$arr)) ? $arr['xchan_photo_mimetype'] : ''), - 'xchan_photo_l' => ((array_key_exists('xchan_photo_l',$arr)) ? $arr['xchan_photo_l'] : ''), - 'xchan_photo_m' => ((array_key_exists('xchan_photo_m',$arr)) ? $arr['xchan_photo_m'] : ''), - 'xchan_photo_s' => ((array_key_exists('xchan_photo_s',$arr)) ? $arr['xchan_photo_s'] : ''), - 'xchan_addr' => ((array_key_exists('xchan_addr',$arr)) ? $arr['xchan_addr'] : ''), - 'xchan_url' => ((array_key_exists('xchan_url',$arr)) ? $arr['xchan_url'] : ''), - 'xchan_connurl' => ((array_key_exists('xchan_connurl',$arr)) ? $arr['xchan_connurl'] : ''), - 'xchan_follow' => ((array_key_exists('xchan_follow',$arr)) ? $arr['xchan_follow'] : ''), - 'xchan_connpage' => ((array_key_exists('xchan_connpage',$arr)) ? $arr['xchan_connpage'] : ''), - 'xchan_name' => ((array_key_exists('xchan_name',$arr)) ? $arr['xchan_name'] : ''), - 'xchan_network' => ((array_key_exists('xchan_network',$arr)) ? $arr['xchan_network'] : ''), - 'xchan_created' => ((array_key_exists('xchan_created',$arr)) ? datetime_convert('UTC','UTC',$arr['xchan_created']) : datetime_convert()), - 'xchan_updated' => ((array_key_exists('xchan_updated',$arr)) ? datetime_convert('UTC','UTC',$arr['xchan_updated']) : datetime_convert()), - 'xchan_photo_date' => ((array_key_exists('xchan_photo_date',$arr)) ? datetime_convert('UTC','UTC',$arr['xchan_photo_date']) : NULL_DATE), - 'xchan_name_date' => ((array_key_exists('xchan_name_date',$arr)) ? datetime_convert('UTC','UTC',$arr['xchan_name_date']) : NULL_DATE), - 'xchan_hidden' => ((array_key_exists('xchan_hidden',$arr)) ? intval($arr['xchan_hidden']) : 0), - 'xchan_orphan' => ((array_key_exists('xchan_orphan',$arr)) ? intval($arr['xchan_orphan']) : 0), - 'xchan_censored' => ((array_key_exists('xchan_censored',$arr)) ? intval($arr['xchan_censored']) : 0), - 'xchan_selfcensored' => ((array_key_exists('xchan_selfcensored',$arr)) ? intval($arr['xchan_selfcensored']) : 0), - 'xchan_system' => ((array_key_exists('xchan_system',$arr)) ? intval($arr['xchan_system']) : 0), - 'xchan_type' => ((array_key_exists('xchan_type',$arr)) ? intval($arr['xchan_type']) : XCHAN_TYPE_PERSON), - 'xchan_deleted' => ((array_key_exists('xchan_deleted',$arr)) ? intval($arr['xchan_deleted']) : 0) - ]; + $store = [ + 'xchan_hash' => ((array_key_exists('xchan_hash', $arr)) ? $arr['xchan_hash'] : ''), + 'xchan_guid' => ((array_key_exists('xchan_guid', $arr)) ? $arr['xchan_guid'] : ''), + 'xchan_guid_sig' => ((array_key_exists('xchan_guid_sig', $arr)) ? $arr['xchan_guid_sig'] : ''), + 'xchan_pubkey' => ((array_key_exists('xchan_pubkey', $arr)) ? $arr['xchan_pubkey'] : ''), + 'xchan_photo_mimetype' => ((array_key_exists('xchan_photo_mimetype', $arr)) ? $arr['xchan_photo_mimetype'] : ''), + 'xchan_photo_l' => ((array_key_exists('xchan_photo_l', $arr)) ? $arr['xchan_photo_l'] : ''), + 'xchan_photo_m' => ((array_key_exists('xchan_photo_m', $arr)) ? $arr['xchan_photo_m'] : ''), + 'xchan_photo_s' => ((array_key_exists('xchan_photo_s', $arr)) ? $arr['xchan_photo_s'] : ''), + 'xchan_addr' => ((array_key_exists('xchan_addr', $arr)) ? $arr['xchan_addr'] : ''), + 'xchan_url' => ((array_key_exists('xchan_url', $arr)) ? $arr['xchan_url'] : ''), + 'xchan_connurl' => ((array_key_exists('xchan_connurl', $arr)) ? $arr['xchan_connurl'] : ''), + 'xchan_follow' => ((array_key_exists('xchan_follow', $arr)) ? $arr['xchan_follow'] : ''), + 'xchan_connpage' => ((array_key_exists('xchan_connpage', $arr)) ? $arr['xchan_connpage'] : ''), + 'xchan_name' => ((array_key_exists('xchan_name', $arr)) ? $arr['xchan_name'] : ''), + 'xchan_network' => ((array_key_exists('xchan_network', $arr)) ? $arr['xchan_network'] : ''), + 'xchan_created' => ((array_key_exists('xchan_created', $arr)) ? datetime_convert('UTC', 'UTC', $arr['xchan_created']) : datetime_convert()), + 'xchan_updated' => ((array_key_exists('xchan_updated', $arr)) ? datetime_convert('UTC', 'UTC', $arr['xchan_updated']) : datetime_convert()), + 'xchan_photo_date' => ((array_key_exists('xchan_photo_date', $arr)) ? datetime_convert('UTC', 'UTC', $arr['xchan_photo_date']) : NULL_DATE), + 'xchan_name_date' => ((array_key_exists('xchan_name_date', $arr)) ? datetime_convert('UTC', 'UTC', $arr['xchan_name_date']) : NULL_DATE), + 'xchan_hidden' => ((array_key_exists('xchan_hidden', $arr)) ? intval($arr['xchan_hidden']) : 0), + 'xchan_orphan' => ((array_key_exists('xchan_orphan', $arr)) ? intval($arr['xchan_orphan']) : 0), + 'xchan_censored' => ((array_key_exists('xchan_censored', $arr)) ? intval($arr['xchan_censored']) : 0), + 'xchan_selfcensored' => ((array_key_exists('xchan_selfcensored', $arr)) ? intval($arr['xchan_selfcensored']) : 0), + 'xchan_system' => ((array_key_exists('xchan_system', $arr)) ? intval($arr['xchan_system']) : 0), + 'xchan_type' => ((array_key_exists('xchan_type', $arr)) ? intval($arr['xchan_type']) : XCHAN_TYPE_PERSON), + 'xchan_deleted' => ((array_key_exists('xchan_deleted', $arr)) ? intval($arr['xchan_deleted']) : 0) + ]; - return create_table_from_array('xchan',$store); + return create_table_from_array('xchan', $store); } // called from the zot api // Anybody can enter an identity into the system, but zot or zot6 identities must be validated // and existing zot6 identities cannot be altered via API -function xchan_store($arr) { - - $update_photo = false; - $update_name = false; - - if(! ($arr['guid'] || $arr['hash'])) { - $arr = json_decode(file_get_contents('php://input'),true); - } - - logger('xchan_store: ' . print_r($arr,true)); - - if(! $arr['hash']) - $arr['hash'] = $arr['guid']; - if(! $arr['hash']) - return false; - - $r = q("select * from xchan where xchan_hash = '%s' limit 1", - dbesc($arr['hash']) - ); - if(! $r) { - - $update_photo = true; - - if(! $arr['network']) - $arr['network'] = 'unknown'; - if(! $arr['name']) - $arr['name'] = 'unknown'; - if(! $arr['url']) - $arr['url'] = z_root(); - if(! $arr['photo']) - $arr['photo'] = z_root() . '/' . get_default_profile_photo(); +function xchan_store($arr) +{ - if(in_array($arr['network'],['nomad','zot6'])) { - if((! $arr['key']) || (! Libzot::verify($arr['id'],$arr['id_sig'],$arr['key']))) { - logger('Unable to verify signature for ' . $arr['hash']); - return false; - } - } + $update_photo = false; + $update_name = false; - if($arr['network'] === 'zot') { - if((! $arr['key']) || (! Libzot::verify($arr['id'],'sha256.' . $arr['id_sig'],$arr['key']))) { - logger('Unable to verify signature for ' . $arr['hash']); - return false; - } - } - - $columns = db_columns('xchan'); - - $x = []; - foreach($arr as $k => $v) { - if($k === 'key') { - $x['xchan_pubkey'] = HTTPSig::convertKey(escape_tags($v)); - continue; - } - if($k === 'photo') { - continue; - } - - if(in_array($columns,'xchan_' . $k)) - $x['xchan_' . $k] = escape_tags($v); - } - - $x['xchan_updated'] = datetime_convert(); - $x['xchan_name_date'] = datetime_convert(); - $x['xchan_photo_date'] = datetime_convert(); - $x['xchan_system'] = false; - - $result = xchan_store_lowlevel($x); - - if(! $result) { - return $result; - } - } - else { - if(in_array($r[0]['network'],['nomad','zot6'])) { - return true; - } - if($r[0]['xchan_photo_date'] < datetime_convert('UTC','UTC',$arr['photo_date'])) { - $update_photo = true; - } - if($r[0]['xchan_name_date'] < datetime_convert('UTC','UTC',$arr['name_date'])) { - $update_name = true; - } - - } - - if($update_photo && $arr['photo']) { - $photos = import_remote_xchan_photo($arr['photo'],$arr['hash']); - if ($photos) { - $x = q("update xchan set xchan_updated = '%s', xchan_photo_date = '%s', xchan_photo_l = '%s', xchan_photo_m = '%s', xchan_photo_s = '%s', xchan_photo_mimetype = '%s' where xchan_hash = '%s'", - dbesc(datetime_convert()), - dbesc(datetime_convert()), - dbesc($photos[0]), - dbesc($photos[1]), - dbesc($photos[2]), - dbesc($photos[3]), - dbesc($arr['hash']) - ); - } - } - if($update_name && $arr['name']) { - $x = q("update xchan set xchan_updated = '%s', xchan_name = '%s', xchan_name_date = '%s' where xchan_hash = '%s'", - dbesc(datetime_convert()), - dbesc(escape_tags($arr['name'])), - dbesc(datetime_convert()), - dbesc($arr['hash']) - ); - } - - return $true; - -} - - -function xchan_match($arr) { - - if(! $arr) - return false; - - $str = ''; - - foreach($arr as $k => $v) { - if($str) { - $str .= " AND "; - } - $str .= " " . TQUOT . dbesc($k) . TQUOT . " = '" . dbesc($v) . "' "; + if (! ($arr['guid'] || $arr['hash'])) { + $arr = json_decode(file_get_contents('php://input'), true); } - $r = q("select * from xchan where $str limit 1"); - - return (($r) ? $r[0] : false); + logger('xchan_store: ' . print_r($arr, true)); + if (! $arr['hash']) { + $arr['hash'] = $arr['guid']; + } + if (! $arr['hash']) { + return false; + } + + $r = q( + "select * from xchan where xchan_hash = '%s' limit 1", + dbesc($arr['hash']) + ); + if (! $r) { + $update_photo = true; + + if (! $arr['network']) { + $arr['network'] = 'unknown'; + } + if (! $arr['name']) { + $arr['name'] = 'unknown'; + } + if (! $arr['url']) { + $arr['url'] = z_root(); + } + if (! $arr['photo']) { + $arr['photo'] = z_root() . '/' . get_default_profile_photo(); + } + + if (in_array($arr['network'], [ 'nomad','zot6' ])) { + if ((! $arr['key']) || (! Libzot::verify($arr['id'], $arr['id_sig'], $arr['key']))) { + logger('Unable to verify signature for ' . $arr['hash']); + return false; + } + } + + if ($arr['network'] === 'zot') { + if ((! $arr['key']) || (! Libzot::verify($arr['id'], 'sha256.' . $arr['id_sig'], $arr['key']))) { + logger('Unable to verify signature for ' . $arr['hash']); + return false; + } + } + + $columns = db_columns('xchan'); + + $x = []; + foreach ($arr as $k => $v) { + if ($k === 'key') { + $x['xchan_pubkey'] = HTTPSig::convertKey(escape_tags($v)); + continue; + } + if ($k === 'photo') { + continue; + } + + if (in_array($columns, 'xchan_' . $k)) { + $x['xchan_' . $k] = escape_tags($v); + } + } + + $x['xchan_updated'] = datetime_convert(); + $x['xchan_name_date'] = datetime_convert(); + $x['xchan_photo_date'] = datetime_convert(); + $x['xchan_system'] = false; + + $result = xchan_store_lowlevel($x); + + if (! $result) { + return $result; + } + } else { + if (in_array($r[0]['network'] , [ 'zot6', 'nomad' ])) { + return true; + } + if ($r[0]['xchan_photo_date'] < datetime_convert('UTC', 'UTC', $arr['photo_date'])) { + $update_photo = true; + } + if ($r[0]['xchan_name_date'] < datetime_convert('UTC', 'UTC', $arr['name_date'])) { + $update_name = true; + } + } + + if ($update_photo && $arr['photo']) { + $photos = import_remote_xchan_photo($arr['photo'], $arr['hash']); + if ($photos) { + $x = q( + "update xchan set xchan_updated = '%s', xchan_photo_date = '%s', xchan_photo_l = '%s', xchan_photo_m = '%s', xchan_photo_s = '%s', xchan_photo_mimetype = '%s' where xchan_hash = '%s'", + dbesc(datetime_convert()), + dbesc(datetime_convert()), + dbesc($photos[0]), + dbesc($photos[1]), + dbesc($photos[2]), + dbesc($photos[3]), + dbesc($arr['hash']) + ); + } + } + if ($update_name && $arr['name']) { + $x = q( + "update xchan set xchan_updated = '%s', xchan_name = '%s', xchan_name_date = '%s' where xchan_hash = '%s'", + dbesc(datetime_convert()), + dbesc(escape_tags($arr['name'])), + dbesc(datetime_convert()), + dbesc($arr['hash']) + ); + } + + return $true; +} + + +function xchan_match($arr) +{ + + if (! $arr) { + return false; + } + + $str = ''; + + foreach ($arr as $k => $v) { + if ($str) { + $str .= " AND "; + } + $str .= " " . TQUOT . dbesc($k) . TQUOT . " = '" . dbesc($v) . "' "; + } + + $r = q("select * from xchan where $str limit 1"); + + return (($r) ? $r[0] : false); } -function xchan_fetch($arr) { +function xchan_fetch($arr) +{ - $key = ''; - if($arr['hash']) { - $key = 'xchan_hash'; - $v = dbesc($arr['hash']); - } - elseif($arr['guid']) { - $key = 'xchan_guid'; - $v = dbesc($arr['guid']); - } - elseif($arr['address']) { - $key = 'xchan_addr'; - $v = dbesc($arr['address']); - } + $key = ''; + if ($arr['hash']) { + $key = 'xchan_hash'; + $v = dbesc($arr['hash']); + } elseif ($arr['guid']) { + $key = 'xchan_guid'; + $v = dbesc($arr['guid']); + } elseif ($arr['address']) { + $key = 'xchan_addr'; + $v = dbesc($arr['address']); + } - if(! $key) - return false; + if (! $key) { + return false; + } - $r = q("select * from xchan where $key = '$v' limit 1"); - if(! $r) - return false; + $r = q("select * from xchan where $key = '$v' limit 1"); + if (! $r) { + return false; + } - $ret = []; - foreach($r[0] as $k => $v) { - if($k === 'xchan_addr') - $ret['address'] = $v; - else - $ret[str_replace('xchan_','',$k)] = $v; - } - - return $ret; + $ret = []; + foreach ($r[0] as $k => $v) { + if ($k === 'xchan_addr') { + $ret['address'] = $v; + } else { + $ret[str_replace('xchan_', '', $k)] = $v; + } + } + return $ret; } -function xchan_keychange_table($table,$column,$oldxchan,$newxchan) { - $r = q("update $table set $column = '%s' where $column = '%s'", - dbesc($newxchan['xchan_hash']), - dbesc($oldxchan['xchan_hash']) - ); - return $r; +function xchan_keychange_table($table, $column, $oldxchan, $newxchan) +{ + $r = q( + "update $table set $column = '%s' where $column = '%s'", + dbesc($newxchan['xchan_hash']), + dbesc($oldxchan['xchan_hash']) + ); + return $r; } -function xchan_keychange_acl($table,$column,$oldxchan,$newxchan) { +function xchan_keychange_acl($table, $column, $oldxchan, $newxchan) +{ - $allow = (($table === 'channel') ? 'channel_allow_cid' : 'allow_cid'); - $deny = (($table === 'channel') ? 'channel_deny_cid' : 'deny_cid'); + $allow = (($table === 'channel') ? 'channel_allow_cid' : 'allow_cid'); + $deny = (($table === 'channel') ? 'channel_deny_cid' : 'deny_cid'); - $r = q("select $column, $allow, $deny from $table where ($allow like '%s' or $deny like '%s') ", - dbesc('<' . $oldxchan['xchan_hash'] . '>'), - dbesc('<' . $oldxchan['xchan_hash'] . '>') - ); + $r = q( + "select $column, $allow, $deny from $table where ($allow like '%s' or $deny like '%s') ", + dbesc('<' . $oldxchan['xchan_hash'] . '>'), + dbesc('<' . $oldxchan['xchan_hash'] . '>') + ); - if($r) { - foreach($r as $rv) { - $z = q("update $table set $allow = '%s', $deny = '%s' where $column = %d", - dbesc(str_replace('<' . $oldxchan['xchan_hash'] . '>', '<' . $newxchan['xchan_hash'] . '>', - $rv[$allow])), - dbesc(str_replace('<' . $oldxchan['xchan_hash'] . '>', '<' . $newxchan['xchan_hash'] . '>', - $rv[$deny])), - intval($rv[$column]) - ); - } - } - return $z; + if ($r) { + foreach ($r as $rv) { + $z = q( + "update $table set $allow = '%s', $deny = '%s' where $column = %d", + dbesc(str_replace( + '<' . $oldxchan['xchan_hash'] . '>', + '<' . $newxchan['xchan_hash'] . '>', + $rv[$allow] + )), + dbesc(str_replace( + '<' . $oldxchan['xchan_hash'] . '>', + '<' . $newxchan['xchan_hash'] . '>', + $rv[$deny] + )), + intval($rv[$column]) + ); + } + } + return $z; } -function xchan_change_key($oldx,$newx,$data) { +function xchan_change_key($oldx, $newx, $data) +{ - $tables = [ - 'abook' => 'abook_xchan', - 'abconfig' => 'xchan', - 'pgrp_member' => 'xchan', - 'chat' => 'chat_xchan', - 'chatpresence' => 'cp_xchan', - 'event' => 'event_xchan', - 'item' => 'owner_xchan', - 'item' => 'author_xchan', - 'item' => 'source_xchan', - 'mail' => 'from_xchan', - 'mail' => 'to_xchan', - 'shares' => 'share_xchan', - 'source' => 'src_channel_xchan', - 'source' => 'src_xchan', - 'xchat' => 'xchat_xchan', - 'xconfig' => 'xchan', - 'xign' => 'xchan', - 'xlink' => 'xlink_xchan', - 'xprof' => 'xprof_hash', - 'xtag' => 'xtag_hash' - ]; - - - $acls = [ - 'channel' => 'channel_id', - 'attach' => 'id', - 'chatroom' => 'cr_id', - 'event' => 'id', - 'item' => 'id', - 'menu_item' => 'mitem_id', - 'obj' => 'obj_id', - 'photo' => 'id' - ]; + $tables = [ + 'abook' => 'abook_xchan', + 'abconfig' => 'xchan', + 'pgrp_member' => 'xchan', + 'chat' => 'chat_xchan', + 'chatpresence' => 'cp_xchan', + 'event' => 'event_xchan', + 'item' => 'owner_xchan', + 'item' => 'author_xchan', + 'item' => 'source_xchan', + 'mail' => 'from_xchan', + 'mail' => 'to_xchan', + 'shares' => 'share_xchan', + 'source' => 'src_channel_xchan', + 'source' => 'src_xchan', + 'xchat' => 'xchat_xchan', + 'xconfig' => 'xchan', + 'xign' => 'xchan', + 'xlink' => 'xlink_xchan', + 'xprof' => 'xprof_hash', + 'xtag' => 'xtag_hash' + ]; - foreach($tables as $k => $v) { - xchan_keychange_table($k,$v,$oldx,$newx); - } + $acls = [ + 'channel' => 'channel_id', + 'attach' => 'id', + 'chatroom' => 'cr_id', + 'event' => 'id', + 'item' => 'id', + 'menu_item' => 'mitem_id', + 'obj' => 'obj_id', + 'photo' => 'id' + ]; - foreach($acls as $k => $v) { - xchan_keychange_acl($k,$v,$oldx,$newx); - } + + foreach ($tables as $k => $v) { + xchan_keychange_table($k, $v, $oldx, $newx); + } + + foreach ($acls as $k => $v) { + xchan_keychange_acl($k, $v, $oldx, $newx); + } } -function migrate_xchan_photos($limit = 100) { +function migrate_xchan_photos($limit = 100) +{ - $r = q("select xchan_photo_l, xchan_hash, photo.xchan, photo.resource_id from photo left join xchan on photo.xchan = xchan_hash where photo.xchan != '' and uid = 0 and imgscale = 4 and photo_usage = 2 and xchan_photo_l like ('%s') limit %d", - dbesc(z_root() . '/photo/%'), - intval($limit) - ); - if ($r) { - foreach ($r as $rv) { - logger('migrating xchan_photo for ' . $rv['xchan_hash']); - $photos = import_remote_xchan_photo($rv['xchan_photo_l'], $rv['xchan_hash']); - if ($photos) { - $r = q("update xchan set xchan_photo_l = '%s', xchan_photo_m = '%s', xchan_photo_s = '%s', xchan_photo_mimetype = '%s' + $r = q( + "select xchan_photo_l, xchan_hash, photo.xchan, photo.resource_id from photo left join xchan on photo.xchan = xchan_hash where photo.xchan != '' and uid = 0 and imgscale = 4 and photo_usage = 2 and xchan_photo_l like ('%s') limit %d", + dbesc(z_root() . '/photo/%'), + intval($limit) + ); + if ($r) { + foreach ($r as $rv) { + logger('migrating xchan_photo for ' . $rv['xchan_hash']); + $photos = import_remote_xchan_photo($rv['xchan_photo_l'], $rv['xchan_hash']); + if ($photos) { + $r = q( + "update xchan set xchan_photo_l = '%s', xchan_photo_m = '%s', xchan_photo_s = '%s', xchan_photo_mimetype = '%s' where xchan_hash = '%s'", - dbesc($photos[0]), - dbesc($photos[1]), - dbesc($photos[2]), - dbesc($photos[3]), - dbesc($rv['xchan_hash']) - ); - } - } - } - + dbesc($photos[0]), + dbesc($photos[1]), + dbesc($photos[2]), + dbesc($photos[3]), + dbesc($rv['xchan_hash']) + ); + } + } + } } -function cleanup_xchan_photos($limit = 500) { +function cleanup_xchan_photos($limit = 500) +{ - $r = q("select photo.xchan, photo.resource_id from photo left join xchan on photo.xchan = xchan_hash where photo.xchan != '' and uid = 0 and imgscale = 4 and photo_usage = 2 and xchan_photo_l like ('%s') limit %d", - dbesc(z_root() . '/xp/%'), - intval($limit) - ); - if ($r) { - foreach ($r as $rv) { - q("delete from photo where xchan = '%s' and resource_id = '%s' and photo_usage = 2 and uid = 0", - dbesc($rv['xchan']), - dbesc($rv['resource_id']) - ); - } - } + $r = q( + "select photo.xchan, photo.resource_id from photo left join xchan on photo.xchan = xchan_hash where photo.xchan != '' and uid = 0 and imgscale = 4 and photo_usage = 2 and xchan_photo_l like ('%s') limit %d", + dbesc(z_root() . '/xp/%'), + intval($limit) + ); + if ($r) { + foreach ($r as $rv) { + q( + "delete from photo where xchan = '%s' and resource_id = '%s' and photo_usage = 2 and uid = 0", + dbesc($rv['xchan']), + dbesc($rv['resource_id']) + ); + } + } } -function xprof_store_lowlevel($profile) { +function xprof_store_lowlevel($profile) +{ - if (! $profile['hash']) { - return false; - } + if (! $profile['hash']) { + return false; + } - $store = [ - $arr['xprof_hash'] => $profile['hash'], - $arr['xprof_dob'] => (($profile['birthday'] === '0000-00-00') ? $profile['birthday'] : datetime_convert('','',$profile['birthday'],'Y-m-d')), - $arr['xprof_age'] => (($profile['age']) ? intval($profile['age']) : 0), - $arr['xprof_desc'] => (($profile['description']) ? htmlspecialchars($profile['description'], ENT_COMPAT,'UTF-8',false) : ''), - $arr['xprof_gender'] => (($profile['gender']) ? htmlspecialchars($profile['gender'], ENT_COMPAT,'UTF-8',false) : ''), - $arr['xprof_marital'] => (($profile['marital']) ? htmlspecialchars($profile['marital'], ENT_COMPAT,'UTF-8',false) : ''), - $arr['xprof_sexual'] => (($profile['sexual']) ? htmlspecialchars($profile['sexual'], ENT_COMPAT,'UTF-8',false) : ''), - $arr['xprof_locale'] => (($profile['locale']) ? htmlspecialchars($profile['locale'], ENT_COMPAT,'UTF-8',false) : ''), - $arr['xprof_region'] => (($profile['region']) ? htmlspecialchars($profile['region'], ENT_COMPAT,'UTF-8',false) : ''), - $arr['xprof_postcode'] => (($profile['postcode']) ? htmlspecialchars($profile['postcode'], ENT_COMPAT,'UTF-8',false) : ''), - $arr['xprof_country'] => (($profile['country']) ? htmlspecialchars($profile['country'], ENT_COMPAT,'UTF-8',false) : ''), - $arr['xprof_about'] => (($profile['about']) ? htmlspecialchars($profile['about'], ENT_COMPAT,'UTF-8',false) : ''), - $arr['xprof_homepage'] => (($profile['homepage']) ? htmlspecialchars($profile['homepage'], ENT_COMPAT,'UTF-8',false) : ''), - $arr['xprof_hometown'] => (($profile['hometown']) ? htmlspecialchars($profile['hometown'], ENT_COMPAT,'UTF-8',false) : ''), - $arr['xprof_keywords'] => (($profile['keywords']) ? htmlspecialchars($profile['keywords'], ENT_COMPAT,'UTF-8',false) : ''), + $store = [ + $arr['xprof_hash'] => $profile['hash'], + $arr['xprof_dob'] => (($profile['birthday'] === '0000-00-00') ? $profile['birthday'] : datetime_convert('', '', $profile['birthday'], 'Y-m-d')), + $arr['xprof_age'] => (($profile['age']) ? intval($profile['age']) : 0), + $arr['xprof_desc'] => (($profile['description']) ? htmlspecialchars($profile['description'], ENT_COMPAT, 'UTF-8', false) : ''), + $arr['xprof_gender'] => (($profile['gender']) ? htmlspecialchars($profile['gender'], ENT_COMPAT, 'UTF-8', false) : ''), + $arr['xprof_marital'] => (($profile['marital']) ? htmlspecialchars($profile['marital'], ENT_COMPAT, 'UTF-8', false) : ''), + $arr['xprof_sexual'] => (($profile['sexual']) ? htmlspecialchars($profile['sexual'], ENT_COMPAT, 'UTF-8', false) : ''), + $arr['xprof_locale'] => (($profile['locale']) ? htmlspecialchars($profile['locale'], ENT_COMPAT, 'UTF-8', false) : ''), + $arr['xprof_region'] => (($profile['region']) ? htmlspecialchars($profile['region'], ENT_COMPAT, 'UTF-8', false) : ''), + $arr['xprof_postcode'] => (($profile['postcode']) ? htmlspecialchars($profile['postcode'], ENT_COMPAT, 'UTF-8', false) : ''), + $arr['xprof_country'] => (($profile['country']) ? htmlspecialchars($profile['country'], ENT_COMPAT, 'UTF-8', false) : ''), + $arr['xprof_about'] => (($profile['about']) ? htmlspecialchars($profile['about'], ENT_COMPAT, 'UTF-8', false) : ''), + $arr['xprof_homepage'] => (($profile['homepage']) ? htmlspecialchars($profile['homepage'], ENT_COMPAT, 'UTF-8', false) : ''), + $arr['xprof_hometown'] => (($profile['hometown']) ? htmlspecialchars($profile['hometown'], ENT_COMPAT, 'UTF-8', false) : ''), + $arr['xprof_keywords'] => (($profile['keywords']) ? htmlspecialchars($profile['keywords'], ENT_COMPAT, 'UTF-8', false) : ''), - ]; + ]; - return create_table_from_array('xchan',$store); - -} \ No newline at end of file + return create_table_from_array('xchan', $store); +} diff --git a/include/zid.php b/include/zid.php index 045957cb0..76bb86675 100644 --- a/include/zid.php +++ b/include/zid.php @@ -3,30 +3,28 @@ use Zotlabs\Lib\Libzot; use Zotlabs\Lib\Verify; +function is_matrix_url($url) +{ -function is_matrix_url($url) { + // in-memory cache to avoid repeated queries for the same host + static $remembered = []; - // in-memory cache to avoid repeated queries for the same host - static $remembered = []; - - $m = @parse_url($url); - if (isset($m['host']) && $m['host']) { - - if (in_array($m['host'],$remembered)) { - return true; - } + $m = @parse_url($url); + if (isset($m['host']) && $m['host']) { + if (in_array($m['host'], $remembered)) { + return true; + } $r = q("select hubloc_url from hubloc where hubloc_host = '%s' and hubloc_network in ('nomad','zot6') limit 1", - dbesc($m['host']) - ); - if ($r) { - $remembered[] = $m['host']; - return true; - } + dbesc($m['host']) + ); + if ($r) { + $remembered[] = $m['host']; + return true; + } + } - } - - return false; + return false; } /** @@ -34,102 +32,110 @@ function is_matrix_url($url) { * * @param string $s * The url to accept the zid - * @param boolean $address + * @param bool $address * $address to use instead of session environment * @return string */ -function zid($s, $address = '') { +function zid($s, $address = '') +{ - if (! strlen($s) || strpos($s,'zid=')) { - return $s; - } + if (! strlen($s) || strpos($s, 'zid=')) { + return $s; + } - $m = parse_url($s); - $fragment = ((array_key_exists('fragment',$m) && $m['fragment']) ? $m['fragment'] : false); - if ($fragment !== false) { - $s = str_replace('#' . $fragment,'',$s); - } + $m = parse_url($s); + $fragment = ((array_key_exists('fragment', $m) && $m['fragment']) ? $m['fragment'] : false); + if ($fragment !== false) { + $s = str_replace('#' . $fragment, '', $s); + } - $has_params = ((strpos($s,'?')) ? true : false); - $num_slashes = substr_count($s, '/'); - if (! $has_params) { - $has_params = ((strpos($s, '&')) ? true : false); - } + $has_params = ((strpos($s, '?')) ? true : false); + $num_slashes = substr_count($s, '/'); + if (! $has_params) { + $has_params = ((strpos($s, '&')) ? true : false); + } - $achar = strpos($s,'?') ? '&' : '?'; + $achar = strpos($s, '?') ? '&' : '?'; - $mine = get_my_url(); - $myaddr = (($address) ? $address : get_my_address()); + $mine = get_my_url(); + $myaddr = (($address) ? $address : get_my_address()); - $mine_parsed = parse_url($mine); - $s_parsed = parse_url($s); - $url_match = false; + $mine_parsed = parse_url($mine); + $s_parsed = parse_url($s); + $url_match = false; - if (isset($mine_parsed['host']) && isset($s_parsed['host']) - && $mine_parsed['host'] === $s_parsed['host']) { - $url_match = true; - } + if ( + isset($mine_parsed['host']) && isset($s_parsed['host']) + && $mine_parsed['host'] === $s_parsed['host'] + ) { + $url_match = true; + } - if ($mine && $myaddr && (! $url_match)) { - $zurl = $s . (($num_slashes >= 3) ? '' : '/') . (($achar === '?') ? '?f=&' : '&') . 'zid=' . urlencode($myaddr); - } - else { - $zurl = $s; - } + if ($mine && $myaddr && (! $url_match)) { + $zurl = $s . (($num_slashes >= 3) ? '' : '/') . (($achar === '?') ? '?f=&' : '&') . 'zid=' . urlencode($myaddr); + } else { + $zurl = $s; + } - // put fragment at the end + // put fragment at the end - if ($fragment) { - $zurl .= '#' . $fragment; - } + if ($fragment) { + $zurl .= '#' . $fragment; + } - $arr = [ - 'url' => $s, - 'zid' => urlencode($myaddr), - 'result' => $zurl - ]; - /** - * @hooks zid - * Called when adding the observer's zid to a URL. - * * \e string \b url - url to accept zid - * * \e string \b zid - urlencoded zid - * * \e string \b result - the return string we calculated, change it if you want to return something else - */ - call_hooks('zid', $arr); + $arr = [ + 'url' => $s, + 'zid' => urlencode($myaddr), + 'result' => $zurl + ]; + /** + * @hooks zid + * Called when adding the observer's zid to a URL. + * * \e string \b url - url to accept zid + * * \e string \b zid - urlencoded zid + * * \e string \b result - the return string we calculated, change it if you want to return something else + */ + call_hooks('zid', $arr); - return $arr['result']; + return $arr['result']; } -function strip_query_param($s,$param) { - return preg_replace('/[\?&]' . $param . '=(.*?)(&|$)/ism','$2',$s); +function strip_query_param($s, $param) +{ + return preg_replace('/[\?&]' . $param . '=(.*?)(&|$)/ism', '$2', $s); } -function strip_zids($s) { - return preg_replace('/[\?&]zid=(.*?)(&|$)/ism','$2',$s); +function strip_zids($s) +{ + return preg_replace('/[\?&]zid=(.*?)(&|$)/ism', '$2', $s); } -function strip_owt($s) { - return preg_replace('/[\?&]owt=(.*?)(&|$)/ism','$2',$s); +function strip_owt($s) +{ + return preg_replace('/[\?&]owt=(.*?)(&|$)/ism', '$2', $s); } -function strip_zats($s) { - return preg_replace('/[\?&]zat=(.*?)(&|$)/ism','$2',$s); +function strip_zats($s) +{ + return preg_replace('/[\?&]zat=(.*?)(&|$)/ism', '$2', $s); } -function strip_escaped_zids($s) { - $x = preg_replace('/&\;zid=(.*?)(&|$)/ism','$2',$s); - return strip_query_param($x,'f'); +function strip_escaped_zids($s) +{ + $x = preg_replace('/&\;zid=(.*?)(&|$)/ism', '$2', $s); + return strip_query_param($x, 'f'); } -function clean_query_string($s = '') { - $x = strip_zids(($s) ? $s : App::$query_string); - $x = strip_owt($x); - $x = strip_zats($x); - $x = strip_query_param($x,'sort'); +function clean_query_string($s = '') +{ + $x = strip_zids(($s) ? $s : App::$query_string); + $x = strip_owt($x); + $x = strip_zats($x); + $x = strip_query_param($x, 'sort'); - return strip_query_param($x,'f'); + return strip_query_param($x, 'f'); } @@ -144,63 +150,69 @@ function clean_query_string($s = '') { * @return string */ -function zidify_callback($match) { +function zidify_callback($match) +{ - $arr = [ 'zid' => ((strpos($match[1],'zrl') || strpos($match[3],'zrl')) ? true : false), 'url' => $match[2] ]; - call_hooks('zidify', $arr); + $arr = [ 'zid' => ((strpos($match[1], 'zrl') || strpos($match[3], 'zrl')) ? true : false), 'url' => $match[2] ]; + call_hooks('zidify', $arr); - $replace = ''; + $replace = ''; - $x = str_replace($match[0], $replace, $match[0]); + $x = str_replace($match[0], $replace, $match[0]); - return $x; + return $x; } -function zidify_img_callback($match) { +function zidify_img_callback($match) +{ - $arr = [ 'zid' => ((strpos($match[1],'zrl') || strpos($match[3],'zrl')) ? true : false), 'url' => $match[2] ]; - call_hooks('zidify', $arr); + $arr = [ 'zid' => ((strpos($match[1], 'zrl') || strpos($match[3], 'zrl')) ? true : false), 'url' => $match[2] ]; + call_hooks('zidify', $arr); - $replace = ''; + $replace = ''; - $x = str_replace($match[0], $replace, $match[0]); + $x = str_replace($match[0], $replace, $match[0]); - return $x; + return $x; } -function zidify_links($s) { - $s = preg_replace_callback('/\/ism','zidify_callback',$s); - $s = preg_replace_callback('/\/ism','zidify_img_callback',$s); +function zidify_links($s) +{ + $s = preg_replace_callback('/\/ism', 'zidify_callback', $s); + $s = preg_replace_callback('/\/ism', 'zidify_img_callback', $s); - return $s; + return $s; } -function zidify_text_callback($match) { - $is_zid = is_matrix_url($match[2]); - $replace = ''; +function zidify_text_callback($match) +{ + $is_zid = is_matrix_url($match[2]); + $replace = ''; - $x = str_replace($match[0], $replace, $match[0]); + $x = str_replace($match[0], $replace, $match[0]); - return $x; + return $x; } -function zidify_text_img_callback($match) { - $is_zid = is_matrix_url($match[2]); - $replace = ''; +function zidify_text_img_callback($match) +{ + $is_zid = is_matrix_url($match[2]); + $replace = ''; - $x = str_replace($match[0], $replace, $match[0]); + $x = str_replace($match[0], $replace, $match[0]); - return $x; + return $x; } -function zidify_text($s) { +function zidify_text($s) +{ - $s = preg_replace_callback('/\/ism','zidify_text_callback',$s); - $s = preg_replace_callback('/\/ism','zidify_text_img_callback',$s); + $s = preg_replace_callback('/\/ism', 'zidify_text_callback', $s); + $s = preg_replace_callback('/\/ism', 'zidify_text_img_callback', $s); - return $s; + return $s; } @@ -215,25 +227,26 @@ function zidify_text($s) { * @param array $matches * @return string */ -function red_zrl_callback($matches) { +function red_zrl_callback($matches) +{ - $zrl = is_matrix_url($matches[2]); + $zrl = is_matrix_url($matches[2]); - $t = strip_zids($matches[2]); - if ($t !== $matches[2]) { - $zrl = true; - $matches[2] = $t; - } + $t = strip_zids($matches[2]); + if ($t !== $matches[2]) { + $zrl = true; + $matches[2] = $t; + } - if ($matches[1] === '#^') { - $matches[1] = ''; - } - - if ($zrl) { - return $matches[1] . '[zrl=' . $matches[2] . ']' . $matches[2] . '[/zrl]'; - } + if ($matches[1] === '#^') { + $matches[1] = ''; + } - return $matches[1] . '[url=' . $matches[2] . ']' . $matches[2] . '[/url]'; + if ($zrl) { + return $matches[1] . '[zrl=' . $matches[2] . ']' . $matches[2] . '[/zrl]'; + } + + return $matches[1] . '[url=' . $matches[2] . ']' . $matches[2] . '[/url]'; } /** @@ -243,42 +256,46 @@ function red_zrl_callback($matches) { * @param array $matches * @return string */ -function red_escape_zrl_callback($matches) { +function red_escape_zrl_callback($matches) +{ - // Uncertain why the url/zrl forms weren't picked up by the non-greedy regex. + // Uncertain why the url/zrl forms weren't picked up by the non-greedy regex. - if ((strpos($matches[3], 'zmg') !== false) || (strpos($matches[3], 'img') !== false) || (strpos($matches[3],'zrl') !== false) || (strpos($matches[3],'url') !== false)) { - return $matches[0]; - } + if ((strpos($matches[3], 'zmg') !== false) || (strpos($matches[3], 'img') !== false) || (strpos($matches[3], 'zrl') !== false) || (strpos($matches[3], 'url') !== false)) { + return $matches[0]; + } - return '[' . $matches[1] . 'rl' . $matches[2] . ']' . $matches[3] . '"' . $matches[4] . '"' . $matches[5] . '[/' . $matches[6] . 'rl]'; + return '[' . $matches[1] . 'rl' . $matches[2] . ']' . $matches[3] . '"' . $matches[4] . '"' . $matches[5] . '[/' . $matches[6] . 'rl]'; } -function red_escape_codeblock($m) { - return '[$b64' . $m[2] . base64_encode($m[1]) . '[/' . $m[2] . ']'; +function red_escape_codeblock($m) +{ + return '[$b64' . $m[2] . base64_encode($m[1]) . '[/' . $m[2] . ']'; } -function red_unescape_codeblock($m) { - return '[' . $m[2] . base64_decode($m[1]) . '[/' . $m[2] . ']'; +function red_unescape_codeblock($m) +{ + return '[' . $m[2] . base64_decode($m[1]) . '[/' . $m[2] . ']'; } -function red_zrlify_img_callback($matches) { +function red_zrlify_img_callback($matches) +{ - $zrl = is_matrix_url($matches[2]); + $zrl = is_matrix_url($matches[2]); - $t = strip_zids($matches[2]); - if ($t !== $matches[2]) { - $zrl = true; - $matches[2] = $t; - } + $t = strip_zids($matches[2]); + if ($t !== $matches[2]) { + $zrl = true; + $matches[2] = $t; + } - if ($zrl) { - return '[zmg' . $matches[1] . ']' . $matches[2] . '[/zmg]'; - } + if ($zrl) { + return '[zmg' . $matches[1] . ']' . $matches[2] . '[/zmg]'; + } - return $matches[0]; + return $matches[0]; } @@ -287,155 +304,161 @@ function red_zrlify_img_callback($matches) { * * @param string $token */ -function owt_init($token) { +function owt_init($token) +{ - require_once('include/security.php'); - - Verify::purge('owt', '3 MINUTE'); + require_once('include/security.php'); - $ob_hash = Verify::get_meta('owt', 0, $token); + Verify::purge('owt', '3 MINUTE'); - if ($ob_hash === false) { - return; - } + $ob_hash = Verify::get_meta('owt', 0, $token); - $r = q("select * from hubloc left join xchan on xchan_hash = hubloc_hash + if ($ob_hash === false) { + return; + } + + $r = q( + "select * from hubloc left join xchan on xchan_hash = hubloc_hash where hubloc_addr = '%s' or hubloc_id_url = '%s' or hubloc_hash = '%s' order by hubloc_id desc", - dbesc($ob_hash), - dbesc($ob_hash), - dbesc($ob_hash) - ); + dbesc($ob_hash), + dbesc($ob_hash), + dbesc($ob_hash) + ); - if (! $r) { - // finger them if they can't be found. - $wf = discover_by_webbie($ob_hash); - if ($wf) { - $r = q("select * from hubloc left join xchan on xchan_hash = hubloc_hash + if (! $r) { + // finger them if they can't be found. + $wf = discover_by_webbie($ob_hash); + if ($wf) { + $r = q( + "select * from hubloc left join xchan on xchan_hash = hubloc_hash where hubloc_addr = '%s' or hubloc_id_url = '%s' or hubloc_hash = '%s' order by hubloc_id desc", - dbesc($ob_hash), - dbesc($ob_hash), - dbesc($ob_hash) - ); - } - } - if (! $r) { - logger('owt: unable to finger ' . $ob_hash); - return; - } + dbesc($ob_hash), + dbesc($ob_hash), + dbesc($ob_hash) + ); + } + } + if (! $r) { + logger('owt: unable to finger ' . $ob_hash); + return; + } - $r = Libzot::zot_record_preferred($r); + $r = Libzot::zot_record_preferred($r); - $hubloc = $r; + $hubloc = $r; - $_SESSION['authenticated'] = 1; + $_SESSION['authenticated'] = 1; - $delegate_success = false; - if ($_REQUEST['delegate']) { - $r = q("select * from channel left join xchan on channel_hash = xchan_hash where xchan_addr = '%s' limit 1", - dbesc($_REQUEST['delegate']) - ); - if ($r && intval($r[0]['channel_id'])) { - $allowed = perm_is_allowed($r[0]['channel_id'],$hubloc['xchan_hash'],'delegate'); - if ($allowed) { - $_SESSION['delegate_channel'] = $r[0]['channel_id']; - $_SESSION['delegate'] = $hubloc['xchan_hash']; - $_SESSION['account_id'] = intval($r[0]['channel_account_id']); - // this will set the local_channel authentication in the session - change_channel($r[0]['channel_id']); - $delegate_success = true; - } - } - } + $delegate_success = false; + if ($_REQUEST['delegate']) { + $r = q( + "select * from channel left join xchan on channel_hash = xchan_hash where xchan_addr = '%s' limit 1", + dbesc($_REQUEST['delegate']) + ); + if ($r && intval($r[0]['channel_id'])) { + $allowed = perm_is_allowed($r[0]['channel_id'], $hubloc['xchan_hash'], 'delegate'); + if ($allowed) { + $_SESSION['delegate_channel'] = $r[0]['channel_id']; + $_SESSION['delegate'] = $hubloc['xchan_hash']; + $_SESSION['account_id'] = intval($r[0]['channel_account_id']); + // this will set the local_channel authentication in the session + change_channel($r[0]['channel_id']); + $delegate_success = true; + } + } + } - if (! $delegate_success) { - // normal visitor (remote_channel) login session credentials - $_SESSION['visitor_id'] = $hubloc['xchan_hash']; - $_SESSION['my_url'] = $hubloc['xchan_url']; - $_SESSION['my_address'] = $hubloc['hubloc_addr']; - $_SESSION['remote_hub'] = $hubloc['hubloc_url']; - $_SESSION['DNT'] = 1; - } + if (! $delegate_success) { + // normal visitor (remote_channel) login session credentials + $_SESSION['visitor_id'] = $hubloc['xchan_hash']; + $_SESSION['my_url'] = $hubloc['xchan_url']; + $_SESSION['my_address'] = $hubloc['hubloc_addr']; + $_SESSION['remote_hub'] = $hubloc['hubloc_url']; + $_SESSION['DNT'] = 1; + } - $arr = [ - 'xchan' => $hubloc, - 'url' => App::$query_string, - 'session' => $_SESSION - ]; - /** - * @hooks magic_auth_success - * Called when a magic-auth was successful. - * * \e array \b xchan - * * \e string \b url - * * \e array \b session - */ - call_hooks('magic_auth_success', $arr); + $arr = [ + 'xchan' => $hubloc, + 'url' => App::$query_string, + 'session' => $_SESSION + ]; + /** + * @hooks magic_auth_success + * Called when a magic-auth was successful. + * * \e array \b xchan + * * \e string \b url + * * \e array \b session + */ + call_hooks('magic_auth_success', $arr); - App::set_observer($hubloc); - App::set_groups(init_groups_visitor($_SESSION['visitor_id'])); - if (! get_config('system', 'hide_owa_greeting')) { - info(sprintf( t('OpenWebAuth: %1$s welcomes %2$s'),App::get_hostname(), $hubloc['xchan_name'])); - } + App::set_observer($hubloc); + App::set_groups(init_groups_visitor($_SESSION['visitor_id'])); + if (! get_config('system', 'hide_owa_greeting')) { + info(sprintf(t('OpenWebAuth: %1$s welcomes %2$s'), App::get_hostname(), $hubloc['xchan_name'])); + } - logger('OpenWebAuth: auth success from ' . $hubloc['xchan_addr']); - return; + logger('OpenWebAuth: auth success from ' . $hubloc['xchan_addr']); + return; } -function observer_auth($ob_hash) { +function observer_auth($ob_hash) +{ - require_once('include/security.php'); + require_once('include/security.php'); - if ($ob_hash === false) { - return; - } + if ($ob_hash === false) { + return; + } - $r = q("select * from hubloc left join xchan on xchan_hash = hubloc_hash + $r = q( + "select * from hubloc left join xchan on xchan_hash = hubloc_hash where hubloc_addr = '%s' or hubloc_id_url = '%s' or hubloc_hash = '%s' order by hubloc_id desc", - dbesc($ob_hash), - dbesc($ob_hash), - dbesc($ob_hash) - ); + dbesc($ob_hash), + dbesc($ob_hash), + dbesc($ob_hash) + ); - if (! $r) { - // finger them if they can't be found. - $wf = discover_by_webbie($ob_hash); - if ($wf) { - $r = q("select * from hubloc left join xchan on xchan_hash = hubloc_hash + if (! $r) { + // finger them if they can't be found. + $wf = discover_by_webbie($ob_hash); + if ($wf) { + $r = q( + "select * from hubloc left join xchan on xchan_hash = hubloc_hash where hubloc_addr = '%s' or hubloc_id_url = '%s' or hubloc_hash = '%s' order by hubloc_id desc", - dbesc($ob_hash), - dbesc($ob_hash), - dbesc($ob_hash) - ); - } - } - if (! $r) { - logger('unable to finger ' . $ob_hash); - return; - } + dbesc($ob_hash), + dbesc($ob_hash), + dbesc($ob_hash) + ); + } + } + if (! $r) { + logger('unable to finger ' . $ob_hash); + return; + } - $hubloc = $r[0]; + $hubloc = $r[0]; - $_SESSION['authenticated'] = 1; + $_SESSION['authenticated'] = 1; - // normal visitor (remote_channel) login session credentials - $_SESSION['visitor_id'] = $hubloc['xchan_hash']; - $_SESSION['my_url'] = $hubloc['xchan_url']; + // normal visitor (remote_channel) login session credentials + $_SESSION['visitor_id'] = $hubloc['xchan_hash']; + $_SESSION['my_url'] = $hubloc['xchan_url']; - // For now, only enable authenticated link substitution for zot6 channels or - // those that arrive with a zid. - // This is to prevent signed ActivityPub fetches from getting zid-enabled links. - // If a pre-set zid applies, $_SESSION['my_address'] will have been set already - // in Zotlabs\Web\WebServer.php - // @FIXME: to work seamlessly with Friendica and other platforms that choose to - // provide OWA we will need to store the OWA endpoints for each site in SConfig - // and refer to this to determine whether or not to provide "zidified" links. + // For now, only enable authenticated link substitution for zot6 channels or + // those that arrive with a zid. + // This is to prevent signed ActivityPub fetches from getting zid-enabled links. + // If a pre-set zid applies, $_SESSION['my_address'] will have been set already + // in Zotlabs\Web\WebServer.php + // @FIXME: to work seamlessly with Friendica and other platforms that choose to + // provide OWA we will need to store the OWA endpoints for each site in SConfig + // and refer to this to determine whether or not to provide "zidified" links. if (in_array($hubloc['hubloc_network'],['nomad','zot6'])) { - $_SESSION['my_address'] = $hubloc['hubloc_addr']; - } - $_SESSION['remote_hub'] = $hubloc['hubloc_url']; - $_SESSION['DNT'] = 1; - - App::set_observer($hubloc); - App::set_groups(init_groups_visitor($_SESSION['visitor_id'])); + $_SESSION['my_address'] = $hubloc['hubloc_addr']; + } + $_SESSION['remote_hub'] = $hubloc['hubloc_url']; + $_SESSION['DNT'] = 1; + App::set_observer($hubloc); + App::set_groups(init_groups_visitor($_SESSION['visitor_id'])); } diff --git a/index.php b/index.php index b4439b39d..1f7991847 100755 --- a/index.php +++ b/index.php @@ -8,9 +8,10 @@ namespace Zotlabs\Web; * @brief The main entry point to the application. */ -require_once('Zotlabs/Web/WebServer.php'); +require_once 'Zotlabs/Web/WebServer.php'; $server = new WebServer(); $server->run(); + diff --git a/install/htconfig.sample.php b/install/htconfig.sample.php index 0bac2289f..407ba9b79 100755 --- a/install/htconfig.sample.php +++ b/install/htconfig.sample.php @@ -1,13 +1,13 @@ exec($stmt); - if($r===false) { - echo "\nError executing $stmt: ".var_export($db->errorInfo(), true)."\n"; - $err++; - } else { - $n++; - } - if($n % 5 == 0) - echo "\033[255DExecuting: $file, $n/$c\033[K"; - } - echo "\n"; -} - -$drivers=true; -if(!class_exists('PDO')) - $drivers=false; -if($drivers) { - $drivers = PDO::getAvailableDrivers(); - if(!in_array('pgsql', $drivers) || !in_array('mysql', $drivers)) - $drivers = false; -} -if(!$drivers) { - echo "Sorry. This migration tool requires both mysql and pgsql PDO drivers.\n"; - $r = ask_question("If you are on dreamhost you can enable them. This might work on other shared hosts too. Type 'n' to do it yourself.\nWould you like to try (Y/n)? ", array('y', 'n'), 'y'); - if($r=='y') { - $path = $_SERVER['HOME'] . '/.php/5.4'; - if(!file_exists($path)) - mkdir($path, 0770, true); - - $rcfile = $path . '/phprc'; - - $str = ''; - $mods = get_loaded_extensions(); - foreach(array('pdo_mysql','pdo_pgsql','pgsql') as $ext) - if(!in_array($ext, $mods)) - $str .= 'extension=' . $ext . ".so\n"; - - file_put_contents($rcfile, $str, FILE_APPEND ); - echo "drivers enabled.\nNow type: \033[1m/usr/local/bin/php-5.4 install/".basename($argv[0])."\033[0m\n"; - } - exit(); +function run_sql($file, $db, &$err, &$n) +{ + $sql = file_get_contents($file); + $sql = explode(';', $sql); + $err = 0; + $n = 0; + $c = count($sql); + if (!$c) { + echo "Unknown error.\n"; + exit(); + } + foreach ($sql as $stmt) { + if ($stmt == '' || $stmt == "\n" || $stmt == "\n\n") { + $c--; + continue; + } + $r = $db->exec($stmt); + if ($r === false) { + echo "\nError executing $stmt: " . var_export($db->errorInfo(), true) . "\n"; + $err++; + } else { + $n++; + } + if ($n % 5 == 0) { + echo "\033[255DExecuting: $file, $n/$c\033[K"; + } + } + echo "\n"; } -foreach(array('install','include','mod','view') as $dir) { - if(!file_exists($dir)) { - echo "You must execute from inside the webroot like the cron\n"; - exit(); - } +$drivers = true; +if (!class_exists('PDO')) { + $drivers = false; +} +if ($drivers) { + $drivers = PDO::getAvailableDrivers(); + if (!in_array('pgsql', $drivers) || !in_array('mysql', $drivers)) { + $drivers = false; + } +} +if (!$drivers) { + echo "Sorry. This migration tool requires both mysql and pgsql PDO drivers.\n"; + $r = ask_question("If you are on dreamhost you can enable them. This might work on other shared hosts too. Type 'n' to do it yourself.\nWould you like to try (Y/n)? ", array('y', 'n'), 'y'); + if ($r == 'y') { + $path = $_SERVER['HOME'] . '/.php/5.4'; + if (!file_exists($path)) { + mkdir($path, 0770, true); + } + + $rcfile = $path . '/phprc'; + + $str = ''; + $mods = get_loaded_extensions(); + foreach (array('pdo_mysql','pdo_pgsql','pgsql') as $ext) { + if (!in_array($ext, $mods)) { + $str .= 'extension=' . $ext . ".so\n"; + } + } + + file_put_contents($rcfile, $str, FILE_APPEND); + echo "drivers enabled.\nNow type: \033[1m/usr/local/bin/php-5.4 install/" . basename($argv[0]) . "\033[0m\n"; + } + exit(); +} + +foreach (array('install','include','mod','view') as $dir) { + if (!file_exists($dir)) { + echo "You must execute from inside the webroot like the cron\n"; + exit(); + } } $cfgfile = '.htconfig.php'; -if($argc >= 2 && $argv[1] == '--resume') { - if($argc < 4) { - echo "Resume usage {$argv[0]} --resume \n"; - exit(); - } - $starttable = $argv[2]; - $startrow = $argv[3]; - $cfgfile = '.htconfig.php-mysql'; +if ($argc >= 2 && $argv[1] == '--resume') { + if ($argc < 4) { + echo "Resume usage {$argv[0]} --resume
                    \n"; + exit(); + } + $starttable = $argv[2]; + $startrow = $argv[3]; + $cfgfile = '.htconfig.php-mysql'; } $cfg = parse_htconfig($cfgfile); $type = get_configtype($cfg); -if($type != 'mysql') { - echo "Error. Must start with standard mysql installation in .htconfig.php.\n"; - exit(); +if ($type != 'mysql') { + echo "Error. Must start with standard mysql installation in .htconfig.php.\n"; + exit(); } -if(!$cfg['port']) - $cfg['port'] = 3306; +if (!$cfg['port']) { + $cfg['port'] = 3306; +} try { - $mydb = new PDO("mysql:host={$cfg['host']};dbname={$cfg['data']};port={$cfg['port']}", $cfg['user'], $cfg['pass']); + $mydb = new PDO("mysql:host={$cfg['host']};dbname={$cfg['data']};port={$cfg['port']}", $cfg['user'], $cfg['pass']); } catch (PDOException $e) { - echo "Error connecting to mysql DB: " . $e->getMessage() . "\n"; - exit(); + echo "Error connecting to mysql DB: " . $e->getMessage() . "\n"; + exit(); } // mysql insists on buffering even when you use fetch() instead of fetchAll() for some stupid reason // http://stackoverflow.com/questions/6895098/pdo-mysql-memory-consumption-with-large-result-set -$mydb->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, false); +$mydb->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, false); -if(!file_exists('.htconfig.php-pgsql')) { - echo "Enter postgres server info:\n"; - $p['host'] = get_data("Hostname: ", '/[\w.]+/'); - $p['port'] = get_data("Enter port (0 for default): ", '/\d+/'); - $p['user'] = get_data("Username: ", '/\w+/'); - $p['pass'] = get_data("Password: ", '/[[:print:]]+/'); - $p['data'] = get_data("Database name: ", '/\w+/'); - $old = file_get_contents('.htconfig.php'); - $new = preg_replace( - array( - '/^(\$db_host\s*=\s*\')([\w.]+)(\';)$/m', - '/^(\$db_port\s*=\s*\')(\d+)(\';)$/m', - '/^(\$db_user\s*=\s*\')(\w+)(\';)$/m', - '/^(\$db_pass\s*=\s*\')([[:print:]]+)(\';)/m', - '/^(\$db_data\s*=\s*\')(\w+)(\';)$/m', - '/^(\$db_type\s*=\s*\')(\d)(\';)$/m' // in case they already have it - ), array( - "$1{$p['host']}$3", - "\${1}{$p['port']}$3", - "$1{$p['user']}$3", - "$1{$p['pass']}$3", - "$1{$p['data']}$3\n\$db_type = '1';\n", // they probably don't - "\${1}1$3" - ), - $old, - 1, - $repl - ); - if($new === false || $repl < 5) { - echo "Failed. Please make a postgres config file named .htconfig.php-pgsql - Be sure to add \"\$db_type = '1';\" to your config.\n"; - exit(); - } - file_put_contents('.htconfig.php-pgsql', $new); +if (!file_exists('.htconfig.php-pgsql')) { + echo "Enter postgres server info:\n"; + $p['host'] = get_data("Hostname: ", '/[\w.]+/'); + $p['port'] = get_data("Enter port (0 for default): ", '/\d+/'); + $p['user'] = get_data("Username: ", '/\w+/'); + $p['pass'] = get_data("Password: ", '/[[:print:]]+/'); + $p['data'] = get_data("Database name: ", '/\w+/'); + $old = file_get_contents('.htconfig.php'); + $new = preg_replace( + array( + '/^(\$db_host\s*=\s*\')([\w.]+)(\';)$/m', + '/^(\$db_port\s*=\s*\')(\d+)(\';)$/m', + '/^(\$db_user\s*=\s*\')(\w+)(\';)$/m', + '/^(\$db_pass\s*=\s*\')([[:print:]]+)(\';)/m', + '/^(\$db_data\s*=\s*\')(\w+)(\';)$/m', + '/^(\$db_type\s*=\s*\')(\d)(\';)$/m' // in case they already have it + ), + array( + "$1{$p['host']}$3", + "\${1}{$p['port']}$3", + "$1{$p['user']}$3", + "$1{$p['pass']}$3", + "$1{$p['data']}$3\n\$db_type = '1';\n", // they probably don't + "\${1}1$3" + ), + $old, + 1, + $repl + ); + if ($new === false || $repl < 5) { + echo "Failed. Please make a postgres config file named .htconfig.php-pgsql - Be sure to add \"\$db_type = '1';\" to your config.\n"; + exit(); + } + file_put_contents('.htconfig.php-pgsql', $new); } $pcfg = parse_htconfig('.htconfig.php-pgsql'); $ptype = get_configtype($pcfg); -if($ptype != 'pgsql') { - echo "Error. Must have a valid pgsql config named .htconfig.php-pgsql. Be sure to add \"\$db_type = '1';\" to your config.\n"; - exit(); +if ($ptype != 'pgsql') { + echo "Error. Must have a valid pgsql config named .htconfig.php-pgsql. Be sure to add \"\$db_type = '1';\" to your config.\n"; + exit(); } -if(!$pcfg['port']) - $pcfg['port'] = 5432; +if (!$pcfg['port']) { + $pcfg['port'] = 5432; +} try { - $pgdb = new PDO("pgsql:host={$pcfg['host']};dbname={$pcfg['data']};port={$pcfg['port']}", $pcfg['user'], $pcfg['pass']); + $pgdb = new PDO("pgsql:host={$pcfg['host']};dbname={$pcfg['data']};port={$pcfg['port']}", $pcfg['user'], $pcfg['pass']); } catch (PDOException $e) { - echo "Error connecting to pgsql DB: " . $e->getMessage() . "\n"; - echo "cfg string: " . "pgsql:host={$pcfg['host']};dbname={$pcfg['data']};port={$pcfg['port']}\n"; - exit(); + echo "Error connecting to pgsql DB: " . $e->getMessage() . "\n"; + echo "cfg string: " . "pgsql:host={$pcfg['host']};dbname={$pcfg['data']};port={$pcfg['port']}\n"; + exit(); } $B = "\033[0;34m"; $H = "\033[0;35m"; @@ -199,159 +223,172 @@ $W = "\033[1;37m"; $M = "\033[1;31m"; $N = "\033[0m"; -if(isset($starttable)) { - $r = ask_question("Ready to migrate {$W}Red{$M}(#){$W}Matrix$N from mysql db @$B{$cfg['host']}$N/$B{$cfg['data']}$N to postgres db @$B{$pcfg['host']}$N/$B{$pcfg['data']}$N. +if (isset($starttable)) { + $r = ask_question( + "Ready to migrate {$W}Red{$M}(#){$W}Matrix$N from mysql db @$B{$cfg['host']}$N/$B{$cfg['data']}$N to postgres db @$B{$pcfg['host']}$N/$B{$pcfg['data']}$N. Resuming failed migration ({$M}experimental$N) starting at table '$starttable' row $startrow. -Are you ready to begin (N/y)? ", - array('y', 'n'), - 'n' - ); - if($r == 'n') - exit(); +Are you ready to begin (N/y)? ", + array('y', 'n'), + 'n' + ); + if ($r == 'n') { + exit(); + } } else { - $r = ask_question("Ready to migrate {$W}Red{$M}(#){$W}Matrix$N from mysql db @$B{$cfg['host']}$N/$B{$cfg['data']}$N to postgres db @$B{$pcfg['host']}$N/$B{$pcfg['data']}$N. + $r = ask_question("Ready to migrate {$W}Red{$M}(#){$W}Matrix$N from mysql db @$B{$cfg['host']}$N/$B{$cfg['data']}$N to postgres db @$B{$pcfg['host']}$N/$B{$pcfg['data']}$N. The site will be disabled during the migration by moving the $H.htconfig.php$N file to $H.htconfig.php-mysql$N. If for any reason the migration fails, you will need to move the config file back into place manually before trying again. -Are you ready to begin (N/y)? ", array('y','n'), 'n' - ); +Are you ready to begin (N/y)? ", array('y','n'), 'n'); - if($r == 'n') - exit(); + if ($r == 'n') { + exit(); + } - rename('.htconfig.php', '.htconfig.php-mysql'); + rename('.htconfig.php', '.htconfig.php-mysql'); - run_sql('install/schema_postgres.sql', $pgdb, $err, $n); - if($err) { - echo "There were $err errors creating the pgsql schema. Unable to continue.\n"; - exit(); - } + run_sql('install/schema_postgres.sql', $pgdb, $err, $n); + if ($err) { + echo "There were $err errors creating the pgsql schema. Unable to continue.\n"; + exit(); + } - echo "pgsql schema created. $n queries executed successfully.\n"; + echo "pgsql schema created. $n queries executed successfully.\n"; } $res = $pgdb->query("select relname, attname, pg_type.typname from ((pg_attribute inner join pg_class on attrelid=pg_class.oid) inner join pg_type on atttypid=pg_type.oid) inner join pg_namespace on relnamespace=pg_namespace.oid where nspname='public' and atttypid not in (26,27,28,29) and relkind='r' and attname <> 'item_search_vector';"); -if($res === false) { - echo "Error reading back schema. Unable to continue.\n"; - var_export($pgdb->errorInfo()); - exit(); +if ($res === false) { + echo "Error reading back schema. Unable to continue.\n"; + var_export($pgdb->errorInfo()); + exit(); } $schema = []; -while(($row = $res->fetch()) !== false) - $schema[$row[0]][$row[1]] = $row[2]; - +while (($row = $res->fetch()) !== false) { + $schema[$row[0]][$row[1]] = $row[2]; +} + $res = $pgdb->query("select relname, attname from pg_attribute inner join pg_class on attrelid=pg_class.oid inner join pg_constraint on conrelid=pg_class.oid and pg_attribute.attnum = any (conkey) where contype='p';"); -if($res === false) { - echo "Error reading back primary keys. Unable to continue.\n"; - var_export($pgdb->errorInfo()); - exit(); +if ($res === false) { + echo "Error reading back primary keys. Unable to continue.\n"; + var_export($pgdb->errorInfo()); + exit(); } $pkeys = []; -while(($row = $res->fetch()) !== false) - $pkeys[$row[0]] = $row[1]; +while (($row = $res->fetch()) !== false) { + $pkeys[$row[0]] = $row[1]; +} -$err = 0; $n = 0; +$err = 0; +$n = 0; $reserved = array('ignore','key','with'); -foreach($schema as $table=>$fields) { - if(isset($starttable) && !$n && $table != $starttable) { - echo "Skipping table $table\n"; - continue; - } - $fnames = array_keys($fields); - $pfnames = array_keys($fields); - - foreach($fnames as &$fname) - if(in_array($fname, $reserved)) - $fname = '`' . $fname . '`'; - $fstr = implode(',', $fnames); - - foreach($pfnames as &$pfname) - if(in_array($pfname, $reserved)) - $pfname = '"' . $pfname . '"'; - $pfstr = implode(',', $pfnames); - - $cres = $mydb->query("SELECT count(*) FROM $table;"); - if($cres === false) { - echo "Fatal error counting table $table: ".var_export($mydb->errorInfo(), true)."\n"; - exit(); - } - $nrows = $cres->fetchColumn(0); - $cres->closeCursor(); - - if(!$nrows) { - echo "TABLE $table has 0 rows in mysql db.\n"; - continue; - } - - $pstr = ''; - for($x=0, $c=count($fields); $x < $c; $x++) - $pstr .= ($x ? ',?' : '?'); +foreach ($schema as $table => $fields) { + if (isset($starttable) && !$n && $table != $starttable) { + echo "Skipping table $table\n"; + continue; + } + $fnames = array_keys($fields); + $pfnames = array_keys($fields); - if(isset($starttable) && $table == $starttable) { - $selectsql = "SELECT $fstr FROM $table ORDER BY {$pkeys[$table]} LIMIT $nrows OFFSET $startrow;"; - $crow = $startrow; - } else { - $selectsql = "SELECT $fstr FROM $table ORDER BY {$pkeys[$table]};"; - $crow = 0; - } + foreach ($fnames as &$fname) { + if (in_array($fname, $reserved)) { + $fname = '`' . $fname . '`'; + } + } + $fstr = implode(',', $fnames); - echo "\033[255DTABLE: $table [$c fields] $crow/$nrows (".number_format(($crow/$nrows)*100,2)."%)\033[K"; - - $res = $mydb->query($selectsql); - if($res === false) { - echo "Fatal Error importing table $table: ".var_export($mydb->errorInfo(), true)."\n"; - exit(); - } + foreach ($pfnames as &$pfname) { + if (in_array($pfname, $reserved)) { + $pfname = '"' . $pfname . '"'; + } + } + $pfstr = implode(',', $pfnames); - $istmt = $pgdb->prepare("INSERT INTO $table ($pfstr) VALUES ($pstr);"); - if($istmt === false) { - echo "Fatal error preparing query. Aborting.\n"; - var_export($pgdb->errorInfo()); - exit(); - } + $cres = $mydb->query("SELECT count(*) FROM $table;"); + if ($cres === false) { + echo "Fatal error counting table $table: " . var_export($mydb->errorInfo(), true) . "\n"; + exit(); + } + $nrows = $cres->fetchColumn(0); + $cres->closeCursor(); - while(($row = $res->fetch(PDO::FETCH_NUM)) !== false) { - foreach($row as $idx => &$val) - if(array_slice(array_values($fields),$idx,1)[0] == 'timestamp' && $val == '0000-00-00 00:00:00') - $istmt->bindParam($idx+1, ($nulldate='0001-01-01 00:00:00')); - else if(array_slice(array_values($fields),$idx,1)[0] == 'bytea') - $istmt->bindParam($idx+1, $val, PDO::PARAM_LOB); - else - $istmt->bindParam($idx+1, $val); - $r = $istmt->execute(); - if($r === false) { - $err++; - echo "Insert error: ".var_export(array($pgdb->errorInfo(), $table, $fields, $row), true)."\nResume with {$argv[0]} --resume $table $crow\n"; - exit(); - } else - $n++; - $crow++; - if(($crow % 10) == 0 || $crow == $nrows) - echo "\033[255DTABLE: $table [$c fields] $crow/$nrows (".number_format(($crow/$nrows)*100,2)."%)\033[K"; - } - $res->closeCursor(); - echo "\n"; + if (!$nrows) { + echo "TABLE $table has 0 rows in mysql db.\n"; + continue; + } + + $pstr = ''; + for ($x = 0, $c = count($fields); $x < $c; $x++) { + $pstr .= ($x ? ',?' : '?'); + } + + if (isset($starttable) && $table == $starttable) { + $selectsql = "SELECT $fstr FROM $table ORDER BY {$pkeys[$table]} LIMIT $nrows OFFSET $startrow;"; + $crow = $startrow; + } else { + $selectsql = "SELECT $fstr FROM $table ORDER BY {$pkeys[$table]};"; + $crow = 0; + } + + echo "\033[255DTABLE: $table [$c fields] $crow/$nrows (" . number_format(($crow / $nrows) * 100, 2) . "%)\033[K"; + + $res = $mydb->query($selectsql); + if ($res === false) { + echo "Fatal Error importing table $table: " . var_export($mydb->errorInfo(), true) . "\n"; + exit(); + } + + $istmt = $pgdb->prepare("INSERT INTO $table ($pfstr) VALUES ($pstr);"); + if ($istmt === false) { + echo "Fatal error preparing query. Aborting.\n"; + var_export($pgdb->errorInfo()); + exit(); + } + + while (($row = $res->fetch(PDO::FETCH_NUM)) !== false) { + foreach ($row as $idx => &$val) { + if (array_slice(array_values($fields), $idx, 1)[0] == 'timestamp' && $val == '0000-00-00 00:00:00') { + $istmt->bindParam($idx + 1, ($nulldate = '0001-01-01 00:00:00')); + } elseif (array_slice(array_values($fields), $idx, 1)[0] == 'bytea') { + $istmt->bindParam($idx + 1, $val, PDO::PARAM_LOB); + } else { + $istmt->bindParam($idx + 1, $val); + } + } + $r = $istmt->execute(); + if ($r === false) { + $err++; + echo "Insert error: " . var_export(array($pgdb->errorInfo(), $table, $fields, $row), true) . "\nResume with {$argv[0]} --resume $table $crow\n"; + exit(); + } else { + $n++; + } + $crow++; + if (($crow % 10) == 0 || $crow == $nrows) { + echo "\033[255DTABLE: $table [$c fields] $crow/$nrows (" . number_format(($crow / $nrows) * 100, 2) . "%)\033[K"; + } + } + $res->closeCursor(); + echo "\n"; } echo "Done with $err errors and $n inserts.\n"; -if($err) { - echo "Migration had errors. Aborting.\n"; - exit(); +if ($err) { + echo "Migration had errors. Aborting.\n"; + exit(); } run_sql('install/migrate_mypg_fixseq.sql', $pgdb, $err, $n); echo "Sequences updated with $err errors and $n inserts.\n"; -if($err) - exit(); - +if ($err) { + exit(); +} + $r = ask_question("Everything successful. Once you connect up the pg database there is no going back. Do you want to make it live (N,y)?", array('y', 'n'), 'n'); -if($r == 'n') { - echo "You can make active by renaming .htconfig.php-pgsql to .htconfig.php, or start over by renaming .htconfig.php-mysql to .htconfig.php\n"; - exit(); -} +if ($r == 'n') { + echo "You can make active by renaming .htconfig.php-pgsql to .htconfig.php, or start over by renaming .htconfig.php-mysql to .htconfig.php\n"; + exit(); +} rename('.htconfig.php-pgsql', '.htconfig.php'); echo "Done. {$W}Red{$M}(#){$W}Matrix$N now running on postgres.\n"; - - diff --git a/install/testargs.php b/install/testargs.php index 4c9bce4e9..a05405944 100644 --- a/install/testargs.php +++ b/install/testargs.php @@ -8,14 +8,14 @@ * During installation we need to check if register_argc_argv is * enabled for the command line PHP processor, because otherwise * deliveries will fail. So we will do a shell exec of php and - * execute this file with a command line argument, and see if it - * echoes the argument back to us. Otherwise notify the person + * execute this file with a command line argument, and see if it + * echoes the argument back to us. Otherwise notify the person * that their installation doesn't meet the system requirements. * - */ + */ - -if(($argc > 1) && isset($argv[1])) - echo $argv[1]; -else - echo ''; +if (($argc > 1) && isset($argv[1])) { + echo $argv[1]; +} else { + echo ''; +} diff --git a/tests/unit/Access/AccessListTest.php b/tests/unit/Access/AccessListTest.php index ee9623a7f..ea3fc5570 100644 --- a/tests/unit/Access/AccessListTest.php +++ b/tests/unit/Access/AccessListTest.php @@ -1,4 +1,5 @@ '', - 'allow_gid' => '', - 'deny_cid' => '', - 'deny_gid' => '' - ]; + /** + * @brief Expected result for most tests. + * @var array + */ + protected $expectedResult = [ + 'allow_cid' => '', + 'allow_gid' => '', + 'deny_cid' => '', + 'deny_gid' => '' + ]; - public function testConstructor() { - $channel = [ - 'channel_allow_cid' => '', - 'channel_allow_gid' => '', - 'channel_deny_cid' => '', - 'channel_deny_gid' => '' - ]; + public function testConstructor() + { + $channel = [ + 'channel_allow_cid' => '', + 'channel_allow_gid' => '', + 'channel_deny_cid' => '', + 'channel_deny_gid' => '' + ]; - $accessList = new AccessControl($channel); + $accessList = new AccessControl($channel); - $this->assertEquals($this->expectedResult, $accessList->get()); - $this->assertFalse($accessList->get_explicit()); - } + $this->assertEquals($this->expectedResult, $accessList->get()); + $this->assertFalse($accessList->get_explicit()); + } - /** - * @expectedException PHPUnit\Framework\Error\Error - */ - public function testPHPErrorOnInvalidConstructor() { - $accessList = new AccessControl('invalid'); - // Causes: "Illegal string offset 'channel_allow_cid'" - } + /** + * @expectedException PHPUnit\Framework\Error\Error + */ + public function testPHPErrorOnInvalidConstructor() + { + $accessList = new AccessControl('invalid'); + // Causes: "Illegal string offset 'channel_allow_cid'" + } - public function testDefaultGetExplicit() { - $accessList = new AccessControl([]); + public function testDefaultGetExplicit() + { + $accessList = new AccessControl([]); - $this->assertFalse($accessList->get_explicit()); - } + $this->assertFalse($accessList->get_explicit()); + } - public function testDefaultGet() { - $arr = [ - 'allow_cid' => '', - 'allow_gid' => '', - 'deny_cid' => '', - 'deny_gid' => '' - ]; + public function testDefaultGet() + { + $arr = [ + 'allow_cid' => '', + 'allow_gid' => '', + 'deny_cid' => '', + 'deny_gid' => '' + ]; - $accessList = new AccessControl([]); + $accessList = new AccessControl([]); - $this->assertEquals($arr, $accessList->get()); - } + $this->assertEquals($arr, $accessList->get()); + } - public function testSet() { - $arr = [ - 'allow_cid' => '', - 'allow_gid' => '', - 'deny_cid' => '', - 'deny_gid' => '' - ]; - $accessList = new AccessControl([]); + public function testSet() + { + $arr = [ + 'allow_cid' => '', + 'allow_gid' => '', + 'deny_cid' => '', + 'deny_gid' => '' + ]; + $accessList = new AccessControl([]); - // default explicit true - $accessList->set($arr); + // default explicit true + $accessList->set($arr); - $this->assertEquals($this->expectedResult, $accessList->get()); - $this->assertTrue($accessList->get_explicit()); + $this->assertEquals($this->expectedResult, $accessList->get()); + $this->assertTrue($accessList->get_explicit()); - // set explicit false - $accessList->set($arr, false); + // set explicit false + $accessList->set($arr, false); - $this->assertEquals($this->expectedResult, $accessList->get()); - $this->assertFalse($accessList->get_explicit()); - } + $this->assertEquals($this->expectedResult, $accessList->get()); + $this->assertFalse($accessList->get_explicit()); + } - /** - * @expectedException PHPUnit\Framework\Error\Error - */ - public function testPHPErrorOnInvalidSet() { - $accessList = new AccessControl([]); + /** + * @expectedException PHPUnit\Framework\Error\Error + */ + public function testPHPErrorOnInvalidSet() + { + $accessList = new AccessControl([]); - $accessList->set('invalid'); - // Causes: "Illegal string offset 'allow_cid'" - } + $accessList->set('invalid'); + // Causes: "Illegal string offset 'allow_cid'" + } - /** - * set_from_[] calls some other functions, too which are not yet unit tested. - * @uses ::perms2str - * @uses ::sanitise_acl - * @uses ::notags - */ - public function testSetFromArray() { - // array - $arraySetFromArray = [ - 'contact_allow' => ['acid', 'acid2'], - 'group_allow' => ['agid'], - 'contact_deny' => [], - 'group_deny' => ['dgid', 'dgid2'] - ]; - $accessList = new AccessControl([]); - $accessList->set_from_array($arraySetFromArray); + /** + * set_from_[] calls some other functions, too which are not yet unit tested. + * @uses ::perms2str + * @uses ::sanitise_acl + * @uses ::notags + */ + public function testSetFromArray() + { + // array + $arraySetFromArray = [ + 'contact_allow' => ['acid', 'acid2'], + 'group_allow' => ['agid'], + 'contact_deny' => [], + 'group_deny' => ['dgid', 'dgid2'] + ]; + $accessList = new AccessControl([]); + $accessList->set_from_array($arraySetFromArray); - $this->assertEquals($this->expectedResult, $accessList->get()); - $this->assertTrue($accessList->get_explicit()); + $this->assertEquals($this->expectedResult, $accessList->get()); + $this->assertTrue($accessList->get_explicit()); - // string - $stringSetFromArray = [ - 'contact_allow' => 'acid,acid2', - 'group_allow' => 'agid', - 'contact_deny' => '', - 'group_deny' => 'dgid, dgid2' - ]; - $accessList2 = new AccessControl([]); - $accessList2->set_from_array($stringSetFromArray, false); + // string + $stringSetFromArray = [ + 'contact_allow' => 'acid,acid2', + 'group_allow' => 'agid', + 'contact_deny' => '', + 'group_deny' => 'dgid, dgid2' + ]; + $accessList2 = new AccessControl([]); + $accessList2->set_from_array($stringSetFromArray, false); - $this->assertEquals($this->expectedResult, $accessList2->get()); - $this->assertFalse($accessList2->get_explicit()); - } + $this->assertEquals($this->expectedResult, $accessList2->get()); + $this->assertFalse($accessList2->get_explicit()); + } - /** - * @dataProvider isprivateProvider - */ - public function testIsPrivate($channel) { - $accessListPublic = new AccessControl([]); - $this->assertFalse($accessListPublic->is_private()); + /** + * @dataProvider isprivateProvider + */ + public function testIsPrivate($channel) + { + $accessListPublic = new AccessControl([]); + $this->assertFalse($accessListPublic->is_private()); - $accessListPrivate = new AccessControl($channel); - $this->assertTrue($accessListPrivate->is_private()); - } + $accessListPrivate = new AccessControl($channel); + $this->assertTrue($accessListPrivate->is_private()); + } - public function isprivateProvider() { - return [ - 'all set' => [[ - 'channel_allow_cid' => '', - 'channel_allow_gid' => '', - 'channel_deny_cid' => '', - 'channel_deny_gid' => '' - ]], - 'only one set' => [[ - 'channel_allow_cid' => '', - 'channel_allow_gid' => '', - 'channel_deny_cid' => '', - 'channel_deny_gid' => '' - ]], - 'acid+null' => [[ - 'channel_allow_cid' => '', - 'channel_allow_gid' => null, - 'channel_deny_cid' => '', - 'channel_deny_gid' => '' - ]] - ]; - } - -} \ No newline at end of file + public function isprivateProvider() + { + return [ + 'all set' => [[ + 'channel_allow_cid' => '', + 'channel_allow_gid' => '', + 'channel_deny_cid' => '', + 'channel_deny_gid' => '' + ]], + 'only one set' => [[ + 'channel_allow_cid' => '', + 'channel_allow_gid' => '', + 'channel_deny_cid' => '', + 'channel_deny_gid' => '' + ]], + 'acid+null' => [[ + 'channel_allow_cid' => '', + 'channel_allow_gid' => null, + 'channel_deny_cid' => '', + 'channel_deny_gid' => '' + ]] + ]; + } +} diff --git a/tests/unit/Access/PermissionLimitsTest.php b/tests/unit/Access/PermissionLimitsTest.php index 57ad42a19..03c9b055b 100644 --- a/tests/unit/Access/PermissionLimitsTest.php +++ b/tests/unit/Access/PermissionLimitsTest.php @@ -1,4 +1,5 @@ getFunctionMock('Zotlabs\Access', 't'); + $t->expects($this->exactly($permsCount)); - // Create a stub for global function t() with expectation - $t = $this->getFunctionMock('Zotlabs\Access', 't'); - $t->expects($this->exactly($permsCount)); + $stdlimits = PermissionLimits::Std_Limits(); + $this->assertCount($permsCount, $stdlimits, "There should be $permsCount permissions."); - $stdlimits = PermissionLimits::Std_Limits(); - $this->assertCount($permsCount, $stdlimits, "There should be $permsCount permissions."); - - $this->assertEquals(PERMS_PUBLIC, $stdlimits['view_stream']); - $this->assertEquals(PERMS_SPECIFIC, $stdlimits['send_stream']); - $this->assertEquals(PERMS_PUBLIC, $stdlimits['view_profile']); - $this->assertEquals(PERMS_PUBLIC, $stdlimits['view_contacts']); - $this->assertEquals(PERMS_PUBLIC, $stdlimits['view_storage']); - $this->assertEquals(PERMS_SPECIFIC, $stdlimits['write_storage']); - $this->assertEquals(PERMS_PUBLIC, $stdlimits['view_pages']); - $this->assertEquals(PERMS_PUBLIC, $stdlimits['view_wiki']); - $this->assertEquals(PERMS_SPECIFIC, $stdlimits['write_pages']); - $this->assertEquals(PERMS_SPECIFIC, $stdlimits['write_wiki']); - $this->assertEquals(PERMS_SPECIFIC, $stdlimits['post_wall']); - $this->assertEquals(PERMS_SPECIFIC, $stdlimits['post_comments']); - $this->assertEquals(PERMS_SPECIFIC, $stdlimits['post_mail']); - $this->assertEquals(PERMS_SPECIFIC, $stdlimits['post_like']); - $this->assertEquals(PERMS_SPECIFIC, $stdlimits['tag_deliver']); - $this->assertEquals(PERMS_SPECIFIC, $stdlimits['chat']); - $this->assertEquals(PERMS_SPECIFIC, $stdlimits['republish']); - $this->assertEquals(PERMS_SPECIFIC, $stdlimits['delegate']); - } - -} \ No newline at end of file + $this->assertEquals(PERMS_PUBLIC, $stdlimits['view_stream']); + $this->assertEquals(PERMS_SPECIFIC, $stdlimits['send_stream']); + $this->assertEquals(PERMS_PUBLIC, $stdlimits['view_profile']); + $this->assertEquals(PERMS_PUBLIC, $stdlimits['view_contacts']); + $this->assertEquals(PERMS_PUBLIC, $stdlimits['view_storage']); + $this->assertEquals(PERMS_SPECIFIC, $stdlimits['write_storage']); + $this->assertEquals(PERMS_PUBLIC, $stdlimits['view_pages']); + $this->assertEquals(PERMS_PUBLIC, $stdlimits['view_wiki']); + $this->assertEquals(PERMS_SPECIFIC, $stdlimits['write_pages']); + $this->assertEquals(PERMS_SPECIFIC, $stdlimits['write_wiki']); + $this->assertEquals(PERMS_SPECIFIC, $stdlimits['post_wall']); + $this->assertEquals(PERMS_SPECIFIC, $stdlimits['post_comments']); + $this->assertEquals(PERMS_SPECIFIC, $stdlimits['post_mail']); + $this->assertEquals(PERMS_SPECIFIC, $stdlimits['post_like']); + $this->assertEquals(PERMS_SPECIFIC, $stdlimits['tag_deliver']); + $this->assertEquals(PERMS_SPECIFIC, $stdlimits['chat']); + $this->assertEquals(PERMS_SPECIFIC, $stdlimits['republish']); + $this->assertEquals(PERMS_SPECIFIC, $stdlimits['delegate']); + } +} diff --git a/tests/unit/Access/PermissionRolesTest.php b/tests/unit/Access/PermissionRolesTest.php index d2189afc9..4ef9b2d8b 100644 --- a/tests/unit/Access/PermissionRolesTest.php +++ b/tests/unit/Access/PermissionRolesTest.php @@ -1,4 +1,5 @@ assertEquals($expectedVersion, PermissionRoles::version()); - $this->assertEquals($expectedVersion, PermissionRoles::version()); - - $pr = new PermissionRoles(); - $this->assertEquals($expectedVersion, $pr->version()); - } + $pr = new PermissionRoles(); + $this->assertEquals($expectedVersion, $pr->version()); + } - public function testRoles() { - // Create a stub for global function t() with expectation - $t = $this->getFunctionMock('Zotlabs\Access', 't'); - $t->expects($this->atLeastOnce())->willReturnCallback( - function ($string) { - return $string; - } - ); + public function testRoles() + { + // Create a stub for global function t() with expectation + $t = $this->getFunctionMock('Zotlabs\Access', 't'); + $t->expects($this->atLeastOnce())->willReturnCallback( + function ($string) { + return $string; + } + ); - $roles = PermissionRoles::roles(); - $r = new PermissionRoles(); - $this->assertEquals($roles, $r->roles()); + $roles = PermissionRoles::roles(); + $r = new PermissionRoles(); + $this->assertEquals($roles, $r->roles()); - $socialNetworking = [ - 'social_federation' => 'Social - Federation', - 'social' => 'Social - Mostly Public', - 'social_restricted' => 'Social - Restricted', - 'social_private' => 'Social - Private' - ]; + $socialNetworking = [ + 'social_federation' => 'Social - Federation', + 'social' => 'Social - Mostly Public', + 'social_restricted' => 'Social - Restricted', + 'social_private' => 'Social - Private' + ]; - $this->assertArraySubset(['Social Networking' => $socialNetworking], $roles); - $this->assertEquals($socialNetworking, $roles['Social Networking']); + $this->assertArraySubset(['Social Networking' => $socialNetworking], $roles); + $this->assertEquals($socialNetworking, $roles['Social Networking']); - $this->assertCount(5, $roles, 'There should be 5 permission groups.'); + $this->assertCount(5, $roles, 'There should be 5 permission groups.'); - $this->assertCount(1, $roles['Other'], "In the 'Other' group should be just one permission role"); - } + $this->assertCount(1, $roles['Other'], "In the 'Other' group should be just one permission role"); + } - /** - * @uses ::call_hooks - * @uses Zotlabs\Access\PermissionLimits::Std_Limits - * @uses Zotlabs\Access\Permissions::Perms - */ - public function testRole_perms() { - // Create a stub for global function t() - $t = $this->getFunctionMock('Zotlabs\Access', 't'); - $t = $this->getFunctionMock('Zotlabs\Access', 'get_config'); + /** + * @uses ::call_hooks + * @uses Zotlabs\Access\PermissionLimits::Std_Limits + * @uses Zotlabs\Access\Permissions::Perms + */ + public function testRole_perms() + { + // Create a stub for global function t() + $t = $this->getFunctionMock('Zotlabs\Access', 't'); + $t = $this->getFunctionMock('Zotlabs\Access', 'get_config'); - $rp_social = PermissionRoles::role_perms('social'); - $this->assertEquals('social', $rp_social['role']); + $rp_social = PermissionRoles::role_perms('social'); + $this->assertEquals('social', $rp_social['role']); - $rp_custom = PermissionRoles::role_perms('custom'); - $this->assertEquals(['role' => 'custom'], $rp_custom); - - $rp_nonexistent = PermissionRoles::role_perms('nonexistent'); - $this->assertEquals(['role' => 'nonexistent'], $rp_nonexistent); - } + $rp_custom = PermissionRoles::role_perms('custom'); + $this->assertEquals(['role' => 'custom'], $rp_custom); + $rp_nonexistent = PermissionRoles::role_perms('nonexistent'); + $this->assertEquals(['role' => 'nonexistent'], $rp_nonexistent); + } } diff --git a/tests/unit/Access/PermissionsTest.php b/tests/unit/Access/PermissionsTest.php index 9052ef4a7..cb643bb5a 100644 --- a/tests/unit/Access/PermissionsTest.php +++ b/tests/unit/Access/PermissionsTest.php @@ -1,4 +1,5 @@ assertEquals($expectedVersion, Permissions::version()); - // static call - $this->assertEquals($expectedVersion, Permissions::version()); + // instance call + $p = new Permissions(); + $this->assertEquals($expectedVersion, $p->version()); + } - // instance call - $p = new Permissions(); - $this->assertEquals($expectedVersion, $p->version()); - } + /** + * @coversNothing + */ + public function testVersionEqualsPermissionRoles() + { + $p = new Permissions(); + $pr = new PermissionRoles(); + $this->assertEquals($p->version(), $pr->version()); + } - /** - * @coversNothing - */ - public function testVersionEqualsPermissionRoles() { - $p = new Permissions(); - $pr = new \Zotlabs\Access\PermissionRoles(); - $this->assertEquals($p->version(), $pr->version()); - } + /** + * @uses ::call_hooks + */ + public function testPerms() + { + // There are 18 default perms + $permsCount = 18; - /** - * @uses ::call_hooks - */ - public function testPerms() { - // There are 18 default perms - $permsCount = 18; + // Create a stub for global function t() with expectation + $t = $this->getFunctionMock('Zotlabs\Access', 't'); + $t->expects($this->exactly(2 * $permsCount))->willReturnCallback( + function ($string) { + return $string; + } + ); - // Create a stub for global function t() with expectation - $t = $this->getFunctionMock('Zotlabs\Access', 't'); - $t->expects($this->exactly(2*$permsCount))->willReturnCallback( - function ($string) { - return $string; - } - ); + // static method Perms() + $perms = Permissions::Perms(); - // static method Perms() - $perms = Permissions::Perms(); + $p = new Permissions(); + $this->assertEquals($perms, $p->Perms()); - $p = new Permissions(); - $this->assertEquals($perms, $p->Perms()); + $this->assertEquals($permsCount, count($perms), "There should be $permsCount permissions."); - $this->assertEquals($permsCount, count($perms), "There should be $permsCount permissions."); + $this->assertEquals('Can view my channel stream and posts', $perms['view_stream']); - $this->assertEquals('Can view my channel stream and posts', $perms['view_stream']); + // non existent perm should not be set + $this->assertFalse(isset($perms['invalid_perm'])); + } - // non existent perm should not be set - $this->assertFalse(isset($perms['invalid_perm'])); - } + /** + * filter parmeter is only used in hook \b permissions_list. So the result + * in this test should be the same as if there was no filter parameter. + * + * @todo Stub call_hooks() function and also test filter + * + * @uses ::call_hooks + */ + public function testPermsFilter() + { + // There are 18 default perms + $permsCount = 18; - /** - * filter parmeter is only used in hook \b permissions_list. So the result - * in this test should be the same as if there was no filter parameter. - * - * @todo Stub call_hooks() function and also test filter - * - * @uses ::call_hooks - */ - public function testPermsFilter() { - // There are 18 default perms - $permsCount = 18; + // Create a stub for global function t() with expectation + $t = $this->getFunctionMock('Zotlabs\Access', 't'); + $t->expects($this->exactly(2 * $permsCount))->willReturnCallback( + function ($string) { + return $string; + } + ); - // Create a stub for global function t() with expectation - $t = $this->getFunctionMock('Zotlabs\Access', 't'); - $t->expects($this->exactly(2*$permsCount))->willReturnCallback( - function ($string) { - return $string; - } - ); + $perms = Permissions::Perms('view_'); + $this->assertEquals($permsCount, count($perms)); - $perms = Permissions::Perms('view_'); - $this->assertEquals($permsCount, count($perms)); + $this->assertEquals('Can view my channel stream and posts', $perms['view_stream']); - $this->assertEquals('Can view my channel stream and posts', $perms['view_stream']); + $perms = Permissions::Perms('invalid_perm'); + $this->assertEquals($permsCount, count($perms)); + } - $perms = Permissions::Perms('invalid_perm'); - $this->assertEquals($permsCount, count($perms)); - } + /** + * Better should mock Permissions::Perms, but not possible with static methods. + * + * @uses ::call_hooks + * + * @dataProvider FilledPermsProvider + * + * @param array $permarr An indexed permissions array to pass + * @param array $expected The expected result perms array + */ + public function testFilledPerms($permarr, $expected) + { + // Create a stub for global function t() + $t = $this->getFunctionMock('Zotlabs\Access', 't'); - /** - * Better should mock Permissions::Perms, but not possible with static methods. - * - * @uses ::call_hooks - * - * @dataProvider FilledPermsProvider - * - * @param array $permarr An indexed permissions array to pass - * @param array $expected The expected result perms array - */ - public function testFilledPerms($permarr, $expected) { - // Create a stub for global function t() - $t = $this->getFunctionMock('Zotlabs\Access', 't'); + $this->assertEquals($expected, Permissions::FilledPerms($permarr)); + } + /** + * @return array An associative array with test values for FilledPerms() + * * \e array Indexed array which is passed as parameter to FilledPerms() + * * \e array Expected associative result array with filled perms + */ + public function FilledPermsProvider() + { + return [ + 'Empty param array' => [ + [], + [ + 'view_stream' => 0, + 'send_stream' => 0, + 'view_profile' => 0, + 'view_contacts' => 0, + 'view_storage' => 0, + 'write_storage' => 0, + 'view_pages' => 0, + 'view_wiki' => 0, + 'write_pages' => 0, + 'write_wiki' => 0, + 'post_wall' => 0, + 'post_comments' => 0, + 'post_mail' => 0, + 'post_like' => 0, + 'tag_deliver' => 0, + 'chat' => 0, + 'republish' => 0, + 'delegate' => 0 + ] + ], + 'provide view_stream and view_pages as param' => [ + ['view_stream', 'view_pages'], + [ + 'view_stream' => 1, + 'send_stream' => 0, + 'view_profile' => 0, + 'view_contacts' => 0, + 'view_storage' => 0, + 'write_storage' => 0, + 'view_pages' => 1, + 'view_wiki' => 0, + 'write_pages' => 0, + 'write_wiki' => 0, + 'post_wall' => 0, + 'post_comments' => 0, + 'post_mail' => 0, + 'post_like' => 0, + 'tag_deliver' => 0, + 'chat' => 0, + 'republish' => 0, + 'delegate' => 0 + ] + ], + 'provide an unknown param' => [ + ['view_stream', 'unknown_perm'], + [ + 'view_stream' => 1, + 'send_stream' => 0, + 'view_profile' => 0, + 'view_contacts' => 0, + 'view_storage' => 0, + 'write_storage' => 0, + 'view_pages' => 0, + 'view_wiki' => 0, + 'write_pages' => 0, + 'write_wiki' => 0, + 'post_wall' => 0, + 'post_comments' => 0, + 'post_mail' => 0, + 'post_like' => 0, + 'tag_deliver' => 0, + 'chat' => 0, + 'republish' => 0, + 'delegate' => 0 + ] + ] + ]; + } + /** + * @uses ::call_hooks + */ + public function testFilledPermsNull() + { + // Create a stub for global function t() with expectation + $t = $this->getFunctionMock('Zotlabs\Access', 't'); + $t->expects($this->atLeastOnce()); + // Create a stub for global function bt() with expectations + $bt = $this->getFunctionMock('Zotlabs\Access', 'btlogger'); + $bt->expects($this->once())->with($this->equalTo('FilledPerms: null')); - $this->assertEquals($expected, Permissions::FilledPerms($permarr)); - } - /** - * @return array An associative array with test values for FilledPerms() - * * \e array Indexed array which is passed as parameter to FilledPerms() - * * \e array Expected associative result array with filled perms - */ - public function FilledPermsProvider() { - return [ - 'Empty param array' => [ - [], - [ - 'view_stream' => 0, - 'send_stream' => 0, - 'view_profile' => 0, - 'view_contacts' => 0, - 'view_storage' => 0, - 'write_storage' => 0, - 'view_pages' => 0, - 'view_wiki' => 0, - 'write_pages' => 0, - 'write_wiki' => 0, - 'post_wall' => 0, - 'post_comments' => 0, - 'post_mail' => 0, - 'post_like' => 0, - 'tag_deliver' => 0, - 'chat' => 0, - 'republish' => 0, - 'delegate' => 0 - ] - ], - 'provide view_stream and view_pages as param' => [ - ['view_stream', 'view_pages'], - [ - 'view_stream' => 1, - 'send_stream' => 0, - 'view_profile' => 0, - 'view_contacts' => 0, - 'view_storage' => 0, - 'write_storage' => 0, - 'view_pages' => 1, - 'view_wiki' => 0, - 'write_pages' => 0, - 'write_wiki' => 0, - 'post_wall' => 0, - 'post_comments' => 0, - 'post_mail' => 0, - 'post_like' => 0, - 'tag_deliver' => 0, - 'chat' => 0, - 'republish' => 0, - 'delegate' => 0 - ] - ], - 'provide an unknown param' => [ - ['view_stream', 'unknown_perm'], - [ - 'view_stream' => 1, - 'send_stream' => 0, - 'view_profile' => 0, - 'view_contacts' => 0, - 'view_storage' => 0, - 'write_storage' => 0, - 'view_pages' => 0, - 'view_wiki' => 0, - 'write_pages' => 0, - 'write_wiki' => 0, - 'post_wall' => 0, - 'post_comments' => 0, - 'post_mail' => 0, - 'post_like' => 0, - 'tag_deliver' => 0, - 'chat' => 0, - 'republish' => 0, - 'delegate' => 0 - ] - ] - ]; - } - /** - * @uses ::call_hooks - */ - public function testFilledPermsNull() { - // Create a stub for global function t() with expectation - $t = $this->getFunctionMock('Zotlabs\Access', 't'); - $t->expects($this->atLeastOnce()); - // Create a stub for global function bt() with expectations - $bt = $this->getFunctionMock('Zotlabs\Access', 'btlogger'); - $bt->expects($this->once())->with($this->equalTo('FilledPerms: null')); + $result = [ + 'view_stream' => 0, + 'send_stream' => 0, + 'view_profile' => 0, + 'view_contacts' => 0, + 'view_storage' => 0, + 'write_storage' => 0, + 'view_pages' => 0, + 'view_wiki' => 0, + 'write_pages' => 0, + 'write_wiki' => 0, + 'post_wall' => 0, + 'post_comments' => 0, + 'post_mail' => 0, + 'post_like' => 0, + 'tag_deliver' => 0, + 'chat' => 0, + 'republish' => 0, + 'delegate' => 0 + ]; - $result = [ - 'view_stream' => 0, - 'send_stream' => 0, - 'view_profile' => 0, - 'view_contacts' => 0, - 'view_storage' => 0, - 'write_storage' => 0, - 'view_pages' => 0, - 'view_wiki' => 0, - 'write_pages' => 0, - 'write_wiki' => 0, - 'post_wall' => 0, - 'post_comments' => 0, - 'post_mail' => 0, - 'post_like' => 0, - 'tag_deliver' => 0, - 'chat' => 0, - 'republish' => 0, - 'delegate' => 0 - ]; + $this->assertEquals($result, Permissions::FilledPerms(null)); + } - $this->assertEquals($result, Permissions::FilledPerms(null)); - } + /** + * @dataProvider OPermsProvider + * + * @param array $permarr The params to pass to the OPerms method + * @param array $expected The expected result + */ + public function testOPerms($permarr, $expected) + { + $this->assertEquals($expected, Permissions::OPerms($permarr)); + } + /** + * @return array An associative array with test values for OPerms() + * * \e array Array with perms to test + * * \e array Expected result array + */ + public function OPermsProvider() + { + return [ + 'empty' => [ + [], + [] + ], + 'valid' => [ + ['perm1' => 1, 'perm2' => 0], + [['name' => 'perm1', 'value' => 1], ['name' => 'perm2', 'value' => 0]] + ], + 'null array' => [ + null, + [] + ] + ]; + } - /** - * @dataProvider OPermsProvider - * - * @param array $permarr The params to pass to the OPerms method - * @param array $expected The expected result - */ - public function testOPerms($permarr, $expected) { - $this->assertEquals($expected, Permissions::OPerms($permarr)); - } - /** - * @return array An associative array with test values for OPerms() - * * \e array Array with perms to test - * * \e array Expected result array - */ - public function OPermsProvider() { - return [ - 'empty' => [ - [], - [] - ], - 'valid' => [ - ['perm1' => 1, 'perm2' => 0], - [['name' => 'perm1', 'value' => 1], ['name' => 'perm2', 'value' => 0]] - ], - 'null array' => [ - null, - [] - ] - ]; - } - - /** - * @dataProvider permsCompareProvider - * - * @param array $p1 The first permission - * @param array $p2 The second permission - * @param boolean $expectedresult The expected result of the tested method - */ - public function testPermsCompare($p1, $p2, $expectedresult) { - $this->assertEquals($expectedresult, Permissions::PermsCompare($p1, $p2)); - } - /** - * @return array An associative array with test values for PermsCompare() - * * \e array 1st array with perms - * * \e array 2nd array with perms - * * \e boolean expected result for the perms comparison - */ - public function permsCompareProvider() { - return [ - 'equal' => [ - ['perm1' => 1, 'perm2' => 0], - ['perm1' => 1, 'perm2' => 0], - true - ], - 'different values' => [ - ['perm1' => 1, 'perm2' => 0], - ['perm1' => 0, 'perm2' => 1], - false - ], - 'different order' => [ - ['perm1' => 1, 'perm2' => 0], - ['perm2' => 0, 'perm1' => 1], - true - ], - 'partial first in second' => [ - ['perm1' => 1], - ['perm1' => 1, 'perm2' => 0], - true - ], - 'partial second in first' => [ - ['perm1' => 1, 'perm2' => 0], - ['perm1' => 1], - false - ] - ]; - } + /** + * @dataProvider permsCompareProvider + * + * @param array $p1 The first permission + * @param array $p2 The second permission + * @param bool $expectedresult The expected result of the tested method + */ + public function testPermsCompare($p1, $p2, $expectedresult) + { + $this->assertEquals($expectedresult, Permissions::PermsCompare($p1, $p2)); + } + /** + * @return array An associative array with test values for PermsCompare() + * * \e array 1st array with perms + * * \e array 2nd array with perms + * * \e boolean expected result for the perms comparison + */ + public function permsCompareProvider() + { + return [ + 'equal' => [ + ['perm1' => 1, 'perm2' => 0], + ['perm1' => 1, 'perm2' => 0], + true + ], + 'different values' => [ + ['perm1' => 1, 'perm2' => 0], + ['perm1' => 0, 'perm2' => 1], + false + ], + 'different order' => [ + ['perm1' => 1, 'perm2' => 0], + ['perm2' => 0, 'perm1' => 1], + true + ], + 'partial first in second' => [ + ['perm1' => 1], + ['perm1' => 1, 'perm2' => 0], + true + ], + 'partial second in first' => [ + ['perm1' => 1, 'perm2' => 0], + ['perm1' => 1], + false + ] + ]; + } } diff --git a/tests/unit/AntiXSSTest.php b/tests/unit/AntiXSSTest.php index ebe0678a3..503d9963a 100644 --- a/tests/unit/AntiXSSTest.php +++ b/tests/unit/AntiXSSTest.php @@ -1,4 +1,5 @@ '; + /** + * test, that tags are escaped + */ + public function testEscapeTags() + { + $invalidstring = ''; - $validstring=notags($invalidstring); - $escapedString=escape_tags($invalidstring); + $validstring = notags($invalidstring); + $escapedString = escape_tags($invalidstring); - $this->assertEquals('[submit type="button" onclick="alert(\'failed!\');" /]', $validstring); - $this->assertEquals("<submit type="button" onclick="alert('failed!');" />", $escapedString); - } + $this->assertEquals('[submit type="button" onclick="alert(\'failed!\');" /]', $validstring); + $this->assertEquals("<submit type="button" onclick="alert('failed!');" />", $escapedString); + } - /** - *xmlify and unxmlify - */ - public function testXmlify() { - $text="I want to break\n this!11!"; - $xml=xmlify($text); - $retext=unxmlify($text); + /** + *xmlify and unxmlify + */ + public function testXmlify() + { + $text = "I want to break\n this!11!"; + $xml = xmlify($text); + $retext = unxmlify($text); - $this->assertEquals($text, $retext); - } + $this->assertEquals($text, $retext); + } - /** - * xmlify and put in a document - */ - public function testXmlifyDocument() { - $tag="I want to break"; - $xml=xmlify($tag); - $text=''.$xml.''; + /** + * xmlify and put in a document + */ + public function testXmlifyDocument() + { + $tag = "I want to break"; + $xml = xmlify($tag); + $text = '' . $xml . ''; - $xml_parser=xml_parser_create(); - //should be possible to parse it - $values=[]; $index=[]; - $this->assertEquals(1, xml_parse_into_struct($xml_parser, $text, $values, $index)); + $xml_parser = xml_parser_create(); + //should be possible to parse it + $values = []; + $index = []; + $this->assertEquals(1, xml_parse_into_struct($xml_parser, $text, $values, $index)); - $this->assertEquals(array('TEXT'=>array(0)), - $index); - $this->assertEquals(array(array('tag'=>'TEXT', 'type'=>'complete', 'level'=>1, 'value'=>$tag)), - $values); + $this->assertEquals( + array('TEXT' => array(0)), + $index + ); + $this->assertEquals( + array(array('tag' => 'TEXT', 'type' => 'complete', 'level' => 1, 'value' => $tag)), + $values + ); - xml_parser_free($xml_parser); - } + xml_parser_free($xml_parser); + } - /** - * test hex2bin and reverse - */ - public function testHex2Bin() { - $this->assertEquals(-3, hex2bin(bin2hex(-3))); - $this->assertEquals(0, hex2bin(bin2hex(0))); - $this->assertEquals(12, hex2bin(bin2hex(12))); - $this->assertEquals(PHP_INT_MAX, hex2bin(bin2hex(PHP_INT_MAX))); - } + /** + * test hex2bin and reverse + */ + public function testHex2Bin() + { + $this->assertEquals(-3, hex2bin(bin2hex(-3))); + $this->assertEquals(0, hex2bin(bin2hex(0))); + $this->assertEquals(12, hex2bin(bin2hex(12))); + $this->assertEquals(PHP_INT_MAX, hex2bin(bin2hex(PHP_INT_MAX))); + } - //function qp, quick and dirty?? - //get_mentions - //get_contact_block, bis Zeile 538 + //function qp, quick and dirty?? + //get_mentions + //get_contact_block, bis Zeile 538 } -?> diff --git a/tests/unit/AutonameTest.php b/tests/unit/AutonameTest.php index 566fe6149..6f0a697c7 100644 --- a/tests/unit/AutonameTest.php +++ b/tests/unit/AutonameTest.php @@ -1,4 +1,5 @@ assertNotEquals($autoname1, $autoname2); - } + $this->assertNotEquals($autoname1, $autoname2); + } - /** - *autonames should be random, odd length - */ - public function testAutonameOdd() { - $autoname1=autoname(9); - $autoname2=autoname(9); + /** + *autonames should be random, odd length + */ + public function testAutonameOdd() + { + $autoname1 = autoname(9); + $autoname2 = autoname(9); - $this->assertNotEquals($autoname1, $autoname2); - } + $this->assertNotEquals($autoname1, $autoname2); + } - /** - * try to fail autonames - */ - public function testAutonameNoLength() { - $autoname1=autoname(0); - $this->assertEquals(0, strlen($autoname1)); - } + /** + * try to fail autonames + */ + public function testAutonameNoLength() + { + $autoname1 = autoname(0); + $this->assertEquals(0, strlen($autoname1)); + } - /** - * try to fail it with invalid input - * - * TODO: What's corect behaviour here? An exception? - */ - public function testAutonameNegativeLength() { - $autoname1=autoname(-23); - $this->assertEquals(0, strlen($autoname1)); - } + /** + * try to fail it with invalid input + * + * TODO: What's corect behaviour here? An exception? + */ + public function testAutonameNegativeLength() + { + $autoname1 = autoname(-23); + $this->assertEquals(0, strlen($autoname1)); + } - // public function testAutonameMaxLength() { - // $autoname2=autoname(PHP_INT_MAX); - // $this->assertEquals(PHP_INT_MAX, strlen($autoname2)); - // } + // public function testAutonameMaxLength() { + // $autoname2=autoname(PHP_INT_MAX); + // $this->assertEquals(PHP_INT_MAX, strlen($autoname2)); + // } - /** - * test with a length, that may be too short - * length is maximum - autoname can return something shorter. - */ - public function testAutonameLength1() { - $autoname1=autoname(1); - $test = ((strlen($autoname1) < 2) ? 1 : 0); - $this->assertEquals(1, $test); + /** + * test with a length, that may be too short + * length is maximum - autoname can return something shorter. + */ + public function testAutonameLength1() + { + $autoname1 = autoname(1); + $test = ((strlen($autoname1) < 2) ? 1 : 0); + $this->assertEquals(1, $test); - $autoname2=autoname(1); - $test = ((strlen($autoname2) < 2) ? 1 : 0); - $this->assertEquals(1, $test); + $autoname2 = autoname(1); + $test = ((strlen($autoname2) < 2) ? 1 : 0); + $this->assertEquals(1, $test); - // The following test is problematic, with only 26 possibilities - // generating the same thing twice happens often aka - // birthday paradox -// $this->assertFalse($autoname1==$autoname2); - } + // The following test is problematic, with only 26 possibilities + // generating the same thing twice happens often aka + // birthday paradox +// $this->assertFalse($autoname1==$autoname2); + } } diff --git a/tests/unit/ContainsAttributeTest.php b/tests/unit/ContainsAttributeTest.php index 0930d9837..96bb9dc88 100644 --- a/tests/unit/ContainsAttributeTest.php +++ b/tests/unit/ContainsAttributeTest.php @@ -1,4 +1,5 @@ assertTrue(attribute_contains($testAttr, "class3")); - $this->assertFalse(attribute_contains($testAttr, "class2")); - } +class ContainsAttributeTest extends TestCase +{ + /** + * test attribute contains + */ + public function testAttributeContains1() + { + $testAttr = "class1 notclass2 class3"; + $this->assertTrue(attribute_contains($testAttr, "class3")); + $this->assertFalse(attribute_contains($testAttr, "class2")); + } - /** - * test attribute contains - */ - public function testAttributeContains2() { - $testAttr="class1 not-class2 class3"; - $this->assertTrue(attribute_contains($testAttr, "class3")); - $this->assertFalse(attribute_contains($testAttr, "class2")); - } + /** + * test attribute contains + */ + public function testAttributeContains2() + { + $testAttr = "class1 not-class2 class3"; + $this->assertTrue(attribute_contains($testAttr, "class3")); + $this->assertFalse(attribute_contains($testAttr, "class2")); + } - /** - * test with empty input - */ - public function testAttributeContainsEmpty() { - $testAttr=""; - $this->assertFalse(attribute_contains($testAttr, "class2")); - } + /** + * test with empty input + */ + public function testAttributeContainsEmpty() + { + $testAttr = ""; + $this->assertFalse(attribute_contains($testAttr, "class2")); + } - /** - * test input with special chars - */ - public function testAttributeContainsSpecialChars() { - $testAttr="--... %\$ä() /(=?}"; - $this->assertFalse(attribute_contains($testAttr, "class2")); - } -} \ No newline at end of file + /** + * test input with special chars + */ + public function testAttributeContainsSpecialChars() + { + $testAttr = "--... %\$ä() /(=?}"; + $this->assertFalse(attribute_contains($testAttr, "class2")); + } +} diff --git a/tests/unit/DatabaseTestCase.php b/tests/unit/DatabaseTestCase.php index 18c1cfb17..60f3cd405 100644 --- a/tests/unit/DatabaseTestCase.php +++ b/tests/unit/DatabaseTestCase.php @@ -1,4 +1,5 @@ conn === null) { - if (self::$pdo === null) { - $dsn = \getenv('hz_db_scheme') . ':host=' . \getenv('hz_db_server') - . ';port=' . \getenv('hz_db_port') . ';dbname=' . \getenv('hz_db_database'); + final public function getConnection() + { + if ($this->conn === null) { + if (self::$pdo === null) { + $dsn = getenv('hz_db_scheme') . ':host=' . getenv('hz_db_server') + . ';port=' . getenv('hz_db_port') . ';dbname=' . getenv('hz_db_database'); - self::$pdo = new \PDO($dsn, \getenv('hz_db_user'), \getenv('hz_db_pass')); - } - $this->conn = $this->createDefaultDBConnection(self::$pdo, \getenv('hz_db_database')); - } + self::$pdo = new PDO($dsn, getenv('hz_db_user'), getenv('hz_db_pass')); + } + $this->conn = $this->createDefaultDBConnection(self::$pdo, getenv('hz_db_database')); + } - return $this->conn; - } + return $this->conn; + } } diff --git a/tests/unit/Lib/MarkdownTest.php b/tests/unit/Lib/MarkdownTest.php index da1d82877..c350397a2 100644 --- a/tests/unit/Lib/MarkdownTest.php +++ b/tests/unit/Lib/MarkdownTest.php @@ -1,4 +1,5 @@ assertEquals($markdown, Markdown::from_html($html)); - } + /** + * @covers ::html2markdown + * @dataProvider html2markdownProvider + */ + public function testHtml2markdown($html, $markdown) + { + $this->assertEquals($markdown, Markdown::from_html($html)); + } - public function html2markdownProvider() { - return [ - 'empty text' => [ - '', - '' - ], - 'space and nbsp only' => [ - '  ', - '' - ], + public function html2markdownProvider() + { + return [ + 'empty text' => [ + '', + '' + ], + 'space and nbsp only' => [ + '  ', + '' + ], - 'strong, b, em, i, bib' => [ - 'strong bold em italic boitalicld', - '**strong** **bold** *em* *italic* **bo*italic*ld**' - ], + 'strong, b, em, i, bib' => [ + 'strong bold em italic boitalicld', + '**strong** **bold** *em* *italic* **bo*italic*ld**' + ], - 'empty tags' => [ - 'text1 text2 ', - 'text1 text2' - ], - 'HTML entities, lt does not work' => [ - '& gt > lt <', - '& gt > lt' - ], - 'escaped HTML entities' => [ - '& lt < gt >', - '& lt < gt >' - ], - 'linebreak' => [ - "line1
                    line2\nline3", - "line1 \nline2 line3" - ], - 'headlines' => [ - '

                    header1

                    Header 3

                    ', - "header1\n=======\n\n### Header 3" - ], - 'unordered list' => [ - '
                    • Item 1
                    • Item 2
                    • Item 3
                    ', - "- Item 1\n- Item 2\n- Item **3**" - ], - 'ordered list' => [ - '
                    1. Item 1
                    2. Item 2
                    3. Item 3
                    ', - "1. Item 1\n2. Item 2\n3. Item **3**" - ], - 'nested lists' => [ - '
                    • Item 1
                      1. Item 1a
                      2. Item 1b
                    • Item 2
                    ', - "- Item 1\n 1. Item 1a\n 2. Item **1b**\n- Item 2" - ], - 'img' => [ - 'alt text', - '![alt text](/path/to/img.png "title text")' - ], - 'link' => [ - 'link', - '[link](http://hubzilla.org "Hubzilla")' - ], - 'img link' => [ - 'alt img text', - '[![alt img text](/img/hubzilla.png "img title")](http://hubzilla.org "Hubzilla")' - ], - 'script' => [ - "", - "" - ], - 'blockquote, issue #793' => [ - '
                    something
                    blah', - "> something\n\nblah" - ], - 'code' => [ - '<p>HTML text</p>', - '`

                    HTML text

                    `' - ], - 'pre' => [ - '
                      one line with spaces  
                    ', - "```\n one line with spaces \n```" - ], - 'div p' => [ - '
                    div

                    p

                    ', - "
                    div
                    p\n\n
                    " - ] - ]; - } + 'empty tags' => [ + 'text1 text2 ', + 'text1 text2' + ], + 'HTML entities, lt does not work' => [ + '& gt > lt <', + '& gt > lt' + ], + 'escaped HTML entities' => [ + '& lt < gt >', + '& lt < gt >' + ], + 'linebreak' => [ + "line1
                    line2\nline3", + "line1 \nline2 line3" + ], + 'headlines' => [ + '

                    header1

                    Header 3

                    ', + "header1\n=======\n\n### Header 3" + ], + 'unordered list' => [ + '
                    • Item 1
                    • Item 2
                    • Item 3
                    ', + "- Item 1\n- Item 2\n- Item **3**" + ], + 'ordered list' => [ + '
                    1. Item 1
                    2. Item 2
                    3. Item 3
                    ', + "1. Item 1\n2. Item 2\n3. Item **3**" + ], + 'nested lists' => [ + '
                    • Item 1
                      1. Item 1a
                      2. Item 1b
                    • Item 2
                    ', + "- Item 1\n 1. Item 1a\n 2. Item **1b**\n- Item 2" + ], + 'img' => [ + 'alt text', + '![alt text](/path/to/img.png "title text")' + ], + 'link' => [ + 'link', + '[link](http://hubzilla.org "Hubzilla")' + ], + 'img link' => [ + 'alt img text', + '[![alt img text](/img/hubzilla.png "img title")](http://hubzilla.org "Hubzilla")' + ], + 'script' => [ + "", + "" + ], + 'blockquote, issue #793' => [ + '
                    something
                    blah', + "> something\n\nblah" + ], + 'code' => [ + '<p>HTML text</p>', + '`

                    HTML text

                    `' + ], + 'pre' => [ + '
                      one line with spaces  
                    ', + "```\n one line with spaces \n```" + ], + 'div p' => [ + '
                    div

                    p

                    ', + "
                    div
                    p\n\n
                    " + ] + ]; + } - /*public function testHtml2markdownException() { - //$this->expectException(\InvalidArgumentException::class); - // need to stub logger() for this to work - $this->assertEquals('', Markdown::from_html('<expectException(\InvalidArgumentException::class); + // need to stub logger() for this to work + $this->assertEquals('', Markdown::from_html('<getFunctionMock(__NAMESPACE__, "bbcode"); - $bbc->expects($this->once())->willReturn('testbold
                    i
                    • li1
                    • li2

                    '); + // php-mock can not mock global functions which is called by a global function. + // If the calling function is in a namespace it does work. + $bbc = $this->getFunctionMock(__NAMESPACE__, "bbcode"); + $bbc->expects($this->once())->willReturn('testbold
                    i
                    • li1
                    • li2

                    '); - $this->assertEquals($bb1, Markdown::from_bbcode($html1)); - } + $this->assertEquals($bb1, Markdown::from_bbcode($html1)); + } */ } diff --git a/tests/unit/Lib/PermissionDescriptionTest.php b/tests/unit/Lib/PermissionDescriptionTest.php index 96c381d0c..ce7853e41 100644 --- a/tests/unit/Lib/PermissionDescriptionTest.php +++ b/tests/unit/Lib/PermissionDescriptionTest.php @@ -1,4 +1,5 @@ assertEquals($permDesc, $permDesc2); + $this->assertNotEquals($permDesc, $permDesc3); + } - $this->assertEquals($permDesc, $permDesc2); - $this->assertNotEquals($permDesc, $permDesc3); - } + public function testFromStandalonePermission() + { + // Create a stub for global function t() + $t = $this->getFunctionMock('Zotlabs\Lib', 't'); + $t->expects($this->atLeastOnce())->willReturnCallback( + function ($string) { + return $string; + } + ); + // Create a mock for global function logger() + $this->getFunctionMock('Zotlabs\Lib', 'logger'); - public function testFromStandalonePermission() { - // Create a stub for global function t() - $t = $this->getFunctionMock('Zotlabs\Lib', 't'); - $t->expects($this->atLeastOnce())->willReturnCallback( - function ($string) { - return $string; - } - ); - // Create a mock for global function logger() - $this->getFunctionMock('Zotlabs\Lib', 'logger'); + $permDescUnknown = PermissionDescription::fromStandalonePermission(-1); + $permDescSelf = PermissionDescription::fromStandalonePermission(0); - $permDescUnknown = PermissionDescription::fromStandalonePermission(-1); - $permDescSelf = PermissionDescription::fromStandalonePermission(0); + $this->assertNull($permDescUnknown); + $this->assertNotNull($permDescSelf); + } - $this->assertNull($permDescUnknown); - $this->assertNotNull($permDescSelf); - } + public function testFromGlobalPermission() + { + //$permDesc = PermissionDescription::fromGlobalPermission('view_profile'); - public function testFromGlobalPermission() { - //$permDesc = PermissionDescription::fromGlobalPermission('view_profile'); + $this->markTestIncomplete( + 'The method fromGlobalPermission() is not yet testable ...' + ); + } - $this->markTestIncomplete( - 'The method fromGlobalPermission() is not yet testable ...' - ); - } + public function testGetPermissionDescription() + { + // Create a stub for global function t() + $t = $this->getFunctionMock('Zotlabs\Lib', 't'); + $t->expects($this->atLeastOnce())->willReturnCallback( + function ($string) { + return $string; + } + ); + // Create a mock for global function logger() + $this->getFunctionMock('Zotlabs\Lib', 'logger'); - public function testGetPermissionDescription() { - // Create a stub for global function t() - $t = $this->getFunctionMock('Zotlabs\Lib', 't'); - $t->expects($this->atLeastOnce())->willReturnCallback( - function ($string) { - return $string; - } - ); - // Create a mock for global function logger() - $this->getFunctionMock('Zotlabs\Lib', 'logger'); + // Create a stub for the PermissionDescription class + $stub = $this->createMock(PermissionDescription::class); + $stub->method('get_permission_description') + ->will($this->returnArgument(0)); - // Create a stub for the PermissionDescription class - $stub = $this->createMock(PermissionDescription::class); - $stub->method('get_permission_description') - ->will($this->returnArgument(0)); + $permDescSelf = PermissionDescription::fromStandalonePermission(0); + $this->assertInstanceOf(PermissionDescription::class, $permDescSelf); + $this->assertEquals($permDescSelf->get_permission_description(), 'Only me'); - $permDescSelf = PermissionDescription::fromStandalonePermission(0); - $this->assertInstanceOf(PermissionDescription::class, $permDescSelf); - $this->assertEquals($permDescSelf->get_permission_description(), 'Only me'); - - $permDescPublic = PermissionDescription::fromStandalonePermission(PERMS_PUBLIC); - $this->assertEquals($permDescPublic->get_permission_description(), 'Public'); - } + $permDescPublic = PermissionDescription::fromStandalonePermission(PERMS_PUBLIC); + $this->assertEquals($permDescPublic->get_permission_description(), 'Public'); + } } diff --git a/tests/unit/UnitTestCase.php b/tests/unit/UnitTestCase.php index 7d706d5be..16d49c618 100644 --- a/tests/unit/UnitTestCase.php +++ b/tests/unit/UnitTestCase.php @@ -1,4 +1,5 @@ assertEquals("audio/ogg", z_mime_content_type($multidots)); - $this->assertNotEquals("application/octet-stream", z_mime_content_type($multidots)); - } +class UploadTest extends TestCase +{ + public function testFileNameMutipleDots() + { + $multidots = "foo.bar.baz.0.1.3.ogg"; + $this->assertEquals("audio/ogg", z_mime_content_type($multidots)); + $this->assertNotEquals("application/octet-stream", z_mime_content_type($multidots)); + } - public function testFileNameOneDot() { - $multidots = "foo.ogg"; - $this->assertEquals("audio/ogg", z_mime_content_type($multidots)); - $this->assertNotEquals("application/octet-stream", z_mime_content_type($multidots)); - } -} \ No newline at end of file + public function testFileNameOneDot() + { + $multidots = "foo.ogg"; + $this->assertEquals("audio/ogg", z_mime_content_type($multidots)); + $this->assertNotEquals("application/octet-stream", z_mime_content_type($multidots)); + } +} diff --git a/tests/unit/Web/HttpSigTest.php b/tests/unit/Web/HttpSigTest.php index 18f2ce92b..1de250843 100644 --- a/tests/unit/Web/HttpSigTest.php +++ b/tests/unit/Web/HttpSigTest.php @@ -33,93 +33,104 @@ use Zotlabs\Web\HTTPSig; * * @covers Zotlabs\Web\HTTPSig */ -class PermissionDescriptionTest extends UnitTestCase { +class PermissionDescriptionTest extends UnitTestCase +{ - use PHPMock; + use PHPMock; - /** - * @dataProvider generate_digestProvider - */ - function testGenerate_digest($text, $digest) { - $this->assertSame( - $digest, - HTTPSig::generate_digest($text, false) - ); - } - public function generate_digestProvider() { - return [ - 'empty body text' => [ - '', - '47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=' - ], - 'sample body text' => [ - 'body text', - '2fu8kUkvuzuo5XyhWwORNOcJgDColXgxWkw1T5EXzPI=' - ], - 'NULL body text' => [ - null, - '47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=' - ], - ]; - } + /** + * @dataProvider generate_digestProvider + */ + public function testGenerate_digest($text, $digest) + { + $this->assertSame( + $digest, + HTTPSig::generate_digest($text, false) + ); + } - function testGeneratedDigestsOfDifferentTextShouldNotBeEqual() { - $this->assertNotSame( - HTTPSig::generate_digest('text1', false), - HTTPSig::generate_digest('text2', false) - ); - } + public function generate_digestProvider() + { + return [ + 'empty body text' => [ + '', + '47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=' + ], + 'sample body text' => [ + 'body text', + '2fu8kUkvuzuo5XyhWwORNOcJgDColXgxWkw1T5EXzPI=' + ], + 'NULL body text' => [ + null, + '47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=' + ], + ]; + } - /** - * Process separation needed for header() check. - * @runInSeparateProcess - */ - function testGenerate_digestSendsHttpHeader() { - $ret = HTTPSig::generate_digest('body text', true); + public function testGeneratedDigestsOfDifferentTextShouldNotBeEqual() + { + $this->assertNotSame( + HTTPSig::generate_digest('text1', false), + HTTPSig::generate_digest('text2', false) + ); + } - $this->assertSame('2fu8kUkvuzuo5XyhWwORNOcJgDColXgxWkw1T5EXzPI=', $ret); - $this->assertContains( - 'Digest: SHA-256=2fu8kUkvuzuo5XyhWwORNOcJgDColXgxWkw1T5EXzPI=', - xdebug_get_headers(), - 'HTTP header Digest does not match' - ); - } + /** + * Process separation needed for header() check. + * @runInSeparateProcess + */ + public function testGenerate_digestSendsHttpHeader() + { + $ret = HTTPSig::generate_digest('body text', true); - /** - * @uses ::crypto_unencapsulate - */ - function testDecrypt_sigheader() { - $header = 'Header: iv="value_iv" key="value_key" alg="value_alg" data="value_data"'; - $result = [ - 'iv' => 'value_iv', - 'key' => 'value_key', - 'alg' => 'value_alg', - 'data' => 'value_data' - ]; + $this->assertSame('2fu8kUkvuzuo5XyhWwORNOcJgDColXgxWkw1T5EXzPI=', $ret); + $this->assertContains( + 'Digest: SHA-256=2fu8kUkvuzuo5XyhWwORNOcJgDColXgxWkw1T5EXzPI=', + xdebug_get_headers(), + 'HTTP header Digest does not match' + ); + } - $this->assertSame($result, HTTPSig::decrypt_sigheader($header, 'site private key')); - } - /** - * @uses ::crypto_unencapsulate - */ - function testDecrypt_sigheaderUseSitePrivateKey() { - // Create a stub for global function get_config() with expectation - $t = $this->getFunctionMock('Zotlabs\Web', 'get_config'); - $t->expects($this->once())->willReturn('system.prvkey'); + /** + * @uses ::crypto_unencapsulate + */ + public function testDecrypt_sigheader() + { + $header = 'Header: iv="value_iv" key="value_key" alg="value_alg" data="value_data"'; + $result = [ + 'iv' => 'value_iv', + 'key' => 'value_key', + 'alg' => 'value_alg', + 'data' => 'value_data' + ]; - $header = 'Header: iv="value_iv" key="value_key" alg="value_alg" data="value_data"'; - $result = [ - 'iv' => 'value_iv', - 'key' => 'value_key', - 'alg' => 'value_alg', - 'data' => 'value_data' - ]; + $this->assertSame($result, HTTPSig::decrypt_sigheader($header, 'site private key')); + } - $this->assertSame($result, HTTPSig::decrypt_sigheader($header)); - } - function testDecrypt_sigheaderIncompleteHeaderShouldReturnEmptyString() { - $header = 'Header: iv="value_iv" key="value_key"'; + /** + * @uses ::crypto_unencapsulate + */ + public function testDecrypt_sigheaderUseSitePrivateKey() + { + // Create a stub for global function get_config() with expectation + $t = $this->getFunctionMock('Zotlabs\Web', 'get_config'); + $t->expects($this->once())->willReturn('system.prvkey'); - $this->assertEmpty(HTTPSig::decrypt_sigheader($header, 'site private key')); - } + $header = 'Header: iv="value_iv" key="value_key" alg="value_alg" data="value_data"'; + $result = [ + 'iv' => 'value_iv', + 'key' => 'value_key', + 'alg' => 'value_alg', + 'data' => 'value_data' + ]; + + $this->assertSame($result, HTTPSig::decrypt_sigheader($header)); + } + + public function testDecrypt_sigheaderIncompleteHeaderShouldReturnEmptyString() + { + $header = 'Header: iv="value_iv" key="value_key"'; + + $this->assertEmpty(HTTPSig::decrypt_sigheader($header, 'site private key')); + } } diff --git a/tests/unit/expand_acl_test.php b/tests/unit/expand_acl_test.php index 700fea46e..790ce58e5 100644 --- a/tests/unit/expand_acl_test.php +++ b/tests/unit/expand_acl_test.php @@ -1,4 +1,5 @@ <2><3>'; - $this->assertEquals(array(1, 2, 3), expand_acl($text)); - } - - /** - * test with a big number - */ - public function testExpandAclBigNumber() { - $text='<1><'.PHP_INT_MAX.'><15>'; - $this->assertEquals(array(1, PHP_INT_MAX, 15), expand_acl($text)); - } - - /** - * test with a string in it. - * - * TODO: is this valid input? Otherwise: should there be an exception? - */ - public function testExpandAclString() { - $text="<1><279012>"; - $this->assertEquals(array(1, 279012), expand_acl($text)); - } - - /** - * test with a ' ' in it. - * - * TODO: is this valid input? Otherwise: should there be an exception? - */ - public function testExpandAclSpace() { - $text="<1><279 012><32>"; - $this->assertEquals(array(1, "279", "32"), expand_acl($text)); - } - - /** - * test empty input - */ - public function testExpandAclEmpty() { - $text=""; - $this->assertEquals([], expand_acl($text)); - } - - /** - * test invalid input, no < at all - * - * TODO: should there be an exception? - */ - public function testExpandAclNoBrackets() { - $text="According to documentation, that's invalid. "; //should be invalid - $this->assertEquals([], expand_acl($text)); - } - - /** - * test invalid input, just open < - * - * TODO: should there be an exception? - */ - public function testExpandAclJustOneBracket1() { - $text="assertEquals([], expand_acl($text)); - } - - /** - * test invalid input, just close > - * - * TODO: should there be an exception? - */ - public function testExpandAclJustOneBracket2() { - $text="Another invalid> string"; //should be invalid - $this->assertEquals([], expand_acl($text)); - } - - /** - * test invalid input, just close > - * - * TODO: should there be an exception? - */ - public function testExpandAclCloseOnly() { - $text="Another> invalid> string>"; //should be invalid - $this->assertEquals([], expand_acl($text)); - } - - /** - * test invalid input, just open < - * - * TODO: should there be an exception? - */ - public function testExpandAclOpenOnly() { - $text="assertEquals([], expand_acl($text)); - } - - /** - * test invalid input, open and close do not match - * - * TODO: should there be an exception? - */ - public function testExpandAclNoMatching1() { - $text=" invalid "; //should be invalid - $this->assertEquals([], expand_acl($text)); - } - - /** - * test invalid input, open and close do not match - * - * TODO: should there be an exception? - */ - public function testExpandAclNoMatching2() { - $text="<1>2><3>"; +/** + * TestCase for the expand_acl function + * + * @author Alexander Kampmann + * @package test.util + */ +class ExpandAclTest extends PHPUnit_Framework_TestCase +{ + + /** + * test expand_acl, perfect input + */ + public function testExpandAclNormal() + { + $text = '<1><2><3>'; + $this->assertEquals(array(1, 2, 3), expand_acl($text)); + } + + /** + * test with a big number + */ + public function testExpandAclBigNumber() + { + $text = '<1><' . PHP_INT_MAX . '><15>'; + $this->assertEquals(array(1, PHP_INT_MAX, 15), expand_acl($text)); + } + + /** + * test with a string in it. + * + * TODO: is this valid input? Otherwise: should there be an exception? + */ + public function testExpandAclString() + { + $text = "<1><279012>"; + $this->assertEquals(array(1, 279012), expand_acl($text)); + } + + /** + * test with a ' ' in it. + * + * TODO: is this valid input? Otherwise: should there be an exception? + */ + public function testExpandAclSpace() + { + $text = "<1><279 012><32>"; + $this->assertEquals(array(1, "279", "32"), expand_acl($text)); + } + + /** + * test empty input + */ + public function testExpandAclEmpty() + { + $text = ""; + $this->assertEquals([], expand_acl($text)); + } + + /** + * test invalid input, no < at all + * + * TODO: should there be an exception? + */ + public function testExpandAclNoBrackets() + { + $text = "According to documentation, that's invalid. "; //should be invalid + $this->assertEquals([], expand_acl($text)); + } + + /** + * test invalid input, just open < + * + * TODO: should there be an exception? + */ + public function testExpandAclJustOneBracket1() + { + $text = "assertEquals([], expand_acl($text)); + } + + /** + * test invalid input, just close > + * + * TODO: should there be an exception? + */ + public function testExpandAclJustOneBracket2() + { + $text = "Another invalid> string"; //should be invalid + $this->assertEquals([], expand_acl($text)); + } + + /** + * test invalid input, just close > + * + * TODO: should there be an exception? + */ + public function testExpandAclCloseOnly() + { + $text = "Another> invalid> string>"; //should be invalid + $this->assertEquals([], expand_acl($text)); + } + + /** + * test invalid input, just open < + * + * TODO: should there be an exception? + */ + public function testExpandAclOpenOnly() + { + $text = "assertEquals([], expand_acl($text)); + } + + /** + * test invalid input, open and close do not match + * + * TODO: should there be an exception? + */ + public function testExpandAclNoMatching1() + { + $text = " invalid "; //should be invalid + $this->assertEquals([], expand_acl($text)); + } + + /** + * test invalid input, open and close do not match + * + * TODO: should there be an exception? + */ + public function testExpandAclNoMatching2() + { + $text = "<1>2><3>"; // The angles are delimiters which aren't important // the important thing is the numeric content, this returns array(1,2,3) currently // we may wish to eliminate 2 from the results, though it isn't harmful // It would be a better test to figure out if there is any ACL input which can // produce this $text and fix that instead. -// $this->assertEquals([], expand_acl($text)); - } - - /** - * test invalid input, empty <> - * - * TODO: should there be an exception? Or array(1, 3) - * (This should be array(1,3) - mike) - */ - public function testExpandAclEmptyMatch() { - $text="<1><><3>"; - $this->assertEquals(array(1,3), expand_acl($text)); - } -} \ No newline at end of file +// $this->assertEquals([], expand_acl($text)); + } + + /** + * test invalid input, empty <> + * + * TODO: should there be an exception? Or array(1, 3) + * (This should be array(1,3) - mike) + */ + public function testExpandAclEmptyMatch() + { + $text = "<1><><3>"; + $this->assertEquals(array(1,3), expand_acl($text)); + } +} diff --git a/tests/unit/get_tags_test.php b/tests/unit/get_tags_test.php index bdffd8311..2c20eeb1a 100644 --- a/tests/unit/get_tags_test.php +++ b/tests/unit/get_tags_test.php @@ -1,7 +1,8 @@ 15, - 'attag'=>'', 'network'=>'dfrn', - 'name'=>'Mike Lastname', 'alias'=>'Mike', - 'nick'=>'Mike', 'url'=>"http://justatest.de")); - - $args=func_get_args(); - - //last parameter is always (in this test) uid, so, it should be 11 - if($args[count($args)-1]!=11) { - return; - } - - - if(3==count($args)) { - //first call in handle_body, id only - if($result[0]['id']==$args[1]) { - return $result; - } - //second call in handle_body, name - if($result[0]['name']===$args[1]) { - return $result; - } - } - //third call in handle_body, nick or attag - if($result[0]['nick']===$args[2] || $result[0]['attag']===$args[1]) { - return $result; - } +class MockApp +{ + public function get_baseurl() + { + return "baseurl"; + } } /** - * replacement for dbesc. + * the test should not rely on a database, + * so this is a replacement for the database access method q. + * + * It simulates the user with uid 11 has one contact, named Mike Lastname. + * + * @param string $sql + */ +function q($sql) +{ + $result = array(array('id' => 15, + 'attag' => '', 'network' => 'dfrn', + 'name' => 'Mike Lastname', 'alias' => 'Mike', + 'nick' => 'Mike', 'url' => "http://justatest.de")); + + $args = func_get_args(); + + //last parameter is always (in this test) uid, so, it should be 11 + if ($args[count($args) - 1] != 11) { + return; + } + + + if (3 == count($args)) { + //first call in handle_body, id only + if ($result[0]['id'] == $args[1]) { + return $result; + } + //second call in handle_body, name + if ($result[0]['name'] === $args[1]) { + return $result; + } + } + //third call in handle_body, nick or attag + if ($result[0]['nick'] === $args[2] || $result[0]['attag'] === $args[1]) { + return $result; + } +} + +/** + * replacement for dbesc. * I don't want to test dbesc here, so - * I just return the input. It won't be a problem, because - * the test does not use a real database. - * + * I just return the input. It won't be a problem, because + * the test does not use a real database. + * * DON'T USE HAT FUNCTION OUTSIDE A TEST! - * + * * @param string $str * @return input */ -function dbesc($str) { - return $str; +function dbesc($str) +{ + return $str; } /** - * TestCase for tag handling. - * + * TestCase for tag handling. + * * @author alexander * @package test.util */ -class GetTagsTest extends PHPUnit_Framework_TestCase { - /** the mock to use as app */ - private $a; +class GetTagsTest extends PHPUnit_Framework_TestCase +{ + /** the mock to use as app */ + private $a; - /** - * initialize the test. That's a phpUnit function, - * don't change its name. - */ - public function setUp() { - $this->a=new MockApp(); - } + /** + * initialize the test. That's a phpUnit function, + * don't change its name. + */ + public function setUp() + { + $this->a = new MockApp(); + } - /** - * test with one Person tag - */ - public function testGetTagsShortPerson() { - $text="hi @Mike"; - - $tags=get_tags($text); + /** + * test with one Person tag + */ + public function testGetTagsShortPerson() + { + $text = "hi @Mike"; - $str_tags=''; - foreach($tags as $tag) { - handle_tag($text, $str_tags, 11, $tag); - } + $tags = get_tags($text); - //correct tags found? - $this->assertEquals(1, count($tags)); - $this->assertTrue(in_array("@Mike", $tags)); - - //correct output from handle_tag? - $this->assertEquals("@[url=http://justatest.de]Mike Lastname[/url]", $str_tags); - $this->assertEquals("hi @[url=http://justatest.de]Mike Lastname[/url]", $text); - } - - /** - * test with one Person tag. - * There's a minor spelling mistake... - */ - public function testGetTagsShortPersonSpelling() { - $text="hi @Mike.because"; - - $tags=get_tags($text); - - //correct tags found? - $this->assertEquals(1, count($tags)); - $this->assertTrue(in_array("@Mike.because", $tags)); - - $str_tags=''; - handle_tag($text, $str_tags, 11, $tags[0]); - - // (mike) - This is a tricky case. - // we support mentions as in @mike@example.com - which contains a period. - // This shouldn't match anything unless you have a contact named "Mike.because". - // We may need another test for "@Mike. because" - which should return the contact - // as we ignore trailing periods in tags. - -// $this->assertEquals("cid:15", $inform); -// $this->assertEquals("@[url=http://justatest.de]Mike Lastname[/url]", $str_tags); -// $this->assertEquals("hi @[url=http://justatest.de]Mike Lastname[/url].because", $text); + $str_tags = ''; + foreach ($tags as $tag) { + handle_tag($text, $str_tags, 11, $tag); + } - $this->assertEquals("", $str_tags); + //correct tags found? + $this->assertEquals(1, count($tags)); + $this->assertTrue(in_array("@Mike", $tags)); - } - - /** - * test with two Person tags. - * There's a minor spelling mistake... - */ + //correct output from handle_tag? + $this->assertEquals("@[url=http://justatest.de]Mike Lastname[/url]", $str_tags); + $this->assertEquals("hi @[url=http://justatest.de]Mike Lastname[/url]", $text); + } - public function testGetTagsPerson2Spelling() { - $text="hi @Mike@campino@friendica.eu"; - - $tags=get_tags($text); + /** + * test with one Person tag. + * There's a minor spelling mistake... + */ + public function testGetTagsShortPersonSpelling() + { + $text = "hi @Mike.because"; -// This construct is not supported. Results are indeterminate -// $this->assertEquals(2, count($tags)); -// $this->assertTrue(in_array("@Mike", $tags)); -// $this->assertTrue(in_array("@campino@friendica.eu", $tags)); - } + $tags = get_tags($text); - /** - * Test with one hash tag. - */ - public function testGetTagsShortTag() { - $text="This is a #test_case"; - - $tags=get_tags($text); + //correct tags found? + $this->assertEquals(1, count($tags)); + $this->assertTrue(in_array("@Mike.because", $tags)); - $this->assertEquals(1, count($tags)); - $this->assertTrue(in_array("#test_case", $tags)); - } + $str_tags = ''; + handle_tag($text, $str_tags, 11, $tags[0]); - /** - * test with a person and a hash tag - */ - public function testGetTagsShortTagAndPerson() { - $text="hi @Mike This is a #test_case"; - - $tags=get_tags($text); + // (mike) - This is a tricky case. + // we support mentions as in @mike@example.com - which contains a period. + // This shouldn't match anything unless you have a contact named "Mike.because". + // We may need another test for "@Mike. because" - which should return the contact + // as we ignore trailing periods in tags. - $this->assertEquals(3, count($tags)); - $this->assertTrue(in_array("@Mike", $tags)); - $this->assertTrue(in_array("@Mike This", $tags)); - $this->assertTrue(in_array("#test_case", $tags)); +// $this->assertEquals("cid:15", $inform); +// $this->assertEquals("@[url=http://justatest.de]Mike Lastname[/url]", $str_tags); +// $this->assertEquals("hi @[url=http://justatest.de]Mike Lastname[/url].because", $text); - $str_tags=''; - foreach($tags as $tag) { - handle_tag($text, $str_tags, 11, $tag); - } - - $this->assertEquals("@[url=http://justatest.de]Mike Lastname[/url],#[url=baseurl/search?tag=test%20case]test case[/url]", $str_tags); - $this->assertEquals("hi @[url=http://justatest.de]Mike Lastname[/url] This is a #[url=baseurl/search?tag=test%20case]test case[/url]", $text); - - } + $this->assertEquals("", $str_tags); + } - /** - * test with a person, a hash tag and some special chars. - */ - public function testGetTagsShortTagAndPersonSpecialChars() { - $text="hi @Mike, This is a #test_case."; - - $tags=get_tags($text); - - $this->assertEquals(2, count($tags)); - $this->assertTrue(in_array("@Mike", $tags)); - $this->assertTrue(in_array("#test_case", $tags)); - } + /** + * test with two Person tags. + * There's a minor spelling mistake... + */ - /** - * Test with a person tag and text behind it. - */ - public function testGetTagsPersonOnly() { - $text="@Test I saw the Theme Dev group was created."; - - $tags=get_tags($text); + public function testGetTagsPerson2Spelling() + { + $text = "hi @Mike@campino@friendica.eu"; - $this->assertEquals(2, count($tags)); - $this->assertTrue(in_array("@Test I", $tags)); - $this->assertTrue(in_array("@Test", $tags)); - } + $tags = get_tags($text); - /** - * this test demonstrates strange behaviour by intval. - * It makes the next test fail. - */ - public function testIntval() { - $this->assertEquals(15, intval("15 it")); - } - - /** - * test a tag with an id in it - */ - public function testIdTag() { - $text="Test with @mike+15 id tag"; - - $tags=get_tags($text); - - $this->assertEquals(2, count($tags)); - $this->assertTrue(in_array("@mike+15", $tags)); - - //happens right now, but it shouldn't be necessary - $this->assertTrue(in_array("@mike+15 id", $tags)); - - $str_tags=''; - foreach($tags as $tag) { - handle_tag($text, $str_tags, 11, $tag); - } - - $this->assertEquals("Test with @[url=http://justatest.de]Mike Lastname[/url] id tag", $text); - $this->assertEquals("@[url=http://justatest.de]Mike Lastname[/url]", $str_tags); - } - - /** - * test with two persons and one special tag. - */ - public function testGetTags2Persons1TagSpecialChars() { - $text="hi @Mike, I'm just writing #test_cases, so" - ." so @somebody@friendica.com may change #things."; - - $tags=get_tags($text); +// This construct is not supported. Results are indeterminate +// $this->assertEquals(2, count($tags)); +// $this->assertTrue(in_array("@Mike", $tags)); +// $this->assertTrue(in_array("@campino@friendica.eu", $tags)); + } - $this->assertEquals(5, count($tags)); - $this->assertTrue(in_array("@Mike", $tags)); - $this->assertTrue(in_array("#test_cases", $tags)); - $this->assertTrue(in_array("@somebody@friendica.com", $tags)); - $this->assertTrue(in_array("@somebody@friendica.com may", $tags)); - $this->assertTrue(in_array("#things", $tags)); - } + /** + * Test with one hash tag. + */ + public function testGetTagsShortTag() + { + $text = "This is a #test_case"; - /** - * test with a long text. - */ - public function testGetTags() { - $text="hi @Mike, I'm just writing #test_cases, " - ." so @somebody@friendica.com may change #things. Of course I " - ."look for a lot of #pitfalls, like #tags at the end of a sentence " - ."@comment. I hope noone forgets about @fullstops.because that might" - ." break #things. @Mike@campino@friendica.eu is also #nice, isn't it? " - ."Now, add a @first_last tag. "; - - $tags=get_tags($text); - - $this->assertTrue(in_array("@Mike", $tags)); - $this->assertTrue(in_array("#test_cases", $tags)); - $this->assertTrue(in_array("@somebody@friendica.com", $tags)); - $this->assertTrue(in_array("#things", $tags)); - $this->assertTrue(in_array("#pitfalls", $tags)); - $this->assertTrue(in_array("#tags", $tags)); - $this->assertTrue(in_array("@comment", $tags)); - $this->assertTrue(in_array("@fullstops.because", $tags)); - $this->assertTrue(in_array("#things", $tags)); - $this->assertTrue(in_array("@Mike", $tags)); - $this->assertTrue(in_array("#nice", $tags)); - $this->assertTrue(in_array("@first_last", $tags)); - - //right now, none of the is matched (unsupported) -// $this->assertFalse(in_array("@Mike@campino@friendica.eu", $tags)); -// $this->assertTrue(in_array("@campino@friendica.eu", $tags)); -// $this->assertTrue(in_array("@campino@friendica.eu is", $tags)); - } + $tags = get_tags($text); - /** - * test with an empty string - */ - public function testGetTagsEmpty() { - $tags=get_tags(""); - $this->assertEquals(0, count($tags)); - } -} \ No newline at end of file + $this->assertEquals(1, count($tags)); + $this->assertTrue(in_array("#test_case", $tags)); + } + + /** + * test with a person and a hash tag + */ + public function testGetTagsShortTagAndPerson() + { + $text = "hi @Mike This is a #test_case"; + + $tags = get_tags($text); + + $this->assertEquals(3, count($tags)); + $this->assertTrue(in_array("@Mike", $tags)); + $this->assertTrue(in_array("@Mike This", $tags)); + $this->assertTrue(in_array("#test_case", $tags)); + + $str_tags = ''; + foreach ($tags as $tag) { + handle_tag($text, $str_tags, 11, $tag); + } + + $this->assertEquals("@[url=http://justatest.de]Mike Lastname[/url],#[url=baseurl/search?tag=test%20case]test case[/url]", $str_tags); + $this->assertEquals("hi @[url=http://justatest.de]Mike Lastname[/url] This is a #[url=baseurl/search?tag=test%20case]test case[/url]", $text); + } + + /** + * test with a person, a hash tag and some special chars. + */ + public function testGetTagsShortTagAndPersonSpecialChars() + { + $text = "hi @Mike, This is a #test_case."; + + $tags = get_tags($text); + + $this->assertEquals(2, count($tags)); + $this->assertTrue(in_array("@Mike", $tags)); + $this->assertTrue(in_array("#test_case", $tags)); + } + + /** + * Test with a person tag and text behind it. + */ + public function testGetTagsPersonOnly() + { + $text = "@Test I saw the Theme Dev group was created."; + + $tags = get_tags($text); + + $this->assertEquals(2, count($tags)); + $this->assertTrue(in_array("@Test I", $tags)); + $this->assertTrue(in_array("@Test", $tags)); + } + + /** + * this test demonstrates strange behaviour by intval. + * It makes the next test fail. + */ + public function testIntval() + { + $this->assertEquals(15, intval("15 it")); + } + + /** + * test a tag with an id in it + */ + public function testIdTag() + { + $text = "Test with @mike+15 id tag"; + + $tags = get_tags($text); + + $this->assertEquals(2, count($tags)); + $this->assertTrue(in_array("@mike+15", $tags)); + + //happens right now, but it shouldn't be necessary + $this->assertTrue(in_array("@mike+15 id", $tags)); + + $str_tags = ''; + foreach ($tags as $tag) { + handle_tag($text, $str_tags, 11, $tag); + } + + $this->assertEquals("Test with @[url=http://justatest.de]Mike Lastname[/url] id tag", $text); + $this->assertEquals("@[url=http://justatest.de]Mike Lastname[/url]", $str_tags); + } + + /** + * test with two persons and one special tag. + */ + public function testGetTags2Persons1TagSpecialChars() + { + $text = "hi @Mike, I'm just writing #test_cases, so" + . " so @somebody@friendica.com may change #things."; + + $tags = get_tags($text); + + $this->assertEquals(5, count($tags)); + $this->assertTrue(in_array("@Mike", $tags)); + $this->assertTrue(in_array("#test_cases", $tags)); + $this->assertTrue(in_array("@somebody@friendica.com", $tags)); + $this->assertTrue(in_array("@somebody@friendica.com may", $tags)); + $this->assertTrue(in_array("#things", $tags)); + } + + /** + * test with a long text. + */ + public function testGetTags() + { + $text = "hi @Mike, I'm just writing #test_cases, " + . " so @somebody@friendica.com may change #things. Of course I " + . "look for a lot of #pitfalls, like #tags at the end of a sentence " + . "@comment. I hope noone forgets about @fullstops.because that might" + . " break #things. @Mike@campino@friendica.eu is also #nice, isn't it? " + . "Now, add a @first_last tag. "; + + $tags = get_tags($text); + + $this->assertTrue(in_array("@Mike", $tags)); + $this->assertTrue(in_array("#test_cases", $tags)); + $this->assertTrue(in_array("@somebody@friendica.com", $tags)); + $this->assertTrue(in_array("#things", $tags)); + $this->assertTrue(in_array("#pitfalls", $tags)); + $this->assertTrue(in_array("#tags", $tags)); + $this->assertTrue(in_array("@comment", $tags)); + $this->assertTrue(in_array("@fullstops.because", $tags)); + $this->assertTrue(in_array("#things", $tags)); + $this->assertTrue(in_array("@Mike", $tags)); + $this->assertTrue(in_array("#nice", $tags)); + $this->assertTrue(in_array("@first_last", $tags)); + + //right now, none of the is matched (unsupported) +// $this->assertFalse(in_array("@Mike@campino@friendica.eu", $tags)); +// $this->assertTrue(in_array("@campino@friendica.eu", $tags)); +// $this->assertTrue(in_array("@campino@friendica.eu is", $tags)); + } + + /** + * test with an empty string + */ + public function testGetTagsEmpty() + { + $tags = get_tags(""); + $this->assertEquals(0, count($tags)); + } +} diff --git a/tests/unit/includes/LanguageTest.php b/tests/unit/includes/LanguageTest.php index 777b5ac14..b713b5d4c 100644 --- a/tests/unit/includes/LanguageTest.php +++ b/tests/unit/includes/LanguageTest.php @@ -1,4 +1,5 @@ getFunctionMock(__NAMESPACE__, 'get_config'); - //$gc->expects($this->once())->willReturn(10) - //$cg = $this->getFunctionMock('Zotlabs\Lib\Config', 'Get'); - //$cg->expects($this->once())->willReturn(10); - //$this->assertEquals($langCode, detect_language($text)); + // php-mock can not mock global functions which is called by a global function. + // If the calling function is in a namespace it would work. + //$gc = $this->getFunctionMock(__NAMESPACE__, 'get_config'); + //$gc->expects($this->once())->willReturn(10) + //$cg = $this->getFunctionMock('Zotlabs\Lib\Config', 'Get'); + //$cg->expects($this->once())->willReturn(10); + //$this->assertEquals($langCode, detect_language($text)); - // Can not unit test detect_language(), therefore test the used library - // only for now to find regressions on library updates. - $l = new Text_LanguageDetect; - // return 2-letter ISO 639-1 (en) language code - $l->setNameMode(2); - $lng = $l->detectConfidence($text); + // Can not unit test detect_language(), therefore test the used library + // only for now to find regressions on library updates. + $l = new Text_LanguageDetect(); + // return 2-letter ISO 639-1 (en) language code + $l->setNameMode(2); + $lng = $l->detectConfidence($text); - $this->assertEquals($langCode, $lng['language']); - $this->assertEquals($confidence, round($lng['confidence'], 6)); - } + $this->assertEquals($langCode, $lng['language']); + $this->assertEquals($confidence, round($lng['confidence'], 6)); + } - public function languageExamplesProvider() { - return [ - 'empty text' => [ - '', - '', - null - ], - 'English' => [ - 'English is a West Germanic language that was first spoken in early medieval England and is now a global lingua franca.[4][5] Named after the Angles, one of the Germanic tribes that migrated to England, it ultimately derives its name from the Anglia (Angeln) peninsula in the Baltic Sea. It is closely related to the Frisian languages, but its vocabulary has been significantly influenced by other Germanic languages, particularly Norse (a North Germanic language), as well as by Latin and Romance languages, especially French.', - 'en', - 0.078422 - ], - 'German' => [ - 'Deutschland ist ein Bundesstaat in Mitteleuropa. Er besteht aus 16 Ländern und ist als freiheitlich-demokratischer und sozialer Rechtsstaat verfasst. Die Bundesrepublik Deutschland stellt die jüngste Ausprägung des deutschen Nationalstaates dar. Mit rund 82,8 Millionen Einwohnern (31. Dezember 2016) zählt Deutschland zu den dicht besiedelten Flächenstaaten.', - 'de', - 0.134339 - ], - 'Norwegian' => [ - 'Kongeriket Norge er et nordisk, europeisk land og en selvstendig stat vest på Den skandinaviske halvøy. Landet er langt og smalt, og kysten strekker seg langs Nord-Atlanteren, hvor også Norges kjente fjorder befinner seg. Totalt dekker det relativt tynt befolkede landet 385 000 kvadratkilometer med litt over fem millioner innbyggere (2016).', - 'no', - 0.007076 - ] - ]; - } + public function languageExamplesProvider() + { + return [ + 'empty text' => [ + '', + '', + null + ], + 'English' => [ + 'English is a West Germanic language that was first spoken in early medieval England and is now a global lingua franca.[4][5] Named after the Angles, one of the Germanic tribes that migrated to England, it ultimately derives its name from the Anglia (Angeln) peninsula in the Baltic Sea. It is closely related to the Frisian languages, but its vocabulary has been significantly influenced by other Germanic languages, particularly Norse (a North Germanic language), as well as by Latin and Romance languages, especially French.', + 'en', + 0.078422 + ], + 'German' => [ + 'Deutschland ist ein Bundesstaat in Mitteleuropa. Er besteht aus 16 Ländern und ist als freiheitlich-demokratischer und sozialer Rechtsstaat verfasst. Die Bundesrepublik Deutschland stellt die jüngste Ausprägung des deutschen Nationalstaates dar. Mit rund 82,8 Millionen Einwohnern (31. Dezember 2016) zählt Deutschland zu den dicht besiedelten Flächenstaaten.', + 'de', + 0.134339 + ], + 'Norwegian' => [ + 'Kongeriket Norge er et nordisk, europeisk land og en selvstendig stat vest på Den skandinaviske halvøy. Landet er langt og smalt, og kysten strekker seg langs Nord-Atlanteren, hvor også Norges kjente fjorder befinner seg. Totalt dekker det relativt tynt befolkede landet 385 000 kvadratkilometer med litt over fem millioner innbyggere (2016).', + 'no', + 0.007076 + ] + ]; + } - /** - * @covers ::get_language_name - * @dataProvider getLanguageNameProvider - */ - public function testGetLanguageName($lang, $name, $trans) { - $this->assertEquals($name, get_language_name($lang)); - foreach ($trans as $k => $v) { - //echo "$k -> $v"; - $this->assertEquals($v, get_language_name($lang, $k)); - } - } + /** + * @covers ::get_language_name + * @dataProvider getLanguageNameProvider + */ + public function testGetLanguageName($lang, $name, $trans) + { + $this->assertEquals($name, get_language_name($lang)); + foreach ($trans as $k => $v) { + //echo "$k -> $v"; + $this->assertEquals($v, get_language_name($lang, $k)); + } + } - public function getLanguageNameProvider() { - return [ - 'empty language code' => [ - '', - '', - ['de' => ''] - ], - 'invalid language code' => [ - 'zz', - 'zz', - ['de' => 'zz'] - ], - 'de' => [ - 'de', - 'German', - [ - 'de' => 'Deutsch', - 'nb' => 'tysk' - ] - ], - 'de-de' => [ - 'de-de', - 'German', - [ - 'de-de' => 'Deutsch', - 'nb' => 'Deutsch' // should be tysk, seems to be a bug upstream - ] - ], - 'en' => [ - 'en', - 'English', - [ - 'de' => 'Englisch', - 'nb' => 'engelsk' - ] - ], - 'en-gb' => [ - 'en-gb', - 'British English', - [ - 'de' => 'Britisches Englisch', - 'nb' => 'engelsk (Storbritannia)' - ] - ], - 'en-au' => [ - 'en-au', - 'Australian English', - [ - 'de' => 'Australisches Englisch', - 'nb' => 'engelsk (Australia)' - ] - ], - 'nb' => [ - 'nb', - 'Norwegian Bokmål', - [ - 'de' => 'Norwegisch Bokmål', - 'nb' => 'norsk bokmål' - ] - ] - ]; - } -} \ No newline at end of file + public function getLanguageNameProvider() + { + return [ + 'empty language code' => [ + '', + '', + ['de' => ''] + ], + 'invalid language code' => [ + 'zz', + 'zz', + ['de' => 'zz'] + ], + 'de' => [ + 'de', + 'German', + [ + 'de' => 'Deutsch', + 'nb' => 'tysk' + ] + ], + 'de-de' => [ + 'de-de', + 'German', + [ + 'de-de' => 'Deutsch', + 'nb' => 'Deutsch' // should be tysk, seems to be a bug upstream + ] + ], + 'en' => [ + 'en', + 'English', + [ + 'de' => 'Englisch', + 'nb' => 'engelsk' + ] + ], + 'en-gb' => [ + 'en-gb', + 'British English', + [ + 'de' => 'Britisches Englisch', + 'nb' => 'engelsk (Storbritannia)' + ] + ], + 'en-au' => [ + 'en-au', + 'Australian English', + [ + 'de' => 'Australisches Englisch', + 'nb' => 'engelsk (Australia)' + ] + ], + 'nb' => [ + 'nb', + 'Norwegian Bokmål', + [ + 'de' => 'Norwegisch Bokmål', + 'nb' => 'norsk bokmål' + ] + ] + ]; + } +} diff --git a/tests/unit/includes/TextTest.php b/tests/unit/includes/TextTest.php index 97fa64895..b23624354 100644 --- a/tests/unit/includes/TextTest.php +++ b/tests/unit/includes/TextTest.php @@ -9,112 +9,121 @@ use Zotlabs\Tests\Unit\UnitTestCase; * * @author ken restivo */ -class TextTest extends UnitTestCase { +class TextTest extends UnitTestCase +{ - public function testPurifyHTML() { - // linebreaks - $htmlbr = 'first line
                    + public function testPurifyHTML() + { + // linebreaks + $htmlbr = 'first line
                    one tab preserved empty line above'; - $this->assertEquals($htmlbr, purify_html($htmlbr)); + $this->assertEquals($htmlbr, purify_html($htmlbr)); - // HTML5 is not supported by HTMLPurifier yet, test our own configuration - $html5elements = '
                    section
                    footer
                    '; - $this->assertEquals($html5elements, purify_html($html5elements)); - $this->assertEquals('', purify_html('')); + // HTML5 is not supported by HTMLPurifier yet, test our own configuration + $html5elements = '
                    section
                    footer
                    '; + $this->assertEquals($html5elements, purify_html($html5elements)); + $this->assertEquals('', purify_html('')); - // unsupported HTML5 elements - $this->assertEquals('Your HTML parser does not support HTML5 video.', purify_html('')); - $this->assertEquals('Your HTML parser does not support HTML5 audio.', purify_html('')); + // unsupported HTML5 elements + $this->assertEquals('Your HTML parser does not support HTML5 video.', purify_html('')); + $this->assertEquals('Your HTML parser does not support HTML5 audio.', purify_html('')); - // preserve f6 and bootstrap additional data attributes from our own configuration - $this->assertEquals('
                    text
                    ', purify_html('
                    text
                    ')); - $this->assertEquals('
                    • item1
                    ', purify_html('
                    • item1
                    ')); - $this->assertEquals('
                    • item1
                    ', purify_html('
                    • item1
                    ')); - } + // preserve f6 and bootstrap additional data attributes from our own configuration + $this->assertEquals('
                    text
                    ', purify_html('
                    text
                    ')); + $this->assertEquals('
                    • item1
                    ', purify_html('
                    • item1
                    ')); + $this->assertEquals('
                    • item1
                    ', purify_html('
                    • item1
                    ')); + } - /** - * @covers ::purify_html - */ - public function testPurifyHTML_html() { - $this->assertEquals('

                    ids und classes

                    ', purify_html('

                    ids und classes

                    ')); - $this->assertEquals('

                    close missing tags

                    ', purify_html('

                    close missing tags')); - $this->assertEquals('

                    deprecated tag
                    ', purify_html('
                    deprecated tag
                    ')); - $this->assertEquals('
                    illegal nesting
                    ', purify_html('
                    illegal nesting
                    ')); - $this->assertEquals('link with target', purify_html('link with target')); - $this->assertEquals('link with rel="nofollow"', purify_html('link with rel="nofollow"')); - $this->assertEquals('a b', purify_html('a b')); - $this->assertEquals('ä ä € €', purify_html('ä ä € €')); - $this->assertEquals('text', purify_html('text')); - $this->assertEquals('', purify_html('')); - } + /** + * @covers ::purify_html + */ + public function testPurifyHTML_html() + { + $this->assertEquals('

                    ids und classes

                    ', purify_html('

                    ids und classes

                    ')); + $this->assertEquals('

                    close missing tags

                    ', purify_html('

                    close missing tags')); + $this->assertEquals('

                    deprecated tag
                    ', purify_html('
                    deprecated tag
                    ')); + $this->assertEquals('
                    illegal nesting
                    ', purify_html('
                    illegal nesting
                    ')); + $this->assertEquals('link with target', purify_html('link with target')); + $this->assertEquals('link with rel="nofollow"', purify_html('link with rel="nofollow"')); + $this->assertEquals('a b', purify_html('a b')); + $this->assertEquals('ä ä € €', purify_html('ä ä € €')); + $this->assertEquals('text', purify_html('text')); + $this->assertEquals('', purify_html('')); + } - /** - * @covers ::purify_html - */ - public function testPurifyHTML_js() { - $this->assertEquals('
                    ', purify_html('
                    ')); - $this->assertEquals('link', purify_html('link')); - $this->assertEquals('', purify_html('')); - $this->assertEquals('', purify_html('')); - } + /** + * @covers ::purify_html + */ + public function testPurifyHTML_js() + { + $this->assertEquals('
                    ', purify_html('
                    ')); + $this->assertEquals('link', purify_html('link')); + $this->assertEquals('', purify_html('')); + $this->assertEquals('', purify_html('')); + } - /** - * @covers ::purify_html - */ - public function testPurifyHTML_css() { - $this->assertEquals('

                    red

                    ', purify_html('

                    red

                    ')); - $this->assertEquals('

                    invalid color

                    ', purify_html('

                    invalid color

                    ')); - $this->assertEquals('

                    invalid style

                    ', purify_html('

                    invalid style

                    ')); + /** + * @covers ::purify_html + */ + public function testPurifyHTML_css() + { + $this->assertEquals('

                    red

                    ', purify_html('

                    red

                    ')); + $this->assertEquals('

                    invalid color

                    ', purify_html('

                    invalid color

                    ')); + $this->assertEquals('

                    invalid style

                    ', purify_html('

                    invalid style

                    ')); - // test our own CSS configuration - $this->assertEquals('
                    position removed
                    ', purify_html('
                    position removed
                    ')); - $this->assertEquals('
                    position preserved
                    ', purify_html('
                    position preserved
                    ', true)); - $this->assertEquals('
                    invalid position removed
                    ', purify_html('
                    invalid position removed
                    ', true)); + // test our own CSS configuration + $this->assertEquals('
                    position removed
                    ', purify_html('
                    position removed
                    ')); + $this->assertEquals('
                    position preserved
                    ', purify_html('
                    position preserved
                    ', true)); + $this->assertEquals('
                    invalid position removed
                    ', purify_html('
                    invalid position removed
                    ', true)); - $this->assertEquals('
                    position removed
                    ', purify_html('
                    position removed
                    ')); - $this->assertEquals('
                    position preserved
                    ', purify_html('
                    position preserved
                    ', true)); - $this->assertEquals('
                    invalid position removed
                    ', purify_html('
                    invalid position removed
                    ', true)); - } + $this->assertEquals('
                    position removed
                    ', purify_html('
                    position removed
                    ')); + $this->assertEquals('
                    position preserved
                    ', purify_html('
                    position preserved
                    ', true)); + $this->assertEquals('
                    invalid position removed
                    ', purify_html('
                    invalid position removed
                    ', true)); + } - /** - * @dataProvider notagsProvider - */ - public function testNotags($string, $expected) { - $this->assertEquals($expected, notags($string)); - } - public function notagsProvider() { - return [ - 'empty string' => ['', ''], - 'simple tag' => ['', '[value]'], - 'tag pair' => ['text', '[b]text[/b]'], - 'double angle bracket' => ['< ['>', '>'] - ]; - } + /** + * @dataProvider notagsProvider + */ + public function testNotags($string, $expected) + { + $this->assertEquals($expected, notags($string)); + } + public function notagsProvider() + { + return [ + 'empty string' => ['', ''], + 'simple tag' => ['', '[value]'], + 'tag pair' => ['text', '[b]text[/b]'], + 'double angle bracket' => ['< ['>', '>'] + ]; + } - /** - * @dataProvider sanitise_aclProvider - */ - public function testSanitise_acl($string, $expected) { - sanitise_acl($string); - $this->assertEquals($expected, $string); - } - public function sanitise_aclProvider() { - return [ - 'text' => ['value', ''], - 'text with angle bracket' => ['', '<[value]>'], - 'comma separated acls' => ['value1,value2', ''] - ]; - } - - public function testUnsetSanitise_acl() { - $empty = ''; - sanitise_acl($empty); - $this->assertTrue(isset($empty)); // unset() not working? Would expect false - $this->assertEmpty($empty); - } + /** + * @dataProvider sanitise_aclProvider + */ + public function testSanitise_acl($string, $expected) + { + sanitise_acl($string); + $this->assertEquals($expected, $string); + } + public function sanitise_aclProvider() + { + return [ + 'text' => ['value', ''], + 'text with angle bracket' => ['', '<[value]>'], + 'comma separated acls' => ['value1,value2', ''] + ]; + } + public function testUnsetSanitise_acl() + { + $empty = ''; + sanitise_acl($empty); + $this->assertTrue(isset($empty)); // unset() not working? Would expect false + $this->assertEmpty($empty); + } } diff --git a/tests/unit/includes/dba/DBATest.php b/tests/unit/includes/dba/DBATest.php index 900d13083..6131e983e 100644 --- a/tests/unit/includes/dba/DBATest.php +++ b/tests/unit/includes/dba/DBATest.php @@ -1,4 +1,5 @@ assertNull(\DBA::$dba); + public function testDbaFactoryMysql() + { + $this->assertNull(DBA::$dba); - $ret = \DBA::dba_factory('server', 'port', 'user', 'pass', 'db', '0'); - $this->assertInstanceOf('dba_pdo', $ret); - $this->assertFalse($ret->connected); + $ret = DBA::dba_factory('server', 'port', 'user', 'pass', 'db', '0'); + $this->assertInstanceOf('dba_pdo', $ret); + $this->assertFalse($ret->connected); - $this->assertSame('mysql', \DBA::$scheme); - $this->assertSame('schema_mysql.sql', \DBA::$install_script); - $this->assertSame('0001-01-01 00:00:00', \DBA::$null_date); - $this->assertSame('UTC_TIMESTAMP()', \DBA::$utc_now); - $this->assertSame('`', \DBA::$tquot); - } + $this->assertSame('mysql', DBA::$scheme); + $this->assertSame('schema_mysql.sql', DBA::$install_script); + $this->assertSame('0001-01-01 00:00:00', DBA::$null_date); + $this->assertSame('UTC_TIMESTAMP()', DBA::$utc_now); + $this->assertSame('`', DBA::$tquot); + } - public function testDbaFactoryPostgresql() { - $this->assertNull(\DBA::$dba); + public function testDbaFactoryPostgresql() + { + $this->assertNull(DBA::$dba); - $ret = \DBA::dba_factory('server', 'port', 'user', 'pass', 'db', '1'); - $this->assertInstanceOf('dba_pdo', $ret); - $this->assertFalse($ret->connected); - - $this->assertSame('pgsql', \DBA::$scheme); - $this->assertSame('schema_postgres.sql', \DBA::$install_script); - $this->assertSame('0001-01-01 00:00:00', \DBA::$null_date); - $this->assertSame("now() at time zone 'UTC'", \DBA::$utc_now); - $this->assertSame('"', \DBA::$tquot); - } + $ret = DBA::dba_factory('server', 'port', 'user', 'pass', 'db', '1'); + $this->assertInstanceOf('dba_pdo', $ret); + $this->assertFalse($ret->connected); + $this->assertSame('pgsql', DBA::$scheme); + $this->assertSame('schema_postgres.sql', DBA::$install_script); + $this->assertSame('0001-01-01 00:00:00', DBA::$null_date); + $this->assertSame("now() at time zone 'UTC'", DBA::$utc_now); + $this->assertSame('"', DBA::$tquot); + } } diff --git a/tests/unit/includes/dba/dba_pdoTest.php b/tests/unit/includes/dba/dba_pdoTest.php index 12e574d42..a3fc14ddd 100644 --- a/tests/unit/includes/dba/dba_pdoTest.php +++ b/tests/unit/includes/dba/dba_pdoTest.php @@ -1,4 +1,5 @@ dba = new \dba_pdo( - \getenv('hz_db_server'), - \getenv('hz_db_scheme'), - \getenv('hz_db_port'), - \getenv('hz_db_user'), - \getenv('hz_db_pass'), - \getenv('hz_db_database') - ); - } - protected function assertPreConditions() { - $this->assertSame('pdo', $this->dba->getdriver(), "Driver is expected to be 'pdo'."); - $this->assertInstanceOf('dba_driver', $this->dba); - $this->assertTrue($this->dba->connected, 'Pre condition failed, DB is not connected.'); - $this->assertInstanceOf('PDO', $this->dba->db); - } - protected function tearDown() { - $this->dba = null; - } + $this->dba = new dba_pdo( + getenv('hz_db_server'), + getenv('hz_db_scheme'), + getenv('hz_db_port'), + getenv('hz_db_user'), + getenv('hz_db_pass'), + getenv('hz_db_database') + ); + } + protected function assertPreConditions() + { + $this->assertSame('pdo', $this->dba->getdriver(), "Driver is expected to be 'pdo'."); + $this->assertInstanceOf('dba_driver', $this->dba); + $this->assertTrue($this->dba->connected, 'Pre condition failed, DB is not connected.'); + $this->assertInstanceOf('PDO', $this->dba->db); + } + protected function tearDown() + { + $this->dba = null; + } - /** - * @group mysql - */ - public function testQuoteintervalOnMysql() { - $this->assertSame('value', $this->dba->quote_interval('value')); - } - /** - * @group postgresql - */ - public function testQuoteintervalOnPostgresql() { - $this->assertSame("'value'", $this->dba->quote_interval('value')); - } + /** + * @group mysql + */ + public function testQuoteintervalOnMysql() + { + $this->assertSame('value', $this->dba->quote_interval('value')); + } + /** + * @group postgresql + */ + public function testQuoteintervalOnPostgresql() + { + $this->assertSame("'value'", $this->dba->quote_interval('value')); + } - /** - * @group mysql - */ - public function testGenerateMysqlConcatSql() { - $this->assertSame('GROUP_CONCAT(DISTINCT field SEPARATOR \';\')', $this->dba->concat('field', ';')); - $this->assertSame('GROUP_CONCAT(DISTINCT field2 SEPARATOR \' \')', $this->dba->concat('field2', ' ')); - } - /** - * @group postgresql - */ - public function testGeneratePostgresqlConcatSql() { - $this->assertSame('string_agg(field,\';\')', $this->dba->concat('field', ';')); - $this->assertSame('string_agg(field2,\' \')', $this->dba->concat('field2', ' ')); - } + /** + * @group mysql + */ + public function testGenerateMysqlConcatSql() + { + $this->assertSame('GROUP_CONCAT(DISTINCT field SEPARATOR \';\')', $this->dba->concat('field', ';')); + $this->assertSame('GROUP_CONCAT(DISTINCT field2 SEPARATOR \' \')', $this->dba->concat('field2', ' ')); + } + /** + * @group postgresql + */ + public function testGeneratePostgresqlConcatSql() + { + $this->assertSame('string_agg(field,\';\')', $this->dba->concat('field', ';')); + $this->assertSame('string_agg(field2,\' \')', $this->dba->concat('field2', ' ')); + } - public function testConnectToSqlServer() { - // connect() is done in dba_pdo constructor which is called in setUp() - $this->assertTrue($this->dba->connected); - } + public function testConnectToSqlServer() + { + // connect() is done in dba_pdo constructor which is called in setUp() + $this->assertTrue($this->dba->connected); + } - /** - * @depends testConnectToSqlServer - */ - public function testCloseSqlServerConnection() { - $this->dba->close(); + /** + * @depends testConnectToSqlServer + */ + public function testCloseSqlServerConnection() + { + $this->dba->close(); - $this->assertNull($this->dba->db); - $this->assertFalse($this->dba->connected); - } + $this->assertNull($this->dba->db); + $this->assertFalse($this->dba->connected); + } - /** - * @depends testConnectToSqlServer - */ - public function testSelectQueryShouldReturnArray() { - $ret = $this->dba->q('SELECT * FROM account'); + /** + * @depends testConnectToSqlServer + */ + public function testSelectQueryShouldReturnArray() + { + $ret = $this->dba->q('SELECT * FROM account'); - $this->assertTrue(is_array($ret)); - } + $this->assertTrue(is_array($ret)); + } - /** - * @depends testConnectToSqlServer - */ - public function testInsertQueryShouldReturnPdostatement() { - // Fixture account.yml adds two entries to account table - $this->assertEquals(2, $this->getConnection()->getRowCount('account'), 'Pre-Condition'); + /** + * @depends testConnectToSqlServer + */ + public function testInsertQueryShouldReturnPdostatement() + { + // Fixture account.yml adds two entries to account table + $this->assertEquals(2, $this->getConnection()->getRowCount('account'), 'Pre-Condition'); - $ret = $this->dba->q('INSERT INTO account + $ret = $this->dba->q('INSERT INTO account (account_id, account_email, account_language) VALUES (100, \'insert@example.com\', \'de\') '); - $this->assertInstanceOf('PDOStatement', $ret); + $this->assertInstanceOf('PDOStatement', $ret); - $this->assertEquals(3, $this->getConnection()->getRowCount('account'), 'Inserting failed'); - } + $this->assertEquals(3, $this->getConnection()->getRowCount('account'), 'Inserting failed'); + } - public function testConnectToWrongSqlServer() { - $nodba = new \dba_pdo('wrongserver', - \getenv('hz_db_scheme'), \getenv('hz_db_port'), - \getenv('hz_db_user'), \getenv('hz_db_pass'), - \getenv('hz_db_database') - ); + public function testConnectToWrongSqlServer() + { + $nodba = new dba_pdo( + 'wrongserver', + getenv('hz_db_scheme'), + getenv('hz_db_port'), + getenv('hz_db_user'), + getenv('hz_db_pass'), + getenv('hz_db_database') + ); - $this->assertSame('pdo', $nodba->getdriver()); - $this->assertInstanceOf('dba_pdo', $nodba); - $this->assertFalse($nodba->connected); - $this->assertNull($nodba->db); + $this->assertSame('pdo', $nodba->getdriver()); + $this->assertInstanceOf('dba_pdo', $nodba); + $this->assertFalse($nodba->connected); + $this->assertNull($nodba->db); - $this->assertFalse($nodba->q('SELECT * FROM account')); - } + $this->assertFalse($nodba->q('SELECT * FROM account')); + } - /** - * @depends testConnectToSqlServer - */ - public function testSelectQueryToNonExistentTableShouldReturnFalse() { - $ret = $this->dba->q('SELECT * FROM non_existent_table'); + /** + * @depends testConnectToSqlServer + */ + public function testSelectQueryToNonExistentTableShouldReturnFalse() + { + $ret = $this->dba->q('SELECT * FROM non_existent_table'); - $this->assertFalse($ret); - } + $this->assertFalse($ret); + } - /** - * @depends testConnectToSqlServer - */ - public function testInsertQueryToNonExistentTableShouldReturnEmptyArray() { - $ret = $this->dba->q('INSERT INTO non_existent_table + /** + * @depends testConnectToSqlServer + */ + public function testInsertQueryToNonExistentTableShouldReturnEmptyArray() + { + $ret = $this->dba->q('INSERT INTO non_existent_table (account_email, account_language) VALUES (\'email@example.com\', \'en\') '); - $this->assertNotInstanceOf('PDOStatement', $ret); - $this->isEmpty($ret); - } - + $this->assertNotInstanceOf('PDOStatement', $ret); + $this->isEmpty($ret); + } } diff --git a/tests/unit/template_test.php b/tests/unit/template_test.php index fc354a9e4..975f1bb38 100644 --- a/tests/unit/template_test.php +++ b/tests/unit/template_test.php @@ -1,218 +1,220 @@ assertTrue(is_null($second)); + } - $this->assertTrue(is_null($second)); - } + public function testSimpleVariableString() + { + $tpl = 'Hello $name!'; + $text = replace_macros($tpl, array('$name' => 'Anna')); + $this->assertEquals('Hello Anna!', $text); + } - public function testSimpleVariableString() { - $tpl='Hello $name!'; + public function testSimpleVariableInt() + { + $tpl = 'There are $num new messages!'; + $text = replace_macros($tpl, array('$num' => 172)); + $this->assertEquals('There are 172 new messages!', $text); + } - $text=replace_macros($tpl, array('$name'=>'Anna')); + public function testConditionalElse() + { + $tpl = 'There{{ if $num!=1 }} are $num new messages{{ else }} is 1 new message{{ endif }}!'; + $text1 = replace_macros($tpl, array('$num' => 1)); + $text22 = replace_macros($tpl, array('$num' => 22)); + $this->assertEquals('There is 1 new message!', $text1); + $this->assertEquals('There are 22 new messages!', $text22); + } - $this->assertEquals('Hello Anna!', $text); - } + public function testConditionalNoElse() + { + $tpl = '{{ if $num!=0 }}There are $num new messages!{{ endif }}'; + $text0 = replace_macros($tpl, array('$num' => 0)); + $text22 = replace_macros($tpl, array('$num' => 22)); + $this->assertEquals('', $text0); + $this->assertEquals('There are 22 new messages!', $text22); + } - public function testSimpleVariableInt() { - $tpl='There are $num new messages!'; - - $text=replace_macros($tpl, array('$num'=>172)); - - $this->assertEquals('There are 172 new messages!', $text); - } + public function testConditionalFail() + { + $tpl = 'There {{ if $num!=1 }} are $num new messages{{ else }} is 1 new message{{ endif }}!'; + $text1 = replace_macros($tpl, []); - public function testConditionalElse() { - $tpl='There{{ if $num!=1 }} are $num new messages{{ else }} is 1 new message{{ endif }}!'; - - $text1=replace_macros($tpl, array('$num'=>1)); - $text22=replace_macros($tpl, array('$num'=>22)); - - $this->assertEquals('There is 1 new message!', $text1); - $this->assertEquals('There are 22 new messages!', $text22); - } + //$this->assertEquals('There is 1 new message!', $text1); + } - public function testConditionalNoElse() { - $tpl='{{ if $num!=0 }}There are $num new messages!{{ endif }}'; - - $text0=replace_macros($tpl, array('$num'=>0)); - $text22=replace_macros($tpl, array('$num'=>22)); - - $this->assertEquals('', $text0); - $this->assertEquals('There are 22 new messages!', $text22); - } + public function testSimpleFor() + { + $tpl = '{{ for $messages as $message }} $message {{ endfor }}'; + $text = replace_macros($tpl, array('$messages' => array('message 1', 'message 2'))); + $this->assertEquals(' message 1 message 2 ', $text); + } - public function testConditionalFail() { - $tpl='There {{ if $num!=1 }} are $num new messages{{ else }} is 1 new message{{ endif }}!'; - - $text1=replace_macros($tpl, []); - - //$this->assertEquals('There is 1 new message!', $text1); - } + public function testFor() + { + $tpl = '{{ for $messages as $message }} from: $message.from to $message.to {{ endfor }}'; + $text = replace_macros($tpl, array('$messages' => array(array('from' => 'Mike', 'to' => 'Alex'), array('from' => 'Alex', 'to' => 'Mike')))); + $this->assertEquals(' from: Mike to Alex from: Alex to Mike ', $text); + } - public function testSimpleFor() { - $tpl='{{ for $messages as $message }} $message {{ endfor }}'; - - $text=replace_macros($tpl, array('$messages'=>array('message 1', 'message 2'))); - - $this->assertEquals(' message 1 message 2 ', $text); - } + public function testKeyedFor() + { + $tpl = '{{ for $messages as $from=>$to }} from: $from to $to {{ endfor }}'; - public function testFor() { - $tpl='{{ for $messages as $message }} from: $message.from to $message.to {{ endfor }}'; - - $text=replace_macros($tpl, array('$messages'=>array(array('from'=>'Mike', 'to'=>'Alex'), array('from'=>'Alex', 'to'=>'Mike')))); - - $this->assertEquals(' from: Mike to Alex from: Alex to Mike ', $text); - } - - public function testKeyedFor() { - $tpl='{{ for $messages as $from=>$to }} from: $from to $to {{ endfor }}'; - - $text=replace_macros($tpl, array('$messages'=>array('Mike'=>'Alex', 'Sven'=>'Mike'))); - - $this->assertEquals(' from: Mike to Alex from: Sven to Mike ', $text); - } + $text = replace_macros($tpl, array('$messages' => array('Mike' => 'Alex', 'Sven' => 'Mike'))); - public function testForEmpty() { - $tpl='messages: {{for $messages as $message}} from: $message.from to $message.to {{ endfor }}'; - - $text=replace_macros($tpl, array('$messages'=>[])); - - $this->assertEquals('messages: ', $text); - } + $this->assertEquals(' from: Mike to Alex from: Sven to Mike ', $text); + } - public function testForWrongType() { - $tpl='messages: {{for $messages as $message}} from: $message.from to $message.to {{ endfor }}'; - - $text=replace_macros($tpl, array('$messages'=>11)); - - $this->assertEquals('messages: ', $text); - } + public function testForEmpty() + { + $tpl = 'messages: {{for $messages as $message}} from: $message.from to $message.to {{ endfor }}'; + $text = replace_macros($tpl, array('$messages' => [])); + $this->assertEquals('messages: ', $text); + } - public function testForConditional() { - $tpl='new messages: {{for $messages as $message}}{{ if $message.new }} $message.text{{endif}}{{ endfor }}'; - - $text=replace_macros($tpl, array('$messages'=>array( - array('new'=>true, 'text'=>'new message'), - array('new'=>false, 'text'=>'old message')))); - - $this->assertEquals('new messages: new message', $text); - } - - public function testConditionalFor() { - $tpl='{{ if $enabled }}new messages:{{for $messages as $message}} $message.text{{ endfor }}{{endif}}'; - - $text=replace_macros($tpl, array('$enabled'=>true, - '$messages'=>array( - array('new'=>true, 'text'=>'new message'), - array('new'=>false, 'text'=>'old message')))); - - $this->assertEquals('new messages: new message old message', $text); - } + public function testForWrongType() + { + $tpl = 'messages: {{for $messages as $message}} from: $message.from to $message.to {{ endfor }}'; + $text = replace_macros($tpl, array('$messages' => 11)); + $this->assertEquals('messages: ', $text); + } - public function testFantasy() { - $tpl='Fantasy: {{fantasy $messages}}'; - - $text=replace_macros($tpl, array('$messages'=>'no no')); - - $this->assertEquals('Fantasy: {{fantasy no no}}', $text); - } + public function testForConditional() + { + $tpl = 'new messages: {{for $messages as $message}}{{ if $message.new }} $message.text{{endif}}{{ endfor }}'; + $text = replace_macros($tpl, array('$messages' => array( + array('new' => true, 'text' => 'new message'), + array('new' => false, 'text' => 'old message')))); + $this->assertEquals('new messages: new message', $text); + } - public function testInc() { - $tpl='{{inc field_input.tpl with $field=$myvar}}{{ endinc }}'; - - $text=replace_macros($tpl, array('$myvar'=>array('myfield', 'label', 'value', 'help'))); - - $this->assertEquals(" \n" - ."
                    \n" - ." \n" - ." \n" - ." help\n" - ."
                    \n", $text); - } + public function testConditionalFor() + { + $tpl = '{{ if $enabled }}new messages:{{for $messages as $message}} $message.text{{ endfor }}{{endif}}'; - public function testIncNoVar() { - $tpl='{{inc field_input.tpl }}{{ endinc }}'; - - $text=replace_macros($tpl, array('$field'=>array('myfield', 'label', 'value', 'help'))); - - $this->assertEquals(" \n
                    \n \n" - ." \n" - ." help\n" - ."
                    \n", $text); - } - - public function testDoubleUse() { - $tpl='Hello $name! {{ if $enabled }} I love you! {{ endif }}'; - - $text=replace_macros($tpl, array('$name'=>'Anna', '$enabled'=>false)); - - $this->assertEquals('Hello Anna! ', $text); - - $tpl='Hey $name! {{ if $enabled }} I hate you! {{ endif }}'; - - $text=replace_macros($tpl, array('$name'=>'Max', '$enabled'=>true)); - - $this->assertEquals('Hey Max! I hate you! ', $text); - } - - public function testIncDouble() { - $tpl='{{inc field_input.tpl with $field=$var1}}{{ endinc }}' - .'{{inc field_input.tpl with $field=$var2}}{{ endinc }}'; - - $text=replace_macros($tpl, array('$var1'=>array('myfield', 'label', 'value', 'help'), - '$var2'=>array('myfield2', 'label2', 'value2', 'help2'))); - - $this->assertEquals(" \n" - ."
                    \n" - ." \n" - ." \n" - ." help\n" - ."
                    \n" - ." \n" - ."
                    \n" - ." \n" - ." \n" - ." help2\n" - ."
                    \n", $text); - } -} \ No newline at end of file + $text = replace_macros($tpl, array('$enabled' => true, + '$messages' => array( + array('new' => true, 'text' => 'new message'), + array('new' => false, 'text' => 'old message')))); + + $this->assertEquals('new messages: new message old message', $text); + } + + public function testFantasy() + { + $tpl = 'Fantasy: {{fantasy $messages}}'; + $text = replace_macros($tpl, array('$messages' => 'no no')); + $this->assertEquals('Fantasy: {{fantasy no no}}', $text); + } + + public function testInc() + { + $tpl = '{{inc field_input.tpl with $field=$myvar}}{{ endinc }}'; + $text = replace_macros($tpl, array('$myvar' => array('myfield', 'label', 'value', 'help'))); + $this->assertEquals(" \n" + . "
                    \n" + . " \n" + . " \n" + . " help\n" + . "
                    \n", $text); + } + + public function testIncNoVar() + { + $tpl = '{{inc field_input.tpl }}{{ endinc }}'; + $text = replace_macros($tpl, array('$field' => array('myfield', 'label', 'value', 'help'))); + $this->assertEquals(" \n
                    \n \n" + . " \n" + . " help\n" + . "
                    \n", $text); + } + + public function testDoubleUse() + { + $tpl = 'Hello $name! {{ if $enabled }} I love you! {{ endif }}'; + + $text = replace_macros($tpl, array('$name' => 'Anna', '$enabled' => false)); + + $this->assertEquals('Hello Anna! ', $text); + + $tpl = 'Hey $name! {{ if $enabled }} I hate you! {{ endif }}'; + + $text = replace_macros($tpl, array('$name' => 'Max', '$enabled' => true)); + + $this->assertEquals('Hey Max! I hate you! ', $text); + } + + public function testIncDouble() + { + $tpl = '{{inc field_input.tpl with $field=$var1}}{{ endinc }}' + . '{{inc field_input.tpl with $field=$var2}}{{ endinc }}'; + + $text = replace_macros($tpl, array('$var1' => array('myfield', 'label', 'value', 'help'), + '$var2' => array('myfield2', 'label2', 'value2', 'help2'))); + + $this->assertEquals(" \n" + . "
                    \n" + . " \n" + . " \n" + . " help\n" + . "
                    \n" + . " \n" + . "
                    \n" + . " \n" + . " \n" + . " help2\n" + . "
                    \n", $text); + } +} diff --git a/util/addons b/util/addons index dadc29bb6..8100bbef3 100755 --- a/util/addons +++ b/util/addons @@ -41,7 +41,7 @@ cli_startup(); if ($idz !== false) { unset(App::$plugins[$idz]); uninstall_plugin($id); - set_config("system","addon", implode(", ",App::$plugins)); + set_config('system', 'addon', implode(', ',App::$plugins)); } } $info['disabled'] = 1-intval($x); @@ -92,7 +92,7 @@ if($argc == 3 && $argv[1] === 'install') { else { App::$plugins[] = $p[0]; install_plugin($p[0]); - set_config("system","addon", implode(", ",App::$plugins)); + set_config('system', 'addon', implode(', ',App::$plugins)); echo $p[0] . ' installed.' . "\n"; } } @@ -121,7 +121,7 @@ if($argc == 3 && $argv[1] === 'uninstall') { if ($idx !== false) unset(App::$plugins[$idx]); uninstall_plugin($p[0]); - set_config("system","addon", implode(", ",App::$plugins)); + set_config('system', 'addon', implode(', ',App::$plugins)); echo $p[0] . ' uninstalled.' . "\n"; } } @@ -135,7 +135,7 @@ if($argc == 3 && $argv[1] === 'uninstall') { if ($idx !== false) unset(App::$plugins[$idx]); uninstall_plugin($argv[2]); - set_config("system","addon", implode(", ",App::$plugins)); + set_config('system', 'addon', implode(', ',App::$plugins)); echo $argv[2] . ' uninstalled.' . "\n"; } diff --git a/util/admins b/util/admins index ecc672a78..8e0fd3f1d 100755 --- a/util/admins +++ b/util/admins @@ -26,7 +26,7 @@ EndOfOutput; } if($argc == 1) { - $r = q("select account_id, account_roles, account_email from account"); + $r = q('select account_id, account_roles, account_email from account'); if($r) { foreach($r as $rr) { echo sprintf('%4u %s %s', $rr['account_id'], $rr['account_email'],(($rr['account_roles'] & 4096) ? '*' : '')) . PHP_EOL; @@ -37,7 +37,7 @@ if($argc == 1) { if($argc > 1 && $argv[1] === 'list') { - $r = q("select account_id, account_roles, account_email from account where (account_roles & 4096) > 0"); + $r = q('select account_id, account_roles, account_email from account where (account_roles & 4096) > 0'); if($r) { foreach($r as $rr) { echo sprintf('%4u %s %s', $rr['account_id'], $rr['account_email'],(($rr['account_roles'] & 4096) ? '*' : '')) . PHP_EOL; @@ -47,13 +47,13 @@ if($argc > 1 && $argv[1] === 'list') { if($argc > 2 && $argv[1] === 'add' && intval($argv[2])) { - $r = q("update account set account_roles = (account_roles | 4096) where account_id = %d", + $r = q('update account set account_roles = (account_roles | 4096) where account_id = %d', intval($argv[2]) ); } if($argc > 2 && $argv[1] === 'remove' && intval($argv[2])) { - $r = q("update account set account_roles = (account_roles - 4096) where account_id = %d and (account_roles & 4096) > 0", + $r = q('update account set account_roles = (account_roles - 4096) where account_id = %d and (account_roles & 4096) > 0', intval($argv[2]) ); } diff --git a/util/config b/util/config index 328b294b4..26e797ce1 100755 --- a/util/config +++ b/util/config @@ -71,7 +71,7 @@ if($argc == 2) { } if($argc == 1) { - $r = q("select * from config where true"); + $r = q('select * from config where true'); if($r) { foreach($r as $rr) { echo "config[{$rr['cat']}][{$rr['k']}] = " . printable_config($rr['v']) . "\n"; diff --git a/util/db_update b/util/db_update index 46a98b4db..9862540be 100755 --- a/util/db_update +++ b/util/db_update @@ -12,12 +12,12 @@ require_once('include/cli_startup.php'); cli_startup(); $build = get_config('system','db_version'); -echo "Old DB VERSION: " . $build . "\n"; -echo "New DB VERSION: " . DB_UPDATE_VERSION . "\n"; +echo 'Old DB VERSION: ' . $build . "\n"; +echo 'New DB VERSION: ' . DB_UPDATE_VERSION . "\n"; if($build != DB_UPDATE_VERSION) { - echo "Updating database..."; + echo 'Updating database...'; check_config(); echo "Done\n"; } diff --git a/util/dcp b/util/dcp index 5ffde886b..a78ac6d1b 100755 --- a/util/dcp +++ b/util/dcp @@ -3,6 +3,8 @@ // file import to DAV utility +use Zotlabs\Daemon\Run; + if(!file_exists('include/cli_startup.php')) { echo 'Run dcp from the top level Hubzilla web directory, as util/dcp ' . PHP_EOL; exit(1); @@ -15,7 +17,7 @@ cli_startup(); if($argc < 3) { - echo "Usage: " . $argv[0] . ' src dstdir' . "\n"; + echo 'Usage: ' . $argv[0] . ' src dstdir' . "\n"; echo 'Always run from the toplevel web directory.' . "\n"; echo 'destination should begin with store/$nickname/desired/path or $nickname/desired/path' . "\n"; echo 'Example: util/dcp /etc/motd store/joe/etc' . "\n"; @@ -106,7 +108,7 @@ if($argc < 3) { else { $dstname = (($isadir) ? '' : basename($dstfile)); $cmd = [ 'Importfile', $channel['channel_id'], $argv[$x], $folder, $dstname ]; - \Zotlabs\Daemon\Run::Summon($cmd); + Run::Summon($cmd); } } @@ -147,7 +149,7 @@ if($argc < 3) { } else { $cmd = [ 'Importfile', $channel['channel_id'], $src . '/' . $entry, $folder ]; - \Zotlabs\Daemon\Run::Summon($cmd); + Run::Summon($cmd); } } closedir($dir); diff --git a/util/dmkdir b/util/dmkdir index a476ca02f..92f01db62 100755 --- a/util/dmkdir +++ b/util/dmkdir @@ -19,7 +19,7 @@ cli_startup(); $dstfile = $argv[1]; if($argc != 2) { - echo "Usage: " . $argv[0] . ' directory' . "\n"; + echo 'Usage: ' . $argv[0] . ' directory' . "\n"; echo 'Always run from the toplevel web directory.' . "\n"; echo 'directory should begin with store/$nickname/desired/path or $nickname/desired/path' . "\n"; echo 'Example: util/dmkdir store/bob/photos/2017' . "\n"; diff --git a/util/docblox_errorchecker.php b/util/docblox_errorchecker.php index 565ac5e1f..259486514 100644 --- a/util/docblox_errorchecker.php +++ b/util/docblox_errorchecker.php @@ -1,4 +1,5 @@ -\n" "Language-Team: LANGUAGE \n" @@ -17,1290 +17,1384 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -#: Zotlabs/Access/PermissionRoles.php:176 +#: Zotlabs/Access/PermissionRoles.php:181 msgid "Social Networking" msgstr "" -#: Zotlabs/Access/PermissionRoles.php:177 +#: Zotlabs/Access/PermissionRoles.php:182 msgid "Social - Normal" msgstr "" -#: Zotlabs/Access/PermissionRoles.php:178 +#: Zotlabs/Access/PermissionRoles.php:183 msgid "Social - Restricted" msgstr "" -#: Zotlabs/Access/PermissionRoles.php:181 +#: Zotlabs/Access/PermissionRoles.php:186 msgid "Community Group" msgstr "" -#: Zotlabs/Access/PermissionRoles.php:182 +#: Zotlabs/Access/PermissionRoles.php:187 msgid "Group - Normal" msgstr "" -#: Zotlabs/Access/PermissionRoles.php:183 +#: Zotlabs/Access/PermissionRoles.php:188 msgid "Group - Restricted" msgstr "" -#: Zotlabs/Access/PermissionRoles.php:184 +#: Zotlabs/Access/PermissionRoles.php:189 msgid "Group - Moderated" msgstr "" -#: Zotlabs/Access/PermissionRoles.php:187 Zotlabs/Module/Directory.php:404 -#: Zotlabs/Module/Manage.php:76 Zotlabs/Module/Manage.php:173 -#: Zotlabs/Storage/Browser.php:145 include/text.php:3113 +#: Zotlabs/Access/PermissionRoles.php:192 Zotlabs/Module/Directory.php:401 +#: Zotlabs/Module/Manage.php:77 Zotlabs/Module/Manage.php:174 +#: Zotlabs/Storage/Browser.php:148 include/text.php:3274 msgid "Collection" msgstr "" -#: Zotlabs/Access/PermissionRoles.php:188 +#: Zotlabs/Access/PermissionRoles.php:193 msgid "Collection - Normal" msgstr "" -#: Zotlabs/Access/PermissionRoles.php:189 +#: Zotlabs/Access/PermissionRoles.php:194 msgid "Collection - Restricted" msgstr "" -#: Zotlabs/Access/Permissions.php:56 +#: Zotlabs/Access/Permissions.php:59 msgid "Grant viewing access to and delivery of your channel stream and posts" msgstr "" -#: Zotlabs/Access/Permissions.php:57 +#: Zotlabs/Access/Permissions.php:60 msgid "Grant viewing access to your default channel profile" msgstr "" -#: Zotlabs/Access/Permissions.php:58 +#: Zotlabs/Access/Permissions.php:61 msgid "Grant viewing access to your address book (connections)" msgstr "" -#: Zotlabs/Access/Permissions.php:59 +#: Zotlabs/Access/Permissions.php:62 msgid "Grant viewing access to your file storage and photos" msgstr "" -#: Zotlabs/Access/Permissions.php:60 +#: Zotlabs/Access/Permissions.php:63 msgid "Grant permission to post on your channel (wall) page" msgstr "" -#: Zotlabs/Access/Permissions.php:61 +#: Zotlabs/Access/Permissions.php:64 msgid "Accept delivery of direct messages and personal mail" msgstr "" -#: Zotlabs/Access/Permissions.php:62 +#: Zotlabs/Access/Permissions.php:65 msgid "Accept delivery of their posts and all comments to their posts" msgstr "" -#: Zotlabs/Access/Permissions.php:63 +#: Zotlabs/Access/Permissions.php:66 msgid "Accept delivery of their comments and likes on your posts" msgstr "" -#: Zotlabs/Access/Permissions.php:64 +#: Zotlabs/Access/Permissions.php:67 msgid "Grant upload permissions to your file storage and photos" msgstr "" -#: Zotlabs/Access/Permissions.php:65 +#: Zotlabs/Access/Permissions.php:68 msgid "Grant permission to republish/mirror your posts" msgstr "" -#: Zotlabs/Access/Permissions.php:66 +#: Zotlabs/Access/Permissions.php:69 msgid "Accept comments and wall posts only after approval (moderation)" msgstr "" -#: Zotlabs/Access/Permissions.php:67 +#: Zotlabs/Access/Permissions.php:70 msgid "Grant channel administration (delegation) permission" msgstr "" -#: Zotlabs/Lib/Libprofile.php:35 -msgid "Requested channel is not available." +#: Zotlabs/Lib/Permcat.php:75 +msgctxt "permcat" +msgid "default" msgstr "" -#: Zotlabs/Lib/Libprofile.php:90 Zotlabs/Module/Cards.php:38 -#: Zotlabs/Module/Blocks.php:37 Zotlabs/Module/Editlayout.php:35 -#: Zotlabs/Module/Editwebpage.php:36 Zotlabs/Module/Hcard.php:15 -#: Zotlabs/Module/Layouts.php:37 Zotlabs/Module/Editblock.php:35 -#: Zotlabs/Module/Menu.php:94 Zotlabs/Module/Connect.php:19 -#: Zotlabs/Module/Webpages.php:40 Zotlabs/Module/Profile.php:27 -#: extend/addon/a/gallery/Mod_Gallery.php:50 -msgid "Requested profile is not available." +#: Zotlabs/Lib/Permcat.php:129 +msgctxt "permcat" +msgid "follower" msgstr "" -#: Zotlabs/Lib/Libprofile.php:186 Zotlabs/Module/Profiles.php:732 -msgid "Change profile photo" +#: Zotlabs/Lib/Permcat.php:133 +msgctxt "permcat" +msgid "contributor" msgstr "" -#: Zotlabs/Lib/Libprofile.php:193 Zotlabs/Module/Profiles.php:838 -#: include/nav.php:119 -msgid "Edit Profiles" +#: Zotlabs/Lib/Permcat.php:137 +msgctxt "permcat" +msgid "publisher" msgstr "" -#: Zotlabs/Lib/Libprofile.php:193 Zotlabs/Lib/Libprofile.php:197 -#: Zotlabs/Lib/ThreadItem.php:146 Zotlabs/Lib/Apps.php:603 -#: Zotlabs/Module/Admin/Profs.php:175 Zotlabs/Module/Blocks.php:164 -#: Zotlabs/Module/Connections.php:318 Zotlabs/Module/Connections.php:357 -#: Zotlabs/Module/Connections.php:380 Zotlabs/Module/Editlayout.php:118 -#: Zotlabs/Module/Editwebpage.php:146 Zotlabs/Module/Thing.php:285 -#: Zotlabs/Module/Lists.php:346 Zotlabs/Module/Layouts.php:199 -#: Zotlabs/Module/Card_edit.php:101 Zotlabs/Module/Editblock.php:118 -#: Zotlabs/Module/Menu.php:178 Zotlabs/Module/Webpages.php:256 -#: Zotlabs/Module/Settings/Oauth.php:150 Zotlabs/Module/Settings/Oauth2.php:196 -#: Zotlabs/Storage/Browser.php:315 Zotlabs/Widget/Cdav.php:138 -#: Zotlabs/Widget/Cdav.php:175 include/menu.php:121 -msgid "Edit" -msgstr "" - -#: Zotlabs/Lib/Libprofile.php:194 -msgid "Create New Profile" -msgstr "" - -#: Zotlabs/Lib/Libprofile.php:197 include/nav.php:121 -msgid "Edit Profile" -msgstr "" - -#: Zotlabs/Lib/Libprofile.php:212 Zotlabs/Module/Profiles.php:828 -msgid "Profile Image" -msgstr "" - -#: Zotlabs/Lib/Libprofile.php:215 -msgid "Visible to everybody" -msgstr "" - -#: Zotlabs/Lib/Libprofile.php:216 Zotlabs/Module/Profiles.php:729 -#: Zotlabs/Module/Profiles.php:832 -msgid "Edit visibility" -msgstr "" - -#: Zotlabs/Lib/Libprofile.php:274 Zotlabs/Module/Directory.php:402 -#: Zotlabs/Module/Fedi_id.php:47 Zotlabs/Widget/Follow.php:36 -#: Zotlabs/Widget/Suggestions.php:47 include/connections.php:133 -#: include/conversation.php:975 -msgid "Connect" -msgstr "" - -#: Zotlabs/Lib/Libprofile.php:289 Zotlabs/Module/Directory.php:384 -#: include/event.php:63 include/event.php:120 -msgid "Location:" -msgstr "" - -#: Zotlabs/Lib/Libprofile.php:293 Zotlabs/Lib/Libprofile.php:451 -msgid "Gender:" -msgstr "" - -#: Zotlabs/Lib/Libprofile.php:294 Zotlabs/Lib/Libprofile.php:499 -msgid "Status:" -msgstr "" - -#: Zotlabs/Lib/Libprofile.php:295 Zotlabs/Lib/Libprofile.php:525 -msgid "Homepage:" -msgstr "" - -#: Zotlabs/Lib/Libprofile.php:296 Zotlabs/Lib/Libprofile.php:523 -msgid "Pronouns:" -msgstr "" - -#: Zotlabs/Lib/Libprofile.php:355 Zotlabs/Module/Connections.php:60 -#: Zotlabs/Module/Connections.php:119 Zotlabs/Module/Connections.php:132 -#: Zotlabs/Module/Connections.php:293 -msgid "Active" -msgstr "" - -#: Zotlabs/Lib/Libprofile.php:359 -msgid "Change your profile photo" -msgstr "" - -#: Zotlabs/Lib/Libprofile.php:360 -msgid "Copy to clipboard" -msgstr "" - -#: Zotlabs/Lib/Libprofile.php:361 -msgid "Address copied to clipboard" -msgstr "" - -#: Zotlabs/Lib/Libprofile.php:388 Zotlabs/Module/Profiles.php:942 -#: Zotlabs/Module/Profiles.php:959 -msgid "Female" -msgstr "" - -#: Zotlabs/Lib/Libprofile.php:390 Zotlabs/Module/Profiles.php:942 -#: Zotlabs/Module/Profiles.php:959 -msgid "Male" -msgstr "" - -#: Zotlabs/Lib/Libprofile.php:392 -msgid "Trans" -msgstr "" - -#: Zotlabs/Lib/Libprofile.php:394 -msgid "Inter" -msgstr "" - -#: Zotlabs/Lib/Libprofile.php:396 Zotlabs/Module/Profiles.php:942 -msgid "Neuter" -msgstr "" - -#: Zotlabs/Lib/Libprofile.php:398 Zotlabs/Module/Profiles.php:942 -msgid "Non-specific" -msgstr "" - -#: Zotlabs/Lib/Libprofile.php:410 -msgid "She" -msgstr "" - -#: Zotlabs/Lib/Libprofile.php:412 -msgid "Him" -msgstr "" - -#: Zotlabs/Lib/Libprofile.php:414 -msgid "Them" -msgstr "" - -#: Zotlabs/Lib/Libprofile.php:449 -msgid "Full Name:" -msgstr "" - -#: Zotlabs/Lib/Libprofile.php:484 -msgid "j F, Y" -msgstr "" - -#: Zotlabs/Lib/Libprofile.php:485 -msgid "j F" -msgstr "" - -#: Zotlabs/Lib/Libprofile.php:492 -msgid "Birthday:" -msgstr "" - -#: Zotlabs/Lib/Libprofile.php:496 Zotlabs/Module/Directory.php:379 -msgid "Age:" -msgstr "" - -#: Zotlabs/Lib/Libprofile.php:505 -#, php-format -msgid "for %1$d %2$s" -msgstr "" - -#: Zotlabs/Lib/Libprofile.php:517 -msgid "Tags:" -msgstr "" - -#: Zotlabs/Lib/Libprofile.php:521 -msgid "Sexual Preference:" -msgstr "" - -#: Zotlabs/Lib/Libprofile.php:527 Zotlabs/Module/Directory.php:399 -msgid "Hometown:" -msgstr "" - -#: Zotlabs/Lib/Libprofile.php:529 -msgid "Political Views:" -msgstr "" - -#: Zotlabs/Lib/Libprofile.php:531 -msgid "Religion:" -msgstr "" - -#: Zotlabs/Lib/Libprofile.php:533 Zotlabs/Module/Directory.php:401 -msgid "About:" -msgstr "" - -#: Zotlabs/Lib/Libprofile.php:535 -msgid "Hobbies/Interests:" -msgstr "" - -#: Zotlabs/Lib/Libprofile.php:537 -msgid "Likes:" -msgstr "" - -#: Zotlabs/Lib/Libprofile.php:539 -msgid "Dislikes:" -msgstr "" - -#: Zotlabs/Lib/Libprofile.php:541 -msgid "Contact information and Social Networks:" -msgstr "" - -#: Zotlabs/Lib/Libprofile.php:543 -msgid "My other channels:" -msgstr "" - -#: Zotlabs/Lib/Libprofile.php:545 -msgid "Musical interests:" -msgstr "" - -#: Zotlabs/Lib/Libprofile.php:547 -msgid "Books, literature:" -msgstr "" - -#: Zotlabs/Lib/Libprofile.php:549 -msgid "Television:" -msgstr "" - -#: Zotlabs/Lib/Libprofile.php:551 -msgid "Film/dance/culture/entertainment:" -msgstr "" - -#: Zotlabs/Lib/Libprofile.php:553 -msgid "Love/Romance:" -msgstr "" - -#: Zotlabs/Lib/Libprofile.php:555 -msgid "Work/employment:" -msgstr "" - -#: Zotlabs/Lib/Libprofile.php:557 -msgid "School/education:" -msgstr "" - -#: Zotlabs/Lib/Libprofile.php:578 Zotlabs/Lib/Activity.php:2894 -#: Zotlabs/Lib/Activity.php:2897 Zotlabs/Lib/Activity.php:2917 -#: Zotlabs/Lib/Apps.php:379 Zotlabs/Module/Profperm.php:117 -msgid "Profile" -msgstr "" - -#: Zotlabs/Lib/Libprofile.php:580 -msgid "Like this thing" -msgstr "" - -#: Zotlabs/Lib/Libprofile.php:581 Zotlabs/Module/Cal.php:346 -#: Zotlabs/Module/Events.php:730 -msgid "Export" -msgstr "" - -#: Zotlabs/Lib/Chatroom.php:25 -msgid "Missing room name" -msgstr "" - -#: Zotlabs/Lib/Chatroom.php:34 -msgid "Duplicate room name" -msgstr "" - -#: Zotlabs/Lib/Chatroom.php:84 Zotlabs/Lib/Chatroom.php:92 -msgid "Invalid room specifier." -msgstr "" - -#: Zotlabs/Lib/Chatroom.php:124 -msgid "Room not found." -msgstr "" - -#: Zotlabs/Lib/Chatroom.php:135 Zotlabs/Module/Calendar.php:263 -#: Zotlabs/Module/Admin/Cover_photo.php:281 -#: Zotlabs/Module/Admin/Cover_photo.php:303 -#: Zotlabs/Module/Admin/Profile_photo.php:307 -#: Zotlabs/Module/Admin/Profile_photo.php:325 Zotlabs/Module/Api.php:32 -#: Zotlabs/Module/Common.php:42 Zotlabs/Module/Block.php:25 -#: Zotlabs/Module/Block.php:75 Zotlabs/Module/Cards.php:82 -#: Zotlabs/Module/Blocks.php:77 Zotlabs/Module/Blocks.php:84 -#: Zotlabs/Module/Connedit.php:330 Zotlabs/Module/Connections.php:33 -#: Zotlabs/Module/Item.php:596 Zotlabs/Module/Item.php:618 -#: Zotlabs/Module/Item.php:629 Zotlabs/Module/Item.php:1738 -#: Zotlabs/Module/Appman.php:87 Zotlabs/Module/Cover_photo.php:342 -#: Zotlabs/Module/Cover_photo.php:355 Zotlabs/Module/Defperms.php:171 -#: Zotlabs/Module/Defperms.php:177 Zotlabs/Module/Display.php:510 -#: Zotlabs/Module/Events.php:295 Zotlabs/Module/Editlayout.php:71 -#: Zotlabs/Module/Editlayout.php:94 Zotlabs/Module/Editwebpage.php:72 -#: Zotlabs/Module/Editwebpage.php:93 Zotlabs/Module/Editwebpage.php:111 -#: Zotlabs/Module/Editwebpage.php:125 Zotlabs/Module/Register.php:82 -#: Zotlabs/Module/Invite.php:99 Zotlabs/Module/Filestorage.php:22 -#: Zotlabs/Module/Filestorage.php:107 Zotlabs/Module/Filestorage.php:125 -#: Zotlabs/Module/Filestorage.php:152 Zotlabs/Module/Filestorage.php:185 -#: Zotlabs/Module/Filestorage.php:193 Zotlabs/Module/Thing.php:299 -#: Zotlabs/Module/Thing.php:319 Zotlabs/Module/Thing.php:360 -#: Zotlabs/Module/Chat.php:107 Zotlabs/Module/Chat.php:112 -#: Zotlabs/Module/Moderate.php:18 Zotlabs/Module/Setup.php:228 -#: Zotlabs/Module/Lists.php:83 Zotlabs/Module/Lists.php:94 -#: Zotlabs/Module/Lists.php:125 Zotlabs/Module/Lists.php:308 -#: Zotlabs/Module/Layouts.php:77 Zotlabs/Module/Layouts.php:84 -#: Zotlabs/Module/Layouts.php:95 Zotlabs/Module/Channel.php:240 -#: Zotlabs/Module/Channel.php:424 Zotlabs/Module/Channel.php:463 -#: Zotlabs/Module/Card_edit.php:53 Zotlabs/Module/Manage.php:15 -#: Zotlabs/Module/Mitem.php:133 Zotlabs/Module/Editblock.php:71 -#: Zotlabs/Module/Menu.php:132 Zotlabs/Module/Menu.php:143 -#: Zotlabs/Module/Editpost.php:20 Zotlabs/Module/Locs.php:98 -#: Zotlabs/Module/Viewsrc.php:22 Zotlabs/Module/New_channel.php:115 -#: Zotlabs/Module/New_channel.php:140 Zotlabs/Module/Notifications.php:11 -#: Zotlabs/Module/Mood.php:125 Zotlabs/Module/Vote.php:19 -#: Zotlabs/Module/Webpages.php:134 Zotlabs/Module/Page.php:38 -#: Zotlabs/Module/Page.php:137 Zotlabs/Module/Pdledit.php:29 -#: Zotlabs/Module/Profiles.php:196 Zotlabs/Module/Profiles.php:639 -#: Zotlabs/Module/Profile_photo.php:344 Zotlabs/Module/Profile_photo.php:358 -#: Zotlabs/Module/Poke.php:111 Zotlabs/Module/Regmod.php:20 -#: Zotlabs/Module/Service_limits.php:11 Zotlabs/Module/Settings.php:66 -#: Zotlabs/Module/Sharedwithme.php:21 Zotlabs/Module/Sources.php:83 -#: Zotlabs/Module/Photos.php:68 Zotlabs/Module/Profile.php:104 -#: Zotlabs/Module/Profile.php:120 Zotlabs/Module/Viewconnections.php:32 -#: Zotlabs/Module/Viewconnections.php:37 Zotlabs/Module/Suggestions.php:31 -#: Zotlabs/Module/Inspect.php:16 Zotlabs/Web/WebServer.php:71 -#: include/photos.php:34 include/attach.php:157 include/attach.php:204 -#: include/attach.php:280 include/attach.php:401 include/attach.php:415 -#: include/attach.php:422 include/attach.php:500 include/attach.php:1083 -#: include/attach.php:1158 include/attach.php:1327 include/items.php:3865 -msgid "Permission denied." -msgstr "" - -#: Zotlabs/Lib/Chatroom.php:145 -msgid "Room is full" -msgstr "" - -#: Zotlabs/Lib/PermissionDescription.php:34 include/acl_selectors.php:34 -msgid "Visible to your default audience" -msgstr "" - -#: Zotlabs/Lib/PermissionDescription.php:107 include/acl_selectors.php:135 -msgid "Only me" -msgstr "" - -#: Zotlabs/Lib/PermissionDescription.php:108 -msgid "Public" -msgstr "" - -#: Zotlabs/Lib/PermissionDescription.php:109 -msgid "Anybody in the $Projectname network" -msgstr "" - -#: Zotlabs/Lib/PermissionDescription.php:110 -#, php-format -msgid "Any account on %s" -msgstr "" - -#: Zotlabs/Lib/PermissionDescription.php:111 -msgid "Any of my connections" -msgstr "" - -#: Zotlabs/Lib/PermissionDescription.php:112 -msgid "Only connections I specifically allow" -msgstr "" - -#: Zotlabs/Lib/PermissionDescription.php:113 -msgid "Anybody authenticated (could include visitors from other networks)" -msgstr "" - -#: Zotlabs/Lib/PermissionDescription.php:114 -msgid "Any connections including those who haven't yet been approved" -msgstr "" - -#: Zotlabs/Lib/PermissionDescription.php:150 -msgid "" -"This is your default setting for the audience of your normal stream, and " -"posts." -msgstr "" - -#: Zotlabs/Lib/PermissionDescription.php:151 -msgid "" -"This is your default setting for who can view your default channel profile" -msgstr "" - -#: Zotlabs/Lib/PermissionDescription.php:152 -msgid "This is your default setting for who can view your connections" -msgstr "" - -#: Zotlabs/Lib/PermissionDescription.php:153 -msgid "" -"This is your default setting for who can view your file storage and photos" -msgstr "" - -#: Zotlabs/Lib/PermissionDescription.php:154 -msgid "This is your default setting for the audience of your webpages" -msgstr "" - -#: Zotlabs/Lib/Libzotdir.php:110 -msgid "Directory Options" -msgstr "" - -#: Zotlabs/Lib/Libzotdir.php:112 include/nav.php:106 -msgid "Safe Mode" -msgstr "" - -#: Zotlabs/Lib/Libzotdir.php:112 Zotlabs/Lib/Libzotdir.php:113 -#: Zotlabs/Lib/Libzotdir.php:116 Zotlabs/Lib/Libzotdir.php:117 -#: Zotlabs/Module/Admin/Site.php:276 Zotlabs/Module/Api.php:107 -#: Zotlabs/Module/Connedit.php:337 Zotlabs/Module/Connedit.php:727 -#: Zotlabs/Module/Connedit.php:801 Zotlabs/Module/Defperms.php:184 -#: Zotlabs/Module/Events.php:501 Zotlabs/Module/Events.php:502 -#: Zotlabs/Module/Events.php:527 Zotlabs/Module/Filestorage.php:236 -#: Zotlabs/Module/Filestorage.php:244 Zotlabs/Module/Mitem.php:180 -#: Zotlabs/Module/Mitem.php:181 Zotlabs/Module/Mitem.php:260 -#: Zotlabs/Module/Mitem.php:261 Zotlabs/Module/Menu.php:165 -#: Zotlabs/Module/Menu.php:224 Zotlabs/Module/Profiles.php:685 -#: Zotlabs/Module/Zot_probe.php:17 Zotlabs/Module/Ap_probe.php:20 -#: Zotlabs/Module/Settings/Display.php:103 -#: Zotlabs/Module/Settings/Channel.php:379 Zotlabs/Module/Sources.php:122 -#: Zotlabs/Module/Sources.php:157 Zotlabs/Module/Photos.php:722 -#: Zotlabs/Module/Import.php:698 Zotlabs/Module/Import.php:702 -#: Zotlabs/Module/Import.php:703 Zotlabs/Storage/Browser.php:432 boot.php:1723 -#: extend/addon/a/zotpost/Mod_zotpost.php:77 -#: extend/addon/a/content_import/Mod_content_import.php:137 -#: extend/addon/a/content_import/Mod_content_import.php:138 -#: include/conversation.php:1409 include/conversation.php:1414 -#: view/theme/redbasic/php/config.php:98 -msgid "No" -msgstr "" - -#: Zotlabs/Lib/Libzotdir.php:112 Zotlabs/Lib/Libzotdir.php:113 -#: Zotlabs/Lib/Libzotdir.php:116 Zotlabs/Lib/Libzotdir.php:117 -#: Zotlabs/Module/Admin/Site.php:278 Zotlabs/Module/Api.php:106 -#: Zotlabs/Module/Connedit.php:337 Zotlabs/Module/Connedit.php:727 -#: Zotlabs/Module/Connedit.php:801 Zotlabs/Module/Defperms.php:184 -#: Zotlabs/Module/Events.php:501 Zotlabs/Module/Events.php:502 -#: Zotlabs/Module/Events.php:527 Zotlabs/Module/Filestorage.php:236 -#: Zotlabs/Module/Filestorage.php:244 Zotlabs/Module/Mitem.php:180 -#: Zotlabs/Module/Mitem.php:181 Zotlabs/Module/Mitem.php:260 -#: Zotlabs/Module/Mitem.php:261 Zotlabs/Module/Menu.php:165 -#: Zotlabs/Module/Menu.php:224 Zotlabs/Module/Profiles.php:685 -#: Zotlabs/Module/Zot_probe.php:17 Zotlabs/Module/Ap_probe.php:20 -#: Zotlabs/Module/Settings/Display.php:103 -#: Zotlabs/Module/Settings/Channel.php:379 Zotlabs/Module/Sources.php:122 -#: Zotlabs/Module/Sources.php:157 Zotlabs/Module/Photos.php:722 -#: Zotlabs/Module/Import.php:698 Zotlabs/Module/Import.php:702 -#: Zotlabs/Module/Import.php:703 Zotlabs/Storage/Browser.php:432 boot.php:1723 -#: extend/addon/a/zotpost/Mod_zotpost.php:77 -#: extend/addon/a/content_import/Mod_content_import.php:137 -#: extend/addon/a/content_import/Mod_content_import.php:138 -#: include/conversation.php:1409 include/conversation.php:1414 -#: view/theme/redbasic/php/config.php:98 -msgid "Yes" -msgstr "" - -#: Zotlabs/Lib/Libzotdir.php:113 -msgid "Groups Only" -msgstr "" - -#: Zotlabs/Lib/Libzotdir.php:116 -msgid "This Website Only" -msgstr "" - -#: Zotlabs/Lib/Libzotdir.php:117 -msgid "Recently Updated" -msgstr "" - -#: Zotlabs/Lib/Libzot.php:743 +#: Zotlabs/Lib/Libzot.php:761 msgid "Unable to verify channel signature" msgstr "" -#: Zotlabs/Lib/Enotify.php:66 -msgid "$Projectname Notification" -msgstr "" - -#: Zotlabs/Lib/Enotify.php:67 -msgid "$projectname" -msgstr "" - -#: Zotlabs/Lib/Enotify.php:69 -msgid "Thank You," -msgstr "" - -#: Zotlabs/Lib/Enotify.php:71 -#, php-format -msgid "%s Administrator" -msgstr "" - -#: Zotlabs/Lib/Enotify.php:72 -#, php-format -msgid "This email was sent by %1$s at %2$s." -msgstr "" - -#: Zotlabs/Lib/Enotify.php:72 Zotlabs/Module/Home.php:140 -#: Zotlabs/Module/Home.php:149 extend/addon/a/opensearch/Mod_opensearch.php:28 -msgid "$Projectname" -msgstr "" - -#: Zotlabs/Lib/Enotify.php:73 -#, php-format -msgid "" -"To stop receiving these messages, please adjust your Notification Settings " -"at %s" -msgstr "" - -#: Zotlabs/Lib/Enotify.php:74 -#, php-format -msgid "To stop receiving these messages, please adjust your %s." -msgstr "" - -#: Zotlabs/Lib/Enotify.php:74 -msgid "Notification Settings" -msgstr "" - -#: Zotlabs/Lib/Enotify.php:129 -#, php-format -msgid "%s " -msgstr "" - -#: Zotlabs/Lib/Enotify.php:133 -#, php-format -msgid "[$Projectname:Notify] New mail received at %s" -msgstr "" - -#: Zotlabs/Lib/Enotify.php:136 -#, php-format -msgid "%1$s sent you a new private message at %2$s." -msgstr "" - -#: Zotlabs/Lib/Enotify.php:137 -#, php-format -msgid "%1$s sent you %2$s." -msgstr "" - -#: Zotlabs/Lib/Enotify.php:137 Zotlabs/Lib/Enotify.php:141 -msgid "a private message" -msgstr "" - -#: Zotlabs/Lib/Enotify.php:140 -#, php-format -msgid "%1$s replied to a private message at %2$s." -msgstr "" - -#: Zotlabs/Lib/Enotify.php:141 -#, php-format -msgid "%1$s replied to %2$s." -msgstr "" - -#: Zotlabs/Lib/Enotify.php:143 -#, php-format -msgid "Please visit %s to view and/or reply to your private messages." -msgstr "" - -#: Zotlabs/Lib/Enotify.php:157 -msgid "commented on" -msgstr "" - -#: Zotlabs/Lib/Enotify.php:168 -msgid "liked" -msgstr "" - -#: Zotlabs/Lib/Enotify.php:171 -msgid "disliked" -msgstr "" - -#: Zotlabs/Lib/Enotify.php:214 -#, php-format -msgid "%1$s %2$s [zrl=%3$s]a %4$s[/zrl]" -msgstr "" - -#: Zotlabs/Lib/Enotify.php:222 -#, php-format -msgid "%1$s %2$s [zrl=%3$s]%4$s's %5$s[/zrl]" -msgstr "" - -#: Zotlabs/Lib/Enotify.php:231 -#, php-format -msgid "%1$s %2$s [zrl=%3$s]your %4$s[/zrl]" -msgstr "" - -#: Zotlabs/Lib/Enotify.php:243 -#, php-format -msgid "[$Projectname:Notify] Moderated Comment to conversation #%1$d by %2$s" -msgstr "" - -#: Zotlabs/Lib/Enotify.php:247 -#, php-format -msgid "[$Projectname:Notify] Comment to conversation #%1$d by %2$s" -msgstr "" - -#: Zotlabs/Lib/Enotify.php:248 -#, php-format -msgid "%1$s commented on an item/conversation you have been following." -msgstr "" - -#: Zotlabs/Lib/Enotify.php:252 -msgid "(Moderated)" -msgstr "" - -#: Zotlabs/Lib/Enotify.php:255 Zotlabs/Lib/Enotify.php:336 -#: Zotlabs/Lib/Enotify.php:362 Zotlabs/Lib/Enotify.php:392 -#: Zotlabs/Lib/Enotify.php:409 Zotlabs/Lib/Enotify.php:422 -#, php-format -msgid "Please visit %s to view and/or reply to the conversation." -msgstr "" - -#: Zotlabs/Lib/Enotify.php:259 Zotlabs/Lib/Enotify.php:260 -#, php-format -msgid "Please visit %s to approve or reject this comment." -msgstr "" - -#: Zotlabs/Lib/Enotify.php:318 -#, php-format -msgid "%1$s liked [zrl=%2$s]your %3$s[/zrl]" -msgstr "" - -#: Zotlabs/Lib/Enotify.php:332 -#, php-format -msgid "[$Projectname:Notify] Like received to conversation #%1$d by %2$s" -msgstr "" - -#: Zotlabs/Lib/Enotify.php:333 -#, php-format -msgid "%1$s liked an item/conversation you created." -msgstr "" - -#: Zotlabs/Lib/Enotify.php:344 -#, php-format -msgid "[$Projectname:Notify] %s posted to your profile wall" -msgstr "" - -#: Zotlabs/Lib/Enotify.php:350 -#, php-format -msgid "%1$s posted to your profile wall at %2$s" -msgstr "" - -#: Zotlabs/Lib/Enotify.php:352 -#, php-format -msgid "%1$s posted to [zrl=%2$s]your wall[/zrl]" -msgstr "" - -#: Zotlabs/Lib/Enotify.php:358 Zotlabs/Lib/Enotify.php:359 -msgid " - " -msgstr "" - -#: Zotlabs/Lib/Enotify.php:358 Zotlabs/Lib/Enotify.php:359 -msgid "Moderated" -msgstr "" - -#: Zotlabs/Lib/Enotify.php:367 Zotlabs/Lib/Enotify.php:368 -#, php-format -msgid "Please visit %s to approve or reject this post." -msgstr "" - -#: Zotlabs/Lib/Enotify.php:386 -#, php-format -msgid "[$Projectname:Notify] %s tagged you" -msgstr "" - -#: Zotlabs/Lib/Enotify.php:387 -#, php-format -msgid "%1$s tagged you at %2$s" -msgstr "" - -#: Zotlabs/Lib/Enotify.php:388 -#, php-format -msgid "%1$s [zrl=%2$s]tagged you[/zrl]." -msgstr "" - -#: Zotlabs/Lib/Enotify.php:399 -#, php-format -msgid "[$Projectname:Notify] %1$s poked you" -msgstr "" - -#: Zotlabs/Lib/Enotify.php:400 -#, php-format -msgid "%1$s poked you at %2$s" -msgstr "" - -#: Zotlabs/Lib/Enotify.php:401 -#, php-format -msgid "%1$s [zrl=%2$s]poked you[/zrl]." -msgstr "" - -#: Zotlabs/Lib/Enotify.php:416 -#, php-format -msgid "[$Projectname:Notify] %s tagged your post" -msgstr "" - -#: Zotlabs/Lib/Enotify.php:417 -#, php-format -msgid "%1$s tagged your post at %2$s" -msgstr "" - -#: Zotlabs/Lib/Enotify.php:418 -#, php-format -msgid "%1$s tagged [zrl=%2$s]your post[/zrl]" -msgstr "" - -#: Zotlabs/Lib/Enotify.php:429 -msgid "[$Projectname:Notify] Introduction received" -msgstr "" - -#: Zotlabs/Lib/Enotify.php:430 -#, php-format -msgid "You've received an new connection request from '%1$s' at %2$s" -msgstr "" - -#: Zotlabs/Lib/Enotify.php:431 -#, php-format -msgid "You've received [zrl=%1$s]a new connection request[/zrl] from %2$s." -msgstr "" - -#: Zotlabs/Lib/Enotify.php:434 Zotlabs/Lib/Enotify.php:452 -#, php-format -msgid "You may visit their profile at %s" -msgstr "" - -#: Zotlabs/Lib/Enotify.php:436 -#, php-format -msgid "Please visit %s to approve or reject the connection request." -msgstr "" - -#: Zotlabs/Lib/Enotify.php:443 -msgid "[$Projectname:Notify] Friend suggestion received" -msgstr "" - -#: Zotlabs/Lib/Enotify.php:444 -#, php-format -msgid "You've received a friend suggestion from '%1$s' at %2$s" -msgstr "" - -#: Zotlabs/Lib/Enotify.php:445 -#, php-format -msgid "You've received [zrl=%1$s]a friend suggestion[/zrl] for %2$s from %3$s." -msgstr "" - -#: Zotlabs/Lib/Enotify.php:450 -msgid "Name:" -msgstr "" - -#: Zotlabs/Lib/Enotify.php:451 -msgid "Photo:" -msgstr "" - -#: Zotlabs/Lib/Enotify.php:454 -#, php-format -msgid "Please visit %s to approve or reject the suggestion." -msgstr "" - -#: Zotlabs/Lib/Enotify.php:705 -msgid "[$Projectname:Notify]" -msgstr "" - -#: Zotlabs/Lib/Enotify.php:888 -msgid "created a new post" -msgstr "" - -#: Zotlabs/Lib/Enotify.php:889 -#, php-format -msgid "reacted to %s's conversation" -msgstr "" - -#: Zotlabs/Lib/Enotify.php:891 -#, php-format -msgid "shared %s's post" -msgstr "" - -#: Zotlabs/Lib/Enotify.php:895 -msgid "sent a direct message" -msgstr "" - -#: Zotlabs/Lib/Enotify.php:902 -#, php-format -msgid "edited a post dated %s" -msgstr "" - -#: Zotlabs/Lib/Enotify.php:906 -#, php-format -msgid "edited a comment dated %s" -msgstr "" - -#: Zotlabs/Lib/Share.php:122 Zotlabs/Lib/Activity.php:2917 -#: Zotlabs/Module/Share.php:103 include/items.php:3313 +#: Zotlabs/Lib/Chatroom.php:28 +msgid "Missing room name" +msgstr "" + +#: Zotlabs/Lib/Chatroom.php:38 +msgid "Duplicate room name" +msgstr "" + +#: Zotlabs/Lib/Chatroom.php:94 Zotlabs/Lib/Chatroom.php:103 +msgid "Invalid room specifier." +msgstr "" + +#: Zotlabs/Lib/Chatroom.php:141 +msgid "Room not found." +msgstr "" + +#: Zotlabs/Lib/Chatroom.php:153 Zotlabs/Module/Calendar.php:264 +#: Zotlabs/Module/Admin/Cover_photo.php:279 +#: Zotlabs/Module/Admin/Cover_photo.php:303 +#: Zotlabs/Module/Admin/Profile_photo.php:308 +#: Zotlabs/Module/Admin/Profile_photo.php:326 Zotlabs/Module/Api.php:35 +#: Zotlabs/Module/Common.php:48 Zotlabs/Module/Block.php:31 +#: Zotlabs/Module/Block.php:84 Zotlabs/Module/Cards.php:86 +#: Zotlabs/Module/Blocks.php:82 Zotlabs/Module/Blocks.php:89 +#: Zotlabs/Module/Connedit.php:338 Zotlabs/Module/Connections.php:36 +#: Zotlabs/Module/Item.php:611 Zotlabs/Module/Item.php:633 +#: Zotlabs/Module/Item.php:645 Zotlabs/Module/Item.php:1786 +#: Zotlabs/Module/Appman.php:91 Zotlabs/Module/Cover_photo.php:336 +#: Zotlabs/Module/Cover_photo.php:350 Zotlabs/Module/Defperms.php:179 +#: Zotlabs/Module/Defperms.php:185 Zotlabs/Module/Display.php:509 +#: Zotlabs/Module/Events.php:305 Zotlabs/Module/Editlayout.php:76 +#: Zotlabs/Module/Editlayout.php:99 Zotlabs/Module/Editwebpage.php:79 +#: Zotlabs/Module/Editwebpage.php:100 Zotlabs/Module/Editwebpage.php:119 +#: Zotlabs/Module/Editwebpage.php:135 Zotlabs/Module/Register.php:87 +#: Zotlabs/Module/Invite.php:99 Zotlabs/Module/Filestorage.php:25 +#: Zotlabs/Module/Filestorage.php:111 Zotlabs/Module/Filestorage.php:127 +#: Zotlabs/Module/Filestorage.php:155 Zotlabs/Module/Filestorage.php:188 +#: Zotlabs/Module/Filestorage.php:196 Zotlabs/Module/Thing.php:315 +#: Zotlabs/Module/Thing.php:336 Zotlabs/Module/Thing.php:378 +#: Zotlabs/Module/Chat.php:111 Zotlabs/Module/Chat.php:116 +#: Zotlabs/Module/Moderate.php:20 Zotlabs/Module/Setup.php:230 +#: Zotlabs/Module/Lists.php:84 Zotlabs/Module/Lists.php:96 +#: Zotlabs/Module/Lists.php:127 Zotlabs/Module/Layouts.php:80 +#: Zotlabs/Module/Layouts.php:87 Zotlabs/Module/Layouts.php:98 +#: Zotlabs/Module/Channel.php:236 Zotlabs/Module/Channel.php:414 +#: Zotlabs/Module/Channel.php:452 Zotlabs/Module/Card_edit.php:61 +#: Zotlabs/Module/Manage.php:18 Zotlabs/Module/Mitem.php:140 +#: Zotlabs/Module/Editblock.php:77 Zotlabs/Module/Menu.php:135 +#: Zotlabs/Module/Menu.php:146 Zotlabs/Module/Editpost.php:23 +#: Zotlabs/Module/Locs.php:107 Zotlabs/Module/Viewsrc.php:23 +#: Zotlabs/Module/New_channel.php:122 Zotlabs/Module/New_channel.php:147 +#: Zotlabs/Module/Notifications.php:16 Zotlabs/Module/Mood.php:132 +#: Zotlabs/Module/Vote.php:22 Zotlabs/Module/Webpages.php:139 +#: Zotlabs/Module/Page.php:41 Zotlabs/Module/Page.php:146 +#: Zotlabs/Module/Pdledit.php:36 Zotlabs/Module/Profiles.php:213 +#: Zotlabs/Module/Profiles.php:674 Zotlabs/Module/Profile_photo.php:344 +#: Zotlabs/Module/Profile_photo.php:358 Zotlabs/Module/Poke.php:115 +#: Zotlabs/Module/Regmod.php:24 Zotlabs/Module/Service_limits.php:16 +#: Zotlabs/Module/Settings.php:70 Zotlabs/Module/Sharedwithme.php:22 +#: Zotlabs/Module/Sources.php:89 Zotlabs/Module/Photos.php:70 +#: Zotlabs/Module/Profile.php:108 Zotlabs/Module/Profile.php:123 +#: Zotlabs/Module/Suggestions.php:36 Zotlabs/Module/Viewconnections.php:35 +#: Zotlabs/Module/Viewconnections.php:40 Zotlabs/Module/Inspect.php:18 +#: Zotlabs/Web/WebServer.php:70 include/attach.php:160 include/attach.php:209 +#: include/attach.php:287 include/attach.php:413 include/attach.php:428 +#: include/attach.php:435 include/attach.php:514 include/attach.php:1131 +#: include/attach.php:1208 include/attach.php:1379 include/photos.php:36 +#: include/items.php:3996 +msgid "Permission denied." +msgstr "" + +#: Zotlabs/Lib/Chatroom.php:164 +msgid "Room is full" +msgstr "" + +#: Zotlabs/Lib/Share.php:124 Zotlabs/Lib/Activity.php:2905 +#: Zotlabs/Module/Share.php:118 include/items.php:3413 #, php-format msgid "🔁 Repeated %1$s's %2$s" msgstr "" -#: Zotlabs/Lib/AccessList.php:28 +#: Zotlabs/Lib/Libsync.php:969 +#, php-format +msgid "Unable to verify site signature for %s" +msgstr "" + +#: Zotlabs/Lib/DB_Upgrade.php:68 +msgid "Source code of failed update: " +msgstr "" + +#: Zotlabs/Lib/DB_Upgrade.php:91 +#, php-format +msgid "Update Error at %s" +msgstr "" + +#: Zotlabs/Lib/DB_Upgrade.php:98 +#, php-format +msgid "Update %s failed. See error logs." +msgstr "" + +#: Zotlabs/Lib/Libzotdir.php:118 +msgid "Directory Options" +msgstr "" + +#: Zotlabs/Lib/Libzotdir.php:120 include/nav.php:107 +msgid "Safe Mode" +msgstr "" + +#: Zotlabs/Lib/Libzotdir.php:120 Zotlabs/Lib/Libzotdir.php:121 +#: Zotlabs/Lib/Libzotdir.php:124 Zotlabs/Lib/Libzotdir.php:125 +#: Zotlabs/Module/Admin/Site.php:287 Zotlabs/Module/Api.php:110 +#: Zotlabs/Module/Connedit.php:345 Zotlabs/Module/Connedit.php:732 +#: Zotlabs/Module/Connedit.php:812 Zotlabs/Module/Defperms.php:192 +#: Zotlabs/Module/Events.php:518 Zotlabs/Module/Events.php:519 +#: Zotlabs/Module/Events.php:544 Zotlabs/Module/Filestorage.php:240 +#: Zotlabs/Module/Filestorage.php:248 Zotlabs/Module/Mitem.php:188 +#: Zotlabs/Module/Mitem.php:189 Zotlabs/Module/Mitem.php:268 +#: Zotlabs/Module/Mitem.php:269 Zotlabs/Module/Menu.php:168 +#: Zotlabs/Module/Menu.php:226 Zotlabs/Module/Profiles.php:724 +#: Zotlabs/Module/Zot_probe.php:20 Zotlabs/Module/Ap_probe.php:22 +#: Zotlabs/Module/Settings/Display.php:113 +#: Zotlabs/Module/Settings/Channel.php:413 Zotlabs/Module/Sources.php:129 +#: Zotlabs/Module/Sources.php:165 Zotlabs/Module/Photos.php:730 +#: Zotlabs/Module/Import.php:692 Zotlabs/Module/Import.php:696 +#: Zotlabs/Module/Import.php:697 Zotlabs/Storage/Browser.php:435 boot.php:1731 +#: extend/addon/a/zotpost/Mod_zotpost.php:77 +#: extend/addon/a/content_import/Mod_content_import.php:137 +#: extend/addon/a/content_import/Mod_content_import.php:138 +#: include/conversation.php:1434 include/conversation.php:1439 +#: view/theme/redbasic/php/config.php:98 +msgid "No" +msgstr "" + +#: Zotlabs/Lib/Libzotdir.php:120 Zotlabs/Lib/Libzotdir.php:121 +#: Zotlabs/Lib/Libzotdir.php:124 Zotlabs/Lib/Libzotdir.php:125 +#: Zotlabs/Module/Admin/Site.php:289 Zotlabs/Module/Api.php:109 +#: Zotlabs/Module/Connedit.php:345 Zotlabs/Module/Connedit.php:732 +#: Zotlabs/Module/Connedit.php:812 Zotlabs/Module/Defperms.php:192 +#: Zotlabs/Module/Events.php:518 Zotlabs/Module/Events.php:519 +#: Zotlabs/Module/Events.php:544 Zotlabs/Module/Filestorage.php:240 +#: Zotlabs/Module/Filestorage.php:248 Zotlabs/Module/Mitem.php:188 +#: Zotlabs/Module/Mitem.php:189 Zotlabs/Module/Mitem.php:268 +#: Zotlabs/Module/Mitem.php:269 Zotlabs/Module/Menu.php:168 +#: Zotlabs/Module/Menu.php:226 Zotlabs/Module/Profiles.php:724 +#: Zotlabs/Module/Zot_probe.php:20 Zotlabs/Module/Ap_probe.php:22 +#: Zotlabs/Module/Settings/Display.php:113 +#: Zotlabs/Module/Settings/Channel.php:413 Zotlabs/Module/Sources.php:129 +#: Zotlabs/Module/Sources.php:165 Zotlabs/Module/Photos.php:730 +#: Zotlabs/Module/Import.php:692 Zotlabs/Module/Import.php:696 +#: Zotlabs/Module/Import.php:697 Zotlabs/Storage/Browser.php:435 boot.php:1731 +#: extend/addon/a/zotpost/Mod_zotpost.php:77 +#: extend/addon/a/content_import/Mod_content_import.php:137 +#: extend/addon/a/content_import/Mod_content_import.php:138 +#: include/conversation.php:1434 include/conversation.php:1439 +#: view/theme/redbasic/php/config.php:98 +msgid "Yes" +msgstr "" + +#: Zotlabs/Lib/Libzotdir.php:121 +msgid "Groups Only" +msgstr "" + +#: Zotlabs/Lib/Libzotdir.php:124 +msgid "This Website Only" +msgstr "" + +#: Zotlabs/Lib/Libzotdir.php:125 +msgid "Recently Updated" +msgstr "" + +#: Zotlabs/Lib/AccessList.php:29 msgid "" "A deleted list with this name was revived. Existing item permissions " "may apply to this list and any future members. If this is " "not what you intended, please create another list with a different name." msgstr "" -#: Zotlabs/Lib/AccessList.php:322 +#: Zotlabs/Lib/AccessList.php:345 msgid "Add new connections to this access list" msgstr "" -#: Zotlabs/Lib/AccessList.php:347 +#: Zotlabs/Lib/AccessList.php:372 msgid "edit" msgstr "" -#: Zotlabs/Lib/AccessList.php:367 Zotlabs/Lib/Apps.php:359 -#: Zotlabs/Widget/Activity_filter.php:172 include/nav.php:102 +#: Zotlabs/Lib/AccessList.php:391 Zotlabs/Lib/Apps.php:371 +#: Zotlabs/Widget/Activity_filter.php:174 include/nav.php:103 msgid "Lists" msgstr "" -#: Zotlabs/Lib/AccessList.php:368 +#: Zotlabs/Lib/AccessList.php:392 msgid "Edit list" msgstr "" -#: Zotlabs/Lib/AccessList.php:369 +#: Zotlabs/Lib/AccessList.php:393 msgid "Create new list" msgstr "" -#: Zotlabs/Lib/AccessList.php:370 +#: Zotlabs/Lib/AccessList.php:394 msgid "Channels not in any access list" msgstr "" -#: Zotlabs/Lib/AccessList.php:372 Zotlabs/Widget/Savedsearch.php:84 +#: Zotlabs/Lib/AccessList.php:396 Zotlabs/Widget/Savedsearch.php:95 msgid "add" msgstr "" -#: Zotlabs/Lib/DB_Upgrade.php:67 -msgid "Source code of failed update: " +#: Zotlabs/Lib/Enotify.php:70 +msgid "$Projectname Notification" msgstr "" -#: Zotlabs/Lib/DB_Upgrade.php:88 +#: Zotlabs/Lib/Enotify.php:71 +msgid "$projectname" +msgstr "" + +#: Zotlabs/Lib/Enotify.php:73 +msgid "Thank You," +msgstr "" + +#: Zotlabs/Lib/Enotify.php:75 #, php-format -msgid "Update Error at %s" +msgid "%s Administrator" msgstr "" -#: Zotlabs/Lib/DB_Upgrade.php:94 +#: Zotlabs/Lib/Enotify.php:76 #, php-format -msgid "Update %s failed. See error logs." +msgid "This email was sent by %1$s at %2$s." msgstr "" -#: Zotlabs/Lib/Permcat.php:73 -msgctxt "permcat" -msgid "default" +#: Zotlabs/Lib/Enotify.php:76 Zotlabs/Module/Home.php:142 +#: Zotlabs/Module/Home.php:151 extend/addon/a/opensearch/Mod_opensearch.php:28 +msgid "$Projectname" msgstr "" -#: Zotlabs/Lib/Permcat.php:124 -msgctxt "permcat" -msgid "follower" +#: Zotlabs/Lib/Enotify.php:77 +#, php-format +msgid "" +"To stop receiving these messages, please adjust your Notification Settings " +"at %s" msgstr "" -#: Zotlabs/Lib/Permcat.php:128 -msgctxt "permcat" -msgid "contributor" +#: Zotlabs/Lib/Enotify.php:78 +#, php-format +msgid "To stop receiving these messages, please adjust your %s." msgstr "" -#: Zotlabs/Lib/Permcat.php:132 -msgctxt "permcat" -msgid "publisher" +#: Zotlabs/Lib/Enotify.php:78 +msgid "Notification Settings" msgstr "" -#: Zotlabs/Lib/Activity.php:2214 Zotlabs/Lib/Apps.php:1101 -#: Zotlabs/Lib/Apps.php:1190 Zotlabs/Module/Cdav.php:853 -#: Zotlabs/Module/Cdav.php:854 Zotlabs/Module/Cdav.php:861 -#: Zotlabs/Module/Embedphotos.php:216 Zotlabs/Module/Photos.php:848 -#: Zotlabs/Module/Photos.php:1320 Zotlabs/Storage/Browser.php:179 -#: Zotlabs/Widget/Portfolio.php:95 Zotlabs/Widget/Album.php:88 -#: include/conversation.php:1083 +#: Zotlabs/Lib/Enotify.php:135 +#, php-format +msgid "%s " +msgstr "" + +#: Zotlabs/Lib/Enotify.php:139 +#, php-format +msgid "[$Projectname:Notify] New mail received at %s" +msgstr "" + +#: Zotlabs/Lib/Enotify.php:142 +#, php-format +msgid "%1$s sent you a new private message at %2$s." +msgstr "" + +#: Zotlabs/Lib/Enotify.php:143 +#, php-format +msgid "%1$s sent you %2$s." +msgstr "" + +#: Zotlabs/Lib/Enotify.php:143 Zotlabs/Lib/Enotify.php:146 +msgid "a private message" +msgstr "" + +#: Zotlabs/Lib/Enotify.php:145 +#, php-format +msgid "%1$s replied to a private message at %2$s." +msgstr "" + +#: Zotlabs/Lib/Enotify.php:146 +#, php-format +msgid "%1$s replied to %2$s." +msgstr "" + +#: Zotlabs/Lib/Enotify.php:148 +#, php-format +msgid "Please visit %s to view and/or reply to your private messages." +msgstr "" + +#: Zotlabs/Lib/Enotify.php:162 +msgid "commented on" +msgstr "" + +#: Zotlabs/Lib/Enotify.php:172 +msgid "liked" +msgstr "" + +#: Zotlabs/Lib/Enotify.php:176 +msgid "disliked" +msgstr "" + +#: Zotlabs/Lib/Enotify.php:222 +#, php-format +msgid "%1$s %2$s [zrl=%3$s]a %4$s[/zrl]" +msgstr "" + +#: Zotlabs/Lib/Enotify.php:232 +#, php-format +msgid "%1$s %2$s [zrl=%3$s]%4$s's %5$s[/zrl]" +msgstr "" + +#: Zotlabs/Lib/Enotify.php:244 +#, php-format +msgid "%1$s %2$s [zrl=%3$s]your %4$s[/zrl]" +msgstr "" + +#: Zotlabs/Lib/Enotify.php:258 +#, php-format +msgid "[$Projectname:Notify] Moderated Comment to conversation #%1$d by %2$s" +msgstr "" + +#: Zotlabs/Lib/Enotify.php:261 +#, php-format +msgid "[$Projectname:Notify] Comment to conversation #%1$d by %2$s" +msgstr "" + +#: Zotlabs/Lib/Enotify.php:263 +#, php-format +msgid "%1$s commented on an item/conversation you have been following." +msgstr "" + +#: Zotlabs/Lib/Enotify.php:267 +msgid "(Moderated)" +msgstr "" + +#: Zotlabs/Lib/Enotify.php:270 Zotlabs/Lib/Enotify.php:354 +#: Zotlabs/Lib/Enotify.php:382 Zotlabs/Lib/Enotify.php:413 +#: Zotlabs/Lib/Enotify.php:432 Zotlabs/Lib/Enotify.php:447 +#, php-format +msgid "Please visit %s to view and/or reply to the conversation." +msgstr "" + +#: Zotlabs/Lib/Enotify.php:274 Zotlabs/Lib/Enotify.php:275 +#, php-format +msgid "Please visit %s to approve or reject this comment." +msgstr "" + +#: Zotlabs/Lib/Enotify.php:335 +#, php-format +msgid "%1$s liked [zrl=%2$s]your %3$s[/zrl]" +msgstr "" + +#: Zotlabs/Lib/Enotify.php:350 +#, php-format +msgid "[$Projectname:Notify] Like received to conversation #%1$d by %2$s" +msgstr "" + +#: Zotlabs/Lib/Enotify.php:351 +#, php-format +msgid "%1$s liked an item/conversation you created." +msgstr "" + +#: Zotlabs/Lib/Enotify.php:362 +#, php-format +msgid "[$Projectname:Notify] %s posted to your profile wall" +msgstr "" + +#: Zotlabs/Lib/Enotify.php:368 +#, php-format +msgid "%1$s posted to your profile wall at %2$s" +msgstr "" + +#: Zotlabs/Lib/Enotify.php:371 +#, php-format +msgid "%1$s posted to [zrl=%2$s]your wall[/zrl]" +msgstr "" + +#: Zotlabs/Lib/Enotify.php:378 Zotlabs/Lib/Enotify.php:379 +msgid " - " +msgstr "" + +#: Zotlabs/Lib/Enotify.php:378 Zotlabs/Lib/Enotify.php:379 +msgid "Moderated" +msgstr "" + +#: Zotlabs/Lib/Enotify.php:387 Zotlabs/Lib/Enotify.php:388 +#, php-format +msgid "Please visit %s to approve or reject this post." +msgstr "" + +#: Zotlabs/Lib/Enotify.php:405 +#, php-format +msgid "[$Projectname:Notify] %s tagged you" +msgstr "" + +#: Zotlabs/Lib/Enotify.php:406 +#, php-format +msgid "%1$s tagged you at %2$s" +msgstr "" + +#: Zotlabs/Lib/Enotify.php:408 +#, php-format +msgid "%1$s [zrl=%2$s]tagged you[/zrl]." +msgstr "" + +#: Zotlabs/Lib/Enotify.php:420 +#, php-format +msgid "[$Projectname:Notify] %1$s poked you" +msgstr "" + +#: Zotlabs/Lib/Enotify.php:421 +#, php-format +msgid "%1$s poked you at %2$s" +msgstr "" + +#: Zotlabs/Lib/Enotify.php:423 +#, php-format +msgid "%1$s [zrl=%2$s]poked you[/zrl]." +msgstr "" + +#: Zotlabs/Lib/Enotify.php:439 +#, php-format +msgid "[$Projectname:Notify] %s tagged your post" +msgstr "" + +#: Zotlabs/Lib/Enotify.php:440 +#, php-format +msgid "%1$s tagged your post at %2$s" +msgstr "" + +#: Zotlabs/Lib/Enotify.php:442 +#, php-format +msgid "%1$s tagged [zrl=%2$s]your post[/zrl]" +msgstr "" + +#: Zotlabs/Lib/Enotify.php:454 +msgid "[$Projectname:Notify] Introduction received" +msgstr "" + +#: Zotlabs/Lib/Enotify.php:455 +#, php-format +msgid "You've received an new connection request from '%1$s' at %2$s" +msgstr "" + +#: Zotlabs/Lib/Enotify.php:457 +#, php-format +msgid "You've received [zrl=%1$s]a new connection request[/zrl] from %2$s." +msgstr "" + +#: Zotlabs/Lib/Enotify.php:461 Zotlabs/Lib/Enotify.php:481 +#, php-format +msgid "You may visit their profile at %s" +msgstr "" + +#: Zotlabs/Lib/Enotify.php:463 +#, php-format +msgid "Please visit %s to approve or reject the connection request." +msgstr "" + +#: Zotlabs/Lib/Enotify.php:470 +msgid "[$Projectname:Notify] Friend suggestion received" +msgstr "" + +#: Zotlabs/Lib/Enotify.php:471 +#, php-format +msgid "You've received a friend suggestion from '%1$s' at %2$s" +msgstr "" + +#: Zotlabs/Lib/Enotify.php:473 +#, php-format +msgid "You've received [zrl=%1$s]a friend suggestion[/zrl] for %2$s from %3$s." +msgstr "" + +#: Zotlabs/Lib/Enotify.php:479 +msgid "Name:" +msgstr "" + +#: Zotlabs/Lib/Enotify.php:480 +msgid "Photo:" +msgstr "" + +#: Zotlabs/Lib/Enotify.php:483 +#, php-format +msgid "Please visit %s to approve or reject the suggestion." +msgstr "" + +#: Zotlabs/Lib/Enotify.php:741 +msgid "[$Projectname:Notify]" +msgstr "" + +#: Zotlabs/Lib/Enotify.php:923 +msgid "created a new post" +msgstr "" + +#: Zotlabs/Lib/Enotify.php:924 +#, php-format +msgid "reacted to %s's conversation" +msgstr "" + +#: Zotlabs/Lib/Enotify.php:926 +#, php-format +msgid "shared %s's post" +msgstr "" + +#: Zotlabs/Lib/Enotify.php:930 +msgid "sent a direct message" +msgstr "" + +#: Zotlabs/Lib/Enotify.php:937 +#, php-format +msgid "edited a post dated %s" +msgstr "" + +#: Zotlabs/Lib/Enotify.php:940 +#, php-format +msgid "edited a comment dated %s" +msgstr "" + +#: Zotlabs/Lib/PermissionDescription.php:40 include/acl_selectors.php:37 +msgid "Visible to your default audience" +msgstr "" + +#: Zotlabs/Lib/PermissionDescription.php:117 include/acl_selectors.php:135 +msgid "Only me" +msgstr "" + +#: Zotlabs/Lib/PermissionDescription.php:119 +msgid "Public" +msgstr "" + +#: Zotlabs/Lib/PermissionDescription.php:121 +msgid "Anybody in the $Projectname network" +msgstr "" + +#: Zotlabs/Lib/PermissionDescription.php:123 +#, php-format +msgid "Any account on %s" +msgstr "" + +#: Zotlabs/Lib/PermissionDescription.php:125 +msgid "Any of my connections" +msgstr "" + +#: Zotlabs/Lib/PermissionDescription.php:127 +msgid "Only connections I specifically allow" +msgstr "" + +#: Zotlabs/Lib/PermissionDescription.php:129 +msgid "Anybody authenticated (could include visitors from other networks)" +msgstr "" + +#: Zotlabs/Lib/PermissionDescription.php:131 +msgid "Any connections including those who haven't yet been approved" +msgstr "" + +#: Zotlabs/Lib/PermissionDescription.php:180 +msgid "" +"This is your default setting for the audience of your normal stream, and " +"posts." +msgstr "" + +#: Zotlabs/Lib/PermissionDescription.php:182 +msgid "" +"This is your default setting for who can view your default channel profile" +msgstr "" + +#: Zotlabs/Lib/PermissionDescription.php:184 +msgid "This is your default setting for who can view your connections" +msgstr "" + +#: Zotlabs/Lib/PermissionDescription.php:186 +msgid "" +"This is your default setting for who can view your file storage and photos" +msgstr "" + +#: Zotlabs/Lib/PermissionDescription.php:188 +msgid "This is your default setting for the audience of your webpages" +msgstr "" + +#: Zotlabs/Lib/Apps.php:339 +msgid "Site Admin" +msgstr "" + +#: Zotlabs/Lib/Apps.php:340 +msgid "Apps" +msgstr "" + +#: Zotlabs/Lib/Apps.php:341 include/features.php:144 include/nav.php:491 +msgid "Articles" +msgstr "" + +#: Zotlabs/Lib/Apps.php:342 +msgid "CalDAV" +msgstr "" + +#: Zotlabs/Lib/Apps.php:343 +msgid "CardDAV" +msgstr "" + +#: Zotlabs/Lib/Apps.php:344 Zotlabs/Module/Cards.php:212 +#: include/conversation.php:2060 include/nav.php:480 +msgid "Cards" +msgstr "" + +#: Zotlabs/Lib/Apps.php:345 Zotlabs/Storage/Browser.php:157 include/nav.php:442 +#: include/nav.php:445 +msgid "Calendar" +msgstr "" + +#: Zotlabs/Lib/Apps.php:346 Zotlabs/Module/Cdav.php:1071 +#: Zotlabs/Widget/Appcategories.php:49 Zotlabs/Widget/Categories.php:85 +#: Zotlabs/Widget/Categories.php:133 Zotlabs/Widget/Categories.php:184 +#: include/taxonomy.php:465 include/taxonomy.php:559 include/taxonomy.php:581 +#: include/taxonomy.php:604 +msgid "Categories" +msgstr "" + +#: Zotlabs/Lib/Apps.php:347 +msgid "Channel Home" +msgstr "" + +#: Zotlabs/Lib/Apps.php:348 +msgid "Channel Manager" +msgstr "" + +#: Zotlabs/Lib/Apps.php:349 Zotlabs/Module/Sources.php:112 +msgid "Channel Sources" +msgstr "" + +#: Zotlabs/Lib/Apps.php:350 +msgid "Chat" +msgstr "" + +#: Zotlabs/Lib/Apps.php:351 Zotlabs/Widget/Chatroom_list.php:22 +#: include/conversation.php:2034 include/conversation.php:2037 +#: include/nav.php:456 include/nav.php:459 +msgid "Chatrooms" +msgstr "" + +#: Zotlabs/Lib/Apps.php:352 +msgid "Clients" +msgstr "" + +#: Zotlabs/Lib/Apps.php:353 include/conversation.php:1443 +msgid "Comment Control" +msgstr "" + +#: Zotlabs/Lib/Apps.php:354 Zotlabs/Module/Connedit.php:674 +#: Zotlabs/Module/Connections.php:368 Zotlabs/Module/Stream.php:135 +#: Zotlabs/Module/Affinity.php:65 Zotlabs/Widget/Activity_filter.php:146 +#: Zotlabs/Widget/Affinity.php:30 include/connections.php:954 +msgid "Connections" +msgstr "" + +#: Zotlabs/Lib/Apps.php:355 +msgid "Content Filter" +msgstr "" + +#: Zotlabs/Lib/Apps.php:356 +#: extend/addon/a/content_import/Mod_content_import.php:135 +msgid "Content Import" +msgstr "" + +#: Zotlabs/Lib/Apps.php:357 Zotlabs/Module/Directory.php:472 +msgid "Directory" +msgstr "" + +#: Zotlabs/Lib/Apps.php:358 Zotlabs/Widget/Activity_filter.php:77 +msgid "Drafts" +msgstr "" + +#: Zotlabs/Lib/Apps.php:359 Zotlabs/Widget/Activity_filter.php:105 +#: include/conversation.php:2020 include/conversation.php:2023 +msgid "Events" +msgstr "" + +#: Zotlabs/Lib/Apps.php:360 +msgid "Expire Posts" +msgstr "" + +#: Zotlabs/Lib/Apps.php:361 +msgid "Features" +msgstr "" + +#: Zotlabs/Lib/Apps.php:362 Zotlabs/Module/Fbrowser.php:93 +#: Zotlabs/Storage/Browser.php:298 include/conversation.php:2009 +#: include/nav.php:431 +msgid "Files" +msgstr "" + +#: Zotlabs/Lib/Apps.php:363 extend/addon/a/followlist/Mod_followlist.php:129 +msgid "Followlist" +msgstr "" + +#: Zotlabs/Lib/Apps.php:364 Zotlabs/Module/Connedit.php:663 +msgid "Friend Zoom" +msgstr "" + +#: Zotlabs/Lib/Apps.php:365 +msgid "Future Posting" +msgstr "" + +#: Zotlabs/Lib/Apps.php:366 extend/addon/a/gallery/Mod_Gallery.php:137 +#: extend/addon/a/gallery/gallery.php:46 +msgid "Gallery" +msgstr "" + +#: Zotlabs/Lib/Apps.php:367 +msgid "Guest Pass" +msgstr "" + +#: Zotlabs/Lib/Apps.php:368 include/help.php:69 include/nav.php:177 +#: include/nav.php:297 +msgid "Help" +msgstr "" + +#: Zotlabs/Lib/Apps.php:369 +msgid "Invite" +msgstr "" + +#: Zotlabs/Lib/Apps.php:370 +msgid "Language" +msgstr "" + +#: Zotlabs/Lib/Apps.php:372 boot.php:1726 include/nav.php:128 +#: include/nav.php:131 +msgid "Login" +msgstr "" + +#: Zotlabs/Lib/Apps.php:373 +msgid "Mail" +msgstr "" + +#: Zotlabs/Lib/Apps.php:374 +msgid "Markup" +msgstr "" + +#: Zotlabs/Lib/Apps.php:375 Zotlabs/Module/Mood.php:162 +msgid "Mood" +msgstr "" + +#: Zotlabs/Lib/Apps.php:376 +msgid "My Chatrooms" +msgstr "" + +#: Zotlabs/Lib/Apps.php:377 +msgid "No Comment" +msgstr "" + +#: Zotlabs/Lib/Apps.php:378 Zotlabs/Widget/Notes.php:22 +msgid "Notes" +msgstr "" + +#: Zotlabs/Lib/Apps.php:379 Zotlabs/Module/Settings/Channel.php:686 +msgid "Notifications" +msgstr "" + +#: Zotlabs/Lib/Apps.php:380 +msgid "OAuth Apps Manager" +msgstr "" + +#: Zotlabs/Lib/Apps.php:381 +msgid "OAuth2 Apps Manager" +msgstr "" + +#: Zotlabs/Lib/Apps.php:382 +msgid "Order Apps" +msgstr "" + +#: Zotlabs/Lib/Apps.php:383 +msgid "PDL Editor" +msgstr "" + +#: Zotlabs/Lib/Apps.php:384 Zotlabs/Module/Settings/Permcats.php:111 +msgid "Permission Categories" +msgstr "" + +#: Zotlabs/Lib/Apps.php:385 Zotlabs/Module/Fbrowser.php:35 +#: include/conversation.php:2001 include/nav.php:423 +msgid "Photos" +msgstr "" + +#: Zotlabs/Lib/Apps.php:386 +msgid "Photomap" +msgstr "" + +#: Zotlabs/Lib/Apps.php:387 Zotlabs/Module/Poke.php:139 +msgid "Poke" +msgstr "" + +#: Zotlabs/Lib/Apps.php:388 +msgid "Post" +msgstr "" + +#: Zotlabs/Lib/Apps.php:389 +msgid "Premium Channel" +msgstr "" + +#: Zotlabs/Lib/Apps.php:390 +msgid "Probe" +msgstr "" + +#: Zotlabs/Lib/Apps.php:391 Zotlabs/Lib/Libprofile.php:656 +#: Zotlabs/Lib/Activity.php:2882 Zotlabs/Lib/Activity.php:2885 +#: Zotlabs/Lib/Activity.php:2905 Zotlabs/Module/Profperm.php:131 +msgid "Profile" +msgstr "" + +#: Zotlabs/Lib/Apps.php:392 +msgid "Profile Photo" +msgstr "" + +#: Zotlabs/Lib/Apps.php:393 +msgid "Profiles" +msgstr "" + +#: Zotlabs/Lib/Apps.php:394 Zotlabs/Module/Pubstream.php:95 +#: Zotlabs/Widget/Notifications.php:131 +msgid "Public Stream" +msgstr "" + +#: Zotlabs/Lib/Apps.php:395 +msgid "Random Channel" +msgstr "" + +#: Zotlabs/Lib/Apps.php:396 +msgid "Remote Diagnostics" +msgstr "" + +#: Zotlabs/Lib/Apps.php:397 +msgid "Report Bug" +msgstr "" + +#: Zotlabs/Lib/Apps.php:398 Zotlabs/Module/Connections.php:375 +#: Zotlabs/Module/Search.php:61 Zotlabs/Widget/Sitesearch.php:35 +#: include/acl_selectors.php:147 include/text.php:1042 include/text.php:1055 +#: include/nav.php:184 +msgid "Search" +msgstr "" + +#: Zotlabs/Lib/Apps.php:399 +msgid "Secrets" +msgstr "" + +#: Zotlabs/Lib/Apps.php:400 Zotlabs/Module/Admin/Themes.php:137 +#: Zotlabs/Module/Admin/Addons.php:346 Zotlabs/Widget/Newmember.php:54 +#: Zotlabs/Widget/Settings_menu.php:135 include/nav.php:105 +msgid "Settings" +msgstr "" + +#: Zotlabs/Lib/Apps.php:401 +msgid "Sites" +msgstr "" + +#: Zotlabs/Lib/Apps.php:402 +msgid "Stream" +msgstr "" + +#: Zotlabs/Lib/Apps.php:403 Zotlabs/Widget/Stream_order.php:137 +msgid "Stream Order" +msgstr "" + +#: Zotlabs/Lib/Apps.php:404 +msgid "Suggest" +msgstr "" + +#: Zotlabs/Lib/Apps.php:405 Zotlabs/Module/Settings/Network.php:112 +#: include/features.php:447 +msgid "Suggest Channels" +msgstr "" + +#: Zotlabs/Lib/Apps.php:406 +msgid "Tagadelic" +msgstr "" + +#: Zotlabs/Lib/Apps.php:407 Zotlabs/Widget/Tasklist.php:30 +msgid "Tasks" +msgstr "" + +#: Zotlabs/Lib/Apps.php:408 +msgid "View Bookmarks" +msgstr "" + +#: Zotlabs/Lib/Apps.php:409 Zotlabs/Module/Connedit.php:548 include/nav.php:117 +msgid "View Profile" +msgstr "" + +#: Zotlabs/Lib/Apps.php:410 +msgid "Virtual Lists" +msgstr "" + +#: Zotlabs/Lib/Apps.php:411 Zotlabs/Module/Webpages.php:262 +#: include/conversation.php:2082 include/nav.php:503 +msgid "Webpages" +msgstr "" + +#: Zotlabs/Lib/Apps.php:412 include/conversation.php:2098 include/nav.php:518 +msgid "Wiki" +msgstr "" + +#: Zotlabs/Lib/Apps.php:413 extend/addon/a/zotpost/Mod_zotpost.php:54 +msgid "ZotPost" +msgstr "" + +#: Zotlabs/Lib/Apps.php:588 +msgid "Installed" +msgstr "" + +#: Zotlabs/Lib/Apps.php:588 Zotlabs/Module/Admin/Addons.php:429 +msgid "Install" +msgstr "" + +#: Zotlabs/Lib/Apps.php:612 +msgid "Purchase" +msgstr "" + +#: Zotlabs/Lib/Apps.php:615 Zotlabs/Lib/ThreadItem.php:154 +#: Zotlabs/Lib/Libprofile.php:203 Zotlabs/Lib/Libprofile.php:206 +#: Zotlabs/Module/Admin/Profs.php:188 Zotlabs/Module/Blocks.php:173 +#: Zotlabs/Module/Connections.php:319 Zotlabs/Module/Connections.php:358 +#: Zotlabs/Module/Connections.php:379 Zotlabs/Module/Editlayout.php:126 +#: Zotlabs/Module/Editwebpage.php:157 Zotlabs/Module/Thing.php:302 +#: Zotlabs/Module/Lists.php:335 Zotlabs/Module/Layouts.php:207 +#: Zotlabs/Module/Card_edit.php:108 Zotlabs/Module/Editblock.php:127 +#: Zotlabs/Module/Menu.php:181 Zotlabs/Module/Webpages.php:265 +#: Zotlabs/Module/Settings/Oauth.php:163 Zotlabs/Module/Settings/Oauth2.php:211 +#: Zotlabs/Storage/Browser.php:318 Zotlabs/Widget/Cdav.php:146 +#: Zotlabs/Widget/Cdav.php:181 include/menu.php:138 +msgid "Edit" +msgstr "" + +#: Zotlabs/Lib/Apps.php:616 Zotlabs/Lib/ThreadItem.php:191 +#: Zotlabs/Module/Admin/Profs.php:189 Zotlabs/Module/Admin/Accounts.php:184 +#: Zotlabs/Module/Admin/Channels.php:173 Zotlabs/Module/Blocks.php:175 +#: Zotlabs/Module/Cdav.php:1058 Zotlabs/Module/Cdav.php:1367 +#: Zotlabs/Module/Connedit.php:616 Zotlabs/Module/Connedit.php:880 +#: Zotlabs/Module/Connections.php:327 Zotlabs/Module/Editlayout.php:150 +#: Zotlabs/Module/Editwebpage.php:182 Zotlabs/Module/Thing.php:303 +#: Zotlabs/Module/Card_edit.php:138 Zotlabs/Module/Editblock.php:152 +#: Zotlabs/Module/Webpages.php:267 Zotlabs/Module/Profiles.php:849 +#: Zotlabs/Module/Settings/Oauth.php:164 Zotlabs/Module/Settings/Oauth2.php:212 +#: Zotlabs/Module/Photos.php:1263 Zotlabs/Storage/Browser.php:319 +#: include/conversation.php:600 include/conversation.php:654 +msgid "Delete" +msgstr "" + +#: Zotlabs/Lib/Apps.php:617 +msgid "Undelete" +msgstr "" + +#: Zotlabs/Lib/Apps.php:627 +msgid "Add to app-tray" +msgstr "" + +#: Zotlabs/Lib/Apps.php:628 +msgid "Remove from app-tray" +msgstr "" + +#: Zotlabs/Lib/Apps.php:629 +msgid "Pin to navbar" +msgstr "" + +#: Zotlabs/Lib/Apps.php:630 +msgid "Unpin from navbar" +msgstr "" + +#: Zotlabs/Lib/Apps.php:1152 Zotlabs/Lib/Apps.php:1244 +#: Zotlabs/Lib/Activity.php:2194 Zotlabs/Module/Cdav.php:858 +#: Zotlabs/Module/Cdav.php:859 Zotlabs/Module/Cdav.php:865 +#: Zotlabs/Module/Embedphotos.php:214 Zotlabs/Module/Photos.php:854 +#: Zotlabs/Module/Photos.php:1329 Zotlabs/Storage/Browser.php:182 +#: Zotlabs/Widget/Album.php:94 Zotlabs/Widget/Portfolio.php:107 +#: include/conversation.php:1082 msgid "Unknown" msgstr "" -#: Zotlabs/Lib/Activity.php:2894 -#, php-format -msgid "Likes %1$s's %2$s" +#: Zotlabs/Lib/ThreadItem.php:116 include/conversation.php:608 +msgid "Public visibility" msgstr "" -#: Zotlabs/Lib/Activity.php:2897 -#, php-format -msgid "Doesn't like %1$s's %2$s" +#: Zotlabs/Lib/ThreadItem.php:118 include/conversation.php:610 +msgid "Direct message (private mail)" msgstr "" -#: Zotlabs/Lib/Activity.php:2903 -#, php-format -msgid "Will attend %s's event" +#: Zotlabs/Lib/ThreadItem.php:121 include/conversation.php:613 +msgid "Restricted visibility" msgstr "" -#: Zotlabs/Lib/Activity.php:2906 -#, php-format -msgid "Will not attend %s's event" -msgstr "" - -#: Zotlabs/Lib/Activity.php:2909 -#, php-format -msgid "May attend %s's event" -msgstr "" - -#: Zotlabs/Lib/Activity.php:2912 -#, php-format -msgid "May not attend %s's event" -msgstr "" - -#: Zotlabs/Lib/Activity.php:3383 include/text.php:1595 include/text.php:2979 -#, php-format -msgid "%1$s (%2$s)" -msgstr "" - -#: Zotlabs/Lib/ThreadItem.php:114 include/conversation.php:617 -msgid "Private Message" -msgstr "" - -#: Zotlabs/Lib/ThreadItem.php:138 +#: Zotlabs/Lib/ThreadItem.php:146 msgid "" "This comment is part of a private conversation, yet was shared with the " "public. Discretion advised." msgstr "" -#: Zotlabs/Lib/ThreadItem.php:178 Zotlabs/Lib/Apps.php:604 -#: Zotlabs/Module/Admin/Channels.php:156 Zotlabs/Module/Admin/Accounts.php:175 -#: Zotlabs/Module/Admin/Profs.php:176 Zotlabs/Module/Blocks.php:166 -#: Zotlabs/Module/Cdav.php:1053 Zotlabs/Module/Cdav.php:1366 -#: Zotlabs/Module/Connedit.php:611 Zotlabs/Module/Connedit.php:869 -#: Zotlabs/Module/Connections.php:326 Zotlabs/Module/Editlayout.php:142 -#: Zotlabs/Module/Editwebpage.php:171 Zotlabs/Module/Thing.php:286 -#: Zotlabs/Module/Card_edit.php:131 Zotlabs/Module/Editblock.php:143 -#: Zotlabs/Module/Webpages.php:258 Zotlabs/Module/Profiles.php:808 -#: Zotlabs/Module/Settings/Oauth.php:151 Zotlabs/Module/Settings/Oauth2.php:197 -#: Zotlabs/Module/Photos.php:1253 Zotlabs/Storage/Browser.php:316 -#: include/conversation.php:608 include/conversation.php:656 -msgid "Delete" -msgstr "" - -#: Zotlabs/Lib/ThreadItem.php:182 Zotlabs/Storage/Browser.php:303 +#: Zotlabs/Lib/ThreadItem.php:194 Zotlabs/Storage/Browser.php:306 msgid "Admin Delete" msgstr "" -#: Zotlabs/Lib/ThreadItem.php:187 include/conversation.php:607 +#: Zotlabs/Lib/ThreadItem.php:199 include/conversation.php:599 msgid "Select" msgstr "" -#: Zotlabs/Lib/ThreadItem.php:191 Zotlabs/Module/Filer.php:52 +#: Zotlabs/Lib/ThreadItem.php:203 Zotlabs/Module/Filer.php:61 msgid "Save to Folder" msgstr "" -#: Zotlabs/Lib/ThreadItem.php:213 +#: Zotlabs/Lib/ThreadItem.php:225 msgid "I will attend" msgstr "" -#: Zotlabs/Lib/ThreadItem.php:213 +#: Zotlabs/Lib/ThreadItem.php:225 msgid "I will not attend" msgstr "" -#: Zotlabs/Lib/ThreadItem.php:213 +#: Zotlabs/Lib/ThreadItem.php:225 msgid "I might attend" msgstr "" -#: Zotlabs/Lib/ThreadItem.php:214 +#: Zotlabs/Lib/ThreadItem.php:226 msgid "Undo attendance" msgstr "" -#: Zotlabs/Lib/ThreadItem.php:229 Zotlabs/Lib/ThreadItem.php:251 -#: Zotlabs/Module/Photos.php:1212 Zotlabs/Module/Photos.php:1223 +#: Zotlabs/Lib/ThreadItem.php:241 Zotlabs/Lib/ThreadItem.php:261 +#: Zotlabs/Module/Photos.php:1223 Zotlabs/Module/Photos.php:1234 msgid "View all" msgstr "" -#: Zotlabs/Lib/ThreadItem.php:234 Zotlabs/Module/Photos.php:1216 -#: include/taxonomy.php:675 include/conversation.php:2089 +#: Zotlabs/Lib/ThreadItem.php:246 Zotlabs/Module/Photos.php:1227 +#: include/taxonomy.php:741 include/conversation.php:2152 msgctxt "noun" msgid "Like" msgid_plural "Likes" msgstr[0] "" msgstr[1] "" -#: Zotlabs/Lib/ThreadItem.php:237 Zotlabs/Lib/ThreadItem.php:482 -#: Zotlabs/Module/Photos.php:1334 include/conversation.php:2092 +#: Zotlabs/Lib/ThreadItem.php:248 Zotlabs/Lib/ThreadItem.php:495 +#: Zotlabs/Module/Photos.php:1343 include/conversation.php:2154 msgctxt "noun" msgid "Likes" msgstr "" -#: Zotlabs/Lib/ThreadItem.php:243 Zotlabs/Module/Photos.php:1220 -#: include/conversation.php:2097 +#: Zotlabs/Lib/ThreadItem.php:254 Zotlabs/Module/Photos.php:1231 +#: include/conversation.php:2159 msgctxt "noun" msgid "Dislike" msgid_plural "Dislikes" msgstr[0] "" msgstr[1] "" -#: Zotlabs/Lib/ThreadItem.php:246 Zotlabs/Lib/ThreadItem.php:483 -#: Zotlabs/Module/Photos.php:1335 include/conversation.php:2100 +#: Zotlabs/Lib/ThreadItem.php:256 Zotlabs/Lib/ThreadItem.php:496 +#: Zotlabs/Module/Photos.php:1344 include/conversation.php:2161 msgctxt "noun" msgid "Dislikes" msgstr "" -#: Zotlabs/Lib/ThreadItem.php:272 Zotlabs/Module/Admin/Profs.php:94 -#: Zotlabs/Module/Admin/Profs.php:114 Zotlabs/Module/Filer.php:51 -#: Zotlabs/Widget/Notes.php:22 -#: extend/addon/a/queueworker/Mod_Queueworker.php:116 include/text.php:985 -#: include/text.php:997 +#: Zotlabs/Lib/ThreadItem.php:282 Zotlabs/Module/Admin/Profs.php:99 +#: Zotlabs/Module/Admin/Profs.php:120 Zotlabs/Module/Filer.php:60 +#: Zotlabs/Widget/Notes.php:24 +#: extend/addon/a/queueworker/Mod_Queueworker.php:116 include/text.php:1043 +#: include/text.php:1056 msgid "Save" msgstr "" -#: Zotlabs/Lib/ThreadItem.php:282 include/conversation.php:624 +#: Zotlabs/Lib/ThreadItem.php:291 include/conversation.php:621 msgid "Message signature validated" msgstr "" -#: Zotlabs/Lib/ThreadItem.php:283 include/conversation.php:625 +#: Zotlabs/Lib/ThreadItem.php:292 include/conversation.php:622 msgid "Message signature incorrect" msgstr "" -#: Zotlabs/Lib/ThreadItem.php:289 +#: Zotlabs/Lib/ThreadItem.php:298 msgid "Add Tag" msgstr "" -#: Zotlabs/Lib/ThreadItem.php:307 Zotlabs/Module/Photos.php:1152 +#: Zotlabs/Lib/ThreadItem.php:318 Zotlabs/Module/Photos.php:1164 msgid "I like this" msgstr "" -#: Zotlabs/Lib/ThreadItem.php:307 Zotlabs/Module/Photos.php:1156 +#: Zotlabs/Lib/ThreadItem.php:318 Zotlabs/Module/Photos.php:1168 msgid "Undo like" msgstr "" -#: Zotlabs/Lib/ThreadItem.php:308 Zotlabs/Module/Photos.php:1155 +#: Zotlabs/Lib/ThreadItem.php:319 Zotlabs/Module/Photos.php:1167 msgid "I don't like this" msgstr "" -#: Zotlabs/Lib/ThreadItem.php:308 Zotlabs/Module/Photos.php:1157 +#: Zotlabs/Lib/ThreadItem.php:319 Zotlabs/Module/Photos.php:1169 msgid "Undo dislike" msgstr "" -#: Zotlabs/Lib/ThreadItem.php:314 +#: Zotlabs/Lib/ThreadItem.php:325 msgid "Repeat This" msgstr "" -#: Zotlabs/Lib/ThreadItem.php:315 +#: Zotlabs/Lib/ThreadItem.php:326 msgid "Share this" msgstr "" -#: Zotlabs/Lib/ThreadItem.php:325 +#: Zotlabs/Lib/ThreadItem.php:337 msgid "Delivery Report" msgstr "" -#: Zotlabs/Lib/ThreadItem.php:353 +#: Zotlabs/Lib/ThreadItem.php:366 #, php-format msgid "%d comment" msgid_plural "%d comments" msgstr[0] "" msgstr[1] "" -#: Zotlabs/Lib/ThreadItem.php:354 +#: Zotlabs/Lib/ThreadItem.php:367 #, php-format msgid "%d unseen" msgstr "" -#: Zotlabs/Lib/ThreadItem.php:393 Zotlabs/Lib/ThreadItem.php:394 +#: Zotlabs/Lib/ThreadItem.php:406 Zotlabs/Lib/ThreadItem.php:407 #, php-format msgid "View %s's profile - %s" msgstr "" -#: Zotlabs/Lib/ThreadItem.php:397 +#: Zotlabs/Lib/ThreadItem.php:410 msgid "to" msgstr "" -#: Zotlabs/Lib/ThreadItem.php:398 include/conversation.php:685 +#: Zotlabs/Lib/ThreadItem.php:411 include/conversation.php:684 msgid "via" msgstr "" -#: Zotlabs/Lib/ThreadItem.php:399 +#: Zotlabs/Lib/ThreadItem.php:412 msgid "Wall-to-Wall" msgstr "" -#: Zotlabs/Lib/ThreadItem.php:400 +#: Zotlabs/Lib/ThreadItem.php:413 msgid "via Wall-To-Wall:" msgstr "" -#: Zotlabs/Lib/ThreadItem.php:415 include/conversation.php:688 +#: Zotlabs/Lib/ThreadItem.php:428 include/conversation.php:687 #, php-format msgid "from %s" msgstr "" -#: Zotlabs/Lib/ThreadItem.php:418 include/conversation.php:691 +#: Zotlabs/Lib/ThreadItem.php:431 include/conversation.php:690 #, php-format msgid "last edited: %s" msgstr "" -#: Zotlabs/Lib/ThreadItem.php:419 include/conversation.php:692 +#: Zotlabs/Lib/ThreadItem.php:432 include/conversation.php:691 #, php-format msgid "Expires: %s" msgstr "" -#: Zotlabs/Lib/ThreadItem.php:429 +#: Zotlabs/Lib/ThreadItem.php:442 msgid "Attend" msgstr "" -#: Zotlabs/Lib/ThreadItem.php:430 +#: Zotlabs/Lib/ThreadItem.php:443 msgid "Attendance Options" msgstr "" -#: Zotlabs/Lib/ThreadItem.php:431 +#: Zotlabs/Lib/ThreadItem.php:444 msgid "Vote" msgstr "" -#: Zotlabs/Lib/ThreadItem.php:432 +#: Zotlabs/Lib/ThreadItem.php:445 msgid "Voting Options" msgstr "" -#: Zotlabs/Lib/ThreadItem.php:433 +#: Zotlabs/Lib/ThreadItem.php:446 msgid "Reply" msgstr "" -#: Zotlabs/Lib/ThreadItem.php:458 include/conversation.php:707 +#: Zotlabs/Lib/ThreadItem.php:471 include/conversation.php:706 msgid "Pinned post" msgstr "" -#: Zotlabs/Lib/ThreadItem.php:460 include/conversation.php:709 -#: include/js_strings.php:39 +#: Zotlabs/Lib/ThreadItem.php:473 include/conversation.php:708 +#: include/js_strings.php:42 msgid "Unpin this post" msgstr "" -#: Zotlabs/Lib/ThreadItem.php:460 include/conversation.php:709 -#: include/js_strings.php:38 +#: Zotlabs/Lib/ThreadItem.php:473 include/conversation.php:708 +#: include/js_strings.php:41 msgid "Pin this post" msgstr "" -#: Zotlabs/Lib/ThreadItem.php:462 +#: Zotlabs/Lib/ThreadItem.php:475 msgid "Saved draft" msgstr "" -#: Zotlabs/Lib/ThreadItem.php:463 +#: Zotlabs/Lib/ThreadItem.php:476 msgid "Save Bookmarks" msgstr "" -#: Zotlabs/Lib/ThreadItem.php:464 +#: Zotlabs/Lib/ThreadItem.php:477 msgid "Add to Calendar" msgstr "" -#: Zotlabs/Lib/ThreadItem.php:475 Zotlabs/Module/Notifications.php:65 +#: Zotlabs/Lib/ThreadItem.php:488 Zotlabs/Module/Notifications.php:72 msgid "Mark all seen" msgstr "" -#: Zotlabs/Lib/ThreadItem.php:488 Zotlabs/Module/Settings/Channel.php:652 -#: Zotlabs/Module/Photos.php:1340 include/acl_selectors.php:154 -#: include/conversation.php:1473 +#: Zotlabs/Lib/ThreadItem.php:501 Zotlabs/Module/Settings/Channel.php:685 +#: Zotlabs/Module/Photos.php:1349 include/acl_selectors.php:154 +#: include/conversation.php:1498 msgid "Close" msgstr "" -#: Zotlabs/Lib/ThreadItem.php:493 include/conversation.php:401 +#: Zotlabs/Lib/ThreadItem.php:506 include/conversation.php:412 msgid "This is an unsaved preview" msgstr "" -#: Zotlabs/Lib/ThreadItem.php:494 Zotlabs/Module/Photos.php:1159 -#: include/conversation.php:712 +#: Zotlabs/Lib/ThreadItem.php:507 Zotlabs/Module/Photos.php:1171 +#: include/conversation.php:711 msgid "Please wait" msgstr "" -#: Zotlabs/Lib/ThreadItem.php:560 include/js_strings.php:8 +#: Zotlabs/Lib/ThreadItem.php:572 include/js_strings.php:11 #, php-format msgid "%s show all" msgstr "" -#: Zotlabs/Lib/ThreadItem.php:902 Zotlabs/Module/Photos.php:1175 -#: Zotlabs/Module/Photos.php:1287 +#: Zotlabs/Lib/ThreadItem.php:947 Zotlabs/Module/Photos.php:1187 +#: Zotlabs/Module/Photos.php:1297 msgid "This is you" msgstr "" -#: Zotlabs/Lib/ThreadItem.php:904 Zotlabs/Module/Photos.php:1177 -#: Zotlabs/Module/Photos.php:1289 include/js_strings.php:7 +#: Zotlabs/Lib/ThreadItem.php:949 Zotlabs/Module/Photos.php:1189 +#: Zotlabs/Module/Photos.php:1299 include/js_strings.php:10 msgid "Comment" msgstr "" -#: Zotlabs/Lib/ThreadItem.php:905 Zotlabs/Module/Admin/Logs.php:84 -#: Zotlabs/Module/Admin/Account_edit.php:71 -#: Zotlabs/Module/Admin/Channels.php:154 Zotlabs/Module/Admin/Accounts.php:168 -#: Zotlabs/Module/Admin/Addons.php:439 Zotlabs/Module/Admin/Profs.php:178 -#: Zotlabs/Module/Admin/Themes.php:158 Zotlabs/Module/Admin/Site.php:301 -#: Zotlabs/Module/Admin/Security.php:168 Zotlabs/Module/Cal.php:350 -#: Zotlabs/Module/Expire.php:46 Zotlabs/Module/Connedit.php:833 -#: Zotlabs/Module/Appman.php:144 Zotlabs/Module/Defperms.php:251 -#: Zotlabs/Module/Events.php:524 Zotlabs/Module/Email_validation.php:40 -#: Zotlabs/Module/Invite.php:165 Zotlabs/Module/Filestorage.php:241 -#: Zotlabs/Module/Thing.php:345 Zotlabs/Module/Thing.php:398 -#: Zotlabs/Module/Chat.php:203 Zotlabs/Module/Chat.php:240 -#: Zotlabs/Module/Setup.php:329 Zotlabs/Module/Setup.php:373 -#: Zotlabs/Module/Lists.php:237 Zotlabs/Module/Lists.php:250 -#: Zotlabs/Module/Import_items.php:132 Zotlabs/Module/Mitem.php:263 -#: Zotlabs/Module/Finger.php:22 Zotlabs/Module/Pconfig.php:130 -#: Zotlabs/Module/Editpost.php:112 Zotlabs/Module/Locs.php:126 -#: Zotlabs/Module/Connect.php:91 Zotlabs/Module/Mood.php:157 -#: Zotlabs/Module/Pdledit.php:105 Zotlabs/Module/Profiles.php:727 -#: Zotlabs/Module/Poke.php:144 Zotlabs/Module/Affinity.php:83 -#: Zotlabs/Module/Zot_probe.php:18 Zotlabs/Module/Ap_probe.php:21 -#: Zotlabs/Module/Settings/Featured.php:56 -#: Zotlabs/Module/Settings/Network.php:41 Zotlabs/Module/Settings/Oauth.php:88 -#: Zotlabs/Module/Settings/Permcats.php:116 -#: Zotlabs/Module/Settings/Oauth2.php:105 -#: Zotlabs/Module/Settings/Account.php:104 -#: Zotlabs/Module/Settings/Features.php:59 -#: Zotlabs/Module/Settings/Tokens.php:296 -#: Zotlabs/Module/Settings/Display.php:190 -#: Zotlabs/Module/Settings/Channel.php:593 Zotlabs/Module/Sources.php:123 -#: Zotlabs/Module/Sources.php:160 Zotlabs/Module/Photos.php:1119 -#: Zotlabs/Module/Photos.php:1178 Zotlabs/Module/Photos.php:1290 -#: Zotlabs/Module/Import.php:709 Zotlabs/Module/Xchan.php:15 -#: Zotlabs/Module/Zotfinger.php:25 Zotlabs/Module/Superblock.php:248 -#: Zotlabs/Module/Content_filter.php:66 Zotlabs/Widget/Eventstools.php:16 +#: Zotlabs/Lib/ThreadItem.php:950 Zotlabs/Module/Admin/Profs.php:191 +#: Zotlabs/Module/Admin/Security.php:169 Zotlabs/Module/Admin/Site.php:312 +#: Zotlabs/Module/Admin/Themes.php:170 Zotlabs/Module/Admin/Account_edit.php:78 +#: Zotlabs/Module/Admin/Accounts.php:177 Zotlabs/Module/Admin/Addons.php:445 +#: Zotlabs/Module/Admin/Channels.php:171 Zotlabs/Module/Admin/Logs.php:84 +#: Zotlabs/Module/Cal.php:359 Zotlabs/Module/Expire.php:48 +#: Zotlabs/Module/Connedit.php:844 Zotlabs/Module/Appman.php:150 +#: Zotlabs/Module/Defperms.php:258 Zotlabs/Module/Events.php:541 +#: Zotlabs/Module/Email_validation.php:44 Zotlabs/Module/Invite.php:168 +#: Zotlabs/Module/Filestorage.php:245 Zotlabs/Module/Thing.php:362 +#: Zotlabs/Module/Thing.php:417 Zotlabs/Module/Chat.php:210 +#: Zotlabs/Module/Chat.php:247 Zotlabs/Module/Setup.php:332 +#: Zotlabs/Module/Setup.php:377 Zotlabs/Module/Lists.php:198 +#: Zotlabs/Module/Lists.php:210 Zotlabs/Module/Import_items.php:142 +#: Zotlabs/Module/Mitem.php:271 Zotlabs/Module/Finger.php:24 +#: Zotlabs/Module/Pconfig.php:129 Zotlabs/Module/Editpost.php:116 +#: Zotlabs/Module/Locs.php:136 Zotlabs/Module/Connect.php:95 +#: Zotlabs/Module/Mood.php:166 Zotlabs/Module/Pdledit.php:114 +#: Zotlabs/Module/Profiles.php:768 Zotlabs/Module/Poke.php:148 +#: Zotlabs/Module/Affinity.php:86 Zotlabs/Module/Zot_probe.php:21 +#: Zotlabs/Module/Ap_probe.php:23 Zotlabs/Module/Settings/Account.php:114 +#: Zotlabs/Module/Settings/Display.php:204 +#: Zotlabs/Module/Settings/Featured.php:59 +#: Zotlabs/Module/Settings/Features.php:63 +#: Zotlabs/Module/Settings/Network.php:44 Zotlabs/Module/Settings/Oauth.php:95 +#: Zotlabs/Module/Settings/Oauth2.php:113 +#: Zotlabs/Module/Settings/Permcats.php:127 +#: Zotlabs/Module/Settings/Tokens.php:314 +#: Zotlabs/Module/Settings/Channel.php:626 Zotlabs/Module/Sources.php:130 +#: Zotlabs/Module/Sources.php:168 Zotlabs/Module/Photos.php:1132 +#: Zotlabs/Module/Photos.php:1190 Zotlabs/Module/Photos.php:1300 +#: Zotlabs/Module/Import.php:703 Zotlabs/Module/Content_filter.php:66 +#: Zotlabs/Module/Xchan.php:18 Zotlabs/Module/Superblock.php:255 +#: Zotlabs/Module/Zotfinger.php:28 Zotlabs/Widget/Eventstools.php:19 #: extend/addon/a/logrot/logrot.php:36 extend/addon/a/nsfw/Mod_Nsfw.php:50 #: extend/addon/a/zotpost/Mod_zotpost.php:81 #: extend/addon/a/content_import/Mod_content_import.php:142 @@ -1308,575 +1402,1862 @@ msgstr "" #: extend/addon/a/flashcards/Mod_Flashcards.php:262 #: extend/addon/a/faces/faces.php:125 extend/addon/a/faces/Mod_Faces.php:180 #: extend/addon/a/followlist/Mod_followlist.php:136 -#: extend/addon/a/fuzzloc/Mod_Fuzzloc.php:56 include/js_strings.php:23 +#: extend/addon/a/fuzzloc/Mod_Fuzzloc.php:56 include/js_strings.php:26 #: view/theme/redbasic/php/config.php:93 msgid "Submit" msgstr "" -#: Zotlabs/Lib/ThreadItem.php:907 include/conversation.php:1391 +#: Zotlabs/Lib/ThreadItem.php:952 include/conversation.php:1416 msgid "Bold" msgstr "" -#: Zotlabs/Lib/ThreadItem.php:908 include/conversation.php:1392 +#: Zotlabs/Lib/ThreadItem.php:953 include/conversation.php:1417 msgid "Italic" msgstr "" -#: Zotlabs/Lib/ThreadItem.php:909 include/conversation.php:1393 +#: Zotlabs/Lib/ThreadItem.php:954 include/conversation.php:1418 msgid "Underline" msgstr "" -#: Zotlabs/Lib/ThreadItem.php:910 include/conversation.php:1394 +#: Zotlabs/Lib/ThreadItem.php:955 include/conversation.php:1419 msgid "Quote" msgstr "" -#: Zotlabs/Lib/ThreadItem.php:911 include/conversation.php:1395 +#: Zotlabs/Lib/ThreadItem.php:956 include/conversation.php:1420 msgid "Code" msgstr "" -#: Zotlabs/Lib/ThreadItem.php:912 +#: Zotlabs/Lib/ThreadItem.php:957 msgid "Image" msgstr "" -#: Zotlabs/Lib/ThreadItem.php:913 include/conversation.php:1396 +#: Zotlabs/Lib/ThreadItem.php:958 include/conversation.php:1421 msgid "Attach/Upload file" msgstr "" -#: Zotlabs/Lib/ThreadItem.php:914 +#: Zotlabs/Lib/ThreadItem.php:959 msgid "Insert Link" msgstr "" -#: Zotlabs/Lib/ThreadItem.php:915 +#: Zotlabs/Lib/ThreadItem.php:960 msgid "Video" msgstr "" -#: Zotlabs/Lib/ThreadItem.php:916 Zotlabs/Module/Events.php:509 -#: Zotlabs/Module/Webpages.php:263 Zotlabs/Module/Photos.php:1179 -#: include/conversation.php:1285 +#: Zotlabs/Lib/ThreadItem.php:961 Zotlabs/Module/Events.php:526 +#: Zotlabs/Module/Webpages.php:272 Zotlabs/Module/Photos.php:1191 +#: include/conversation.php:1305 msgid "Preview" msgstr "" -#: Zotlabs/Lib/ThreadItem.php:917 Zotlabs/Module/Lostpass.php:133 -#: Zotlabs/Module/Pdledit.php:63 Zotlabs/Module/Pdledit.php:71 +#: Zotlabs/Lib/ThreadItem.php:962 Zotlabs/Module/Lostpass.php:141 +#: Zotlabs/Module/Pdledit.php:71 Zotlabs/Module/Pdledit.php:79 msgid "Reset" msgstr "" -#: Zotlabs/Lib/ThreadItem.php:922 Zotlabs/Module/Chat.php:211 -#: include/conversation.php:1465 +#: Zotlabs/Lib/ThreadItem.php:967 Zotlabs/Module/Chat.php:218 +#: include/conversation.php:1490 msgid "Encrypt text" msgstr "" -#: Zotlabs/Lib/ThreadItem.php:927 +#: Zotlabs/Lib/ThreadItem.php:972 msgid "Your full name (required)" msgstr "" -#: Zotlabs/Lib/ThreadItem.php:928 +#: Zotlabs/Lib/ThreadItem.php:973 msgid "Your email address (required)" msgstr "" -#: Zotlabs/Lib/ThreadItem.php:929 +#: Zotlabs/Lib/ThreadItem.php:974 msgid "Your website URL (optional)" msgstr "" -#: Zotlabs/Lib/Markdown.php:203 include/bbcode.php:632 +#: Zotlabs/Lib/Libprofile.php:36 +msgid "Requested channel is not available." +msgstr "" + +#: Zotlabs/Lib/Libprofile.php:96 Zotlabs/Module/Cards.php:42 +#: Zotlabs/Module/Blocks.php:41 Zotlabs/Module/Editlayout.php:39 +#: Zotlabs/Module/Editwebpage.php:42 Zotlabs/Module/Hcard.php:18 +#: Zotlabs/Module/Layouts.php:39 Zotlabs/Module/Editblock.php:40 +#: Zotlabs/Module/Menu.php:97 Zotlabs/Module/Connect.php:19 +#: Zotlabs/Module/Webpages.php:44 Zotlabs/Module/Profile.php:28 +#: extend/addon/a/gallery/Mod_Gallery.php:50 +msgid "Requested profile is not available." +msgstr "" + +#: Zotlabs/Lib/Libprofile.php:196 Zotlabs/Module/Profiles.php:773 +msgid "Change profile photo" +msgstr "" + +#: Zotlabs/Lib/Libprofile.php:203 Zotlabs/Module/Profiles.php:878 +#: include/nav.php:120 +msgid "Edit Profiles" +msgstr "" + +#: Zotlabs/Lib/Libprofile.php:204 +msgid "Create New Profile" +msgstr "" + +#: Zotlabs/Lib/Libprofile.php:206 include/nav.php:122 +msgid "Edit Profile" +msgstr "" + +#: Zotlabs/Lib/Libprofile.php:223 Zotlabs/Module/Profiles.php:868 +msgid "Profile Image" +msgstr "" + +#: Zotlabs/Lib/Libprofile.php:226 +msgid "Visible to everybody" +msgstr "" + +#: Zotlabs/Lib/Libprofile.php:227 Zotlabs/Module/Profiles.php:770 +#: Zotlabs/Module/Profiles.php:872 +msgid "Edit visibility" +msgstr "" + +#: Zotlabs/Lib/Libprofile.php:287 Zotlabs/Module/Directory.php:399 +#: Zotlabs/Module/Fedi_id.php:52 Zotlabs/Widget/Follow.php:38 +#: Zotlabs/Widget/Suggestions.php:50 include/conversation.php:975 +#: include/connections.php:147 +msgid "Connect" +msgstr "" + +#: Zotlabs/Lib/Libprofile.php:306 Zotlabs/Module/Directory.php:381 +#: include/event.php:81 include/event.php:139 +msgid "Location:" +msgstr "" + +#: Zotlabs/Lib/Libprofile.php:311 Zotlabs/Lib/Libprofile.php:485 +msgid "Gender:" +msgstr "" + +#: Zotlabs/Lib/Libprofile.php:312 Zotlabs/Lib/Libprofile.php:535 +msgid "Status:" +msgstr "" + +#: Zotlabs/Lib/Libprofile.php:313 Zotlabs/Lib/Libprofile.php:568 +msgid "Homepage:" +msgstr "" + +#: Zotlabs/Lib/Libprofile.php:314 Zotlabs/Lib/Libprofile.php:564 +msgid "Pronouns:" +msgstr "" + +#: Zotlabs/Lib/Libprofile.php:375 Zotlabs/Module/Connections.php:63 +#: Zotlabs/Module/Connections.php:122 Zotlabs/Module/Connections.php:133 +#: Zotlabs/Module/Connections.php:293 +msgid "Active" +msgstr "" + +#: Zotlabs/Lib/Libprofile.php:379 +msgid "Change your profile photo" +msgstr "" + +#: Zotlabs/Lib/Libprofile.php:380 +msgid "Copy to clipboard" +msgstr "" + +#: Zotlabs/Lib/Libprofile.php:381 +msgid "Address copied to clipboard" +msgstr "" + +#: Zotlabs/Lib/Libprofile.php:409 Zotlabs/Module/Profiles.php:985 +#: Zotlabs/Module/Profiles.php:1003 +msgid "Female" +msgstr "" + +#: Zotlabs/Lib/Libprofile.php:412 Zotlabs/Module/Profiles.php:985 +#: Zotlabs/Module/Profiles.php:1003 +msgid "Male" +msgstr "" + +#: Zotlabs/Lib/Libprofile.php:415 +msgid "Trans" +msgstr "" + +#: Zotlabs/Lib/Libprofile.php:418 +msgid "Inter" +msgstr "" + +#: Zotlabs/Lib/Libprofile.php:421 Zotlabs/Module/Profiles.php:985 +msgid "Neuter" +msgstr "" + +#: Zotlabs/Lib/Libprofile.php:424 Zotlabs/Module/Profiles.php:985 +msgid "Non-specific" +msgstr "" + +#: Zotlabs/Lib/Libprofile.php:438 +msgid "She" +msgstr "" + +#: Zotlabs/Lib/Libprofile.php:441 +msgid "Him" +msgstr "" + +#: Zotlabs/Lib/Libprofile.php:444 +msgid "Them" +msgstr "" + +#: Zotlabs/Lib/Libprofile.php:482 +msgid "Full Name:" +msgstr "" + +#: Zotlabs/Lib/Libprofile.php:519 +msgid "j F, Y" +msgstr "" + +#: Zotlabs/Lib/Libprofile.php:520 +msgid "j F" +msgstr "" + +#: Zotlabs/Lib/Libprofile.php:527 +msgid "Birthday:" +msgstr "" + +#: Zotlabs/Lib/Libprofile.php:531 Zotlabs/Module/Directory.php:376 +msgid "Age:" +msgstr "" + +#: Zotlabs/Lib/Libprofile.php:543 +#, php-format +msgid "for %1$d %2$s" +msgstr "" + +#: Zotlabs/Lib/Libprofile.php:555 +msgid "Tags:" +msgstr "" + +#: Zotlabs/Lib/Libprofile.php:560 +msgid "Sexual Preference:" +msgstr "" + +#: Zotlabs/Lib/Libprofile.php:572 Zotlabs/Module/Directory.php:396 +msgid "Hometown:" +msgstr "" + +#: Zotlabs/Lib/Libprofile.php:576 +msgid "Political Views:" +msgstr "" + +#: Zotlabs/Lib/Libprofile.php:580 +msgid "Religion:" +msgstr "" + +#: Zotlabs/Lib/Libprofile.php:584 Zotlabs/Module/Directory.php:398 +msgid "About:" +msgstr "" + +#: Zotlabs/Lib/Libprofile.php:588 +msgid "Hobbies/Interests:" +msgstr "" + +#: Zotlabs/Lib/Libprofile.php:592 +msgid "Likes:" +msgstr "" + +#: Zotlabs/Lib/Libprofile.php:596 +msgid "Dislikes:" +msgstr "" + +#: Zotlabs/Lib/Libprofile.php:600 +msgid "Contact information and Social Networks:" +msgstr "" + +#: Zotlabs/Lib/Libprofile.php:604 +msgid "My other channels:" +msgstr "" + +#: Zotlabs/Lib/Libprofile.php:608 +msgid "Musical interests:" +msgstr "" + +#: Zotlabs/Lib/Libprofile.php:612 +msgid "Books, literature:" +msgstr "" + +#: Zotlabs/Lib/Libprofile.php:616 +msgid "Television:" +msgstr "" + +#: Zotlabs/Lib/Libprofile.php:620 +msgid "Film/dance/culture/entertainment:" +msgstr "" + +#: Zotlabs/Lib/Libprofile.php:624 +msgid "Love/Romance:" +msgstr "" + +#: Zotlabs/Lib/Libprofile.php:628 +msgid "Work/employment:" +msgstr "" + +#: Zotlabs/Lib/Libprofile.php:632 +msgid "School/education:" +msgstr "" + +#: Zotlabs/Lib/Libprofile.php:658 +msgid "Like this thing" +msgstr "" + +#: Zotlabs/Lib/Libprofile.php:659 Zotlabs/Module/Cal.php:355 +#: Zotlabs/Module/Events.php:756 +msgid "Export" +msgstr "" + +#: Zotlabs/Lib/Markdown.php:219 include/bbcode.php:683 #, php-format msgid "%1$s wrote the following %2$s %3$s" msgstr "" -#: Zotlabs/Lib/Markdown.php:205 Zotlabs/Module/Tagger.php:84 -#: include/bbcode.php:628 include/text.php:2251 include/conversation.php:158 +#: Zotlabs/Lib/Markdown.php:221 Zotlabs/Module/Tagger.php:92 +#: include/bbcode.php:677 include/conversation.php:161 include/text.php:2375 msgid "post" msgstr "" -#: Zotlabs/Lib/Language.php:408 include/text.php:2089 include/language.php:396 +#: Zotlabs/Lib/Activity.php:2882 +#, php-format +msgid "Likes %1$s's %2$s" +msgstr "" + +#: Zotlabs/Lib/Activity.php:2885 +#, php-format +msgid "Doesn't like %1$s's %2$s" +msgstr "" + +#: Zotlabs/Lib/Activity.php:2891 +#, php-format +msgid "Will attend %s's event" +msgstr "" + +#: Zotlabs/Lib/Activity.php:2894 +#, php-format +msgid "Will not attend %s's event" +msgstr "" + +#: Zotlabs/Lib/Activity.php:2897 +#, php-format +msgid "May attend %s's event" +msgstr "" + +#: Zotlabs/Lib/Activity.php:2900 +#, php-format +msgid "May not attend %s's event" +msgstr "" + +#: Zotlabs/Lib/Activity.php:3352 include/text.php:1687 include/text.php:3141 +#, php-format +msgid "%1$s (%2$s)" +msgstr "" + +#: Zotlabs/Lib/Language.php:408 include/text.php:2186 include/language.php:429 msgid "default" msgstr "" -#: Zotlabs/Lib/Language.php:421 include/language.php:409 +#: Zotlabs/Lib/Language.php:421 include/language.php:442 msgid "Select an alternate language" msgstr "" -#: Zotlabs/Lib/Connect.php:48 Zotlabs/Lib/Connect.php:169 +#: Zotlabs/Lib/Connect.php:50 Zotlabs/Lib/Connect.php:172 msgid "Channel is blocked on this site." msgstr "" -#: Zotlabs/Lib/Connect.php:53 +#: Zotlabs/Lib/Connect.php:55 msgid "Channel location missing." msgstr "" -#: Zotlabs/Lib/Connect.php:130 +#: Zotlabs/Lib/Connect.php:133 msgid "Remote channel or protocol unavailable." msgstr "" -#: Zotlabs/Lib/Connect.php:163 +#: Zotlabs/Lib/Connect.php:166 msgid "Channel discovery failed." msgstr "" -#: Zotlabs/Lib/Connect.php:178 +#: Zotlabs/Lib/Connect.php:180 msgid "Protocol not supported" msgstr "" -#: Zotlabs/Lib/Connect.php:191 +#: Zotlabs/Lib/Connect.php:193 msgid "Cannot connect to yourself." msgstr "" -#: Zotlabs/Lib/Connect.php:269 +#: Zotlabs/Lib/Connect.php:271 msgid "error saving data" msgstr "" -#: Zotlabs/Lib/Libsync.php:948 -#, php-format -msgid "Unable to verify site signature for %s" -msgstr "" - -#: Zotlabs/Lib/Apps.php:327 -msgid "Site Admin" -msgstr "" - -#: Zotlabs/Lib/Apps.php:328 -msgid "Apps" -msgstr "" - -#: Zotlabs/Lib/Apps.php:329 include/features.php:136 include/nav.php:489 -msgid "Articles" -msgstr "" - -#: Zotlabs/Lib/Apps.php:330 -msgid "CalDAV" -msgstr "" - -#: Zotlabs/Lib/Apps.php:331 -msgid "CardDAV" -msgstr "" - -#: Zotlabs/Lib/Apps.php:332 Zotlabs/Module/Cards.php:204 include/nav.php:478 -#: include/conversation.php:2000 -msgid "Cards" -msgstr "" - -#: Zotlabs/Lib/Apps.php:333 Zotlabs/Storage/Browser.php:154 include/nav.php:440 -#: include/nav.php:443 -msgid "Calendar" -msgstr "" - -#: Zotlabs/Lib/Apps.php:334 Zotlabs/Module/Cdav.php:1066 -#: Zotlabs/Widget/Categories.php:75 Zotlabs/Widget/Categories.php:119 -#: Zotlabs/Widget/Categories.php:166 Zotlabs/Widget/Appcategories.php:43 -#: include/taxonomy.php:419 include/taxonomy.php:505 include/taxonomy.php:525 -#: include/taxonomy.php:546 -msgid "Categories" -msgstr "" - -#: Zotlabs/Lib/Apps.php:335 -msgid "Channel Home" -msgstr "" - -#: Zotlabs/Lib/Apps.php:336 -msgid "Channel Manager" -msgstr "" - -#: Zotlabs/Lib/Apps.php:337 Zotlabs/Module/Sources.php:105 -msgid "Channel Sources" -msgstr "" - -#: Zotlabs/Lib/Apps.php:338 -msgid "Chat" -msgstr "" - -#: Zotlabs/Lib/Apps.php:339 Zotlabs/Widget/Chatroom_list.php:16 -#: include/nav.php:454 include/nav.php:457 include/conversation.php:1974 -#: include/conversation.php:1977 -msgid "Chatrooms" -msgstr "" - -#: Zotlabs/Lib/Apps.php:340 -msgid "Clients" -msgstr "" - -#: Zotlabs/Lib/Apps.php:341 include/conversation.php:1418 -msgid "Comment Control" -msgstr "" - -#: Zotlabs/Lib/Apps.php:342 Zotlabs/Module/Connedit.php:669 -#: Zotlabs/Module/Connections.php:369 Zotlabs/Module/Stream.php:132 -#: Zotlabs/Module/Affinity.php:62 Zotlabs/Widget/Activity_filter.php:144 -#: Zotlabs/Widget/Affinity.php:28 include/connections.php:907 -msgid "Connections" -msgstr "" - -#: Zotlabs/Lib/Apps.php:343 -msgid "Content Filter" -msgstr "" - -#: Zotlabs/Lib/Apps.php:344 -#: extend/addon/a/content_import/Mod_content_import.php:135 -msgid "Content Import" -msgstr "" - -#: Zotlabs/Lib/Apps.php:345 Zotlabs/Module/Directory.php:478 -msgid "Directory" -msgstr "" - -#: Zotlabs/Lib/Apps.php:346 Zotlabs/Widget/Activity_filter.php:75 -msgid "Drafts" -msgstr "" - -#: Zotlabs/Lib/Apps.php:347 Zotlabs/Widget/Activity_filter.php:103 -#: include/conversation.php:1960 include/conversation.php:1963 -msgid "Events" -msgstr "" - -#: Zotlabs/Lib/Apps.php:348 -msgid "Expire Posts" -msgstr "" - -#: Zotlabs/Lib/Apps.php:349 -msgid "Features" -msgstr "" - -#: Zotlabs/Lib/Apps.php:350 Zotlabs/Module/Fbrowser.php:82 -#: Zotlabs/Storage/Browser.php:295 include/nav.php:429 -#: include/conversation.php:1949 -msgid "Files" -msgstr "" - -#: Zotlabs/Lib/Apps.php:351 extend/addon/a/followlist/Mod_followlist.php:129 -msgid "Followlist" -msgstr "" - -#: Zotlabs/Lib/Apps.php:352 Zotlabs/Module/Connedit.php:658 -msgid "Friend Zoom" -msgstr "" - -#: Zotlabs/Lib/Apps.php:353 -msgid "Future Posting" -msgstr "" - -#: Zotlabs/Lib/Apps.php:354 extend/addon/a/gallery/Mod_Gallery.php:137 -#: extend/addon/a/gallery/gallery.php:46 -msgid "Gallery" -msgstr "" - -#: Zotlabs/Lib/Apps.php:355 -msgid "Guest Pass" -msgstr "" - -#: Zotlabs/Lib/Apps.php:356 include/help.php:67 include/nav.php:178 -#: include/nav.php:300 -msgid "Help" -msgstr "" - -#: Zotlabs/Lib/Apps.php:357 -msgid "Invite" -msgstr "" - -#: Zotlabs/Lib/Apps.php:358 -msgid "Language" -msgstr "" - -#: Zotlabs/Lib/Apps.php:360 boot.php:1718 include/nav.php:128 -#: include/nav.php:132 -msgid "Login" -msgstr "" - -#: Zotlabs/Lib/Apps.php:361 -msgid "Mail" -msgstr "" - -#: Zotlabs/Lib/Apps.php:362 -msgid "Markup" -msgstr "" - -#: Zotlabs/Lib/Apps.php:363 Zotlabs/Module/Mood.php:153 -msgid "Mood" -msgstr "" - -#: Zotlabs/Lib/Apps.php:364 -msgid "My Chatrooms" -msgstr "" - -#: Zotlabs/Lib/Apps.php:365 -msgid "No Comment" -msgstr "" - -#: Zotlabs/Lib/Apps.php:366 Zotlabs/Widget/Notes.php:20 -msgid "Notes" -msgstr "" - -#: Zotlabs/Lib/Apps.php:367 Zotlabs/Module/Settings/Channel.php:653 -msgid "Notifications" -msgstr "" - -#: Zotlabs/Lib/Apps.php:368 -msgid "OAuth Apps Manager" -msgstr "" - -#: Zotlabs/Lib/Apps.php:369 -msgid "OAuth2 Apps Manager" -msgstr "" - -#: Zotlabs/Lib/Apps.php:370 -msgid "Order Apps" -msgstr "" - -#: Zotlabs/Lib/Apps.php:371 -msgid "PDL Editor" -msgstr "" - -#: Zotlabs/Lib/Apps.php:372 Zotlabs/Module/Settings/Permcats.php:100 -msgid "Permission Categories" -msgstr "" - -#: Zotlabs/Lib/Apps.php:373 Zotlabs/Module/Fbrowser.php:26 include/nav.php:421 -#: include/conversation.php:1941 -msgid "Photos" -msgstr "" - -#: Zotlabs/Lib/Apps.php:374 -msgid "Photomap" -msgstr "" - -#: Zotlabs/Lib/Apps.php:375 Zotlabs/Module/Poke.php:135 -msgid "Poke" -msgstr "" - -#: Zotlabs/Lib/Apps.php:376 -msgid "Post" -msgstr "" - -#: Zotlabs/Lib/Apps.php:377 -msgid "Premium Channel" -msgstr "" - -#: Zotlabs/Lib/Apps.php:378 -msgid "Probe" -msgstr "" - -#: Zotlabs/Lib/Apps.php:380 -msgid "Profile Photo" -msgstr "" - -#: Zotlabs/Lib/Apps.php:381 -msgid "Profiles" -msgstr "" - -#: Zotlabs/Lib/Apps.php:382 Zotlabs/Module/Pubstream.php:95 -#: Zotlabs/Widget/Notifications.php:128 -msgid "Public Stream" -msgstr "" - -#: Zotlabs/Lib/Apps.php:383 -msgid "Random Channel" -msgstr "" - -#: Zotlabs/Lib/Apps.php:384 -msgid "Remote Diagnostics" -msgstr "" - -#: Zotlabs/Lib/Apps.php:385 -msgid "Report Bug" -msgstr "" - -#: Zotlabs/Lib/Apps.php:386 Zotlabs/Module/Connections.php:376 -#: Zotlabs/Module/Search.php:57 Zotlabs/Widget/Sitesearch.php:31 -#: include/acl_selectors.php:147 include/nav.php:185 include/text.php:984 -#: include/text.php:996 -msgid "Search" -msgstr "" - -#: Zotlabs/Lib/Apps.php:387 -msgid "Secrets" -msgstr "" - -#: Zotlabs/Lib/Apps.php:388 Zotlabs/Module/Admin/Addons.php:342 -#: Zotlabs/Module/Admin/Themes.php:125 Zotlabs/Widget/Settings_menu.php:129 -#: Zotlabs/Widget/Newmember.php:51 include/nav.php:104 -msgid "Settings" -msgstr "" - -#: Zotlabs/Lib/Apps.php:389 -msgid "Sites" -msgstr "" - -#: Zotlabs/Lib/Apps.php:390 -msgid "Stream" -msgstr "" - -#: Zotlabs/Lib/Apps.php:391 Zotlabs/Widget/Stream_order.php:129 -msgid "Stream Order" -msgstr "" - -#: Zotlabs/Lib/Apps.php:392 -msgid "Suggest" -msgstr "" - -#: Zotlabs/Lib/Apps.php:393 Zotlabs/Module/Settings/Network.php:108 -#: include/features.php:439 -msgid "Suggest Channels" -msgstr "" - -#: Zotlabs/Lib/Apps.php:394 -msgid "Tagadelic" -msgstr "" - -#: Zotlabs/Lib/Apps.php:395 Zotlabs/Widget/Tasklist.php:28 -msgid "Tasks" -msgstr "" - -#: Zotlabs/Lib/Apps.php:396 -msgid "View Bookmarks" -msgstr "" - -#: Zotlabs/Lib/Apps.php:397 Zotlabs/Module/Connedit.php:543 include/nav.php:116 -msgid "View Profile" -msgstr "" - -#: Zotlabs/Lib/Apps.php:398 -msgid "Virtual Lists" -msgstr "" - -#: Zotlabs/Lib/Apps.php:399 Zotlabs/Module/Webpages.php:253 include/nav.php:501 -#: include/conversation.php:2022 -msgid "Webpages" -msgstr "" - -#: Zotlabs/Lib/Apps.php:400 include/nav.php:516 include/conversation.php:2038 -msgid "Wiki" -msgstr "" - -#: Zotlabs/Lib/Apps.php:401 extend/addon/a/zotpost/Mod_zotpost.php:54 -msgid "ZotPost" -msgstr "" - -#: Zotlabs/Lib/Apps.php:576 Zotlabs/Module/Admin/Addons.php:454 -#: Zotlabs/Module/Cdav.php:1050 Zotlabs/Module/Cdav.php:1365 -#: Zotlabs/Module/Connedit.php:868 Zotlabs/Module/Profiles.php:807 -#: Zotlabs/Module/Settings/Oauth.php:43 Zotlabs/Module/Settings/Oauth.php:114 -#: Zotlabs/Module/Settings/Oauth2.php:52 Zotlabs/Module/Settings/Oauth2.php:134 -msgid "Update" -msgstr "" - -#: Zotlabs/Lib/Apps.php:576 Zotlabs/Module/Admin/Addons.php:423 -msgid "Install" -msgstr "" - -#: Zotlabs/Lib/Apps.php:600 -msgid "Purchase" -msgstr "" - -#: Zotlabs/Lib/Apps.php:605 -msgid "Undelete" -msgstr "" - -#: Zotlabs/Lib/Apps.php:615 -msgid "Add to app-tray" -msgstr "" - -#: Zotlabs/Lib/Apps.php:616 -msgid "Remove from app-tray" -msgstr "" - -#: Zotlabs/Lib/Apps.php:617 -msgid "Pin to navbar" -msgstr "" - -#: Zotlabs/Lib/Apps.php:618 -msgid "Unpin from navbar" -msgstr "" - -#: Zotlabs/Module/Lockview.php:79 Zotlabs/Module/Lockview.php:118 +#: Zotlabs/Module/Lockview.php:86 Zotlabs/Module/Lockview.php:126 msgid "Visible to:" msgstr "" -#: Zotlabs/Module/Calendar.php:93 Zotlabs/Module/Events.php:135 +#: Zotlabs/Module/Calendar.php:93 Zotlabs/Module/Events.php:142 msgid "Event can not end before it has started." msgstr "" #: Zotlabs/Module/Calendar.php:95 Zotlabs/Module/Calendar.php:103 -#: Zotlabs/Module/Calendar.php:119 Zotlabs/Module/Events.php:137 -#: Zotlabs/Module/Events.php:146 Zotlabs/Module/Events.php:167 +#: Zotlabs/Module/Calendar.php:120 Zotlabs/Module/Events.php:144 +#: Zotlabs/Module/Events.php:153 Zotlabs/Module/Events.php:175 msgid "Unable to generate preview." msgstr "" -#: Zotlabs/Module/Calendar.php:101 Zotlabs/Module/Events.php:144 +#: Zotlabs/Module/Calendar.php:101 Zotlabs/Module/Events.php:151 msgid "Event title and start time are required." msgstr "" -#: Zotlabs/Module/Calendar.php:117 Zotlabs/Module/Calendar.php:257 -#: Zotlabs/Module/Events.php:165 Zotlabs/Module/Events.php:289 +#: Zotlabs/Module/Calendar.php:118 Zotlabs/Module/Calendar.php:258 +#: Zotlabs/Module/Events.php:173 Zotlabs/Module/Events.php:299 msgid "Event not found." msgstr "" -#: Zotlabs/Module/Calendar.php:252 Zotlabs/Module/Events.php:284 -#: Zotlabs/Module/Like.php:174 Zotlabs/Module/Tagger.php:80 -#: include/text.php:2248 include/conversation.php:130 include/event.php:1212 +#: Zotlabs/Module/Calendar.php:254 Zotlabs/Module/Events.php:295 +#: Zotlabs/Module/Like.php:174 Zotlabs/Module/Tagger.php:88 +#: include/conversation.php:132 include/event.php:1338 include/text.php:2372 msgid "event" msgstr "" -#: Zotlabs/Module/Calendar.php:419 Zotlabs/Module/Events.php:671 +#: Zotlabs/Module/Calendar.php:423 Zotlabs/Module/Events.php:699 msgid "Edit event" msgstr "" -#: Zotlabs/Module/Calendar.php:421 Zotlabs/Module/Events.php:673 +#: Zotlabs/Module/Calendar.php:425 Zotlabs/Module/Events.php:701 msgid "Delete event" msgstr "" -#: Zotlabs/Module/Calendar.php:435 Zotlabs/Module/Cdav.php:947 +#: Zotlabs/Module/Calendar.php:439 Zotlabs/Module/Cdav.php:951 msgid "Link to source" msgstr "" -#: Zotlabs/Module/Calendar.php:449 Zotlabs/Module/Events.php:708 +#: Zotlabs/Module/Calendar.php:453 Zotlabs/Module/Events.php:734 msgid "calendar" msgstr "" -#: Zotlabs/Module/Calendar.php:482 Zotlabs/Module/Events.php:829 +#: Zotlabs/Module/Calendar.php:489 Zotlabs/Module/Events.php:860 msgid "Failed to remove event" msgstr "" -#: Zotlabs/Module/Chanview.php:96 Zotlabs/Module/Cal.php:70 -#: Zotlabs/Module/Block.php:42 Zotlabs/Module/Card_edit.php:46 -#: Zotlabs/Module/Page.php:79 Zotlabs/Module/Wall_upload.php:31 -#: Zotlabs/Module/Superblock.php:62 include/items.php:4412 +#: Zotlabs/Module/Chanview.php:102 Zotlabs/Module/Cal.php:70 +#: Zotlabs/Module/Block.php:49 Zotlabs/Module/Card_edit.php:54 +#: Zotlabs/Module/Page.php:84 Zotlabs/Module/Superblock.php:64 +#: Zotlabs/Module/Wall_upload.php:37 include/items.php:4563 msgid "Channel not found." msgstr "" -#: Zotlabs/Module/Chanview.php:143 Zotlabs/Module/Chanview.php:144 +#: Zotlabs/Module/Chanview.php:149 Zotlabs/Module/Chanview.php:150 msgid "Not available" msgstr "" -#: Zotlabs/Module/Chanview.php:163 +#: Zotlabs/Module/Chanview.php:169 msgid "Cover photo for this channel" msgstr "" -#: Zotlabs/Module/Chanview.php:165 +#: Zotlabs/Module/Chanview.php:171 msgid "Followers" msgstr "" -#: Zotlabs/Module/Chanview.php:166 +#: Zotlabs/Module/Chanview.php:172 msgid "Following" msgstr "" -#: Zotlabs/Module/Chanview.php:169 include/conversation.php:955 +#: Zotlabs/Module/Chanview.php:175 include/conversation.php:955 msgid "Visit" msgstr "" -#: Zotlabs/Module/Chanview.php:170 +#: Zotlabs/Module/Chanview.php:176 msgid "toggle full screen mode" msgstr "" +#: Zotlabs/Module/Admin/Profs.php:94 +msgid "New Profile Field" +msgstr "" + +#: Zotlabs/Module/Admin/Profs.php:95 Zotlabs/Module/Admin/Profs.php:116 +msgid "Field nickname" +msgstr "" + +#: Zotlabs/Module/Admin/Profs.php:95 Zotlabs/Module/Admin/Profs.php:116 +msgid "System name of field" +msgstr "" + +#: Zotlabs/Module/Admin/Profs.php:96 Zotlabs/Module/Admin/Profs.php:117 +msgid "Input type" +msgstr "" + +#: Zotlabs/Module/Admin/Profs.php:97 Zotlabs/Module/Admin/Profs.php:118 +msgid "Field Name" +msgstr "" + +#: Zotlabs/Module/Admin/Profs.php:97 Zotlabs/Module/Admin/Profs.php:118 +msgid "Label on profile pages" +msgstr "" + +#: Zotlabs/Module/Admin/Profs.php:98 Zotlabs/Module/Admin/Profs.php:119 +msgid "Help text" +msgstr "" + +#: Zotlabs/Module/Admin/Profs.php:98 Zotlabs/Module/Admin/Profs.php:119 +msgid "Additional info (optional)" +msgstr "" + +#: Zotlabs/Module/Admin/Profs.php:109 +msgid "Field definition not found" +msgstr "" + +#: Zotlabs/Module/Admin/Profs.php:115 +msgid "Edit Profile Field" +msgstr "" + +#: Zotlabs/Module/Admin/Profs.php:181 +msgid "Profile Fields" +msgstr "" + +#: Zotlabs/Module/Admin/Profs.php:182 +msgid "Basic Profile Fields" +msgstr "" + +#: Zotlabs/Module/Admin/Profs.php:183 +msgid "Advanced Profile Fields" +msgstr "" + +#: Zotlabs/Module/Admin/Profs.php:183 +msgid "(In addition to basic fields)" +msgstr "" + +#: Zotlabs/Module/Admin/Profs.php:185 +msgid "All available fields" +msgstr "" + +#: Zotlabs/Module/Admin/Profs.php:186 +msgid "Custom Fields" +msgstr "" + +#: Zotlabs/Module/Admin/Profs.php:190 +msgid "Create Custom Field" +msgstr "" + +#: Zotlabs/Module/Admin/Queue.php:35 +msgid "Queue Statistics" +msgstr "" + +#: Zotlabs/Module/Admin/Queue.php:36 +msgid "Total Entries" +msgstr "" + +#: Zotlabs/Module/Admin/Queue.php:37 +msgid "Priority" +msgstr "" + +#: Zotlabs/Module/Admin/Queue.php:38 +msgid "Destination URL" +msgstr "" + +#: Zotlabs/Module/Admin/Queue.php:39 +msgid "Mark hub permanently offline" +msgstr "" + +#: Zotlabs/Module/Admin/Queue.php:40 +msgid "Empty queue for this hub" +msgstr "" + +#: Zotlabs/Module/Admin/Queue.php:41 +msgid "Last known contact" +msgstr "" + +#: Zotlabs/Module/Admin/Security.php:126 +msgid "" +"By default, unfiltered HTML is allowed in embedded media. This is inherently " +"insecure." +msgstr "" + +#: Zotlabs/Module/Admin/Security.php:129 +msgid "" +"The recommended setting is to only allow unfiltered HTML from the following " +"sites:" +msgstr "" + +#: Zotlabs/Module/Admin/Security.php:130 +msgid "" +"https://youtube.com/
                    https://www.youtube.com/
                    https://youtu.be/" +"
                    https://vimeo.com/
                    https://soundcloud.com/
                    " +msgstr "" + +#: Zotlabs/Module/Admin/Security.php:131 +msgid "" +"All other embedded content will be filtered, unless " +"embedded content from that site is explicitly blocked." +msgstr "" + +#: Zotlabs/Module/Admin/Security.php:135 Zotlabs/Module/Admin/Site.php:310 +#: Zotlabs/Module/Admin/Themes.php:134 Zotlabs/Module/Admin/Themes.php:168 +#: Zotlabs/Module/Admin/Accounts.php:175 Zotlabs/Module/Admin/Addons.php:343 +#: Zotlabs/Module/Admin/Addons.php:443 Zotlabs/Module/Admin/Channels.php:169 +#: Zotlabs/Module/Admin/Logs.php:82 Zotlabs/Module/Admin.php:179 +msgid "Administration" +msgstr "" + +#: Zotlabs/Module/Admin/Security.php:136 Zotlabs/Widget/Admin.php:31 +msgid "Security" +msgstr "" + +#: Zotlabs/Module/Admin/Security.php:138 +msgid "Block public" +msgstr "" + +#: Zotlabs/Module/Admin/Security.php:138 +msgid "" +"Check to block public access to all otherwise public personal pages on this " +"site unless you are currently authenticated." +msgstr "" + +#: Zotlabs/Module/Admin/Security.php:139 +msgid "Block public search" +msgstr "" + +#: Zotlabs/Module/Admin/Security.php:139 +msgid "" +"Prevent access to search content unless you are currently authenticated." +msgstr "" + +#: Zotlabs/Module/Admin/Security.php:140 Zotlabs/Module/Admin/Site.php:339 +msgid "Block directory from visitors" +msgstr "" + +#: Zotlabs/Module/Admin/Security.php:140 Zotlabs/Module/Admin/Site.php:339 +msgid "Only allow authenticated access to directory." +msgstr "" + +#: Zotlabs/Module/Admin/Security.php:141 +msgid "Hide local directory" +msgstr "" + +#: Zotlabs/Module/Admin/Security.php:141 +msgid "Only use the global directory" +msgstr "" + +#: Zotlabs/Module/Admin/Security.php:142 +msgid "Provide a cloud root directory" +msgstr "" + +#: Zotlabs/Module/Admin/Security.php:142 +msgid "" +"The cloud root directory lists all channel names which provide public files. " +"Otherwise only the names of connections are shown." +msgstr "" + +#: Zotlabs/Module/Admin/Security.php:143 +msgid "Show total disk space available to cloud uploads" +msgstr "" + +#: Zotlabs/Module/Admin/Security.php:144 +msgid "Allow SVG thumbnails in file browser" +msgstr "" + +#: Zotlabs/Module/Admin/Security.php:144 +msgid "WARNING: SVG images may contain malicious code." +msgstr "" + +#: Zotlabs/Module/Admin/Security.php:146 +msgid "Allow embedded (inline) PDF files" +msgstr "" + +#: Zotlabs/Module/Admin/Security.php:147 +msgid "Permit anonymous comments" +msgstr "" + +#: Zotlabs/Module/Admin/Security.php:147 +msgid "" +"Moderation will be performed by channels that select this comment option." +msgstr "" + +#: Zotlabs/Module/Admin/Security.php:148 +msgid "Set \"Transport Security\" HTTP header" +msgstr "" + +#: Zotlabs/Module/Admin/Security.php:149 +msgid "Set \"Content Security Policy\" HTTP header" +msgstr "" + +#: Zotlabs/Module/Admin/Security.php:150 +msgid "Allowed email domains" +msgstr "" + +#: Zotlabs/Module/Admin/Security.php:150 +msgid "" +"Comma separated list of domains which are allowed in email addresses for " +"registrations to this site. Wildcards are accepted. Empty to allow any " +"domains" +msgstr "" + +#: Zotlabs/Module/Admin/Security.php:151 +msgid "Not allowed email domains" +msgstr "" + +#: Zotlabs/Module/Admin/Security.php:151 +msgid "" +"Comma separated list of domains which are not allowed in email addresses for " +"registrations to this site. Wildcards are accepted. Empty to allow any " +"domains, unless allowed domains have been defined." +msgstr "" + +#: Zotlabs/Module/Admin/Security.php:152 +msgid "Allow communications only from these sites" +msgstr "" + +#: Zotlabs/Module/Admin/Security.php:152 Zotlabs/Module/Admin/Security.php:157 +msgid "" +"One site per line. Leave empty to allow communication from anywhere by " +"default" +msgstr "" + +#: Zotlabs/Module/Admin/Security.php:153 +msgid "Block communications from these sites" +msgstr "" + +#: Zotlabs/Module/Admin/Security.php:154 +msgid "Allow communications only from these channels" +msgstr "" + +#: Zotlabs/Module/Admin/Security.php:154 Zotlabs/Module/Admin/Security.php:159 +msgid "" +"One channel (hash) per line. Leave empty to allow communication from any " +"channel by default" +msgstr "" + +#: Zotlabs/Module/Admin/Security.php:155 +msgid "Block communications from these channels" +msgstr "" + +#: Zotlabs/Module/Admin/Security.php:157 +msgid "Allow public stream communications only from these sites" +msgstr "" + +#: Zotlabs/Module/Admin/Security.php:158 +msgid "Block public stream communications from these sites" +msgstr "" + +#: Zotlabs/Module/Admin/Security.php:159 +msgid "Allow public stream communications only from these channels" +msgstr "" + +#: Zotlabs/Module/Admin/Security.php:160 +msgid "Block public stream communications from these channels" +msgstr "" + +#: Zotlabs/Module/Admin/Security.php:163 +msgid "Only allow embeds from secure (SSL) websites and links." +msgstr "" + +#: Zotlabs/Module/Admin/Security.php:164 +msgid "Allow unfiltered embedded HTML content only from these domains" +msgstr "" + +#: Zotlabs/Module/Admin/Security.php:164 +msgid "One site per line. By default embedded content is filtered." +msgstr "" + +#: Zotlabs/Module/Admin/Security.php:165 +msgid "Block embedded HTML from these domains" +msgstr "" + +#: Zotlabs/Module/Admin/Site.php:192 +msgid "Site settings updated." +msgstr "" + +#: Zotlabs/Module/Admin/Site.php:221 include/text.php:3568 +#: view/theme/redbasic/php/config.php:15 +msgid "Default" +msgstr "" + +#: Zotlabs/Module/Admin/Site.php:232 Zotlabs/Module/Settings/Display.php:147 +#, php-format +msgid "%s - (Incompatible)" +msgstr "" + +#: Zotlabs/Module/Admin/Site.php:240 +msgid "mobile" +msgstr "" + +#: Zotlabs/Module/Admin/Site.php:243 +msgid "experimental" +msgstr "" + +#: Zotlabs/Module/Admin/Site.php:246 +msgid "unsupported" +msgstr "" + +#: Zotlabs/Module/Admin/Site.php:288 +msgid "Yes - with approval" +msgstr "" + +#: Zotlabs/Module/Admin/Site.php:294 +msgid "My site is not a public server" +msgstr "" + +#: Zotlabs/Module/Admin/Site.php:295 +msgid "My site provides free public access" +msgstr "" + +#: Zotlabs/Module/Admin/Site.php:296 +msgid "My site provides paid public access" +msgstr "" + +#: Zotlabs/Module/Admin/Site.php:297 +msgid "My site provides free public access and premium paid plans" +msgstr "" + +#: Zotlabs/Module/Admin/Site.php:303 +msgid "Default permission role for new accounts" +msgstr "" + +#: Zotlabs/Module/Admin/Site.php:303 +msgid "" +"This role will be used for the first channel created after registration." +msgstr "" + +#: Zotlabs/Module/Admin/Site.php:311 Zotlabs/Widget/Admin.php:26 +msgid "Site" +msgstr "" + +#: Zotlabs/Module/Admin/Site.php:313 +msgid "Site Configuration" +msgstr "" + +#: Zotlabs/Module/Admin/Site.php:314 Zotlabs/Module/Register.php:287 +msgid "Registration" +msgstr "" + +#: Zotlabs/Module/Admin/Site.php:315 +msgid "File upload" +msgstr "" + +#: Zotlabs/Module/Admin/Site.php:316 +msgid "Policies" +msgstr "" + +#: Zotlabs/Module/Admin/Site.php:317 Zotlabs/Widget/Findpeople.php:26 +msgid "Advanced" +msgstr "" + +#: Zotlabs/Module/Admin/Site.php:319 +msgid "Site name" +msgstr "" + +#: Zotlabs/Module/Admin/Site.php:320 +msgid "Administrator Information" +msgstr "" + +#: Zotlabs/Module/Admin/Site.php:320 +msgid "" +"Contact information for site administrators. Displayed on siteinfo page. " +"BBCode may be used here." +msgstr "" + +#: Zotlabs/Module/Admin/Site.php:321 Zotlabs/Module/Siteinfo.php:38 +msgid "Site Information" +msgstr "" + +#: Zotlabs/Module/Admin/Site.php:321 +msgid "" +"Publicly visible description of this site. Displayed on siteinfo page. " +"BBCode may be used here." +msgstr "" + +#: Zotlabs/Module/Admin/Site.php:322 +msgid "System language" +msgstr "" + +#: Zotlabs/Module/Admin/Site.php:323 +msgid "System theme" +msgstr "" + +#: Zotlabs/Module/Admin/Site.php:323 +msgid "" +"Default system theme - may be over-ridden by user profiles - change theme settings" +msgstr "" + +#: Zotlabs/Module/Admin/Site.php:326 +msgid "ActivityPub protocol" +msgstr "" + +#: Zotlabs/Module/Admin/Site.php:326 +msgid "Provides access to software supporting the ActivityPub protocol." +msgstr "" + +#: Zotlabs/Module/Admin/Site.php:327 +msgid "Maximum image size" +msgstr "" + +#: Zotlabs/Module/Admin/Site.php:327 +msgid "" +"Maximum size in bytes of uploaded images. Default is 0, which means no " +"limits." +msgstr "" + +#: Zotlabs/Module/Admin/Site.php:328 +msgid "Cache all public images" +msgstr "" + +#: Zotlabs/Module/Admin/Site.php:328 +msgid "If disabled, proxy non-SSL images, but do not store locally" +msgstr "" + +#: Zotlabs/Module/Admin/Site.php:329 +msgid "Does this site allow new member registration?" +msgstr "" + +#: Zotlabs/Module/Admin/Site.php:330 +msgid "Invitation only" +msgstr "" + +#: Zotlabs/Module/Admin/Site.php:330 +msgid "" +"Only allow new member registrations with an invitation code. New member " +"registration must be allowed for this to work." +msgstr "" + +#: Zotlabs/Module/Admin/Site.php:332 +msgid "Minimum age" +msgstr "" + +#: Zotlabs/Module/Admin/Site.php:332 +msgid "Minimum age (in years) for who may register on this site." +msgstr "" + +#: Zotlabs/Module/Admin/Site.php:333 +msgid "Which best describes the types of account offered by this hub?" +msgstr "" + +#: Zotlabs/Module/Admin/Site.php:333 +msgid "" +"If a public server policy is selected, this information may be displayed on " +"the public server site list." +msgstr "" + +#: Zotlabs/Module/Admin/Site.php:334 +msgid "Register text" +msgstr "" + +#: Zotlabs/Module/Admin/Site.php:334 +msgid "Will be displayed prominently on the registration page." +msgstr "" + +#: Zotlabs/Module/Admin/Site.php:336 +msgid "Site homepage to show visitors (default: login box)" +msgstr "" + +#: Zotlabs/Module/Admin/Site.php:336 +msgid "" +"example: 'public' to show public stream, 'page/sys/home' to show a system " +"webpage called 'home' or 'include:home.html' to include a file." +msgstr "" + +#: Zotlabs/Module/Admin/Site.php:337 +msgid "Preserve site homepage URL" +msgstr "" + +#: Zotlabs/Module/Admin/Site.php:337 +msgid "" +"Present the site homepage in a frame at the original location instead of " +"redirecting" +msgstr "" + +#: Zotlabs/Module/Admin/Site.php:338 +msgid "Accounts abandoned after x days" +msgstr "" + +#: Zotlabs/Module/Admin/Site.php:338 +msgid "" +"Will not waste system resources polling external sites for abandonded " +"accounts. Enter 0 for no time limit." +msgstr "" + +#: Zotlabs/Module/Admin/Site.php:340 +msgid "Verify Email Addresses" +msgstr "" + +#: Zotlabs/Module/Admin/Site.php:340 +msgid "" +"Check to verify email addresses used in account registration (recommended)." +msgstr "" + +#: Zotlabs/Module/Admin/Site.php:341 +msgid "Force publish in directory" +msgstr "" + +#: Zotlabs/Module/Admin/Site.php:341 +msgid "" +"Check to force all profiles on this site to be listed in the site directory." +msgstr "" + +#: Zotlabs/Module/Admin/Site.php:343 +msgid "Public stream" +msgstr "" + +#: Zotlabs/Module/Admin/Site.php:343 +msgid "Provide a Public stream on your site. This content is unmoderated." +msgstr "" + +#: Zotlabs/Module/Admin/Site.php:344 +msgid "the Public stream is disabled" +msgstr "" + +#: Zotlabs/Module/Admin/Site.php:345 +msgid "the Public stream contains public conversations from this site only" +msgstr "" + +#: Zotlabs/Module/Admin/Site.php:346 +msgid "" +"the Public stream contains public conversations from anywhere on the internet" +msgstr "" + +#: Zotlabs/Module/Admin/Site.php:349 +msgid "Allow anybody on the internet to access the Public stream" +msgstr "" + +#: Zotlabs/Module/Admin/Site.php:349 +msgid "" +"Default is to only allow viewing by site members. Warning: this content is " +"unmoderated." +msgstr "" + +#: Zotlabs/Module/Admin/Site.php:350 +msgid "Show numbers of likes and dislikes in conversations" +msgstr "" + +#: Zotlabs/Module/Admin/Site.php:350 +msgid "" +"If disabled, the presence of likes and dislikes will be shown, but without " +"totals." +msgstr "" + +#: Zotlabs/Module/Admin/Site.php:351 +msgid "Permit animated profile photos" +msgstr "" + +#: Zotlabs/Module/Admin/Site.php:351 +msgid "Changing this may take several days to work through the system" +msgstr "" + +#: Zotlabs/Module/Admin/Site.php:352 +msgid "Only import Public stream posts with this text" +msgstr "" + +#: Zotlabs/Module/Admin/Site.php:352 Zotlabs/Module/Admin/Site.php:353 +msgid "" +"words one per line or #tags or /patterns/ or lang=xx, leave blank to import " +"all posts" +msgstr "" + +#: Zotlabs/Module/Admin/Site.php:353 +msgid "Do not import Public stream posts with this text" +msgstr "" + +#: Zotlabs/Module/Admin/Site.php:354 +msgid "Maximum number of imported friends of friends" +msgstr "" + +#: Zotlabs/Module/Admin/Site.php:354 +msgid "" +"Warning: higher numbers will improve the quality of friend suggestions and " +"directory results but can exponentially increase resource usage" +msgstr "" + +#: Zotlabs/Module/Admin/Site.php:355 +msgid "Login on Homepage" +msgstr "" + +#: Zotlabs/Module/Admin/Site.php:355 +msgid "" +"Present a login box to visitors on the home page if no other content has " +"been configured." +msgstr "" + +#: Zotlabs/Module/Admin/Site.php:356 +msgid "Enable context help" +msgstr "" + +#: Zotlabs/Module/Admin/Site.php:356 +msgid "" +"Display contextual help for the current page when the help button is pressed." +msgstr "" + +#: Zotlabs/Module/Admin/Site.php:357 +msgid "Reply-to email address for system generated email." +msgstr "" + +#: Zotlabs/Module/Admin/Site.php:358 +msgid "Sender (From) email address for system generated email." +msgstr "" + +#: Zotlabs/Module/Admin/Site.php:359 +msgid "Display name of email sender for system generated email." +msgstr "" + +#: Zotlabs/Module/Admin/Site.php:360 +msgid "Directory Server URL" +msgstr "" + +#: Zotlabs/Module/Admin/Site.php:360 +msgid "Default directory server" +msgstr "" + +#: Zotlabs/Module/Admin/Site.php:361 +msgid "Proxy user" +msgstr "" + +#: Zotlabs/Module/Admin/Site.php:362 +msgid "Proxy URL" +msgstr "" + +#: Zotlabs/Module/Admin/Site.php:363 +msgid "Network fetch timeout" +msgstr "" + +#: Zotlabs/Module/Admin/Site.php:363 Zotlabs/Module/Admin/Site.php:364 +msgid "Value is in seconds. Set to 0 for unlimited (not recommended)." +msgstr "" + +#: Zotlabs/Module/Admin/Site.php:364 +msgid "Network post timeout" +msgstr "" + +#: Zotlabs/Module/Admin/Site.php:365 +msgid "Delivery interval" +msgstr "" + +#: Zotlabs/Module/Admin/Site.php:365 +msgid "" +"Delay background delivery processes by this many seconds to reduce system " +"load. Recommend: 4-5 for shared hosts, 2-3 for virtual private servers. 0-1 " +"for large dedicated servers." +msgstr "" + +#: Zotlabs/Module/Admin/Site.php:366 +msgid "Deliveries per process" +msgstr "" + +#: Zotlabs/Module/Admin/Site.php:366 +msgid "" +"Number of deliveries to attempt in a single operating system process. Adjust " +"if necessary to tune system performance. Recommend: 1-5." +msgstr "" + +#: Zotlabs/Module/Admin/Site.php:367 +msgid "Queue Threshold" +msgstr "" + +#: Zotlabs/Module/Admin/Site.php:367 +msgid "" +"Always defer immediate delivery if queue contains more than this number of " +"entries." +msgstr "" + +#: Zotlabs/Module/Admin/Site.php:368 +msgid "Poll interval" +msgstr "" + +#: Zotlabs/Module/Admin/Site.php:368 +msgid "" +"Delay background polling processes by this many seconds to reduce system " +"load. If 0, use delivery interval." +msgstr "" + +#: Zotlabs/Module/Admin/Site.php:369 +msgid "Path to ImageMagick convert program" +msgstr "" + +#: Zotlabs/Module/Admin/Site.php:369 +msgid "" +"If set, use this program to generate photo thumbnails for huge images ( > " +"4000 pixels in either dimension), otherwise memory exhaustion may occur. " +"Example: /usr/bin/convert" +msgstr "" + +#: Zotlabs/Module/Admin/Site.php:370 +msgid "Maximum Load Average" +msgstr "" + +#: Zotlabs/Module/Admin/Site.php:370 +msgid "" +"Maximum system load before delivery and poll processes are deferred - " +"default 50." +msgstr "" + +#: Zotlabs/Module/Admin/Site.php:371 +msgid "Expiration period in days for imported streams and cached images" +msgstr "" + +#: Zotlabs/Module/Admin/Site.php:371 +msgid "0 for no expiration of imported content" +msgstr "" + +#: Zotlabs/Module/Admin/Site.php:372 +msgid "" +"Do not expire any posts which have comments less than this many days ago" +msgstr "" + +#: Zotlabs/Module/Admin/Site.php:373 +msgid "" +"Public servers: Optional landing (marketing) webpage for new registrants" +msgstr "" + +#: Zotlabs/Module/Admin/Site.php:373 +#, php-format +msgid "Create this page first. Default is %s/register" +msgstr "" + +#: Zotlabs/Module/Admin/Site.php:374 +msgid "Page to display after creating a new channel" +msgstr "" + +#: Zotlabs/Module/Admin/Site.php:374 +msgid "Default: profiles" +msgstr "" + +#: Zotlabs/Module/Admin/Site.php:375 +msgid "Site location" +msgstr "" + +#: Zotlabs/Module/Admin/Site.php:375 +msgid "Region or country - shared with other sites" +msgstr "" + +#: Zotlabs/Module/Admin/Themes.php:29 +msgid "Theme settings updated." +msgstr "" + +#: Zotlabs/Module/Admin/Themes.php:69 +msgid "No themes found." +msgstr "" + +#: Zotlabs/Module/Admin/Themes.php:80 Zotlabs/Module/Admin/Addons.php:259 +#: Zotlabs/Module/Admin.php:89 Zotlabs/Module/Display.php:64 +#: Zotlabs/Module/Display.php:512 Zotlabs/Module/Filestorage.php:37 +#: Zotlabs/Module/Thing.php:108 Zotlabs/Module/Viewsrc.php:28 +#: Zotlabs/Module/Inspect.php:34 +#: extend/addon/a/flashcards/Mod_Flashcards.php:284 +#: extend/addon/a/flashcards/Mod_Flashcards.php:285 include/items.php:3894 +msgid "Item not found." +msgstr "" + +#: Zotlabs/Module/Admin/Themes.php:105 Zotlabs/Module/Admin/Addons.php:311 +msgid "Disable" +msgstr "" + +#: Zotlabs/Module/Admin/Themes.php:108 Zotlabs/Module/Admin/Addons.php:314 +msgid "Enable" +msgstr "" + +#: Zotlabs/Module/Admin/Themes.php:127 +msgid "Screenshot" +msgstr "" + +#: Zotlabs/Module/Admin/Themes.php:135 Zotlabs/Module/Admin/Themes.php:169 +#: Zotlabs/Widget/Admin.php:34 +msgid "Themes" +msgstr "" + +#: Zotlabs/Module/Admin/Themes.php:136 Zotlabs/Module/Admin/Addons.php:345 +msgid "Toggle" +msgstr "" + +#: Zotlabs/Module/Admin/Themes.php:146 Zotlabs/Module/Admin/Addons.php:353 +msgid "Author: " +msgstr "" + +#: Zotlabs/Module/Admin/Themes.php:147 Zotlabs/Module/Admin/Addons.php:354 +msgid "Maintainer: " +msgstr "" + +#: Zotlabs/Module/Admin/Themes.php:174 +msgid "[Experimental]" +msgstr "" + +#: Zotlabs/Module/Admin/Themes.php:175 +msgid "[Unsupported]" +msgstr "" + +#: Zotlabs/Module/Admin/Account_edit.php:31 +#, php-format +msgid "Password changed for account %d." +msgstr "" + +#: Zotlabs/Module/Admin/Account_edit.php:47 +msgid "Account settings updated." +msgstr "" + +#: Zotlabs/Module/Admin/Account_edit.php:66 +msgid "Account not found." +msgstr "" + +#: Zotlabs/Module/Admin/Account_edit.php:73 +msgid "Account Edit" +msgstr "" + +#: Zotlabs/Module/Admin/Account_edit.php:74 +msgid "New Password" +msgstr "" + +#: Zotlabs/Module/Admin/Account_edit.php:75 +msgid "New Password again" +msgstr "" + +#: Zotlabs/Module/Admin/Account_edit.php:76 +msgid "Account language (for emails)" +msgstr "" + +#: Zotlabs/Module/Admin/Account_edit.php:77 +msgid "Service class" +msgstr "" + +#: Zotlabs/Module/Admin/Accounts.php:40 +#, php-format +msgid "%s account blocked/unblocked" +msgid_plural "%s accounts blocked/unblocked" +msgstr[0] "" +msgstr[1] "" + +#: Zotlabs/Module/Admin/Accounts.php:48 +#, php-format +msgid "%s account deleted" +msgid_plural "%s accounts deleted" +msgstr[0] "" +msgstr[1] "" + +#: Zotlabs/Module/Admin/Accounts.php:88 +msgid "Account not found" +msgstr "" + +#: Zotlabs/Module/Admin/Accounts.php:99 include/channel.php:2312 +#, php-format +msgid "Account '%s' deleted" +msgstr "" + +#: Zotlabs/Module/Admin/Accounts.php:108 +#, php-format +msgid "Account '%s' blocked" +msgstr "" + +#: Zotlabs/Module/Admin/Accounts.php:117 +#, php-format +msgid "Account '%s' unblocked" +msgstr "" + +#: Zotlabs/Module/Admin/Accounts.php:176 Zotlabs/Module/Admin/Accounts.php:189 +#: Zotlabs/Module/Admin.php:123 Zotlabs/Widget/Admin.php:29 +msgid "Accounts" +msgstr "" + +#: Zotlabs/Module/Admin/Accounts.php:178 Zotlabs/Module/Admin/Channels.php:172 +msgid "select all" +msgstr "" + +#: Zotlabs/Module/Admin/Accounts.php:179 +msgid "Registrations waiting for confirm" +msgstr "" + +#: Zotlabs/Module/Admin/Accounts.php:180 +msgid "Request date" +msgstr "" + +#: Zotlabs/Module/Admin/Accounts.php:180 Zotlabs/Module/Admin/Accounts.php:192 +#: Zotlabs/Module/Cdav.php:1354 Zotlabs/Module/Connedit.php:867 +#: Zotlabs/Module/Profiles.php:836 include/network.php:1690 +msgid "Email" +msgstr "" + +#: Zotlabs/Module/Admin/Accounts.php:181 +msgid "No registrations." +msgstr "" + +#: Zotlabs/Module/Admin/Accounts.php:182 Zotlabs/Module/Connections.php:341 +#: include/conversation.php:653 +msgid "Approve" +msgstr "" + +#: Zotlabs/Module/Admin/Accounts.php:183 Zotlabs/Module/Authorize.php:39 +msgid "Deny" +msgstr "" + +#: Zotlabs/Module/Admin/Accounts.php:185 Zotlabs/Module/Connedit.php:576 +#: Zotlabs/Widget/Sblock.php:21 +msgid "Block" +msgstr "" + +#: Zotlabs/Module/Admin/Accounts.php:186 Zotlabs/Module/Connedit.php:576 +msgid "Unblock" +msgstr "" + +#: Zotlabs/Module/Admin/Accounts.php:191 +msgid "ID" +msgstr "" + +#: Zotlabs/Module/Admin/Accounts.php:193 +msgid "All Channels" +msgstr "" + +#: Zotlabs/Module/Admin/Accounts.php:194 +msgid "Register date" +msgstr "" + +#: Zotlabs/Module/Admin/Accounts.php:195 +msgid "Last login" +msgstr "" + +#: Zotlabs/Module/Admin/Accounts.php:196 +msgid "Expires" +msgstr "" + +#: Zotlabs/Module/Admin/Accounts.php:197 +msgid "Service Class" +msgstr "" + +#: Zotlabs/Module/Admin/Accounts.php:199 +msgid "" +"Selected accounts will be deleted!\\n\\nEverything these accounts had posted " +"on this site will be permanently deleted!\\n\\nAre you sure?" +msgstr "" + +#: Zotlabs/Module/Admin/Accounts.php:200 +msgid "" +"The account {0} will be deleted!\\n\\nEverything this account has posted on " +"this site will be permanently deleted!\\n\\nAre you sure?" +msgstr "" + +#: Zotlabs/Module/Admin/Addons.php:289 +#, php-format +msgid "Plugin %s disabled." +msgstr "" + +#: Zotlabs/Module/Admin/Addons.php:294 +#, php-format +msgid "Plugin %s enabled." +msgstr "" + +#: Zotlabs/Module/Admin/Addons.php:344 Zotlabs/Module/Admin/Addons.php:444 +#: Zotlabs/Widget/Admin.php:33 +msgid "Addons" +msgstr "" + +#: Zotlabs/Module/Admin/Addons.php:355 +msgid "Minimum project version: " +msgstr "" + +#: Zotlabs/Module/Admin/Addons.php:356 +msgid "Maximum project version: " +msgstr "" + +#: Zotlabs/Module/Admin/Addons.php:357 +msgid "Minimum PHP version: " +msgstr "" + +#: Zotlabs/Module/Admin/Addons.php:358 +msgid "Compatible Server Roles: " +msgstr "" + +#: Zotlabs/Module/Admin/Addons.php:359 +msgid "Requires: " +msgstr "" + +#: Zotlabs/Module/Admin/Addons.php:360 Zotlabs/Module/Admin/Addons.php:449 +msgid "Disabled - version incompatibility" +msgstr "" + +#: Zotlabs/Module/Admin/Addons.php:417 +msgid "Enter the public git repository URL of the addon repo." +msgstr "" + +#: Zotlabs/Module/Admin/Addons.php:418 +msgid "Addon repo git URL" +msgstr "" + +#: Zotlabs/Module/Admin/Addons.php:419 +msgid "Custom repo name" +msgstr "" + +#: Zotlabs/Module/Admin/Addons.php:419 +msgid "(optional)" +msgstr "" + +#: Zotlabs/Module/Admin/Addons.php:420 +msgid "Download Addon Repo" +msgstr "" + +#: Zotlabs/Module/Admin/Addons.php:428 +msgid "Install new repo" +msgstr "" + +#: Zotlabs/Module/Admin/Addons.php:430 Zotlabs/Module/Admin/Cover_photo.php:378 +#: Zotlabs/Module/Admin/Profile_photo.php:474 Zotlabs/Module/Cdav.php:1060 +#: Zotlabs/Module/Cdav.php:1368 Zotlabs/Module/Connedit.php:881 +#: Zotlabs/Module/Cover_photo.php:424 Zotlabs/Module/Editlayout.php:152 +#: Zotlabs/Module/Editwebpage.php:184 Zotlabs/Module/Filer.php:62 +#: Zotlabs/Module/Fbrowser.php:73 Zotlabs/Module/Fbrowser.php:96 +#: Zotlabs/Module/Card_edit.php:140 Zotlabs/Module/Editblock.php:154 +#: Zotlabs/Module/Editpost.php:147 Zotlabs/Module/Profiles.php:850 +#: Zotlabs/Module/Profile_photo.php:513 Zotlabs/Module/Settings/Oauth.php:96 +#: Zotlabs/Module/Settings/Oauth.php:124 Zotlabs/Module/Settings/Oauth2.php:114 +#: Zotlabs/Module/Settings/Oauth2.php:144 Zotlabs/Module/Tagrm.php:21 +#: Zotlabs/Module/Tagrm.php:152 include/conversation.php:1427 +#: include/conversation.php:1493 include/conversation.php:1495 +#: include/conversation.php:1497 +msgid "Cancel" +msgstr "" + +#: Zotlabs/Module/Admin/Addons.php:452 +msgid "Manage Repos" +msgstr "" + +#: Zotlabs/Module/Admin/Addons.php:453 +msgid "Installed Addon Repositories" +msgstr "" + +#: Zotlabs/Module/Admin/Addons.php:454 +msgid "Install a New Addon Repository" +msgstr "" + +#: Zotlabs/Module/Admin/Addons.php:460 Zotlabs/Module/Cdav.php:1055 +#: Zotlabs/Module/Cdav.php:1366 Zotlabs/Module/Connedit.php:879 +#: Zotlabs/Module/Profiles.php:848 Zotlabs/Module/Settings/Oauth.php:45 +#: Zotlabs/Module/Settings/Oauth.php:123 Zotlabs/Module/Settings/Oauth2.php:55 +#: Zotlabs/Module/Settings/Oauth2.php:143 +msgid "Update" +msgstr "" + +#: Zotlabs/Module/Admin/Addons.php:461 +msgid "Switch branch" +msgstr "" + +#: Zotlabs/Module/Admin/Addons.php:462 Zotlabs/Module/Photos.php:1068 +#: Zotlabs/Module/Tagrm.php:151 Zotlabs/Module/Superblock.php:234 +#: Zotlabs/Module/Superblock.php:251 +msgid "Remove" +msgstr "" + +#: Zotlabs/Module/Admin/Channels.php:37 +#, php-format +msgid "%s channel censored/uncensored" +msgid_plural "%s channels censored/uncensored" +msgstr[0] "" +msgstr[1] "" + +#: Zotlabs/Module/Admin/Channels.php:47 +#, php-format +msgid "%s channel code allowed/disallowed" +msgid_plural "%s channels code allowed/disallowed" +msgstr[0] "" +msgstr[1] "" + +#: Zotlabs/Module/Admin/Channels.php:53 +#, php-format +msgid "%s channel deleted" +msgid_plural "%s channels deleted" +msgstr[0] "" +msgstr[1] "" + +#: Zotlabs/Module/Admin/Channels.php:74 +msgid "Channel not found" +msgstr "" + +#: Zotlabs/Module/Admin/Channels.php:85 +#, php-format +msgid "Channel '%s' deleted" +msgstr "" + +#: Zotlabs/Module/Admin/Channels.php:100 +#, php-format +msgid "Channel '%s' censored" +msgstr "" + +#: Zotlabs/Module/Admin/Channels.php:100 +#, php-format +msgid "Channel '%s' uncensored" +msgstr "" + +#: Zotlabs/Module/Admin/Channels.php:114 +#, php-format +msgid "Channel '%s' code allowed" +msgstr "" + +#: Zotlabs/Module/Admin/Channels.php:114 +#, php-format +msgid "Channel '%s' code disallowed" +msgstr "" + +#: Zotlabs/Module/Admin/Channels.php:170 Zotlabs/Module/Admin.php:142 +#: Zotlabs/Module/Manage.php:182 Zotlabs/Widget/Admin.php:30 +#: include/nav.php:100 +msgid "Channels" +msgstr "" + +#: Zotlabs/Module/Admin/Channels.php:174 Zotlabs/Module/Connedit.php:592 +#: Zotlabs/Module/Directory.php:391 +msgid "Censor" +msgstr "" + +#: Zotlabs/Module/Admin/Channels.php:175 Zotlabs/Module/Connedit.php:592 +#: Zotlabs/Module/Directory.php:391 +msgid "Uncensor" +msgstr "" + +#: Zotlabs/Module/Admin/Channels.php:176 +msgid "Allow Code" +msgstr "" + +#: Zotlabs/Module/Admin/Channels.php:177 +msgid "Disallow Code" +msgstr "" + +#: Zotlabs/Module/Admin/Channels.php:178 include/conversation.php:1978 +#: include/nav.php:400 +msgid "Channel" +msgstr "" + +#: Zotlabs/Module/Admin/Channels.php:182 +msgid "UID" +msgstr "" + +#: Zotlabs/Module/Admin/Channels.php:183 Zotlabs/Module/Cdav.php:1350 +#: Zotlabs/Module/Connedit.php:863 Zotlabs/Module/Connections.php:236 +#: Zotlabs/Module/Chat.php:256 Zotlabs/Module/Lists.php:202 +#: Zotlabs/Module/Settings/Oauth.php:97 Zotlabs/Module/Settings/Oauth.php:125 +#: Zotlabs/Module/Settings/Oauth2.php:115 +#: Zotlabs/Module/Settings/Oauth2.php:145 Zotlabs/Module/Sharedwithme.php:107 +#: Zotlabs/Storage/Browser.php:313 Zotlabs/Widget/Activity_filter.php:294 +msgid "Name" +msgstr "" + +#: Zotlabs/Module/Admin/Channels.php:184 Zotlabs/Module/Cdav.php:1357 +#: Zotlabs/Module/Connedit.php:870 Zotlabs/Module/Locs.php:133 +#: Zotlabs/Module/Profiles.php:527 Zotlabs/Module/Profiles.php:839 +msgid "Address" +msgstr "" + +#: Zotlabs/Module/Admin/Channels.php:186 +msgid "" +"Selected channels will be deleted!\\n\\nEverything that was posted in these " +"channels on this site will be permanently deleted!\\n\\nAre you sure?" +msgstr "" + +#: Zotlabs/Module/Admin/Channels.php:187 +msgid "" +"The channel {0} will be deleted!\\n\\nEverything that was posted in this " +"channel on this site will be permanently deleted!\\n\\nAre you sure?" +msgstr "" + +#: Zotlabs/Module/Admin/Cover_photo.php:64 +#: Zotlabs/Module/Admin/Profile_photo.php:67 Zotlabs/Module/Cover_photo.php:64 +#: Zotlabs/Module/Profile_photo.php:64 +msgid "Image uploaded but image cropping failed." +msgstr "" + +#: Zotlabs/Module/Admin/Cover_photo.php:175 +#: Zotlabs/Module/Admin/Cover_photo.php:259 Zotlabs/Module/Cover_photo.php:175 +#: Zotlabs/Module/Cover_photo.php:262 +msgid "Cover Photos" +msgstr "" + +#: Zotlabs/Module/Admin/Cover_photo.php:199 +#: Zotlabs/Module/Admin/Profile_photo.php:145 +#: Zotlabs/Module/Cover_photo.php:199 Zotlabs/Module/Profile_photo.php:144 +msgid "Image resize failed." +msgstr "" + +#: Zotlabs/Module/Admin/Cover_photo.php:208 +#: Zotlabs/Module/Admin/Profile_photo.php:191 +#: Zotlabs/Module/Cover_photo.php:211 Zotlabs/Module/Profile_photo.php:227 +#: include/photos.php:200 +msgid "Unable to process image" +msgstr "" + +#: Zotlabs/Module/Admin/Cover_photo.php:317 +#: Zotlabs/Module/Admin/Cover_photo.php:333 +#: Zotlabs/Module/Admin/Profile_photo.php:354 +#: Zotlabs/Module/Admin/Profile_photo.php:397 +#: Zotlabs/Module/Cover_photo.php:364 Zotlabs/Module/Cover_photo.php:380 +#: Zotlabs/Module/Profile_photo.php:386 Zotlabs/Module/Profile_photo.php:437 +msgid "Photo not available." +msgstr "" + +#: Zotlabs/Module/Admin/Cover_photo.php:369 Zotlabs/Module/Cover_photo.php:415 +msgid "Your cover photo may be visible to anybody on the internet" +msgstr "" + +#: Zotlabs/Module/Admin/Cover_photo.php:371 +#: Zotlabs/Module/Admin/Profile_photo.php:465 +#: Zotlabs/Module/Cover_photo.php:417 Zotlabs/Module/Profile_photo.php:504 +msgid "Upload File:" +msgstr "" + +#: Zotlabs/Module/Admin/Cover_photo.php:372 +#: Zotlabs/Module/Admin/Profile_photo.php:466 +#: Zotlabs/Module/Cover_photo.php:418 Zotlabs/Module/Profile_photo.php:505 +msgid "Select a profile:" +msgstr "" + +#: Zotlabs/Module/Admin/Cover_photo.php:373 Zotlabs/Module/Cover_photo.php:419 +msgid "Change Cover Photo" +msgstr "" + +#: Zotlabs/Module/Admin/Cover_photo.php:374 +#: Zotlabs/Module/Admin/Profile_photo.php:468 +#: Zotlabs/Module/Cover_photo.php:420 Zotlabs/Module/Embedphotos.php:225 +#: Zotlabs/Module/Profile_photo.php:507 Zotlabs/Module/Photos.php:746 +#: Zotlabs/Storage/Browser.php:422 Zotlabs/Widget/Album.php:107 +#: Zotlabs/Widget/Cdav.php:154 Zotlabs/Widget/Cdav.php:188 +#: Zotlabs/Widget/Portfolio.php:122 +msgid "Upload" +msgstr "" + +#: Zotlabs/Module/Admin/Cover_photo.php:376 +#: Zotlabs/Module/Admin/Cover_photo.php:377 +#: Zotlabs/Module/Admin/Profile_photo.php:472 +#: Zotlabs/Module/Admin/Profile_photo.php:473 +#: Zotlabs/Module/Cover_photo.php:422 Zotlabs/Module/Cover_photo.php:423 +#: Zotlabs/Module/Profile_photo.php:511 Zotlabs/Module/Profile_photo.php:512 +msgid "Use a photo from your albums" +msgstr "" + +#: Zotlabs/Module/Admin/Cover_photo.php:379 +#: Zotlabs/Module/Admin/Profile_photo.php:475 +#: Zotlabs/Module/Cover_photo.php:425 Zotlabs/Module/Profile_photo.php:514 +#: include/conversation.php:1428 include/conversation.php:1492 +#: include/conversation.php:1494 include/conversation.php:1496 +msgid "OK" +msgstr "" + +#: Zotlabs/Module/Admin/Cover_photo.php:380 +#: Zotlabs/Module/Admin/Profile_photo.php:476 +#: Zotlabs/Module/Cover_photo.php:426 Zotlabs/Module/Profile_photo.php:515 +#: include/conversation.php:1292 +msgid "Choose images to embed" +msgstr "" + +#: Zotlabs/Module/Admin/Cover_photo.php:381 +#: Zotlabs/Module/Admin/Profile_photo.php:477 +#: Zotlabs/Module/Cover_photo.php:427 Zotlabs/Module/Profile_photo.php:516 +#: include/conversation.php:1293 +msgid "Choose an album" +msgstr "" + +#: Zotlabs/Module/Admin/Cover_photo.php:382 +#: Zotlabs/Module/Admin/Profile_photo.php:478 +#: Zotlabs/Module/Cover_photo.php:428 Zotlabs/Module/Profile_photo.php:517 +msgid "Choose a different album" +msgstr "" + +#: Zotlabs/Module/Admin/Cover_photo.php:383 +#: Zotlabs/Module/Admin/Profile_photo.php:479 +#: Zotlabs/Module/Cover_photo.php:429 Zotlabs/Module/Profile_photo.php:518 +#: include/conversation.php:1295 +msgid "Error getting album list" +msgstr "" + +#: Zotlabs/Module/Admin/Cover_photo.php:384 +#: Zotlabs/Module/Admin/Profile_photo.php:480 +#: Zotlabs/Module/Cover_photo.php:430 Zotlabs/Module/Profile_photo.php:519 +#: include/conversation.php:1296 +msgid "Error getting photo link" +msgstr "" + +#: Zotlabs/Module/Admin/Cover_photo.php:385 +#: Zotlabs/Module/Admin/Profile_photo.php:481 +#: Zotlabs/Module/Cover_photo.php:431 Zotlabs/Module/Profile_photo.php:520 +#: include/conversation.php:1297 +msgid "Error getting album" +msgstr "" + +#: Zotlabs/Module/Admin/Cover_photo.php:387 +#: Zotlabs/Module/Admin/Profile_photo.php:483 +#: Zotlabs/Module/Cover_photo.php:433 Zotlabs/Module/Profile_photo.php:522 +msgid "Select previously uploaded photo" +msgstr "" + +#: Zotlabs/Module/Admin/Cover_photo.php:403 +#: Zotlabs/Module/Admin/Profile_photo.php:498 +#: Zotlabs/Module/Cover_photo.php:449 Zotlabs/Module/Profile_photo.php:537 +msgid "Crop Image" +msgstr "" + +#: Zotlabs/Module/Admin/Cover_photo.php:404 +#: Zotlabs/Module/Admin/Profile_photo.php:499 +#: Zotlabs/Module/Cover_photo.php:450 Zotlabs/Module/Profile_photo.php:538 +msgid "Please adjust the image cropping for optimum viewing." +msgstr "" + +#: Zotlabs/Module/Admin/Cover_photo.php:406 +#: Zotlabs/Module/Admin/Profile_photo.php:501 +#: Zotlabs/Module/Cover_photo.php:452 Zotlabs/Module/Profile_photo.php:540 +msgid "Done Editing" +msgstr "" + +#: Zotlabs/Module/Admin/Dbsync.php:20 Zotlabs/Module/Admin/Dbsync.php:55 +msgid "Update has been marked successful" +msgstr "" + +#: Zotlabs/Module/Admin/Dbsync.php:32 +#, php-format +msgid "Verification of update %s failed. Check system logs." +msgstr "" + +#: Zotlabs/Module/Admin/Dbsync.php:34 Zotlabs/Module/Admin/Dbsync.php:69 +#, php-format +msgid "Update %s was successfully applied." +msgstr "" + +#: Zotlabs/Module/Admin/Dbsync.php:37 +#, php-format +msgid "Verifying update %s did not return a status. Unknown if it succeeded." +msgstr "" + +#: Zotlabs/Module/Admin/Dbsync.php:40 +#, php-format +msgid "Update %s does not contain a verification function." +msgstr "" + +#: Zotlabs/Module/Admin/Dbsync.php:43 Zotlabs/Module/Admin/Dbsync.php:75 +#, php-format +msgid "Update function %s could not be found." +msgstr "" + +#: Zotlabs/Module/Admin/Dbsync.php:67 +#, php-format +msgid "Executing update procedure %s failed. Check system logs." +msgstr "" + +#: Zotlabs/Module/Admin/Dbsync.php:72 +#, php-format +msgid "" +"Update %s did not return a status. It cannot be determined if it was " +"successful." +msgstr "" + +#: Zotlabs/Module/Admin/Dbsync.php:95 +msgid "Failed Updates" +msgstr "" + +#: Zotlabs/Module/Admin/Dbsync.php:97 +msgid "Mark success (if update was manually applied)" +msgstr "" + +#: Zotlabs/Module/Admin/Dbsync.php:98 +msgid "Attempt to verify this update if a verification procedure exists" +msgstr "" + +#: Zotlabs/Module/Admin/Dbsync.php:99 +msgid "Attempt to execute this update step automatically" +msgstr "" + +#: Zotlabs/Module/Admin/Dbsync.php:103 +msgid "No failed updates." +msgstr "" + #: Zotlabs/Module/Admin/Logs.php:28 msgid "Log settings updated." msgstr "" -#: Zotlabs/Module/Admin/Logs.php:82 Zotlabs/Module/Admin/Channels.php:152 -#: Zotlabs/Module/Admin/Accounts.php:166 Zotlabs/Module/Admin/Addons.php:339 -#: Zotlabs/Module/Admin/Addons.php:437 Zotlabs/Module/Admin/Themes.php:122 -#: Zotlabs/Module/Admin/Themes.php:156 Zotlabs/Module/Admin/Site.php:299 -#: Zotlabs/Module/Admin/Security.php:134 Zotlabs/Module/Admin.php:175 -msgid "Administration" -msgstr "" - -#: Zotlabs/Module/Admin/Logs.php:83 Zotlabs/Widget/Admin.php:50 -#: Zotlabs/Widget/Admin.php:60 +#: Zotlabs/Module/Admin/Logs.php:83 Zotlabs/Widget/Admin.php:54 +#: Zotlabs/Widget/Admin.php:64 msgid "Logs" msgstr "" @@ -1902,1520 +3283,150 @@ msgstr "" msgid "Log level" msgstr "" -#: Zotlabs/Module/Admin/Queue.php:33 -msgid "Queue Statistics" -msgstr "" - -#: Zotlabs/Module/Admin/Queue.php:34 -msgid "Total Entries" -msgstr "" - -#: Zotlabs/Module/Admin/Queue.php:35 -msgid "Priority" -msgstr "" - -#: Zotlabs/Module/Admin/Queue.php:36 -msgid "Destination URL" -msgstr "" - -#: Zotlabs/Module/Admin/Queue.php:37 -msgid "Mark hub permanently offline" -msgstr "" - -#: Zotlabs/Module/Admin/Queue.php:38 -msgid "Empty queue for this hub" -msgstr "" - -#: Zotlabs/Module/Admin/Queue.php:39 -msgid "Last known contact" -msgstr "" - -#: Zotlabs/Module/Admin/Account_edit.php:29 -#, php-format -msgid "Password changed for account %d." -msgstr "" - -#: Zotlabs/Module/Admin/Account_edit.php:44 -msgid "Account settings updated." -msgstr "" - -#: Zotlabs/Module/Admin/Account_edit.php:59 -msgid "Account not found." -msgstr "" - -#: Zotlabs/Module/Admin/Account_edit.php:66 -msgid "Account Edit" -msgstr "" - -#: Zotlabs/Module/Admin/Account_edit.php:67 -msgid "New Password" -msgstr "" - -#: Zotlabs/Module/Admin/Account_edit.php:68 -msgid "New Password again" -msgstr "" - -#: Zotlabs/Module/Admin/Account_edit.php:69 -msgid "Account language (for emails)" -msgstr "" - -#: Zotlabs/Module/Admin/Account_edit.php:70 -msgid "Service class" -msgstr "" - -#: Zotlabs/Module/Admin/Channels.php:35 -#, php-format -msgid "%s channel censored/uncensored" -msgid_plural "%s channels censored/uncensored" -msgstr[0] "" -msgstr[1] "" - -#: Zotlabs/Module/Admin/Channels.php:44 -#, php-format -msgid "%s channel code allowed/disallowed" -msgid_plural "%s channels code allowed/disallowed" -msgstr[0] "" -msgstr[1] "" - -#: Zotlabs/Module/Admin/Channels.php:50 -#, php-format -msgid "%s channel deleted" -msgid_plural "%s channels deleted" -msgstr[0] "" -msgstr[1] "" - -#: Zotlabs/Module/Admin/Channels.php:69 -msgid "Channel not found" -msgstr "" - -#: Zotlabs/Module/Admin/Channels.php:79 -#, php-format -msgid "Channel '%s' deleted" -msgstr "" - -#: Zotlabs/Module/Admin/Channels.php:91 -#, php-format -msgid "Channel '%s' censored" -msgstr "" - -#: Zotlabs/Module/Admin/Channels.php:91 -#, php-format -msgid "Channel '%s' uncensored" -msgstr "" - -#: Zotlabs/Module/Admin/Channels.php:102 -#, php-format -msgid "Channel '%s' code allowed" -msgstr "" - -#: Zotlabs/Module/Admin/Channels.php:102 -#, php-format -msgid "Channel '%s' code disallowed" -msgstr "" - -#: Zotlabs/Module/Admin/Channels.php:153 Zotlabs/Module/Admin.php:137 -#: Zotlabs/Module/Manage.php:183 Zotlabs/Widget/Admin.php:26 include/nav.php:99 -msgid "Channels" -msgstr "" - -#: Zotlabs/Module/Admin/Channels.php:155 Zotlabs/Module/Admin/Accounts.php:169 -msgid "select all" -msgstr "" - -#: Zotlabs/Module/Admin/Channels.php:157 Zotlabs/Module/Connedit.php:587 -#: Zotlabs/Module/Directory.php:394 -msgid "Censor" -msgstr "" - -#: Zotlabs/Module/Admin/Channels.php:158 Zotlabs/Module/Connedit.php:587 -#: Zotlabs/Module/Directory.php:394 -msgid "Uncensor" -msgstr "" - -#: Zotlabs/Module/Admin/Channels.php:159 -msgid "Allow Code" -msgstr "" - -#: Zotlabs/Module/Admin/Channels.php:160 -msgid "Disallow Code" -msgstr "" - -#: Zotlabs/Module/Admin/Channels.php:161 include/nav.php:398 -#: include/conversation.php:1918 -msgid "Channel" -msgstr "" - -#: Zotlabs/Module/Admin/Channels.php:165 -msgid "UID" -msgstr "" - -#: Zotlabs/Module/Admin/Channels.php:166 Zotlabs/Module/Cdav.php:1349 -#: Zotlabs/Module/Connedit.php:852 Zotlabs/Module/Connections.php:234 -#: Zotlabs/Module/Chat.php:249 Zotlabs/Module/Lists.php:241 -#: Zotlabs/Module/Settings/Oauth.php:90 Zotlabs/Module/Settings/Oauth.php:116 -#: Zotlabs/Module/Settings/Oauth2.php:107 -#: Zotlabs/Module/Settings/Oauth2.php:136 Zotlabs/Module/Sharedwithme.php:109 -#: Zotlabs/Storage/Browser.php:310 Zotlabs/Widget/Activity_filter.php:293 -msgid "Name" -msgstr "" - -#: Zotlabs/Module/Admin/Channels.php:167 Zotlabs/Module/Cdav.php:1356 -#: Zotlabs/Module/Connedit.php:859 Zotlabs/Module/Locs.php:123 -#: Zotlabs/Module/Profiles.php:499 Zotlabs/Module/Profiles.php:798 -msgid "Address" -msgstr "" - -#: Zotlabs/Module/Admin/Channels.php:169 -msgid "" -"Selected channels will be deleted!\\n\\nEverything that was posted in these " -"channels on this site will be permanently deleted!\\n\\nAre you sure?" -msgstr "" - -#: Zotlabs/Module/Admin/Channels.php:170 -msgid "" -"The channel {0} will be deleted!\\n\\nEverything that was posted in this " -"channel on this site will be permanently deleted!\\n\\nAre you sure?" -msgstr "" - -#: Zotlabs/Module/Admin/Cover_photo.php:63 -#: Zotlabs/Module/Admin/Profile_photo.php:66 Zotlabs/Module/Cover_photo.php:63 -#: Zotlabs/Module/Profile_photo.php:63 -msgid "Image uploaded but image cropping failed." -msgstr "" - -#: Zotlabs/Module/Admin/Cover_photo.php:174 -#: Zotlabs/Module/Admin/Cover_photo.php:261 Zotlabs/Module/Cover_photo.php:174 -#: Zotlabs/Module/Cover_photo.php:265 -msgid "Cover Photos" -msgstr "" - -#: Zotlabs/Module/Admin/Cover_photo.php:198 -#: Zotlabs/Module/Admin/Profile_photo.php:143 -#: Zotlabs/Module/Cover_photo.php:198 Zotlabs/Module/Profile_photo.php:142 -msgid "Image resize failed." -msgstr "" - -#: Zotlabs/Module/Admin/Cover_photo.php:208 -#: Zotlabs/Module/Admin/Profile_photo.php:188 -#: Zotlabs/Module/Cover_photo.php:212 Zotlabs/Module/Profile_photo.php:225 -#: include/photos.php:198 -msgid "Unable to process image" -msgstr "" - -#: Zotlabs/Module/Admin/Cover_photo.php:316 -#: Zotlabs/Module/Admin/Cover_photo.php:332 -#: Zotlabs/Module/Admin/Profile_photo.php:352 -#: Zotlabs/Module/Admin/Profile_photo.php:392 -#: Zotlabs/Module/Cover_photo.php:368 Zotlabs/Module/Cover_photo.php:384 -#: Zotlabs/Module/Profile_photo.php:385 Zotlabs/Module/Profile_photo.php:433 -msgid "Photo not available." -msgstr "" - -#: Zotlabs/Module/Admin/Cover_photo.php:369 Zotlabs/Module/Cover_photo.php:420 -msgid "Your cover photo may be visible to anybody on the internet" -msgstr "" - -#: Zotlabs/Module/Admin/Cover_photo.php:371 -#: Zotlabs/Module/Admin/Profile_photo.php:462 -#: Zotlabs/Module/Cover_photo.php:422 Zotlabs/Module/Profile_photo.php:502 -msgid "Upload File:" -msgstr "" - -#: Zotlabs/Module/Admin/Cover_photo.php:372 -#: Zotlabs/Module/Admin/Profile_photo.php:463 -#: Zotlabs/Module/Cover_photo.php:423 Zotlabs/Module/Profile_photo.php:503 -msgid "Select a profile:" -msgstr "" - -#: Zotlabs/Module/Admin/Cover_photo.php:373 Zotlabs/Module/Cover_photo.php:424 -msgid "Change Cover Photo" -msgstr "" - -#: Zotlabs/Module/Admin/Cover_photo.php:374 -#: Zotlabs/Module/Admin/Profile_photo.php:465 -#: Zotlabs/Module/Cover_photo.php:425 Zotlabs/Module/Embedphotos.php:227 -#: Zotlabs/Module/Profile_photo.php:505 Zotlabs/Module/Photos.php:738 -#: Zotlabs/Storage/Browser.php:419 Zotlabs/Widget/Portfolio.php:110 -#: Zotlabs/Widget/Cdav.php:146 Zotlabs/Widget/Cdav.php:182 -#: Zotlabs/Widget/Album.php:101 -msgid "Upload" -msgstr "" - -#: Zotlabs/Module/Admin/Cover_photo.php:376 -#: Zotlabs/Module/Admin/Cover_photo.php:377 -#: Zotlabs/Module/Admin/Profile_photo.php:469 -#: Zotlabs/Module/Admin/Profile_photo.php:470 -#: Zotlabs/Module/Cover_photo.php:427 Zotlabs/Module/Cover_photo.php:428 -#: Zotlabs/Module/Profile_photo.php:509 Zotlabs/Module/Profile_photo.php:510 -msgid "Use a photo from your albums" -msgstr "" - -#: Zotlabs/Module/Admin/Cover_photo.php:378 -#: Zotlabs/Module/Admin/Profile_photo.php:471 -#: Zotlabs/Module/Admin/Addons.php:424 Zotlabs/Module/Cdav.php:1055 -#: Zotlabs/Module/Cdav.php:1367 Zotlabs/Module/Connedit.php:870 -#: Zotlabs/Module/Cover_photo.php:429 Zotlabs/Module/Editlayout.php:144 -#: Zotlabs/Module/Editwebpage.php:173 Zotlabs/Module/Filer.php:53 -#: Zotlabs/Module/Fbrowser.php:63 Zotlabs/Module/Fbrowser.php:85 -#: Zotlabs/Module/Card_edit.php:133 Zotlabs/Module/Editblock.php:145 -#: Zotlabs/Module/Editpost.php:142 Zotlabs/Module/Profiles.php:809 -#: Zotlabs/Module/Profile_photo.php:511 Zotlabs/Module/Settings/Oauth.php:89 -#: Zotlabs/Module/Settings/Oauth.php:115 Zotlabs/Module/Settings/Oauth2.php:106 -#: Zotlabs/Module/Settings/Oauth2.php:135 Zotlabs/Module/Tagrm.php:15 -#: Zotlabs/Module/Tagrm.php:138 include/conversation.php:1402 -#: include/conversation.php:1468 include/conversation.php:1470 -#: include/conversation.php:1472 -msgid "Cancel" -msgstr "" - -#: Zotlabs/Module/Admin/Cover_photo.php:379 -#: Zotlabs/Module/Admin/Profile_photo.php:472 -#: Zotlabs/Module/Cover_photo.php:430 Zotlabs/Module/Profile_photo.php:512 -#: include/conversation.php:1403 include/conversation.php:1467 -#: include/conversation.php:1469 include/conversation.php:1471 -msgid "OK" -msgstr "" - -#: Zotlabs/Module/Admin/Cover_photo.php:380 -#: Zotlabs/Module/Admin/Profile_photo.php:473 -#: Zotlabs/Module/Cover_photo.php:431 Zotlabs/Module/Profile_photo.php:513 -#: include/conversation.php:1272 -msgid "Choose images to embed" -msgstr "" - -#: Zotlabs/Module/Admin/Cover_photo.php:381 -#: Zotlabs/Module/Admin/Profile_photo.php:474 -#: Zotlabs/Module/Cover_photo.php:432 Zotlabs/Module/Profile_photo.php:514 -#: include/conversation.php:1273 -msgid "Choose an album" -msgstr "" - -#: Zotlabs/Module/Admin/Cover_photo.php:382 -#: Zotlabs/Module/Admin/Profile_photo.php:475 -#: Zotlabs/Module/Cover_photo.php:433 Zotlabs/Module/Profile_photo.php:515 -msgid "Choose a different album" -msgstr "" - -#: Zotlabs/Module/Admin/Cover_photo.php:383 -#: Zotlabs/Module/Admin/Profile_photo.php:476 -#: Zotlabs/Module/Cover_photo.php:434 Zotlabs/Module/Profile_photo.php:516 -#: include/conversation.php:1275 -msgid "Error getting album list" -msgstr "" - -#: Zotlabs/Module/Admin/Cover_photo.php:384 -#: Zotlabs/Module/Admin/Profile_photo.php:477 -#: Zotlabs/Module/Cover_photo.php:435 Zotlabs/Module/Profile_photo.php:517 -#: include/conversation.php:1276 -msgid "Error getting photo link" -msgstr "" - -#: Zotlabs/Module/Admin/Cover_photo.php:385 -#: Zotlabs/Module/Admin/Profile_photo.php:478 -#: Zotlabs/Module/Cover_photo.php:436 Zotlabs/Module/Profile_photo.php:518 -#: include/conversation.php:1277 -msgid "Error getting album" -msgstr "" - -#: Zotlabs/Module/Admin/Cover_photo.php:387 -#: Zotlabs/Module/Admin/Profile_photo.php:480 -#: Zotlabs/Module/Cover_photo.php:438 Zotlabs/Module/Profile_photo.php:520 -msgid "Select previously uploaded photo" -msgstr "" - -#: Zotlabs/Module/Admin/Cover_photo.php:404 -#: Zotlabs/Module/Admin/Profile_photo.php:497 -#: Zotlabs/Module/Cover_photo.php:455 Zotlabs/Module/Profile_photo.php:537 -msgid "Crop Image" -msgstr "" - -#: Zotlabs/Module/Admin/Cover_photo.php:405 -#: Zotlabs/Module/Admin/Profile_photo.php:498 -#: Zotlabs/Module/Cover_photo.php:456 Zotlabs/Module/Profile_photo.php:538 -msgid "Please adjust the image cropping for optimum viewing." -msgstr "" - -#: Zotlabs/Module/Admin/Cover_photo.php:407 -#: Zotlabs/Module/Admin/Profile_photo.php:500 -#: Zotlabs/Module/Cover_photo.php:458 Zotlabs/Module/Profile_photo.php:540 -msgid "Done Editing" -msgstr "" - -#: Zotlabs/Module/Admin/Profile_photo.php:119 -#: Zotlabs/Module/Admin/Profile_photo.php:252 -#: Zotlabs/Module/Profile_photo.php:116 Zotlabs/Module/Profile_photo.php:289 -#: include/photo_factory.php:550 +#: Zotlabs/Module/Admin/Profile_photo.php:121 +#: Zotlabs/Module/Admin/Profile_photo.php:251 +#: Zotlabs/Module/Profile_photo.php:118 Zotlabs/Module/Profile_photo.php:287 +#: include/photo_factory.php:539 msgid "Profile Photos" msgstr "" -#: Zotlabs/Module/Admin/Profile_photo.php:184 -#: Zotlabs/Module/Profile_photo.php:218 +#: Zotlabs/Module/Admin/Profile_photo.php:189 +#: Zotlabs/Module/Profile_photo.php:222 msgid "" "Shift-reload the page or clear browser cache if the new photo does not " "display immediately." msgstr "" #: Zotlabs/Module/Admin/Profile_photo.php:266 -#: Zotlabs/Module/Profile_photo.php:303 +#: Zotlabs/Module/Profile_photo.php:302 msgid "Image upload failed." msgstr "" #: Zotlabs/Module/Admin/Profile_photo.php:285 -#: Zotlabs/Module/Profile_photo.php:322 +#: Zotlabs/Module/Profile_photo.php:321 msgid "Unable to process image." msgstr "" -#: Zotlabs/Module/Admin/Profile_photo.php:460 -#: Zotlabs/Module/Profile_photo.php:500 +#: Zotlabs/Module/Admin/Profile_photo.php:463 +#: Zotlabs/Module/Profile_photo.php:502 msgid "" "Your default profile photo is visible to anybody on the internet. Profile " "photos for alternate profiles will inherit the permissions of the profile" msgstr "" -#: Zotlabs/Module/Admin/Profile_photo.php:460 +#: Zotlabs/Module/Admin/Profile_photo.php:463 msgid "" "Your site photo is visible to anybody on the internet and may be distributed " "to other websites." msgstr "" -#: Zotlabs/Module/Admin/Profile_photo.php:464 +#: Zotlabs/Module/Admin/Profile_photo.php:467 msgid "Use Photo for Site Logo" msgstr "" -#: Zotlabs/Module/Admin/Profile_photo.php:464 +#: Zotlabs/Module/Admin/Profile_photo.php:467 msgid "Change Site Logo" msgstr "" -#: Zotlabs/Module/Admin/Profile_photo.php:465 -#: Zotlabs/Module/Profile_photo.php:505 +#: Zotlabs/Module/Admin/Profile_photo.php:468 +#: Zotlabs/Module/Profile_photo.php:507 msgid "Use" msgstr "" -#: Zotlabs/Module/Admin/Accounts.php:37 -#, php-format -msgid "%s account blocked/unblocked" -msgid_plural "%s accounts blocked/unblocked" -msgstr[0] "" -msgstr[1] "" - -#: Zotlabs/Module/Admin/Accounts.php:45 -#, php-format -msgid "%s account deleted" -msgid_plural "%s accounts deleted" -msgstr[0] "" -msgstr[1] "" - -#: Zotlabs/Module/Admin/Accounts.php:83 -msgid "Account not found" -msgstr "" - -#: Zotlabs/Module/Admin/Accounts.php:94 include/channel.php:2181 -#, php-format -msgid "Account '%s' deleted" -msgstr "" - -#: Zotlabs/Module/Admin/Accounts.php:102 -#, php-format -msgid "Account '%s' blocked" -msgstr "" - -#: Zotlabs/Module/Admin/Accounts.php:110 -#, php-format -msgid "Account '%s' unblocked" -msgstr "" - -#: Zotlabs/Module/Admin/Accounts.php:167 Zotlabs/Module/Admin/Accounts.php:180 -#: Zotlabs/Module/Admin.php:119 Zotlabs/Widget/Admin.php:25 -msgid "Accounts" -msgstr "" - -#: Zotlabs/Module/Admin/Accounts.php:170 -msgid "Registrations waiting for confirm" -msgstr "" - -#: Zotlabs/Module/Admin/Accounts.php:171 -msgid "Request date" -msgstr "" - -#: Zotlabs/Module/Admin/Accounts.php:171 Zotlabs/Module/Admin/Accounts.php:183 -#: Zotlabs/Module/Cdav.php:1353 Zotlabs/Module/Connedit.php:856 -#: Zotlabs/Module/Profiles.php:795 include/network.php:1600 -msgid "Email" -msgstr "" - -#: Zotlabs/Module/Admin/Accounts.php:172 -msgid "No registrations." -msgstr "" - -#: Zotlabs/Module/Admin/Accounts.php:173 Zotlabs/Module/Connections.php:340 -#: include/conversation.php:655 -msgid "Approve" -msgstr "" - -#: Zotlabs/Module/Admin/Accounts.php:174 Zotlabs/Module/Authorize.php:40 -msgid "Deny" -msgstr "" - -#: Zotlabs/Module/Admin/Accounts.php:176 Zotlabs/Module/Connedit.php:571 -#: Zotlabs/Widget/Sblock.php:19 -msgid "Block" -msgstr "" - -#: Zotlabs/Module/Admin/Accounts.php:177 Zotlabs/Module/Connedit.php:571 -msgid "Unblock" -msgstr "" - -#: Zotlabs/Module/Admin/Accounts.php:182 -msgid "ID" -msgstr "" - -#: Zotlabs/Module/Admin/Accounts.php:184 -msgid "All Channels" -msgstr "" - -#: Zotlabs/Module/Admin/Accounts.php:185 -msgid "Register date" -msgstr "" - -#: Zotlabs/Module/Admin/Accounts.php:186 -msgid "Last login" -msgstr "" - -#: Zotlabs/Module/Admin/Accounts.php:187 -msgid "Expires" -msgstr "" - -#: Zotlabs/Module/Admin/Accounts.php:188 -msgid "Service Class" -msgstr "" - -#: Zotlabs/Module/Admin/Accounts.php:190 -msgid "" -"Selected accounts will be deleted!\\n\\nEverything these accounts had posted " -"on this site will be permanently deleted!\\n\\nAre you sure?" -msgstr "" - -#: Zotlabs/Module/Admin/Accounts.php:191 -msgid "" -"The account {0} will be deleted!\\n\\nEverything this account has posted on " -"this site will be permanently deleted!\\n\\nAre you sure?" -msgstr "" - -#: Zotlabs/Module/Admin/Addons.php:257 Zotlabs/Module/Admin/Themes.php:72 -#: Zotlabs/Module/Admin.php:85 Zotlabs/Module/Display.php:59 -#: Zotlabs/Module/Display.php:514 Zotlabs/Module/Filestorage.php:34 -#: Zotlabs/Module/Thing.php:101 Zotlabs/Module/Viewsrc.php:27 -#: Zotlabs/Module/Inspect.php:33 -#: extend/addon/a/flashcards/Mod_Flashcards.php:284 -#: extend/addon/a/flashcards/Mod_Flashcards.php:285 include/items.php:3763 -msgid "Item not found." -msgstr "" - -#: Zotlabs/Module/Admin/Addons.php:287 -#, php-format -msgid "Plugin %s disabled." -msgstr "" - -#: Zotlabs/Module/Admin/Addons.php:292 -#, php-format -msgid "Plugin %s enabled." -msgstr "" - -#: Zotlabs/Module/Admin/Addons.php:308 Zotlabs/Module/Admin/Themes.php:95 -msgid "Disable" -msgstr "" - -#: Zotlabs/Module/Admin/Addons.php:311 Zotlabs/Module/Admin/Themes.php:97 -msgid "Enable" -msgstr "" - -#: Zotlabs/Module/Admin/Addons.php:340 Zotlabs/Module/Admin/Addons.php:438 -#: Zotlabs/Widget/Admin.php:29 -msgid "Addons" -msgstr "" - -#: Zotlabs/Module/Admin/Addons.php:341 Zotlabs/Module/Admin/Themes.php:124 -msgid "Toggle" -msgstr "" - -#: Zotlabs/Module/Admin/Addons.php:349 Zotlabs/Module/Admin/Themes.php:134 -msgid "Author: " -msgstr "" - -#: Zotlabs/Module/Admin/Addons.php:350 Zotlabs/Module/Admin/Themes.php:135 -msgid "Maintainer: " -msgstr "" - -#: Zotlabs/Module/Admin/Addons.php:351 -msgid "Minimum project version: " -msgstr "" - -#: Zotlabs/Module/Admin/Addons.php:352 -msgid "Maximum project version: " -msgstr "" - -#: Zotlabs/Module/Admin/Addons.php:353 -msgid "Minimum PHP version: " -msgstr "" - -#: Zotlabs/Module/Admin/Addons.php:354 -msgid "Compatible Server Roles: " -msgstr "" - -#: Zotlabs/Module/Admin/Addons.php:355 -msgid "Requires: " -msgstr "" - -#: Zotlabs/Module/Admin/Addons.php:356 Zotlabs/Module/Admin/Addons.php:443 -msgid "Disabled - version incompatibility" -msgstr "" - -#: Zotlabs/Module/Admin/Addons.php:412 -msgid "Enter the public git repository URL of the addon repo." -msgstr "" - -#: Zotlabs/Module/Admin/Addons.php:413 -msgid "Addon repo git URL" -msgstr "" - -#: Zotlabs/Module/Admin/Addons.php:414 -msgid "Custom repo name" -msgstr "" - -#: Zotlabs/Module/Admin/Addons.php:414 -msgid "(optional)" -msgstr "" - -#: Zotlabs/Module/Admin/Addons.php:415 -msgid "Download Addon Repo" -msgstr "" - -#: Zotlabs/Module/Admin/Addons.php:422 -msgid "Install new repo" -msgstr "" - -#: Zotlabs/Module/Admin/Addons.php:446 -msgid "Manage Repos" -msgstr "" - -#: Zotlabs/Module/Admin/Addons.php:447 -msgid "Installed Addon Repositories" -msgstr "" - -#: Zotlabs/Module/Admin/Addons.php:448 -msgid "Install a New Addon Repository" -msgstr "" - -#: Zotlabs/Module/Admin/Addons.php:455 -msgid "Switch branch" -msgstr "" - -#: Zotlabs/Module/Admin/Addons.php:456 Zotlabs/Module/Photos.php:1055 -#: Zotlabs/Module/Tagrm.php:137 Zotlabs/Module/Superblock.php:227 -#: Zotlabs/Module/Superblock.php:244 -msgid "Remove" -msgstr "" - -#: Zotlabs/Module/Admin/Dbsync.php:19 Zotlabs/Module/Admin/Dbsync.php:59 -msgid "Update has been marked successful" -msgstr "" - -#: Zotlabs/Module/Admin/Dbsync.php:32 -#, php-format -msgid "Verification of update %s failed. Check system logs." -msgstr "" - -#: Zotlabs/Module/Admin/Dbsync.php:35 Zotlabs/Module/Admin/Dbsync.php:74 -#, php-format -msgid "Update %s was successfully applied." -msgstr "" - -#: Zotlabs/Module/Admin/Dbsync.php:39 -#, php-format -msgid "Verifying update %s did not return a status. Unknown if it succeeded." -msgstr "" - -#: Zotlabs/Module/Admin/Dbsync.php:42 -#, php-format -msgid "Update %s does not contain a verification function." -msgstr "" - -#: Zotlabs/Module/Admin/Dbsync.php:46 Zotlabs/Module/Admin/Dbsync.php:81 -#, php-format -msgid "Update function %s could not be found." -msgstr "" - -#: Zotlabs/Module/Admin/Dbsync.php:71 -#, php-format -msgid "Executing update procedure %s failed. Check system logs." -msgstr "" - -#: Zotlabs/Module/Admin/Dbsync.php:78 -#, php-format -msgid "" -"Update %s did not return a status. It cannot be determined if it was " -"successful." -msgstr "" - -#: Zotlabs/Module/Admin/Dbsync.php:99 -msgid "Failed Updates" -msgstr "" - -#: Zotlabs/Module/Admin/Dbsync.php:101 -msgid "Mark success (if update was manually applied)" -msgstr "" - -#: Zotlabs/Module/Admin/Dbsync.php:102 -msgid "Attempt to verify this update if a verification procedure exists" -msgstr "" - -#: Zotlabs/Module/Admin/Dbsync.php:103 -msgid "Attempt to execute this update step automatically" -msgstr "" - -#: Zotlabs/Module/Admin/Dbsync.php:108 -msgid "No failed updates." -msgstr "" - -#: Zotlabs/Module/Admin/Profs.php:89 -msgid "New Profile Field" -msgstr "" - -#: Zotlabs/Module/Admin/Profs.php:90 Zotlabs/Module/Admin/Profs.php:110 -msgid "Field nickname" -msgstr "" - -#: Zotlabs/Module/Admin/Profs.php:90 Zotlabs/Module/Admin/Profs.php:110 -msgid "System name of field" -msgstr "" - -#: Zotlabs/Module/Admin/Profs.php:91 Zotlabs/Module/Admin/Profs.php:111 -msgid "Input type" -msgstr "" - -#: Zotlabs/Module/Admin/Profs.php:92 Zotlabs/Module/Admin/Profs.php:112 -msgid "Field Name" -msgstr "" - -#: Zotlabs/Module/Admin/Profs.php:92 Zotlabs/Module/Admin/Profs.php:112 -msgid "Label on profile pages" -msgstr "" - -#: Zotlabs/Module/Admin/Profs.php:93 Zotlabs/Module/Admin/Profs.php:113 -msgid "Help text" -msgstr "" - -#: Zotlabs/Module/Admin/Profs.php:93 Zotlabs/Module/Admin/Profs.php:113 -msgid "Additional info (optional)" -msgstr "" - -#: Zotlabs/Module/Admin/Profs.php:103 -msgid "Field definition not found" -msgstr "" - -#: Zotlabs/Module/Admin/Profs.php:109 -msgid "Edit Profile Field" -msgstr "" - -#: Zotlabs/Module/Admin/Profs.php:168 -msgid "Profile Fields" -msgstr "" - -#: Zotlabs/Module/Admin/Profs.php:169 -msgid "Basic Profile Fields" -msgstr "" - -#: Zotlabs/Module/Admin/Profs.php:170 -msgid "Advanced Profile Fields" -msgstr "" - -#: Zotlabs/Module/Admin/Profs.php:170 -msgid "(In addition to basic fields)" -msgstr "" - -#: Zotlabs/Module/Admin/Profs.php:172 -msgid "All available fields" -msgstr "" - -#: Zotlabs/Module/Admin/Profs.php:173 -msgid "Custom Fields" -msgstr "" - -#: Zotlabs/Module/Admin/Profs.php:177 -msgid "Create Custom Field" -msgstr "" - -#: Zotlabs/Module/Admin/Themes.php:26 -msgid "Theme settings updated." -msgstr "" - -#: Zotlabs/Module/Admin/Themes.php:61 -msgid "No themes found." -msgstr "" - -#: Zotlabs/Module/Admin/Themes.php:116 -msgid "Screenshot" -msgstr "" - -#: Zotlabs/Module/Admin/Themes.php:123 Zotlabs/Module/Admin/Themes.php:157 -#: Zotlabs/Widget/Admin.php:30 -msgid "Themes" -msgstr "" - -#: Zotlabs/Module/Admin/Themes.php:162 -msgid "[Experimental]" -msgstr "" - -#: Zotlabs/Module/Admin/Themes.php:163 -msgid "[Unsupported]" -msgstr "" - -#: Zotlabs/Module/Admin/Site.php:187 -msgid "Site settings updated." -msgstr "" - -#: Zotlabs/Module/Admin/Site.php:214 include/text.php:3391 -#: view/theme/redbasic/php/config.php:15 -msgid "Default" -msgstr "" - -#: Zotlabs/Module/Admin/Site.php:225 Zotlabs/Module/Settings/Display.php:133 -#, php-format -msgid "%s - (Incompatible)" -msgstr "" - -#: Zotlabs/Module/Admin/Site.php:232 -msgid "mobile" -msgstr "" - -#: Zotlabs/Module/Admin/Site.php:234 -msgid "experimental" -msgstr "" - -#: Zotlabs/Module/Admin/Site.php:236 -msgid "unsupported" -msgstr "" - -#: Zotlabs/Module/Admin/Site.php:277 -msgid "Yes - with approval" -msgstr "" - -#: Zotlabs/Module/Admin/Site.php:283 -msgid "My site is not a public server" -msgstr "" - -#: Zotlabs/Module/Admin/Site.php:284 -msgid "My site provides free public access" -msgstr "" - -#: Zotlabs/Module/Admin/Site.php:285 -msgid "My site provides paid public access" -msgstr "" - -#: Zotlabs/Module/Admin/Site.php:286 -msgid "My site provides free public access and premium paid plans" -msgstr "" - -#: Zotlabs/Module/Admin/Site.php:292 -msgid "Default permission role for new accounts" -msgstr "" - -#: Zotlabs/Module/Admin/Site.php:292 -msgid "" -"This role will be used for the first channel created after registration." -msgstr "" - -#: Zotlabs/Module/Admin/Site.php:300 Zotlabs/Widget/Admin.php:22 -msgid "Site" -msgstr "" - -#: Zotlabs/Module/Admin/Site.php:302 -msgid "Site Configuration" -msgstr "" - -#: Zotlabs/Module/Admin/Site.php:303 Zotlabs/Module/Register.php:288 -msgid "Registration" -msgstr "" - -#: Zotlabs/Module/Admin/Site.php:304 -msgid "File upload" -msgstr "" - -#: Zotlabs/Module/Admin/Site.php:305 -msgid "Policies" -msgstr "" - -#: Zotlabs/Module/Admin/Site.php:306 Zotlabs/Widget/Findpeople.php:23 -msgid "Advanced" -msgstr "" - -#: Zotlabs/Module/Admin/Site.php:308 -msgid "Site name" -msgstr "" - -#: Zotlabs/Module/Admin/Site.php:309 -msgid "Administrator Information" -msgstr "" - -#: Zotlabs/Module/Admin/Site.php:309 -msgid "" -"Contact information for site administrators. Displayed on siteinfo page. " -"BBCode may be used here." -msgstr "" - -#: Zotlabs/Module/Admin/Site.php:310 Zotlabs/Module/Siteinfo.php:33 -msgid "Site Information" -msgstr "" - -#: Zotlabs/Module/Admin/Site.php:310 -msgid "" -"Publicly visible description of this site. Displayed on siteinfo page. " -"BBCode may be used here." -msgstr "" - -#: Zotlabs/Module/Admin/Site.php:311 -msgid "System language" -msgstr "" - -#: Zotlabs/Module/Admin/Site.php:312 -msgid "System theme" -msgstr "" - -#: Zotlabs/Module/Admin/Site.php:312 -msgid "" -"Default system theme - may be over-ridden by user profiles - change theme settings" -msgstr "" - -#: Zotlabs/Module/Admin/Site.php:315 -msgid "ActivityPub protocol" -msgstr "" - -#: Zotlabs/Module/Admin/Site.php:315 -msgid "Provides access to software supporting the ActivityPub protocol." -msgstr "" - -#: Zotlabs/Module/Admin/Site.php:316 -msgid "Maximum image size" -msgstr "" - -#: Zotlabs/Module/Admin/Site.php:316 -msgid "" -"Maximum size in bytes of uploaded images. Default is 0, which means no " -"limits." -msgstr "" - -#: Zotlabs/Module/Admin/Site.php:317 -msgid "Cache all public images" -msgstr "" - -#: Zotlabs/Module/Admin/Site.php:317 -msgid "If disabled, proxy non-SSL images, but do not store locally" -msgstr "" - -#: Zotlabs/Module/Admin/Site.php:318 -msgid "Does this site allow new member registration?" -msgstr "" - -#: Zotlabs/Module/Admin/Site.php:319 -msgid "Invitation only" -msgstr "" - -#: Zotlabs/Module/Admin/Site.php:319 -msgid "" -"Only allow new member registrations with an invitation code. New member " -"registration must be allowed for this to work." -msgstr "" - -#: Zotlabs/Module/Admin/Site.php:321 -msgid "Minimum age" -msgstr "" - -#: Zotlabs/Module/Admin/Site.php:321 -msgid "Minimum age (in years) for who may register on this site." -msgstr "" - -#: Zotlabs/Module/Admin/Site.php:322 -msgid "Which best describes the types of account offered by this hub?" -msgstr "" - -#: Zotlabs/Module/Admin/Site.php:322 -msgid "" -"If a public server policy is selected, this information may be displayed on " -"the public server site list." -msgstr "" - -#: Zotlabs/Module/Admin/Site.php:323 -msgid "Register text" -msgstr "" - -#: Zotlabs/Module/Admin/Site.php:323 -msgid "Will be displayed prominently on the registration page." -msgstr "" - -#: Zotlabs/Module/Admin/Site.php:325 -msgid "Site homepage to show visitors (default: login box)" -msgstr "" - -#: Zotlabs/Module/Admin/Site.php:325 -msgid "" -"example: 'public' to show public stream, 'page/sys/home' to show a system " -"webpage called 'home' or 'include:home.html' to include a file." -msgstr "" - -#: Zotlabs/Module/Admin/Site.php:326 -msgid "Preserve site homepage URL" -msgstr "" - -#: Zotlabs/Module/Admin/Site.php:326 -msgid "" -"Present the site homepage in a frame at the original location instead of " -"redirecting" -msgstr "" - -#: Zotlabs/Module/Admin/Site.php:327 -msgid "Accounts abandoned after x days" -msgstr "" - -#: Zotlabs/Module/Admin/Site.php:327 -msgid "" -"Will not waste system resources polling external sites for abandonded " -"accounts. Enter 0 for no time limit." -msgstr "" - -#: Zotlabs/Module/Admin/Site.php:328 Zotlabs/Module/Admin/Security.php:139 -msgid "Block directory from visitors" -msgstr "" - -#: Zotlabs/Module/Admin/Site.php:328 Zotlabs/Module/Admin/Security.php:139 -msgid "Only allow authenticated access to directory." -msgstr "" - -#: Zotlabs/Module/Admin/Site.php:329 -msgid "Verify Email Addresses" -msgstr "" - -#: Zotlabs/Module/Admin/Site.php:329 -msgid "" -"Check to verify email addresses used in account registration (recommended)." -msgstr "" - -#: Zotlabs/Module/Admin/Site.php:330 -msgid "Force publish in directory" -msgstr "" - -#: Zotlabs/Module/Admin/Site.php:330 -msgid "" -"Check to force all profiles on this site to be listed in the site directory." -msgstr "" - -#: Zotlabs/Module/Admin/Site.php:332 -msgid "Public stream" -msgstr "" - -#: Zotlabs/Module/Admin/Site.php:332 -msgid "Provide a Public stream on your site. This content is unmoderated." -msgstr "" - -#: Zotlabs/Module/Admin/Site.php:333 -msgid "the Public stream is disabled" -msgstr "" - -#: Zotlabs/Module/Admin/Site.php:334 -msgid "the Public stream contains public conversations from this site only" -msgstr "" - -#: Zotlabs/Module/Admin/Site.php:335 -msgid "" -"the Public stream contains public conversations from anywhere on the internet" -msgstr "" - -#: Zotlabs/Module/Admin/Site.php:338 -msgid "Allow anybody on the internet to access the Public stream" -msgstr "" - -#: Zotlabs/Module/Admin/Site.php:338 -msgid "" -"Default is to only allow viewing by site members. Warning: this content is " -"unmoderated." -msgstr "" - -#: Zotlabs/Module/Admin/Site.php:339 -msgid "Show numbers of likes and dislikes in conversations" -msgstr "" - -#: Zotlabs/Module/Admin/Site.php:339 -msgid "" -"If disabled, the presence of likes and dislikes will be shown, but without " -"totals." -msgstr "" - -#: Zotlabs/Module/Admin/Site.php:340 -msgid "Permit animated profile photos" -msgstr "" - -#: Zotlabs/Module/Admin/Site.php:340 -msgid "Changing this may take several days to work through the system" -msgstr "" - -#: Zotlabs/Module/Admin/Site.php:341 -msgid "Only import Public stream posts with this text" -msgstr "" - -#: Zotlabs/Module/Admin/Site.php:341 Zotlabs/Module/Admin/Site.php:342 -msgid "" -"words one per line or #tags or /patterns/ or lang=xx, leave blank to import " -"all posts" -msgstr "" - -#: Zotlabs/Module/Admin/Site.php:342 -msgid "Do not import Public stream posts with this text" -msgstr "" - -#: Zotlabs/Module/Admin/Site.php:343 -msgid "Maximum number of imported friends of friends" -msgstr "" - -#: Zotlabs/Module/Admin/Site.php:343 -msgid "" -"Warning: higher numbers will improve the quality of friend suggestions and " -"directory results but can exponentially increase resource usage" -msgstr "" - -#: Zotlabs/Module/Admin/Site.php:344 -msgid "Login on Homepage" -msgstr "" - -#: Zotlabs/Module/Admin/Site.php:344 -msgid "" -"Present a login box to visitors on the home page if no other content has " -"been configured." -msgstr "" - -#: Zotlabs/Module/Admin/Site.php:345 -msgid "Enable context help" -msgstr "" - -#: Zotlabs/Module/Admin/Site.php:345 -msgid "" -"Display contextual help for the current page when the help button is pressed." -msgstr "" - -#: Zotlabs/Module/Admin/Site.php:346 -msgid "Reply-to email address for system generated email." -msgstr "" - -#: Zotlabs/Module/Admin/Site.php:347 -msgid "Sender (From) email address for system generated email." -msgstr "" - -#: Zotlabs/Module/Admin/Site.php:348 -msgid "Display name of email sender for system generated email." -msgstr "" - -#: Zotlabs/Module/Admin/Site.php:349 -msgid "Directory Server URL" -msgstr "" - -#: Zotlabs/Module/Admin/Site.php:349 -msgid "Default directory server" -msgstr "" - -#: Zotlabs/Module/Admin/Site.php:350 -msgid "Proxy user" -msgstr "" - -#: Zotlabs/Module/Admin/Site.php:351 -msgid "Proxy URL" -msgstr "" - -#: Zotlabs/Module/Admin/Site.php:352 -msgid "Network fetch timeout" -msgstr "" - -#: Zotlabs/Module/Admin/Site.php:352 Zotlabs/Module/Admin/Site.php:353 -msgid "Value is in seconds. Set to 0 for unlimited (not recommended)." -msgstr "" - -#: Zotlabs/Module/Admin/Site.php:353 -msgid "Network post timeout" -msgstr "" - -#: Zotlabs/Module/Admin/Site.php:354 -msgid "Delivery interval" -msgstr "" - -#: Zotlabs/Module/Admin/Site.php:354 -msgid "" -"Delay background delivery processes by this many seconds to reduce system " -"load. Recommend: 4-5 for shared hosts, 2-3 for virtual private servers. 0-1 " -"for large dedicated servers." -msgstr "" - -#: Zotlabs/Module/Admin/Site.php:355 -msgid "Deliveries per process" -msgstr "" - -#: Zotlabs/Module/Admin/Site.php:355 -msgid "" -"Number of deliveries to attempt in a single operating system process. Adjust " -"if necessary to tune system performance. Recommend: 1-5." -msgstr "" - -#: Zotlabs/Module/Admin/Site.php:356 -msgid "Queue Threshold" -msgstr "" - -#: Zotlabs/Module/Admin/Site.php:356 -msgid "" -"Always defer immediate delivery if queue contains more than this number of " -"entries." -msgstr "" - -#: Zotlabs/Module/Admin/Site.php:357 -msgid "Poll interval" -msgstr "" - -#: Zotlabs/Module/Admin/Site.php:357 -msgid "" -"Delay background polling processes by this many seconds to reduce system " -"load. If 0, use delivery interval." -msgstr "" - -#: Zotlabs/Module/Admin/Site.php:358 -msgid "Path to ImageMagick convert program" -msgstr "" - -#: Zotlabs/Module/Admin/Site.php:358 -msgid "" -"If set, use this program to generate photo thumbnails for huge images ( > " -"4000 pixels in either dimension), otherwise memory exhaustion may occur. " -"Example: /usr/bin/convert" -msgstr "" - -#: Zotlabs/Module/Admin/Site.php:359 -msgid "Maximum Load Average" -msgstr "" - -#: Zotlabs/Module/Admin/Site.php:359 -msgid "" -"Maximum system load before delivery and poll processes are deferred - " -"default 50." -msgstr "" - -#: Zotlabs/Module/Admin/Site.php:360 -msgid "Expiration period in days for imported streams and cached images" -msgstr "" - -#: Zotlabs/Module/Admin/Site.php:360 -msgid "0 for no expiration of imported content" -msgstr "" - -#: Zotlabs/Module/Admin/Site.php:361 -msgid "" -"Do not expire any posts which have comments less than this many days ago" -msgstr "" - -#: Zotlabs/Module/Admin/Site.php:362 -msgid "" -"Public servers: Optional landing (marketing) webpage for new registrants" -msgstr "" - -#: Zotlabs/Module/Admin/Site.php:362 -#, php-format -msgid "Create this page first. Default is %s/register" -msgstr "" - -#: Zotlabs/Module/Admin/Site.php:363 -msgid "Page to display after creating a new channel" -msgstr "" - -#: Zotlabs/Module/Admin/Site.php:363 -msgid "Default: profiles" -msgstr "" - -#: Zotlabs/Module/Admin/Site.php:364 -msgid "Site location" -msgstr "" - -#: Zotlabs/Module/Admin/Site.php:364 -msgid "Region or country - shared with other sites" -msgstr "" - -#: Zotlabs/Module/Admin/Security.php:125 -msgid "" -"By default, unfiltered HTML is allowed in embedded media. This is inherently " -"insecure." -msgstr "" - -#: Zotlabs/Module/Admin/Security.php:128 -msgid "" -"The recommended setting is to only allow unfiltered HTML from the following " -"sites:" -msgstr "" - -#: Zotlabs/Module/Admin/Security.php:129 -msgid "" -"https://youtube.com/
                    https://www.youtube.com/
                    https://youtu.be/" -"
                    https://vimeo.com/
                    https://soundcloud.com/
                    " -msgstr "" - -#: Zotlabs/Module/Admin/Security.php:130 -msgid "" -"All other embedded content will be filtered, unless " -"embedded content from that site is explicitly blocked." -msgstr "" - -#: Zotlabs/Module/Admin/Security.php:135 Zotlabs/Widget/Admin.php:27 -msgid "Security" -msgstr "" - -#: Zotlabs/Module/Admin/Security.php:137 -msgid "Block public" -msgstr "" - -#: Zotlabs/Module/Admin/Security.php:137 -msgid "" -"Check to block public access to all otherwise public personal pages on this " -"site unless you are currently authenticated." -msgstr "" - -#: Zotlabs/Module/Admin/Security.php:138 -msgid "Block public search" -msgstr "" - -#: Zotlabs/Module/Admin/Security.php:138 -msgid "" -"Prevent access to search content unless you are currently authenticated." -msgstr "" - -#: Zotlabs/Module/Admin/Security.php:140 -msgid "Hide local directory" -msgstr "" - -#: Zotlabs/Module/Admin/Security.php:140 -msgid "Only use the global directory" -msgstr "" - -#: Zotlabs/Module/Admin/Security.php:141 -msgid "Provide a cloud root directory" -msgstr "" - -#: Zotlabs/Module/Admin/Security.php:141 -msgid "" -"The cloud root directory lists all channel names which provide public files. " -"Otherwise only the names of connections are shown." -msgstr "" - -#: Zotlabs/Module/Admin/Security.php:142 -msgid "Show total disk space available to cloud uploads" -msgstr "" - -#: Zotlabs/Module/Admin/Security.php:143 -msgid "Allow SVG thumbnails in file browser" -msgstr "" - -#: Zotlabs/Module/Admin/Security.php:143 -msgid "WARNING: SVG images may contain malicious code." -msgstr "" - -#: Zotlabs/Module/Admin/Security.php:145 -msgid "Allow embedded (inline) PDF files" -msgstr "" - -#: Zotlabs/Module/Admin/Security.php:146 -msgid "Permit anonymous comments" -msgstr "" - -#: Zotlabs/Module/Admin/Security.php:146 -msgid "" -"Moderation will be performed by channels that select this comment option." -msgstr "" - -#: Zotlabs/Module/Admin/Security.php:147 -msgid "Set \"Transport Security\" HTTP header" -msgstr "" - -#: Zotlabs/Module/Admin/Security.php:148 -msgid "Set \"Content Security Policy\" HTTP header" -msgstr "" - -#: Zotlabs/Module/Admin/Security.php:149 -msgid "Allowed email domains" -msgstr "" - -#: Zotlabs/Module/Admin/Security.php:149 -msgid "" -"Comma separated list of domains which are allowed in email addresses for " -"registrations to this site. Wildcards are accepted. Empty to allow any " -"domains" -msgstr "" - -#: Zotlabs/Module/Admin/Security.php:150 -msgid "Not allowed email domains" -msgstr "" - -#: Zotlabs/Module/Admin/Security.php:150 -msgid "" -"Comma separated list of domains which are not allowed in email addresses for " -"registrations to this site. Wildcards are accepted. Empty to allow any " -"domains, unless allowed domains have been defined." -msgstr "" - -#: Zotlabs/Module/Admin/Security.php:151 -msgid "Allow communications only from these sites" -msgstr "" - -#: Zotlabs/Module/Admin/Security.php:151 Zotlabs/Module/Admin/Security.php:156 -msgid "" -"One site per line. Leave empty to allow communication from anywhere by " -"default" -msgstr "" - -#: Zotlabs/Module/Admin/Security.php:152 -msgid "Block communications from these sites" -msgstr "" - -#: Zotlabs/Module/Admin/Security.php:153 -msgid "Allow communications only from these channels" -msgstr "" - -#: Zotlabs/Module/Admin/Security.php:153 Zotlabs/Module/Admin/Security.php:158 -msgid "" -"One channel (hash) per line. Leave empty to allow communication from any " -"channel by default" -msgstr "" - -#: Zotlabs/Module/Admin/Security.php:154 -msgid "Block communications from these channels" -msgstr "" - -#: Zotlabs/Module/Admin/Security.php:156 -msgid "Allow public stream communications only from these sites" -msgstr "" - -#: Zotlabs/Module/Admin/Security.php:157 -msgid "Block public stream communications from these sites" -msgstr "" - -#: Zotlabs/Module/Admin/Security.php:158 -msgid "Allow public stream communications only from these channels" -msgstr "" - -#: Zotlabs/Module/Admin/Security.php:159 -msgid "Block public stream communications from these channels" -msgstr "" - -#: Zotlabs/Module/Admin/Security.php:162 -msgid "Only allow embeds from secure (SSL) websites and links." -msgstr "" - -#: Zotlabs/Module/Admin/Security.php:163 -msgid "Allow unfiltered embedded HTML content only from these domains" -msgstr "" - -#: Zotlabs/Module/Admin/Security.php:163 -msgid "One site per line. By default embedded content is filtered." -msgstr "" - -#: Zotlabs/Module/Admin/Security.php:164 -msgid "Block embedded HTML from these domains" -msgstr "" - -#: Zotlabs/Module/Drafts.php:20 +#: Zotlabs/Module/Drafts.php:23 msgid "" "This app allows you to save posts you are writing and finish them later " "prior to sharing/publishing." msgstr "" -#: Zotlabs/Module/Api.php:82 Zotlabs/Module/Api.php:103 +#: Zotlabs/Module/Api.php:84 Zotlabs/Module/Api.php:106 msgid "Authorize application connection" msgstr "" -#: Zotlabs/Module/Api.php:83 +#: Zotlabs/Module/Api.php:85 msgid "Return to your app and insert this Security Code:" msgstr "" -#: Zotlabs/Module/Api.php:93 +#: Zotlabs/Module/Api.php:95 msgid "Please login to continue." msgstr "" -#: Zotlabs/Module/Api.php:105 +#: Zotlabs/Module/Api.php:108 msgid "" "Do you want to authorize this application to access your posts and contacts, " "and/or create new posts for you?" msgstr "" -#: Zotlabs/Module/Apporder.php:45 +#: Zotlabs/Module/Apporder.php:46 msgid "Change Order of Pinned Navbar Apps" msgstr "" -#: Zotlabs/Module/Apporder.php:45 +#: Zotlabs/Module/Apporder.php:46 msgid "Change Order of App Tray Apps" msgstr "" -#: Zotlabs/Module/Apporder.php:46 +#: Zotlabs/Module/Apporder.php:47 msgid "" "Use arrows to move the corresponding app left (top) or right (bottom) in the " "navbar" msgstr "" -#: Zotlabs/Module/Apporder.php:47 +#: Zotlabs/Module/Apporder.php:48 msgid "Use arrows to move the corresponding app up or down in the app tray" msgstr "" -#: Zotlabs/Module/Follow.php:133 +#: Zotlabs/Module/Follow.php:135 msgid "Connection added." msgstr "" -#: Zotlabs/Module/Apps.php:50 Zotlabs/Widget/Appstore.php:14 -#: include/nav.php:318 +#: Zotlabs/Module/Apps.php:59 Zotlabs/Widget/Appstore.php:15 +#: include/nav.php:315 msgid "Available Apps" msgstr "" -#: Zotlabs/Module/Apps.php:50 Zotlabs/Widget/Appstore.php:13 -#: include/nav.php:317 +#: Zotlabs/Module/Apps.php:59 Zotlabs/Widget/Appstore.php:14 +#: include/nav.php:314 msgid "Installed Apps" msgstr "" -#: Zotlabs/Module/Apps.php:53 +#: Zotlabs/Module/Apps.php:62 msgid "Manage apps" msgstr "" -#: Zotlabs/Module/Apps.php:54 +#: Zotlabs/Module/Apps.php:63 msgid "Create Custom App" msgstr "" -#: Zotlabs/Module/Common.php:18 +#: Zotlabs/Module/Common.php:21 msgid "No channel." msgstr "" -#: Zotlabs/Module/Common.php:49 +#: Zotlabs/Module/Common.php:55 msgid "No connections in common." msgstr "" -#: Zotlabs/Module/Common.php:69 +#: Zotlabs/Module/Common.php:75 msgid "View Common Connections" msgstr "" -#: Zotlabs/Module/Attach.php:13 +#: Zotlabs/Module/Attach.php:18 msgid "Item not available." msgstr "" -#: Zotlabs/Module/Authorize.php:24 +#: Zotlabs/Module/Authorize.php:23 msgid "Unknown App" msgstr "" -#: Zotlabs/Module/Authorize.php:36 +#: Zotlabs/Module/Authorize.php:35 msgid "Authorize" msgstr "" -#: Zotlabs/Module/Authorize.php:37 +#: Zotlabs/Module/Authorize.php:36 #, php-format msgid "Do you authorize the app %s to access your channel data?" msgstr "" -#: Zotlabs/Module/Authorize.php:39 +#: Zotlabs/Module/Authorize.php:38 msgid "Allow" msgstr "" @@ -3423,50 +3434,50 @@ msgstr "" msgid "Permissions denied." msgstr "" -#: Zotlabs/Module/Cal.php:270 Zotlabs/Module/Events.php:643 +#: Zotlabs/Module/Cal.php:283 Zotlabs/Module/Events.php:672 msgid "l, F j" msgstr "" -#: Zotlabs/Module/Cal.php:320 Zotlabs/Module/Events.php:699 -#: include/text.php:2071 +#: Zotlabs/Module/Cal.php:331 Zotlabs/Module/Events.php:727 +#: include/text.php:2167 msgid "Link to Source" msgstr "" -#: Zotlabs/Module/Cal.php:343 Zotlabs/Module/Events.php:727 +#: Zotlabs/Module/Cal.php:352 Zotlabs/Module/Events.php:753 msgid "Edit Event" msgstr "" -#: Zotlabs/Module/Cal.php:343 Zotlabs/Module/Events.php:727 +#: Zotlabs/Module/Cal.php:352 Zotlabs/Module/Events.php:753 msgid "Create Event" msgstr "" -#: Zotlabs/Module/Cal.php:344 Zotlabs/Module/Cal.php:351 -#: Zotlabs/Module/Cdav.php:1032 Zotlabs/Module/Events.php:728 -#: Zotlabs/Module/Events.php:737 Zotlabs/Module/Photos.php:1004 +#: Zotlabs/Module/Cal.php:353 Zotlabs/Module/Cal.php:360 +#: Zotlabs/Module/Cdav.php:1037 Zotlabs/Module/Events.php:754 +#: Zotlabs/Module/Events.php:763 Zotlabs/Module/Photos.php:1015 msgid "Previous" msgstr "" -#: Zotlabs/Module/Cal.php:345 Zotlabs/Module/Cal.php:352 -#: Zotlabs/Module/Cdav.php:1033 Zotlabs/Module/Events.php:729 -#: Zotlabs/Module/Events.php:738 Zotlabs/Module/Setup.php:284 -#: Zotlabs/Module/Photos.php:1013 +#: Zotlabs/Module/Cal.php:354 Zotlabs/Module/Cal.php:361 +#: Zotlabs/Module/Cdav.php:1038 Zotlabs/Module/Events.php:755 +#: Zotlabs/Module/Events.php:764 Zotlabs/Module/Setup.php:286 +#: Zotlabs/Module/Photos.php:1025 msgid "Next" msgstr "" -#: Zotlabs/Module/Cal.php:349 include/text.php:2716 +#: Zotlabs/Module/Cal.php:358 include/text.php:2875 msgid "Import" msgstr "" -#: Zotlabs/Module/Cal.php:353 Zotlabs/Module/Cdav.php:1034 -#: Zotlabs/Module/Events.php:739 +#: Zotlabs/Module/Cal.php:362 Zotlabs/Module/Cdav.php:1039 +#: Zotlabs/Module/Events.php:765 msgid "Today" msgstr "" -#: Zotlabs/Module/Expire.php:23 +#: Zotlabs/Module/Expire.php:25 msgid "Expiration settings updated." msgstr "" -#: Zotlabs/Module/Expire.php:33 +#: Zotlabs/Module/Expire.php:35 msgid "" "This app allows you to set an optional expiration date/time for your own " "posts, after which they will be deleted. This must be at least fifteen " @@ -3474,860 +3485,861 @@ msgid "" "your posts after a set number of days" msgstr "" -#: Zotlabs/Module/Expire.php:42 +#: Zotlabs/Module/Expire.php:44 msgid "Expire and delete all my posts after this many days" msgstr "" -#: Zotlabs/Module/Expire.php:42 +#: Zotlabs/Module/Expire.php:44 msgid "" "Leave at 0 if you wish to manually control expiration of specific posts." msgstr "" -#: Zotlabs/Module/Expire.php:46 +#: Zotlabs/Module/Expire.php:48 msgid "Automatic Expiration Settings" msgstr "" -#: Zotlabs/Module/Admin.php:120 +#: Zotlabs/Module/Admin.php:124 msgid "Blocked accounts" msgstr "" -#: Zotlabs/Module/Admin.php:121 +#: Zotlabs/Module/Admin.php:125 msgid "Expired accounts" msgstr "" -#: Zotlabs/Module/Admin.php:122 +#: Zotlabs/Module/Admin.php:126 msgid "Expiring accounts" msgstr "" -#: Zotlabs/Module/Admin.php:138 Zotlabs/Module/Locs.php:124 +#: Zotlabs/Module/Admin.php:143 Zotlabs/Module/Locs.php:134 msgid "Primary" msgstr "" -#: Zotlabs/Module/Admin.php:139 +#: Zotlabs/Module/Admin.php:144 msgid "Clones" msgstr "" -#: Zotlabs/Module/Admin.php:145 +#: Zotlabs/Module/Admin.php:150 msgid "Message queues" msgstr "" -#: Zotlabs/Module/Admin.php:170 +#: Zotlabs/Module/Admin.php:174 msgid "Your software should be updated" msgstr "" -#: Zotlabs/Module/Admin.php:176 include/conversation.php:1479 +#: Zotlabs/Module/Admin.php:180 include/conversation.php:1504 msgid "Summary" msgstr "" -#: Zotlabs/Module/Admin.php:179 +#: Zotlabs/Module/Admin.php:183 msgid "Registered accounts" msgstr "" -#: Zotlabs/Module/Admin.php:180 +#: Zotlabs/Module/Admin.php:184 msgid "Pending registrations" msgstr "" -#: Zotlabs/Module/Admin.php:181 +#: Zotlabs/Module/Admin.php:185 msgid "Registered channels" msgstr "" -#: Zotlabs/Module/Admin.php:182 +#: Zotlabs/Module/Admin.php:186 msgid "Active addons" msgstr "" -#: Zotlabs/Module/Admin.php:183 Zotlabs/Module/Sites.php:100 +#: Zotlabs/Module/Admin.php:187 Zotlabs/Module/Sites.php:103 msgid "Version" msgstr "" -#: Zotlabs/Module/Admin.php:184 +#: Zotlabs/Module/Admin.php:188 msgid "Repository version (release)" msgstr "" -#: Zotlabs/Module/Admin.php:185 +#: Zotlabs/Module/Admin.php:189 msgid "Repository version (dev)" msgstr "" -#: Zotlabs/Module/Block.php:30 Zotlabs/Module/Page.php:43 +#: Zotlabs/Module/Block.php:36 Zotlabs/Module/Page.php:46 msgid "Invalid item." msgstr "" -#: Zotlabs/Module/Block.php:78 Zotlabs/Module/Display.php:151 -#: Zotlabs/Module/Display.php:168 Zotlabs/Module/Display.php:185 -#: Zotlabs/Module/Display.php:191 Zotlabs/Module/Page.php:140 -#: Zotlabs/Web/Router.php:169 +#: Zotlabs/Module/Block.php:86 Zotlabs/Module/Display.php:158 +#: Zotlabs/Module/Display.php:176 Zotlabs/Module/Display.php:194 +#: Zotlabs/Module/Display.php:200 Zotlabs/Module/Page.php:148 +#: Zotlabs/Web/Router.php:166 msgid "Page not found." msgstr "" -#: Zotlabs/Module/Cards.php:48 +#: Zotlabs/Module/Cards.php:52 msgid "Create personal planning cards" msgstr "" -#: Zotlabs/Module/Cards.php:109 +#: Zotlabs/Module/Cards.php:110 msgid "Add Card" msgstr "" -#: Zotlabs/Module/Cards.php:110 Zotlabs/Module/Blocks.php:163 -#: Zotlabs/Module/Cdav.php:1056 Zotlabs/Module/Cdav.php:1364 -#: Zotlabs/Module/Connedit.php:867 Zotlabs/Module/Layouts.php:191 -#: Zotlabs/Module/Menu.php:184 Zotlabs/Module/New_channel.php:196 -#: Zotlabs/Module/Webpages.php:255 Zotlabs/Module/Profiles.php:806 -#: Zotlabs/Storage/Browser.php:299 Zotlabs/Storage/Browser.php:417 -#: Zotlabs/Widget/Cdav.php:140 Zotlabs/Widget/Cdav.php:178 +#: Zotlabs/Module/Cards.php:111 Zotlabs/Module/Blocks.php:172 +#: Zotlabs/Module/Cdav.php:1061 Zotlabs/Module/Cdav.php:1365 +#: Zotlabs/Module/Connedit.php:878 Zotlabs/Module/Layouts.php:199 +#: Zotlabs/Module/Menu.php:187 Zotlabs/Module/New_channel.php:203 +#: Zotlabs/Module/Webpages.php:264 Zotlabs/Module/Profiles.php:847 +#: Zotlabs/Storage/Browser.php:302 Zotlabs/Storage/Browser.php:420 +#: Zotlabs/Widget/Cdav.php:148 Zotlabs/Widget/Cdav.php:184 msgid "Create" msgstr "" -#: Zotlabs/Module/Blocks.php:101 Zotlabs/Module/Blocks.php:159 -#: Zotlabs/Module/Editblock.php:117 +#: Zotlabs/Module/Blocks.php:106 Zotlabs/Module/Blocks.php:168 +#: Zotlabs/Module/Editblock.php:126 msgid "Block Name" msgstr "" -#: Zotlabs/Module/Blocks.php:158 include/text.php:2692 +#: Zotlabs/Module/Blocks.php:167 include/text.php:2850 msgid "Blocks" msgstr "" -#: Zotlabs/Module/Blocks.php:160 +#: Zotlabs/Module/Blocks.php:169 msgid "Block Title" msgstr "" -#: Zotlabs/Module/Blocks.php:161 Zotlabs/Module/Connections.php:248 -#: Zotlabs/Module/Layouts.php:197 Zotlabs/Module/Menu.php:180 -#: Zotlabs/Module/Webpages.php:267 +#: Zotlabs/Module/Blocks.php:170 Zotlabs/Module/Connections.php:250 +#: Zotlabs/Module/Layouts.php:205 Zotlabs/Module/Menu.php:183 +#: Zotlabs/Module/Webpages.php:276 msgid "Created" msgstr "" -#: Zotlabs/Module/Blocks.php:162 Zotlabs/Module/Layouts.php:198 -#: Zotlabs/Module/Menu.php:181 Zotlabs/Module/Webpages.php:268 +#: Zotlabs/Module/Blocks.php:171 Zotlabs/Module/Layouts.php:206 +#: Zotlabs/Module/Menu.php:184 Zotlabs/Module/Webpages.php:277 msgid "Edited" msgstr "" -#: Zotlabs/Module/Blocks.php:165 Zotlabs/Module/Layouts.php:200 -#: Zotlabs/Module/Webpages.php:257 Zotlabs/Module/Photos.php:1158 -#: Zotlabs/Widget/Cdav.php:136 include/conversation.php:1376 +#: Zotlabs/Module/Blocks.php:174 Zotlabs/Module/Layouts.php:208 +#: Zotlabs/Module/Webpages.php:266 Zotlabs/Module/Photos.php:1170 +#: Zotlabs/Widget/Cdav.php:144 include/conversation.php:1401 msgid "Share" msgstr "" -#: Zotlabs/Module/Blocks.php:170 Zotlabs/Module/Events.php:733 -#: Zotlabs/Module/Layouts.php:204 Zotlabs/Module/Webpages.php:262 +#: Zotlabs/Module/Blocks.php:179 Zotlabs/Module/Events.php:759 +#: Zotlabs/Module/Layouts.php:212 Zotlabs/Module/Webpages.php:271 msgid "View" msgstr "" -#: Zotlabs/Module/Cdav.php:786 Zotlabs/Module/Events.php:33 +#: Zotlabs/Module/Cdav.php:793 Zotlabs/Module/Events.php:39 msgid "Calendar entries imported." msgstr "" -#: Zotlabs/Module/Cdav.php:789 Zotlabs/Module/Events.php:35 +#: Zotlabs/Module/Cdav.php:795 Zotlabs/Module/Events.php:41 msgid "No calendar entries found." msgstr "" -#: Zotlabs/Module/Cdav.php:852 +#: Zotlabs/Module/Cdav.php:857 msgid "INVALID EVENT DISMISSED!" msgstr "" -#: Zotlabs/Module/Cdav.php:853 +#: Zotlabs/Module/Cdav.php:858 msgid "Summary: " msgstr "" -#: Zotlabs/Module/Cdav.php:854 +#: Zotlabs/Module/Cdav.php:859 msgid "Date: " msgstr "" -#: Zotlabs/Module/Cdav.php:855 Zotlabs/Module/Cdav.php:862 +#: Zotlabs/Module/Cdav.php:860 Zotlabs/Module/Cdav.php:866 msgid "Reason: " msgstr "" -#: Zotlabs/Module/Cdav.php:860 +#: Zotlabs/Module/Cdav.php:864 msgid "INVALID CARD DISMISSED!" msgstr "" -#: Zotlabs/Module/Cdav.php:861 +#: Zotlabs/Module/Cdav.php:865 msgid "Name: " msgstr "" -#: Zotlabs/Module/Cdav.php:882 +#: Zotlabs/Module/Cdav.php:886 msgid "CardDAV App" msgstr "" -#: Zotlabs/Module/Cdav.php:882 extend/addon/a/fuzzloc/Mod_Fuzzloc.php:37 +#: Zotlabs/Module/Cdav.php:886 extend/addon/a/fuzzloc/Mod_Fuzzloc.php:37 #: extend/addon/a/gallery/Mod_Gallery.php:59 msgid "Not Installed" msgstr "" -#: Zotlabs/Module/Cdav.php:883 +#: Zotlabs/Module/Cdav.php:887 msgid "CalDAV capable addressbook" msgstr "" -#: Zotlabs/Module/Cdav.php:1009 Zotlabs/Module/Events.php:491 +#: Zotlabs/Module/Cdav.php:1014 Zotlabs/Module/Events.php:508 msgid "Event title" msgstr "" -#: Zotlabs/Module/Cdav.php:1010 Zotlabs/Module/Events.php:497 +#: Zotlabs/Module/Cdav.php:1015 Zotlabs/Module/Events.php:514 msgid "Start date and time" msgstr "" -#: Zotlabs/Module/Cdav.php:1011 +#: Zotlabs/Module/Cdav.php:1016 msgid "End date and time" msgstr "" -#: Zotlabs/Module/Cdav.php:1012 Zotlabs/Module/Appman.php:134 -#: Zotlabs/Module/Events.php:504 +#: Zotlabs/Module/Cdav.php:1017 Zotlabs/Module/Appman.php:140 +#: Zotlabs/Module/Events.php:521 msgid "Description" msgstr "" -#: Zotlabs/Module/Cdav.php:1013 Zotlabs/Module/Events.php:506 -#: Zotlabs/Module/Sites.php:91 Zotlabs/Module/Locs.php:122 -#: Zotlabs/Module/Profiles.php:506 Zotlabs/Module/Profiles.php:738 -#: include/js_strings.php:26 +#: Zotlabs/Module/Cdav.php:1018 Zotlabs/Module/Events.php:523 +#: Zotlabs/Module/Sites.php:94 Zotlabs/Module/Locs.php:132 +#: Zotlabs/Module/Profiles.php:536 Zotlabs/Module/Profiles.php:779 +#: include/js_strings.php:29 msgid "Location" msgstr "" -#: Zotlabs/Module/Cdav.php:1035 Zotlabs/Module/Events.php:734 +#: Zotlabs/Module/Cdav.php:1040 Zotlabs/Module/Events.php:760 msgid "Month" msgstr "" -#: Zotlabs/Module/Cdav.php:1036 Zotlabs/Module/Events.php:735 +#: Zotlabs/Module/Cdav.php:1041 Zotlabs/Module/Events.php:761 msgid "Week" msgstr "" -#: Zotlabs/Module/Cdav.php:1037 Zotlabs/Module/Events.php:736 +#: Zotlabs/Module/Cdav.php:1042 Zotlabs/Module/Events.php:762 msgid "Day" msgstr "" -#: Zotlabs/Module/Cdav.php:1038 +#: Zotlabs/Module/Cdav.php:1043 msgid "List month" msgstr "" -#: Zotlabs/Module/Cdav.php:1039 +#: Zotlabs/Module/Cdav.php:1044 msgid "List week" msgstr "" -#: Zotlabs/Module/Cdav.php:1040 +#: Zotlabs/Module/Cdav.php:1045 msgid "List day" msgstr "" -#: Zotlabs/Module/Cdav.php:1048 +#: Zotlabs/Module/Cdav.php:1053 msgid "More" msgstr "" -#: Zotlabs/Module/Cdav.php:1049 +#: Zotlabs/Module/Cdav.php:1054 msgid "Less" msgstr "" -#: Zotlabs/Module/Cdav.php:1051 +#: Zotlabs/Module/Cdav.php:1056 msgid "Select calendar" msgstr "" -#: Zotlabs/Module/Cdav.php:1052 Zotlabs/Widget/Cdav.php:143 +#: Zotlabs/Module/Cdav.php:1057 Zotlabs/Widget/Cdav.php:151 msgid "Channel Calendars" msgstr "" -#: Zotlabs/Module/Cdav.php:1052 Zotlabs/Widget/Cdav.php:129 -#: Zotlabs/Widget/Cdav.php:143 +#: Zotlabs/Module/Cdav.php:1057 Zotlabs/Widget/Cdav.php:137 +#: Zotlabs/Widget/Cdav.php:151 msgid "CalDAV Calendars" msgstr "" -#: Zotlabs/Module/Cdav.php:1054 +#: Zotlabs/Module/Cdav.php:1059 msgid "Delete all" msgstr "" -#: Zotlabs/Module/Cdav.php:1057 +#: Zotlabs/Module/Cdav.php:1062 msgid "Sorry! Editing of recurrent events is not yet implemented." msgstr "" -#: Zotlabs/Module/Cdav.php:1350 Zotlabs/Module/Connedit.php:853 +#: Zotlabs/Module/Cdav.php:1351 Zotlabs/Module/Connedit.php:864 msgid "Organisation" msgstr "" -#: Zotlabs/Module/Cdav.php:1351 Zotlabs/Module/Connedit.php:854 +#: Zotlabs/Module/Cdav.php:1352 Zotlabs/Module/Connedit.php:865 msgid "Title" msgstr "" -#: Zotlabs/Module/Cdav.php:1352 Zotlabs/Module/Connedit.php:855 -#: Zotlabs/Module/Profiles.php:794 +#: Zotlabs/Module/Cdav.php:1353 Zotlabs/Module/Connedit.php:866 +#: Zotlabs/Module/Profiles.php:835 msgid "Phone" msgstr "" -#: Zotlabs/Module/Cdav.php:1354 Zotlabs/Module/Connedit.php:857 -#: Zotlabs/Module/Profiles.php:796 +#: Zotlabs/Module/Cdav.php:1355 Zotlabs/Module/Connedit.php:868 +#: Zotlabs/Module/Profiles.php:837 msgid "Instant messenger" msgstr "" -#: Zotlabs/Module/Cdav.php:1355 Zotlabs/Module/Connedit.php:858 -#: Zotlabs/Module/Profiles.php:797 +#: Zotlabs/Module/Cdav.php:1356 Zotlabs/Module/Connedit.php:869 +#: Zotlabs/Module/Profiles.php:838 msgid "Website" msgstr "" -#: Zotlabs/Module/Cdav.php:1357 Zotlabs/Module/Connedit.php:860 -#: Zotlabs/Module/Profiles.php:799 +#: Zotlabs/Module/Cdav.php:1358 Zotlabs/Module/Connedit.php:871 +#: Zotlabs/Module/Profiles.php:840 msgid "Note" msgstr "" -#: Zotlabs/Module/Cdav.php:1358 Zotlabs/Module/Connedit.php:861 -#: Zotlabs/Module/Profiles.php:800 include/connections.php:790 -#: include/event.php:1377 +#: Zotlabs/Module/Cdav.php:1359 Zotlabs/Module/Connedit.php:872 +#: Zotlabs/Module/Profiles.php:841 include/event.php:1514 +#: include/connections.php:825 msgid "Mobile" msgstr "" -#: Zotlabs/Module/Cdav.php:1359 Zotlabs/Module/Connedit.php:862 -#: Zotlabs/Module/Profiles.php:801 include/connections.php:791 -#: include/event.php:1378 +#: Zotlabs/Module/Cdav.php:1360 Zotlabs/Module/Connedit.php:873 +#: Zotlabs/Module/Profiles.php:842 include/event.php:1515 +#: include/connections.php:826 msgid "Home" msgstr "" -#: Zotlabs/Module/Cdav.php:1360 Zotlabs/Module/Connedit.php:863 -#: Zotlabs/Module/Profiles.php:802 include/connections.php:794 -#: include/event.php:1381 +#: Zotlabs/Module/Cdav.php:1361 Zotlabs/Module/Connedit.php:874 +#: Zotlabs/Module/Profiles.php:843 include/event.php:1518 +#: include/connections.php:829 msgid "Work" msgstr "" -#: Zotlabs/Module/Cdav.php:1361 Zotlabs/Module/Connedit.php:864 -#: Zotlabs/Module/Profiles.php:803 Zotlabs/Module/Profiles.php:942 -#: Zotlabs/Module/Profiles.php:959 Zotlabs/Module/Profiles.php:1027 -#: Zotlabs/Module/Profiles.php:1063 include/connections.php:797 -#: include/connections.php:804 include/event.php:1384 include/event.php:1391 +#: Zotlabs/Module/Cdav.php:1362 Zotlabs/Module/Connedit.php:875 +#: Zotlabs/Module/Profiles.php:844 Zotlabs/Module/Profiles.php:985 +#: Zotlabs/Module/Profiles.php:1003 Zotlabs/Module/Profiles.php:1073 +#: Zotlabs/Module/Profiles.php:1110 include/event.php:1521 +#: include/event.php:1527 include/connections.php:832 +#: include/connections.php:838 msgid "Other" msgstr "" -#: Zotlabs/Module/Cdav.php:1362 Zotlabs/Module/Connedit.php:865 -#: Zotlabs/Module/Profiles.php:804 +#: Zotlabs/Module/Cdav.php:1363 Zotlabs/Module/Connedit.php:876 +#: Zotlabs/Module/Profiles.php:845 msgid "Add Contact" msgstr "" -#: Zotlabs/Module/Cdav.php:1363 Zotlabs/Module/Connedit.php:866 -#: Zotlabs/Module/Profiles.php:805 +#: Zotlabs/Module/Cdav.php:1364 Zotlabs/Module/Connedit.php:877 +#: Zotlabs/Module/Profiles.php:846 msgid "Add Field" msgstr "" -#: Zotlabs/Module/Cdav.php:1368 Zotlabs/Module/Connedit.php:871 +#: Zotlabs/Module/Cdav.php:1369 Zotlabs/Module/Connedit.php:882 msgid "P.O. Box" msgstr "" -#: Zotlabs/Module/Cdav.php:1369 Zotlabs/Module/Connedit.php:872 +#: Zotlabs/Module/Cdav.php:1370 Zotlabs/Module/Connedit.php:883 msgid "Additional" msgstr "" -#: Zotlabs/Module/Cdav.php:1370 Zotlabs/Module/Connedit.php:873 +#: Zotlabs/Module/Cdav.php:1371 Zotlabs/Module/Connedit.php:884 msgid "Street" msgstr "" -#: Zotlabs/Module/Cdav.php:1371 Zotlabs/Module/Connedit.php:874 +#: Zotlabs/Module/Cdav.php:1372 Zotlabs/Module/Connedit.php:885 msgid "Locality" msgstr "" -#: Zotlabs/Module/Cdav.php:1372 Zotlabs/Module/Connedit.php:875 +#: Zotlabs/Module/Cdav.php:1373 Zotlabs/Module/Connedit.php:886 msgid "Region" msgstr "" -#: Zotlabs/Module/Cdav.php:1373 Zotlabs/Module/Connedit.php:876 +#: Zotlabs/Module/Cdav.php:1374 Zotlabs/Module/Connedit.php:887 msgid "ZIP Code" msgstr "" -#: Zotlabs/Module/Cdav.php:1374 Zotlabs/Module/Connedit.php:877 -#: Zotlabs/Module/Profiles.php:762 +#: Zotlabs/Module/Cdav.php:1375 Zotlabs/Module/Connedit.php:888 +#: Zotlabs/Module/Profiles.php:803 msgid "Country" msgstr "" -#: Zotlabs/Module/Cdav.php:1423 +#: Zotlabs/Module/Cdav.php:1426 msgid "Default Calendar" msgstr "" -#: Zotlabs/Module/Cdav.php:1434 +#: Zotlabs/Module/Cdav.php:1437 msgid "Default Addressbook" msgstr "" -#: Zotlabs/Module/Changeaddr.php:52 +#: Zotlabs/Module/Changeaddr.php:57 msgid "" "Channel name changes are not allowed within 48 hours of changing the account " "password." msgstr "" -#: Zotlabs/Module/Changeaddr.php:63 include/channel.php:282 -#: include/channel.php:715 +#: Zotlabs/Module/Changeaddr.php:69 include/channel.php:298 +#: include/channel.php:749 msgid "Reserved nickname. Please choose another." msgstr "" -#: Zotlabs/Module/Changeaddr.php:68 include/channel.php:287 -#: include/channel.php:720 +#: Zotlabs/Module/Changeaddr.php:74 include/channel.php:303 +#: include/channel.php:754 msgid "" "Nickname has unsupported characters or is already being used on this site." msgstr "" -#: Zotlabs/Module/Changeaddr.php:82 +#: Zotlabs/Module/Changeaddr.php:88 msgid "Feature has been disabled" msgstr "" -#: Zotlabs/Module/Changeaddr.php:101 +#: Zotlabs/Module/Changeaddr.php:107 msgid "Change channel nickname/address" msgstr "" -#: Zotlabs/Module/Changeaddr.php:102 Zotlabs/Module/Removeme.php:68 -#: Zotlabs/Module/Removeaccount.php:68 +#: Zotlabs/Module/Changeaddr.php:108 Zotlabs/Module/Removeme.php:72 +#: Zotlabs/Module/Removeaccount.php:72 msgid "WARNING: " msgstr "" -#: Zotlabs/Module/Changeaddr.php:102 +#: Zotlabs/Module/Changeaddr.php:108 msgid "Any/all connections on other networks will be lost!" msgstr "" -#: Zotlabs/Module/Changeaddr.php:103 Zotlabs/Module/Removeme.php:69 -#: Zotlabs/Module/Removeaccount.php:69 +#: Zotlabs/Module/Changeaddr.php:109 Zotlabs/Module/Removeme.php:73 +#: Zotlabs/Module/Removeaccount.php:73 msgid "Please enter your password for verification:" msgstr "" -#: Zotlabs/Module/Changeaddr.php:104 +#: Zotlabs/Module/Changeaddr.php:110 msgid "New channel address" msgstr "" -#: Zotlabs/Module/Changeaddr.php:105 +#: Zotlabs/Module/Changeaddr.php:111 msgid "Rename Channel" msgstr "" -#: Zotlabs/Module/Ping.php:412 +#: Zotlabs/Module/Ping.php:432 msgid "added your channel" msgstr "" -#: Zotlabs/Module/Ping.php:436 +#: Zotlabs/Module/Ping.php:457 msgid "requires approval" msgstr "" -#: Zotlabs/Module/Ping.php:445 +#: Zotlabs/Module/Ping.php:466 msgid "g A l F d" msgstr "" -#: Zotlabs/Module/Ping.php:463 +#: Zotlabs/Module/Ping.php:484 msgid "[today]" msgstr "" -#: Zotlabs/Module/Ping.php:473 +#: Zotlabs/Module/Ping.php:494 msgid "posted an event" msgstr "" -#: Zotlabs/Module/Ping.php:506 +#: Zotlabs/Module/Ping.php:528 msgid "shared a file with you" msgstr "" -#: Zotlabs/Module/Ping.php:534 +#: Zotlabs/Module/Ping.php:556 msgid "reported content" msgstr "" -#: Zotlabs/Module/Ping.php:730 +#: Zotlabs/Module/Ping.php:770 msgid "Private group" msgstr "" -#: Zotlabs/Module/Ping.php:730 +#: Zotlabs/Module/Ping.php:770 msgid "Public group" msgstr "" -#: Zotlabs/Module/Help.php:21 +#: Zotlabs/Module/Help.php:24 msgid "Documentation Search" msgstr "" -#: Zotlabs/Module/Help.php:92 +#: Zotlabs/Module/Help.php:95 msgid "Unknown language" msgstr "" -#: Zotlabs/Module/Help.php:112 +#: Zotlabs/Module/Help.php:114 msgid "$Projectname Documentation" msgstr "" -#: Zotlabs/Module/Help.php:113 +#: Zotlabs/Module/Help.php:115 msgid "Contents" msgstr "" -#: Zotlabs/Module/Chatsvc.php:131 +#: Zotlabs/Module/Chatsvc.php:146 msgid "Away" msgstr "" -#: Zotlabs/Module/Chatsvc.php:136 +#: Zotlabs/Module/Chatsvc.php:151 msgid "Online" msgstr "" -#: Zotlabs/Module/Connedit.php:83 Zotlabs/Module/Defperms.php:68 +#: Zotlabs/Module/Connedit.php:88 Zotlabs/Module/Defperms.php:74 msgid "Could not access contact record." msgstr "" -#: Zotlabs/Module/Connedit.php:114 +#: Zotlabs/Module/Connedit.php:120 msgid "Could not locate selected profile." msgstr "" -#: Zotlabs/Module/Connedit.php:197 +#: Zotlabs/Module/Connedit.php:203 msgid "Connection updated." msgstr "" -#: Zotlabs/Module/Connedit.php:200 +#: Zotlabs/Module/Connedit.php:205 msgid "Failed to update connection record." msgstr "" -#: Zotlabs/Module/Connedit.php:244 +#: Zotlabs/Module/Connedit.php:250 msgid "is now connected to" msgstr "" -#: Zotlabs/Module/Connedit.php:369 +#: Zotlabs/Module/Connedit.php:377 msgid "Could not access address book record." msgstr "" -#: Zotlabs/Module/Connedit.php:414 +#: Zotlabs/Module/Connedit.php:423 msgid "Refresh failed - channel is currently unavailable." msgstr "" -#: Zotlabs/Module/Connedit.php:443 +#: Zotlabs/Module/Connedit.php:450 msgid "Added by Connedit" msgstr "" -#: Zotlabs/Module/Connedit.php:481 +#: Zotlabs/Module/Connedit.php:488 msgid "Unable to set address book parameters." msgstr "" -#: Zotlabs/Module/Connedit.php:498 +#: Zotlabs/Module/Connedit.php:505 msgid "Connection has been removed." msgstr "" -#: Zotlabs/Module/Connedit.php:546 +#: Zotlabs/Module/Connedit.php:551 #, php-format msgid "View %s's profile" msgstr "" -#: Zotlabs/Module/Connedit.php:550 +#: Zotlabs/Module/Connedit.php:555 msgid "Refresh Permissions" msgstr "" -#: Zotlabs/Module/Connedit.php:553 +#: Zotlabs/Module/Connedit.php:558 msgid "Fetch updated permissions" msgstr "" -#: Zotlabs/Module/Connedit.php:557 +#: Zotlabs/Module/Connedit.php:562 msgid "Refresh Photo" msgstr "" -#: Zotlabs/Module/Connedit.php:560 +#: Zotlabs/Module/Connedit.php:565 msgid "Fetch updated photo" msgstr "" -#: Zotlabs/Module/Connedit.php:564 include/conversation.php:965 +#: Zotlabs/Module/Connedit.php:569 include/conversation.php:965 msgid "Recent Activity" msgstr "" -#: Zotlabs/Module/Connedit.php:567 +#: Zotlabs/Module/Connedit.php:572 msgid "View recent posts and comments" msgstr "" -#: Zotlabs/Module/Connedit.php:574 +#: Zotlabs/Module/Connedit.php:579 msgid "Block (or Unblock) all communications with this connection" msgstr "" -#: Zotlabs/Module/Connedit.php:575 +#: Zotlabs/Module/Connedit.php:580 msgid "This connection is blocked" msgstr "" -#: Zotlabs/Module/Connedit.php:579 +#: Zotlabs/Module/Connedit.php:584 msgid "Unignore" msgstr "" -#: Zotlabs/Module/Connedit.php:579 Zotlabs/Module/Connections.php:342 +#: Zotlabs/Module/Connedit.php:584 Zotlabs/Module/Connections.php:343 msgid "Ignore" msgstr "" -#: Zotlabs/Module/Connedit.php:582 +#: Zotlabs/Module/Connedit.php:587 msgid "Ignore (or Unignore) all inbound communications from this connection" msgstr "" -#: Zotlabs/Module/Connedit.php:583 +#: Zotlabs/Module/Connedit.php:588 msgid "This connection is ignored" msgstr "" -#: Zotlabs/Module/Connedit.php:590 +#: Zotlabs/Module/Connedit.php:595 msgid "Censor (or Uncensor) images from this connection" msgstr "" -#: Zotlabs/Module/Connedit.php:591 +#: Zotlabs/Module/Connedit.php:596 msgid "This connection is censored" msgstr "" -#: Zotlabs/Module/Connedit.php:595 +#: Zotlabs/Module/Connedit.php:600 msgid "Unarchive" msgstr "" -#: Zotlabs/Module/Connedit.php:595 +#: Zotlabs/Module/Connedit.php:600 msgid "Archive" msgstr "" -#: Zotlabs/Module/Connedit.php:598 +#: Zotlabs/Module/Connedit.php:603 msgid "" "Archive (or Unarchive) this connection - mark channel dead but keep content" msgstr "" -#: Zotlabs/Module/Connedit.php:599 +#: Zotlabs/Module/Connedit.php:604 msgid "This connection is archived" msgstr "" -#: Zotlabs/Module/Connedit.php:603 +#: Zotlabs/Module/Connedit.php:608 msgid "Unhide" msgstr "" -#: Zotlabs/Module/Connedit.php:603 +#: Zotlabs/Module/Connedit.php:608 msgid "Hide" msgstr "" -#: Zotlabs/Module/Connedit.php:606 +#: Zotlabs/Module/Connedit.php:611 msgid "Hide or Unhide this connection from your other connections" msgstr "" -#: Zotlabs/Module/Connedit.php:607 +#: Zotlabs/Module/Connedit.php:612 msgid "This connection is hidden" msgstr "" -#: Zotlabs/Module/Connedit.php:614 +#: Zotlabs/Module/Connedit.php:619 msgid "Delete this connection" msgstr "" -#: Zotlabs/Module/Connedit.php:622 +#: Zotlabs/Module/Connedit.php:627 msgid "Fetch Vcard" msgstr "" -#: Zotlabs/Module/Connedit.php:625 +#: Zotlabs/Module/Connedit.php:630 msgid "Fetch electronic calling card for this connection" msgstr "" -#: Zotlabs/Module/Connedit.php:632 Zotlabs/Module/Filestorage.php:226 -#: Zotlabs/Module/Thing.php:338 Zotlabs/Module/Thing.php:391 -#: Zotlabs/Module/Chat.php:233 Zotlabs/Module/Photos.php:728 -#: Zotlabs/Module/Photos.php:1108 +#: Zotlabs/Module/Connedit.php:637 Zotlabs/Module/Filestorage.php:230 +#: Zotlabs/Module/Thing.php:355 Zotlabs/Module/Thing.php:410 +#: Zotlabs/Module/Chat.php:240 Zotlabs/Module/Photos.php:736 +#: Zotlabs/Module/Photos.php:1121 #: extend/addon/a/flashcards/Mod_Flashcards.php:254 #: extend/addon/a/faces/Mod_Faces.php:172 include/acl_selectors.php:152 msgid "Permissions" msgstr "" -#: Zotlabs/Module/Connedit.php:635 +#: Zotlabs/Module/Connedit.php:640 msgid "Open Individual Permissions section by default" msgstr "" -#: Zotlabs/Module/Connedit.php:661 +#: Zotlabs/Module/Connedit.php:666 msgid "Open Friend Zoom section by default" msgstr "" -#: Zotlabs/Module/Connedit.php:665 Zotlabs/Module/Affinity.php:58 -#: Zotlabs/Widget/Affinity.php:24 +#: Zotlabs/Module/Connedit.php:670 Zotlabs/Module/Affinity.php:61 +#: Zotlabs/Widget/Affinity.php:26 msgid "Me" msgstr "" -#: Zotlabs/Module/Connedit.php:666 Zotlabs/Module/Affinity.php:59 -#: Zotlabs/Widget/Affinity.php:25 +#: Zotlabs/Module/Connedit.php:671 Zotlabs/Module/Affinity.php:62 +#: Zotlabs/Widget/Affinity.php:27 msgid "Family" msgstr "" -#: Zotlabs/Module/Connedit.php:667 Zotlabs/Module/Profiles.php:1046 -#: Zotlabs/Module/Affinity.php:60 Zotlabs/Module/Settings/Channel.php:75 -#: Zotlabs/Module/Settings/Channel.php:79 -#: Zotlabs/Module/Settings/Channel.php:80 -#: Zotlabs/Module/Settings/Channel.php:83 -#: Zotlabs/Module/Settings/Channel.php:94 Zotlabs/Widget/Affinity.php:26 -#: Zotlabs/Import/Friendica.php:242 Zotlabs/Import/Friendica.php:243 -#: Zotlabs/Import/Friendica.php:250 include/channel.php:505 -#: include/channel.php:507 +#: Zotlabs/Module/Connedit.php:672 Zotlabs/Module/Profiles.php:1092 +#: Zotlabs/Module/Affinity.php:63 Zotlabs/Module/Settings/Channel.php:78 +#: Zotlabs/Module/Settings/Channel.php:81 +#: Zotlabs/Module/Settings/Channel.php:82 +#: Zotlabs/Module/Settings/Channel.php:86 +#: Zotlabs/Module/Settings/Channel.php:97 Zotlabs/Widget/Affinity.php:28 +#: Zotlabs/Import/Friendica.php:244 Zotlabs/Import/Friendica.php:245 +#: Zotlabs/Import/Friendica.php:253 include/channel.php:529 +#: include/channel.php:531 msgid "Friends" msgstr "" -#: Zotlabs/Module/Connedit.php:668 Zotlabs/Module/Affinity.php:61 -#: Zotlabs/Widget/Affinity.php:27 +#: Zotlabs/Module/Connedit.php:673 Zotlabs/Module/Affinity.php:64 +#: Zotlabs/Widget/Affinity.php:29 msgid "Peers" msgstr "" -#: Zotlabs/Module/Connedit.php:670 Zotlabs/Module/Connections.php:85 -#: Zotlabs/Module/Connections.php:108 Zotlabs/Module/Affinity.php:63 -#: Zotlabs/Widget/Affinity.php:29 +#: Zotlabs/Module/Connedit.php:675 Zotlabs/Module/Connections.php:88 +#: Zotlabs/Module/Connections.php:111 Zotlabs/Module/Affinity.php:66 +#: Zotlabs/Widget/Affinity.php:31 msgid "All" msgstr "" -#: Zotlabs/Module/Connedit.php:687 +#: Zotlabs/Module/Connedit.php:692 msgid "Filter" msgstr "" -#: Zotlabs/Module/Connedit.php:690 +#: Zotlabs/Module/Connedit.php:695 msgid "Open Custom Filter section by default" msgstr "" -#: Zotlabs/Module/Connedit.php:727 +#: Zotlabs/Module/Connedit.php:732 msgid "Approve this connection" msgstr "" -#: Zotlabs/Module/Connedit.php:727 +#: Zotlabs/Module/Connedit.php:732 msgid "Accept connection to allow communication" msgstr "" -#: Zotlabs/Module/Connedit.php:732 +#: Zotlabs/Module/Connedit.php:737 msgid "Set Friend Zoom" msgstr "" -#: Zotlabs/Module/Connedit.php:735 +#: Zotlabs/Module/Connedit.php:741 msgid "Set Profile" msgstr "" -#: Zotlabs/Module/Connedit.php:738 +#: Zotlabs/Module/Connedit.php:745 msgid "Set Friend Zoom & Profile" msgstr "" -#: Zotlabs/Module/Connedit.php:782 +#: Zotlabs/Module/Connedit.php:793 msgid "This connection is unreachable from this location." msgstr "" -#: Zotlabs/Module/Connedit.php:783 +#: Zotlabs/Module/Connedit.php:794 msgid "This connection may be unreachable from other channel locations." msgstr "" -#: Zotlabs/Module/Connedit.php:785 +#: Zotlabs/Module/Connedit.php:796 msgid "Location independence is not supported by their network." msgstr "" -#: Zotlabs/Module/Connedit.php:791 +#: Zotlabs/Module/Connedit.php:801 msgid "" "This connection is unreachable from this location. Location independence is " "not supported by their network." msgstr "" -#: Zotlabs/Module/Connedit.php:794 Zotlabs/Module/Defperms.php:240 +#: Zotlabs/Module/Connedit.php:805 Zotlabs/Module/Defperms.php:247 msgid "Connection Default Permissions" msgstr "" -#: Zotlabs/Module/Connedit.php:794 include/items.php:4409 +#: Zotlabs/Module/Connedit.php:805 include/items.php:4561 #, php-format msgid "Connection: %s" msgstr "" -#: Zotlabs/Module/Connedit.php:795 Zotlabs/Module/Defperms.php:241 +#: Zotlabs/Module/Connedit.php:806 Zotlabs/Module/Defperms.php:248 msgid "Apply these permissions automatically" msgstr "" -#: Zotlabs/Module/Connedit.php:795 +#: Zotlabs/Module/Connedit.php:806 msgid "Connection requests will be approved without your interaction" msgstr "" -#: Zotlabs/Module/Connedit.php:796 Zotlabs/Module/Defperms.php:242 +#: Zotlabs/Module/Connedit.php:807 Zotlabs/Module/Defperms.php:249 msgid "Permission role" msgstr "" -#: Zotlabs/Module/Connedit.php:796 Zotlabs/Module/Defperms.php:242 -#: Zotlabs/Module/New_channel.php:164 Zotlabs/Module/New_channel.php:171 -#: Zotlabs/Widget/Notifications.php:148 include/nav.php:304 +#: Zotlabs/Module/Connedit.php:807 Zotlabs/Module/Defperms.php:249 +#: Zotlabs/Module/New_channel.php:171 Zotlabs/Module/New_channel.php:178 +#: Zotlabs/Widget/Notifications.php:151 include/nav.php:301 msgid "Loading" msgstr "" -#: Zotlabs/Module/Connedit.php:797 Zotlabs/Module/Defperms.php:243 +#: Zotlabs/Module/Connedit.php:808 Zotlabs/Module/Defperms.php:250 msgid "Add permission role" msgstr "" -#: Zotlabs/Module/Connedit.php:801 +#: Zotlabs/Module/Connedit.php:812 msgid "Ignore shares and repeats this connection posts" msgstr "" -#: Zotlabs/Module/Connedit.php:801 +#: Zotlabs/Module/Connedit.php:812 msgid "Note: This is not recommended for Groups." msgstr "" -#: Zotlabs/Module/Connedit.php:805 +#: Zotlabs/Module/Connedit.php:816 msgid "This connection's primary address is" msgstr "" -#: Zotlabs/Module/Connedit.php:806 +#: Zotlabs/Module/Connedit.php:817 msgid "Available locations:" msgstr "" -#: Zotlabs/Module/Connedit.php:811 Zotlabs/Module/Defperms.php:247 +#: Zotlabs/Module/Connedit.php:822 Zotlabs/Module/Defperms.php:254 msgid "" "The permissions indicated on this page will be applied to all new " "connections." msgstr "" -#: Zotlabs/Module/Connedit.php:812 +#: Zotlabs/Module/Connedit.php:823 msgid "Connection Tools" msgstr "" -#: Zotlabs/Module/Connedit.php:814 +#: Zotlabs/Module/Connedit.php:825 msgid "Slide to adjust your degree of friendship" msgstr "" -#: Zotlabs/Module/Connedit.php:815 include/js_strings.php:21 +#: Zotlabs/Module/Connedit.php:826 include/js_strings.php:24 msgid "Rating" msgstr "" -#: Zotlabs/Module/Connedit.php:816 +#: Zotlabs/Module/Connedit.php:827 msgid "Slide to adjust your rating" msgstr "" -#: Zotlabs/Module/Connedit.php:817 Zotlabs/Module/Connedit.php:823 +#: Zotlabs/Module/Connedit.php:828 Zotlabs/Module/Connedit.php:834 msgid "Optionally explain your rating" msgstr "" -#: Zotlabs/Module/Connedit.php:819 +#: Zotlabs/Module/Connedit.php:830 msgid "Custom Filter" msgstr "" -#: Zotlabs/Module/Connedit.php:820 Zotlabs/Module/Content_filter.php:51 +#: Zotlabs/Module/Connedit.php:831 Zotlabs/Module/Content_filter.php:51 msgid "Only import posts with this text" msgstr "" -#: Zotlabs/Module/Connedit.php:820 Zotlabs/Module/Connedit.php:821 +#: Zotlabs/Module/Connedit.php:831 Zotlabs/Module/Connedit.php:832 msgid "" "words one per line or #tags, $categories, /patterns/, or lang=xx, leave " "blank to import all posts" msgstr "" -#: Zotlabs/Module/Connedit.php:821 Zotlabs/Module/Content_filter.php:59 +#: Zotlabs/Module/Connedit.php:832 Zotlabs/Module/Content_filter.php:59 msgid "Do not import posts with this text" msgstr "" -#: Zotlabs/Module/Connedit.php:822 extend/addon/a/zotpost/Mod_zotpost.php:69 +#: Zotlabs/Module/Connedit.php:833 extend/addon/a/zotpost/Mod_zotpost.php:69 msgid "Nickname" msgstr "" -#: Zotlabs/Module/Connedit.php:822 +#: Zotlabs/Module/Connedit.php:833 msgid "optional - allows you to search by a name that you have chosen" msgstr "" -#: Zotlabs/Module/Connedit.php:824 +#: Zotlabs/Module/Connedit.php:835 msgid "This information is public!" msgstr "" -#: Zotlabs/Module/Connedit.php:829 +#: Zotlabs/Module/Connedit.php:840 msgid "Connection Pending Approval" msgstr "" -#: Zotlabs/Module/Connedit.php:832 Zotlabs/Module/Defperms.php:250 -#: Zotlabs/Module/Settings/Permcats.php:111 -#: Zotlabs/Module/Settings/Tokens.php:291 +#: Zotlabs/Module/Connedit.php:843 Zotlabs/Module/Defperms.php:257 +#: Zotlabs/Module/Settings/Permcats.php:122 +#: Zotlabs/Module/Settings/Tokens.php:309 msgid "inherited" msgstr "" -#: Zotlabs/Module/Connedit.php:834 +#: Zotlabs/Module/Connedit.php:845 #, php-format msgid "" "Please choose the profile you would like to display to %s when viewing your " "profile securely." msgstr "" -#: Zotlabs/Module/Connedit.php:836 Zotlabs/Module/Settings/Tokens.php:288 +#: Zotlabs/Module/Connedit.php:847 Zotlabs/Module/Settings/Tokens.php:306 msgid "Their Settings" msgstr "" -#: Zotlabs/Module/Connedit.php:837 Zotlabs/Module/Defperms.php:252 -#: Zotlabs/Module/Settings/Permcats.php:109 -#: Zotlabs/Module/Settings/Tokens.php:289 +#: Zotlabs/Module/Connedit.php:848 Zotlabs/Module/Defperms.php:259 +#: Zotlabs/Module/Settings/Permcats.php:120 +#: Zotlabs/Module/Settings/Tokens.php:307 msgid "My Settings" msgstr "" -#: Zotlabs/Module/Connedit.php:839 Zotlabs/Module/Defperms.php:255 -#: Zotlabs/Module/Settings/Permcats.php:114 -#: Zotlabs/Module/Settings/Tokens.php:294 +#: Zotlabs/Module/Connedit.php:850 Zotlabs/Module/Defperms.php:262 +#: Zotlabs/Module/Settings/Permcats.php:125 +#: Zotlabs/Module/Settings/Tokens.php:312 msgid "Individual Permissions" msgstr "" -#: Zotlabs/Module/Connedit.php:840 Zotlabs/Module/Settings/Permcats.php:115 -#: Zotlabs/Module/Settings/Tokens.php:295 +#: Zotlabs/Module/Connedit.php:851 Zotlabs/Module/Settings/Permcats.php:126 +#: Zotlabs/Module/Settings/Tokens.php:313 msgid "" "Some permissions may be inherited from your channel's privacy settings, which have higher priority than " "individual settings. You can not change those settings here." msgstr "" -#: Zotlabs/Module/Connedit.php:841 +#: Zotlabs/Module/Connedit.php:852 msgid "" "Some permissions may be inherited from your channel's privacy settings, which have higher priority than " @@ -4335,91 +4347,91 @@ msgid "" "any impact unless the inherited setting changes." msgstr "" -#: Zotlabs/Module/Connedit.php:842 +#: Zotlabs/Module/Connedit.php:853 msgid "Last update:" msgstr "" -#: Zotlabs/Module/Connedit.php:850 +#: Zotlabs/Module/Connedit.php:861 msgid "Details" msgstr "" -#: Zotlabs/Module/Connections.php:65 Zotlabs/Module/Connections.php:154 +#: Zotlabs/Module/Connections.php:68 Zotlabs/Module/Connections.php:155 #: Zotlabs/Module/Connections.php:298 msgid "Blocked" msgstr "" -#: Zotlabs/Module/Connections.php:70 Zotlabs/Module/Connections.php:161 +#: Zotlabs/Module/Connections.php:73 Zotlabs/Module/Connections.php:162 #: Zotlabs/Module/Connections.php:297 msgid "Ignored" msgstr "" -#: Zotlabs/Module/Connections.php:75 Zotlabs/Module/Connections.php:175 +#: Zotlabs/Module/Connections.php:78 Zotlabs/Module/Connections.php:176 #: Zotlabs/Module/Connections.php:296 msgid "Hidden" msgstr "" -#: Zotlabs/Module/Connections.php:80 Zotlabs/Module/Connections.php:168 +#: Zotlabs/Module/Connections.php:83 Zotlabs/Module/Connections.php:169 msgid "Archived/Unreachable" msgstr "" -#: Zotlabs/Module/Connections.php:90 Zotlabs/Module/Connections.php:103 -#: Zotlabs/Module/Menu.php:182 Zotlabs/Module/Notifications.php:55 +#: Zotlabs/Module/Connections.php:93 Zotlabs/Module/Connections.php:107 +#: Zotlabs/Module/Menu.php:185 Zotlabs/Module/Notifications.php:63 msgid "New" msgstr "" -#: Zotlabs/Module/Connections.php:140 +#: Zotlabs/Module/Connections.php:141 msgid "Active Connections" msgstr "" -#: Zotlabs/Module/Connections.php:143 +#: Zotlabs/Module/Connections.php:144 msgid "Show active connections" msgstr "" -#: Zotlabs/Module/Connections.php:147 Zotlabs/Widget/Notifications.php:70 +#: Zotlabs/Module/Connections.php:148 Zotlabs/Widget/Notifications.php:73 msgid "New Connections" msgstr "" -#: Zotlabs/Module/Connections.php:150 +#: Zotlabs/Module/Connections.php:151 msgid "Show pending (new) connections" msgstr "" -#: Zotlabs/Module/Connections.php:157 +#: Zotlabs/Module/Connections.php:158 msgid "Only show blocked connections" msgstr "" -#: Zotlabs/Module/Connections.php:164 +#: Zotlabs/Module/Connections.php:165 msgid "Only show ignored connections" msgstr "" -#: Zotlabs/Module/Connections.php:171 +#: Zotlabs/Module/Connections.php:172 msgid "Only show archived/unreachable connections" msgstr "" -#: Zotlabs/Module/Connections.php:178 +#: Zotlabs/Module/Connections.php:179 msgid "Only show hidden connections" msgstr "" -#: Zotlabs/Module/Connections.php:182 Zotlabs/Module/Profperm.php:144 +#: Zotlabs/Module/Connections.php:183 Zotlabs/Module/Profperm.php:158 msgid "All Connections" msgstr "" -#: Zotlabs/Module/Connections.php:185 +#: Zotlabs/Module/Connections.php:186 msgid "Show all connections" msgstr "" -#: Zotlabs/Module/Connections.php:237 +#: Zotlabs/Module/Connections.php:239 msgid "Order by name" msgstr "" -#: Zotlabs/Module/Connections.php:241 +#: Zotlabs/Module/Connections.php:243 msgid "Recent" msgstr "" -#: Zotlabs/Module/Connections.php:244 +#: Zotlabs/Module/Connections.php:246 msgid "Order by recent" msgstr "" -#: Zotlabs/Module/Connections.php:251 +#: Zotlabs/Module/Connections.php:253 msgid "Order by date" msgstr "" @@ -4435,366 +4447,370 @@ msgstr "" msgid "Not connected at this location" msgstr "" -#: Zotlabs/Module/Connections.php:316 +#: Zotlabs/Module/Connections.php:317 #, php-format msgid "%1$s [%2$s]" msgstr "" -#: Zotlabs/Module/Connections.php:317 +#: Zotlabs/Module/Connections.php:318 msgid "Edit connection" msgstr "" -#: Zotlabs/Module/Connections.php:319 +#: Zotlabs/Module/Connections.php:320 msgid "Delete connection" msgstr "" -#: Zotlabs/Module/Connections.php:328 +#: Zotlabs/Module/Connections.php:329 msgid "Channel address" msgstr "" -#: Zotlabs/Module/Connections.php:330 +#: Zotlabs/Module/Connections.php:331 msgid "Network" msgstr "" -#: Zotlabs/Module/Connections.php:333 +#: Zotlabs/Module/Connections.php:334 msgid "Call" msgstr "" -#: Zotlabs/Module/Connections.php:335 +#: Zotlabs/Module/Connections.php:336 msgid "Status" msgstr "" -#: Zotlabs/Module/Connections.php:337 +#: Zotlabs/Module/Connections.php:338 msgid "Connected" msgstr "" -#: Zotlabs/Module/Connections.php:339 +#: Zotlabs/Module/Connections.php:340 msgid "Approve connection" msgstr "" -#: Zotlabs/Module/Connections.php:341 +#: Zotlabs/Module/Connections.php:342 msgid "Ignore connection" msgstr "" -#: Zotlabs/Module/Connections.php:343 +#: Zotlabs/Module/Connections.php:344 msgid "Recent activity" msgstr "" -#: Zotlabs/Module/Connections.php:372 +#: Zotlabs/Module/Connections.php:371 msgid "Filter by" msgstr "" -#: Zotlabs/Module/Connections.php:373 +#: Zotlabs/Module/Connections.php:372 msgid "Sort by" msgstr "" -#: Zotlabs/Module/Connections.php:377 +#: Zotlabs/Module/Connections.php:376 msgid "Search your connections" msgstr "" -#: Zotlabs/Module/Connections.php:378 +#: Zotlabs/Module/Connections.php:377 msgid "Connections search" msgstr "" -#: Zotlabs/Module/Connections.php:379 Zotlabs/Module/Directory.php:483 -#: Zotlabs/Module/Directory.php:488 Zotlabs/Widget/Findpeople.php:30 +#: Zotlabs/Module/Connections.php:378 Zotlabs/Module/Directory.php:477 +#: Zotlabs/Module/Directory.php:482 Zotlabs/Widget/Findpeople.php:33 msgid "Find" msgstr "" -#: Zotlabs/Module/Item.php:554 Zotlabs/Module/Pin.php:42 +#: Zotlabs/Module/Item.php:570 Zotlabs/Module/Pin.php:49 msgid "Unable to locate original post." msgstr "" -#: Zotlabs/Module/Item.php:647 +#: Zotlabs/Module/Item.php:665 msgid "Comment may be moderated." msgstr "" -#: Zotlabs/Module/Item.php:875 +#: Zotlabs/Module/Item.php:901 msgid "Empty post discarded." msgstr "" -#: Zotlabs/Module/Item.php:1475 +#: Zotlabs/Module/Item.php:1501 msgid "Duplicate post suppressed." msgstr "" -#: Zotlabs/Module/Item.php:1512 +#: Zotlabs/Module/Item.php:1548 msgid "" "Draft saved. Use Drafts app to continue " "editing." msgstr "" -#: Zotlabs/Module/Item.php:1555 +#: Zotlabs/Module/Item.php:1592 msgid "Your comment has been posted." msgstr "" -#: Zotlabs/Module/Item.php:1626 +#: Zotlabs/Module/Item.php:1661 msgid "System error. Post not saved." msgstr "" -#: Zotlabs/Module/Item.php:1666 +#: Zotlabs/Module/Item.php:1703 msgid "Your post/comment is awaiting approval." msgstr "" -#: Zotlabs/Module/Item.php:1821 +#: Zotlabs/Module/Item.php:1738 Zotlabs/Module/Cloud.php:132 +msgid "Not found" +msgstr "" + +#: Zotlabs/Module/Item.php:1872 msgid "Unable to obtain post information from database." msgstr "" -#: Zotlabs/Module/Item.php:1829 +#: Zotlabs/Module/Item.php:1880 #, php-format msgid "You have reached your limit of %1$.0f top level posts." msgstr "" -#: Zotlabs/Module/Item.php:1836 +#: Zotlabs/Module/Item.php:1886 #, php-format msgid "You have reached your limit of %1$.0f webpages." msgstr "" -#: Zotlabs/Module/Appman.php:40 Zotlabs/Module/Appman.php:57 +#: Zotlabs/Module/Appman.php:42 Zotlabs/Module/Appman.php:59 msgid "App installed." msgstr "" -#: Zotlabs/Module/Appman.php:50 +#: Zotlabs/Module/Appman.php:52 msgid "Malformed app." msgstr "" -#: Zotlabs/Module/Appman.php:123 +#: Zotlabs/Module/Appman.php:129 msgid "Embed code" msgstr "" -#: Zotlabs/Module/Appman.php:127 +#: Zotlabs/Module/Appman.php:133 msgid "Edit App" msgstr "" -#: Zotlabs/Module/Appman.php:127 +#: Zotlabs/Module/Appman.php:133 msgid "Create App" msgstr "" -#: Zotlabs/Module/Appman.php:132 +#: Zotlabs/Module/Appman.php:138 msgid "Name of app" msgstr "" -#: Zotlabs/Module/Appman.php:132 Zotlabs/Module/Appman.php:133 -#: Zotlabs/Module/Events.php:491 Zotlabs/Module/Events.php:496 -#: Zotlabs/Module/Profiles.php:750 Zotlabs/Module/Profiles.php:754 -#: include/datetime.php:213 +#: Zotlabs/Module/Appman.php:138 Zotlabs/Module/Appman.php:139 +#: Zotlabs/Module/Events.php:508 Zotlabs/Module/Events.php:513 +#: Zotlabs/Module/Profiles.php:791 Zotlabs/Module/Profiles.php:795 +#: include/datetime.php:249 msgid "Required" msgstr "" -#: Zotlabs/Module/Appman.php:133 +#: Zotlabs/Module/Appman.php:139 msgid "Location (URL) of app" msgstr "" -#: Zotlabs/Module/Appman.php:135 +#: Zotlabs/Module/Appman.php:141 msgid "Photo icon URL" msgstr "" -#: Zotlabs/Module/Appman.php:135 +#: Zotlabs/Module/Appman.php:141 msgid "80 x 80 pixels - optional" msgstr "" -#: Zotlabs/Module/Appman.php:136 +#: Zotlabs/Module/Appman.php:142 msgid "Categories (optional, comma separated list)" msgstr "" -#: Zotlabs/Module/Appman.php:137 +#: Zotlabs/Module/Appman.php:143 msgid "Version ID" msgstr "" -#: Zotlabs/Module/Appman.php:138 +#: Zotlabs/Module/Appman.php:144 msgid "Price of app" msgstr "" -#: Zotlabs/Module/Appman.php:139 +#: Zotlabs/Module/Appman.php:145 msgid "Location (URL) to purchase app" msgstr "" -#: Zotlabs/Module/Cover_photo.php:284 include/items.php:4766 +#: Zotlabs/Module/Cover_photo.php:281 include/items.php:4912 msgid "female" msgstr "" -#: Zotlabs/Module/Cover_photo.php:285 include/items.php:4767 +#: Zotlabs/Module/Cover_photo.php:282 include/items.php:4913 #, php-format msgid "%1$s updated her %2$s" msgstr "" -#: Zotlabs/Module/Cover_photo.php:287 include/items.php:4768 +#: Zotlabs/Module/Cover_photo.php:283 include/items.php:4914 msgid "male" msgstr "" -#: Zotlabs/Module/Cover_photo.php:288 include/items.php:4769 +#: Zotlabs/Module/Cover_photo.php:284 include/items.php:4915 #, php-format msgid "%1$s updated his %2$s" msgstr "" -#: Zotlabs/Module/Cover_photo.php:291 include/items.php:4771 +#: Zotlabs/Module/Cover_photo.php:286 include/items.php:4917 #, php-format msgid "%1$s updated their %2$s" msgstr "" -#: Zotlabs/Module/Cover_photo.php:294 include/channel.php:1734 +#: Zotlabs/Module/Cover_photo.php:289 include/channel.php:1841 msgid "cover photo" msgstr "" -#: Zotlabs/Module/Defperms.php:101 Zotlabs/Module/Settings/Channel.php:328 +#: Zotlabs/Module/Defperms.php:106 Zotlabs/Module/Settings/Channel.php:357 #: extend/addon/a/logrot/logrot.php:55 #: extend/addon/a/openstreetmap/openstreetmap.php:222 #: extend/addon/a/faces/faces.php:208 msgid "Settings updated." msgstr "" -#: Zotlabs/Module/Defperms.php:241 Zotlabs/Module/Settings/Channel.php:547 +#: Zotlabs/Module/Defperms.php:248 Zotlabs/Module/Settings/Channel.php:583 msgid "" "If enabled, connection requests will be approved without your interaction" msgstr "" -#: Zotlabs/Module/Defperms.php:248 +#: Zotlabs/Module/Defperms.php:255 msgid "Automatic approval settings" msgstr "" -#: Zotlabs/Module/Defperms.php:256 +#: Zotlabs/Module/Defperms.php:263 msgid "" "Some individual permissions may have been preset or locked based on your " "channel type and privacy settings." msgstr "" -#: Zotlabs/Module/Directory.php:93 Zotlabs/Module/Directory.php:100 -#: Zotlabs/Module/Display.php:43 Zotlabs/Module/Search.php:37 -#: Zotlabs/Module/Photos.php:570 Zotlabs/Module/Viewconnections.php:27 +#: Zotlabs/Module/Directory.php:96 Zotlabs/Module/Directory.php:103 +#: Zotlabs/Module/Display.php:47 Zotlabs/Module/Search.php:41 +#: Zotlabs/Module/Photos.php:579 Zotlabs/Module/Viewconnections.php:30 msgid "Public access denied." msgstr "" -#: Zotlabs/Module/Directory.php:158 +#: Zotlabs/Module/Directory.php:159 msgid "No default suggestions were found." msgstr "" -#: Zotlabs/Module/Directory.php:329 +#: Zotlabs/Module/Directory.php:326 msgid "Gender: " msgstr "" -#: Zotlabs/Module/Directory.php:330 +#: Zotlabs/Module/Directory.php:327 msgid "Status: " msgstr "" -#: Zotlabs/Module/Directory.php:331 +#: Zotlabs/Module/Directory.php:328 msgid "Homepage: " msgstr "" -#: Zotlabs/Module/Directory.php:392 +#: Zotlabs/Module/Directory.php:389 msgid "Description:" msgstr "" -#: Zotlabs/Module/Directory.php:403 Zotlabs/Module/Manage.php:77 -#: Zotlabs/Module/Manage.php:174 +#: Zotlabs/Module/Directory.php:400 Zotlabs/Module/Manage.php:78 +#: Zotlabs/Module/Manage.php:175 msgid "Group" msgstr "" -#: Zotlabs/Module/Directory.php:407 +#: Zotlabs/Module/Directory.php:404 msgid "Keywords: " msgstr "" -#: Zotlabs/Module/Directory.php:410 +#: Zotlabs/Module/Directory.php:407 msgid "Don't suggest" msgstr "" -#: Zotlabs/Module/Directory.php:412 +#: Zotlabs/Module/Directory.php:409 msgid "Suggestion ranking:" msgstr "" -#: Zotlabs/Module/Directory.php:478 +#: Zotlabs/Module/Directory.php:472 msgid "Local Directory" msgstr "" -#: Zotlabs/Module/Directory.php:484 +#: Zotlabs/Module/Directory.php:478 msgid "Finding:" msgstr "" -#: Zotlabs/Module/Directory.php:487 Zotlabs/Widget/Findpeople.php:31 +#: Zotlabs/Module/Directory.php:481 Zotlabs/Widget/Findpeople.php:34 msgid "Channel Suggestions" msgstr "" -#: Zotlabs/Module/Directory.php:489 +#: Zotlabs/Module/Directory.php:483 msgid "next page" msgstr "" -#: Zotlabs/Module/Directory.php:489 +#: Zotlabs/Module/Directory.php:483 msgid "previous page" msgstr "" -#: Zotlabs/Module/Directory.php:490 +#: Zotlabs/Module/Directory.php:484 msgid "Sort options" msgstr "" -#: Zotlabs/Module/Directory.php:491 +#: Zotlabs/Module/Directory.php:485 msgid "Alphabetic" msgstr "" -#: Zotlabs/Module/Directory.php:492 +#: Zotlabs/Module/Directory.php:486 msgid "Reverse Alphabetic" msgstr "" -#: Zotlabs/Module/Directory.php:493 +#: Zotlabs/Module/Directory.php:487 msgid "Newest to Oldest" msgstr "" -#: Zotlabs/Module/Directory.php:494 +#: Zotlabs/Module/Directory.php:488 msgid "Oldest to Newest" msgstr "" -#: Zotlabs/Module/Directory.php:521 +#: Zotlabs/Module/Directory.php:513 msgid "No entries (some entries may be hidden)." msgstr "" -#: Zotlabs/Module/Display.php:94 Zotlabs/Module/Channel.php:294 -#: Zotlabs/Module/Hq.php:141 Zotlabs/Module/Rpost.php:240 -#: Zotlabs/Module/Pubstream.php:85 Zotlabs/Module/Stream.php:246 +#: Zotlabs/Module/Display.php:98 Zotlabs/Module/Channel.php:287 +#: Zotlabs/Module/Hq.php:153 Zotlabs/Module/Rpost.php:236 +#: Zotlabs/Module/Pubstream.php:86 Zotlabs/Module/Stream.php:246 msgid "Reset form" msgstr "" -#: Zotlabs/Module/Display.php:382 +#: Zotlabs/Module/Display.php:390 #, php-format msgid "\"%1$s\", shared by %2$s with %3$s" msgstr "" -#: Zotlabs/Module/Display.php:383 +#: Zotlabs/Module/Display.php:391 #, php-format msgid "%1$s shared this post with %2$s" msgstr "" -#: Zotlabs/Module/Display.php:387 +#: Zotlabs/Module/Display.php:395 msgid "Not much to read, click to see the post." msgstr "" -#: Zotlabs/Module/Display.php:392 +#: Zotlabs/Module/Display.php:399 #, php-format msgid "%1$s shared a reaction to \"%2$s\"" msgstr "" -#: Zotlabs/Module/Display.php:393 +#: Zotlabs/Module/Display.php:400 #, php-format msgid "%s shared a reaction to this post/conversation" msgstr "" -#: Zotlabs/Module/Display.php:401 +#: Zotlabs/Module/Display.php:407 #, php-format msgid "%1$s commented \"%2$s\"" msgstr "" -#: Zotlabs/Module/Display.php:402 +#: Zotlabs/Module/Display.php:408 #, php-format msgid "%s shared a comment of this post/conversation" msgstr "" -#: Zotlabs/Module/Display.php:406 +#: Zotlabs/Module/Display.php:412 #, php-format msgid "%1$s wrote this: \"%2$s\"" msgstr "" -#: Zotlabs/Module/Display.php:435 Zotlabs/Module/Channel.php:573 +#: Zotlabs/Module/Display.php:437 Zotlabs/Module/Channel.php:556 msgid "" "You must enable javascript for your browser to be able to view this content." msgstr "" -#: Zotlabs/Module/Display.php:453 +#: Zotlabs/Module/Display.php:454 msgid "Article" msgstr "" @@ -4802,179 +4818,179 @@ msgstr "" msgid "Item has been removed." msgstr "" -#: Zotlabs/Module/Events.php:475 +#: Zotlabs/Module/Events.php:492 msgid "day(s)" msgstr "" -#: Zotlabs/Module/Events.php:476 +#: Zotlabs/Module/Events.php:493 msgid "week(s)" msgstr "" -#: Zotlabs/Module/Events.php:477 +#: Zotlabs/Module/Events.php:494 msgid "month(s)" msgstr "" -#: Zotlabs/Module/Events.php:478 +#: Zotlabs/Module/Events.php:495 msgid "year(s)" msgstr "" -#: Zotlabs/Module/Events.php:491 +#: Zotlabs/Module/Events.php:508 msgid "Edit event title" msgstr "" -#: Zotlabs/Module/Events.php:493 +#: Zotlabs/Module/Events.php:510 msgid "Categories (comma-separated list)" msgstr "" -#: Zotlabs/Module/Events.php:494 +#: Zotlabs/Module/Events.php:511 msgid "Edit Category" msgstr "" -#: Zotlabs/Module/Events.php:494 +#: Zotlabs/Module/Events.php:511 msgid "Category" msgstr "" -#: Zotlabs/Module/Events.php:497 +#: Zotlabs/Module/Events.php:514 msgid "Edit start date and time" msgstr "" -#: Zotlabs/Module/Events.php:498 Zotlabs/Module/Events.php:501 +#: Zotlabs/Module/Events.php:515 Zotlabs/Module/Events.php:518 msgid "Finish date and time are not known or not relevant" msgstr "" -#: Zotlabs/Module/Events.php:500 +#: Zotlabs/Module/Events.php:517 msgid "Edit finish date and time" msgstr "" -#: Zotlabs/Module/Events.php:500 +#: Zotlabs/Module/Events.php:517 msgid "Finish date and time" msgstr "" -#: Zotlabs/Module/Events.php:502 Zotlabs/Module/Events.php:503 +#: Zotlabs/Module/Events.php:519 Zotlabs/Module/Events.php:520 msgid "Adjust for viewer timezone" msgstr "" -#: Zotlabs/Module/Events.php:502 +#: Zotlabs/Module/Events.php:519 msgid "" "Important for events that happen in a particular place. Not practical for " "global holidays." msgstr "" -#: Zotlabs/Module/Events.php:504 +#: Zotlabs/Module/Events.php:521 msgid "Edit Description" msgstr "" -#: Zotlabs/Module/Events.php:506 +#: Zotlabs/Module/Events.php:523 msgid "Edit Location" msgstr "" -#: Zotlabs/Module/Events.php:510 include/conversation.php:1430 +#: Zotlabs/Module/Events.php:527 include/conversation.php:1455 msgid "Permission settings" msgstr "" -#: Zotlabs/Module/Events.php:520 +#: Zotlabs/Module/Events.php:537 msgid "Timezone:" msgstr "" -#: Zotlabs/Module/Events.php:525 +#: Zotlabs/Module/Events.php:542 msgid "Advanced Options" msgstr "" -#: Zotlabs/Module/Events.php:527 +#: Zotlabs/Module/Events.php:544 msgid "Event repeat" msgstr "" -#: Zotlabs/Module/Events.php:528 +#: Zotlabs/Module/Events.php:545 msgid "Repeat frequency" msgstr "" -#: Zotlabs/Module/Events.php:529 +#: Zotlabs/Module/Events.php:546 msgid "Repeat every" msgstr "" -#: Zotlabs/Module/Events.php:530 +#: Zotlabs/Module/Events.php:547 msgid "Number of total repeats" msgstr "" -#: Zotlabs/Module/Events.php:826 +#: Zotlabs/Module/Events.php:858 msgid "Event removed" msgstr "" -#: Zotlabs/Module/Editlayout.php:83 Zotlabs/Module/Editwebpage.php:84 -#: Zotlabs/Module/Card_edit.php:19 Zotlabs/Module/Card_edit.php:35 -#: Zotlabs/Module/Editblock.php:83 Zotlabs/Module/Editblock.php:99 -#: Zotlabs/Module/Editpost.php:27 Zotlabs/Module/Dreport.php:64 +#: Zotlabs/Module/Editlayout.php:88 Zotlabs/Module/Editwebpage.php:91 +#: Zotlabs/Module/Card_edit.php:25 Zotlabs/Module/Card_edit.php:43 +#: Zotlabs/Module/Editblock.php:89 Zotlabs/Module/Editblock.php:107 +#: Zotlabs/Module/Editpost.php:30 Zotlabs/Module/Dreport.php:67 msgid "Item not found" msgstr "" -#: Zotlabs/Module/Editlayout.php:132 Zotlabs/Module/Layouts.php:135 -#: Zotlabs/Module/Layouts.php:195 +#: Zotlabs/Module/Editlayout.php:140 Zotlabs/Module/Layouts.php:139 +#: Zotlabs/Module/Layouts.php:203 msgid "Layout Name" msgstr "" -#: Zotlabs/Module/Editlayout.php:133 Zotlabs/Module/Layouts.php:138 +#: Zotlabs/Module/Editlayout.php:141 Zotlabs/Module/Layouts.php:142 msgid "Layout Description (Optional)" msgstr "" -#: Zotlabs/Module/Editlayout.php:141 +#: Zotlabs/Module/Editlayout.php:149 msgid "Edit Layout" msgstr "" -#: Zotlabs/Module/Plike.php:124 include/conversation.php:133 +#: Zotlabs/Module/Plike.php:128 include/conversation.php:135 msgid "channel" msgstr "" -#: Zotlabs/Module/Plike.php:196 Zotlabs/Module/Like.php:220 -#: include/conversation.php:174 +#: Zotlabs/Module/Plike.php:202 Zotlabs/Module/Like.php:221 +#: include/conversation.php:178 #, php-format msgid "%1$s likes %2$s's %3$s" msgstr "" -#: Zotlabs/Module/Plike.php:198 Zotlabs/Module/Like.php:223 -#: include/conversation.php:177 +#: Zotlabs/Module/Plike.php:205 Zotlabs/Module/Like.php:224 +#: include/conversation.php:180 #, php-format msgid "%1$s doesn't like %2$s's %3$s" msgstr "" -#: Zotlabs/Module/Notes.php:45 +#: Zotlabs/Module/Notes.php:52 msgid "This app allows you to create private notes for your personal use." msgstr "" -#: Zotlabs/Module/Notes.php:53 +#: Zotlabs/Module/Notes.php:60 msgid "This app is installed. The Notes tool can be found on your stream page." msgstr "" -#: Zotlabs/Module/Editwebpage.php:143 +#: Zotlabs/Module/Editwebpage.php:154 msgid "Page link" msgstr "" -#: Zotlabs/Module/Editwebpage.php:147 Zotlabs/Module/Chat.php:212 -#: Zotlabs/Module/Card_edit.php:103 Zotlabs/Module/Editblock.php:120 -#: include/conversation.php:1220 +#: Zotlabs/Module/Editwebpage.php:158 Zotlabs/Module/Chat.php:219 +#: Zotlabs/Module/Card_edit.php:110 Zotlabs/Module/Editblock.php:129 +#: include/conversation.php:1237 msgid "Insert web link" msgstr "" -#: Zotlabs/Module/Editwebpage.php:170 +#: Zotlabs/Module/Editwebpage.php:181 msgid "Edit Webpage" msgstr "" -#: Zotlabs/Module/Email_resend.php:12 Zotlabs/Module/Email_validation.php:24 +#: Zotlabs/Module/Email_resend.php:15 Zotlabs/Module/Email_validation.php:27 msgid "Token verification failed." msgstr "" -#: Zotlabs/Module/Email_resend.php:30 +#: Zotlabs/Module/Email_resend.php:33 msgid "Email verification resent" msgstr "" -#: Zotlabs/Module/Email_resend.php:33 +#: Zotlabs/Module/Email_resend.php:35 msgid "Unable to resend email verification message." msgstr "" -#: Zotlabs/Module/Email_validation.php:36 +#: Zotlabs/Module/Email_validation.php:40 msgid "Email Verification Required" msgstr "" -#: Zotlabs/Module/Email_validation.php:37 +#: Zotlabs/Module/Email_validation.php:41 #, php-format msgid "" "A verification token was sent to your email address [%s]. Enter that token " @@ -4982,70 +4998,70 @@ msgid "" "for delivery, and check your spam folder if you do not see the message." msgstr "" -#: Zotlabs/Module/Email_validation.php:38 +#: Zotlabs/Module/Email_validation.php:42 msgid "Resend Email" msgstr "" -#: Zotlabs/Module/Email_validation.php:41 +#: Zotlabs/Module/Email_validation.php:45 msgid "Validation token" msgstr "" -#: Zotlabs/Module/Embedphotos.php:105 include/bbcode.php:232 -#: include/bbcode.php:1041 include/bbcode.php:1924 include/bbcode.php:1926 -#: include/bbcode.php:1929 include/text.php:1735 include/text.php:1736 +#: Zotlabs/Module/Embedphotos.php:103 include/bbcode.php:258 +#: include/bbcode.php:1125 include/bbcode.php:2031 include/bbcode.php:2033 +#: include/bbcode.php:2036 include/text.php:1828 include/text.php:1829 msgid "Image/photo" msgstr "" -#: Zotlabs/Module/Embedphotos.php:210 Zotlabs/Module/Photos.php:842 -#: Zotlabs/Module/Photos.php:1394 Zotlabs/Widget/Portfolio.php:87 -#: Zotlabs/Widget/Album.php:82 +#: Zotlabs/Module/Embedphotos.php:208 Zotlabs/Module/Photos.php:848 +#: Zotlabs/Module/Photos.php:1405 Zotlabs/Widget/Album.php:88 +#: Zotlabs/Widget/Portfolio.php:99 msgid "View Photo" msgstr "" -#: Zotlabs/Module/Embedphotos.php:225 Zotlabs/Module/Photos.php:875 -#: Zotlabs/Widget/Portfolio.php:108 Zotlabs/Widget/Album.php:99 +#: Zotlabs/Module/Embedphotos.php:223 Zotlabs/Module/Photos.php:879 +#: Zotlabs/Widget/Album.php:105 Zotlabs/Widget/Portfolio.php:120 msgid "Edit Album" msgstr "" -#: Zotlabs/Module/Register.php:54 +#: Zotlabs/Module/Register.php:60 msgid "Maximum daily site registrations exceeded. Please try again tomorrow." msgstr "" -#: Zotlabs/Module/Register.php:60 +#: Zotlabs/Module/Register.php:66 msgid "" "Please indicate acceptance of the Terms of Service. Registration failed." msgstr "" -#: Zotlabs/Module/Register.php:95 +#: Zotlabs/Module/Register.php:100 msgid "Passwords do not match." msgstr "" -#: Zotlabs/Module/Register.php:147 +#: Zotlabs/Module/Register.php:151 msgid "Registration successful. Continue to create your first channel..." msgstr "" -#: Zotlabs/Module/Register.php:150 +#: Zotlabs/Module/Register.php:153 msgid "" "Registration successful. Please check your email for validation instructions." msgstr "" -#: Zotlabs/Module/Register.php:157 +#: Zotlabs/Module/Register.php:159 msgid "Your registration is pending approval by the site owner." msgstr "" -#: Zotlabs/Module/Register.php:160 +#: Zotlabs/Module/Register.php:161 msgid "Your registration can not be processed." msgstr "" -#: Zotlabs/Module/Register.php:207 +#: Zotlabs/Module/Register.php:206 msgid "Registration on this website is disabled." msgstr "" -#: Zotlabs/Module/Register.php:217 +#: Zotlabs/Module/Register.php:215 msgid "Registration on this website is by approval only." msgstr "" -#: Zotlabs/Module/Register.php:225 +#: Zotlabs/Module/Register.php:223 msgid "Registration on this site is by invitation only." msgstr "" @@ -5055,7 +5071,7 @@ msgid "" "Please try again tomorrow." msgstr "" -#: Zotlabs/Module/Register.php:252 Zotlabs/Module/Siteinfo.php:37 +#: Zotlabs/Module/Register.php:252 Zotlabs/Module/Siteinfo.php:42 msgid "Terms of Service" msgstr "" @@ -5064,95 +5080,95 @@ msgstr "" msgid "I accept the %s for this website" msgstr "" -#: Zotlabs/Module/Register.php:265 +#: Zotlabs/Module/Register.php:264 #, php-format msgid "I am over %s years of age and accept the %s for this website" msgstr "" -#: Zotlabs/Module/Register.php:270 +#: Zotlabs/Module/Register.php:269 msgid "Your email address" msgstr "" -#: Zotlabs/Module/Register.php:271 +#: Zotlabs/Module/Register.php:270 msgid "Choose a password" msgstr "" -#: Zotlabs/Module/Register.php:272 +#: Zotlabs/Module/Register.php:271 msgid "Please re-enter your password" msgstr "" -#: Zotlabs/Module/Register.php:273 +#: Zotlabs/Module/Register.php:272 msgid "Please enter your invitation code" msgstr "" -#: Zotlabs/Module/Register.php:274 +#: Zotlabs/Module/Register.php:273 msgid "Your Name" msgstr "" -#: Zotlabs/Module/Register.php:274 +#: Zotlabs/Module/Register.php:273 msgid "Real names are preferred." msgstr "" -#: Zotlabs/Module/Register.php:276 Zotlabs/Module/New_channel.php:184 +#: Zotlabs/Module/Register.php:275 Zotlabs/Module/New_channel.php:191 msgid "Choose a short nickname" msgstr "" -#: Zotlabs/Module/Register.php:276 +#: Zotlabs/Module/Register.php:275 #, php-format msgid "" "Your nickname will be used to create an easy to remember channel address e." "g. nickname%s" msgstr "" -#: Zotlabs/Module/Register.php:277 Zotlabs/Module/New_channel.php:185 +#: Zotlabs/Module/Register.php:276 Zotlabs/Module/New_channel.php:192 msgid "Channel role and privacy" msgstr "" -#: Zotlabs/Module/Register.php:277 +#: Zotlabs/Module/Register.php:276 msgid "" "Select a channel permission role for your usage needs and privacy " "requirements." msgstr "" -#: Zotlabs/Module/Register.php:278 +#: Zotlabs/Module/Register.php:277 msgid "no" msgstr "" -#: Zotlabs/Module/Register.php:278 +#: Zotlabs/Module/Register.php:277 msgid "yes" msgstr "" -#: Zotlabs/Module/Register.php:291 +#: Zotlabs/Module/Register.php:290 msgid "" "Show affiliated sites - some of which may allow " "registration." msgstr "" -#: Zotlabs/Module/Register.php:304 Zotlabs/Module/Sites.php:51 boot.php:1698 -#: include/nav.php:166 +#: Zotlabs/Module/Register.php:303 Zotlabs/Module/Sites.php:55 boot.php:1706 +#: include/nav.php:164 msgid "Register" msgstr "" -#: Zotlabs/Module/Register.php:305 +#: Zotlabs/Module/Register.php:304 msgid "" "This site requires email verification. After completing this form, please " "check your email for further instructions." msgstr "" -#: Zotlabs/Module/Invite.php:34 +#: Zotlabs/Module/Invite.php:35 msgid "Total invitation limit exceeded." msgstr "" -#: Zotlabs/Module/Invite.php:58 +#: Zotlabs/Module/Invite.php:60 #, php-format msgid "%s : Not a valid email address." msgstr "" -#: Zotlabs/Module/Invite.php:72 +#: Zotlabs/Module/Invite.php:73 msgid "Please join us on $Projectname" msgstr "" -#: Zotlabs/Module/Invite.php:82 +#: Zotlabs/Module/Invite.php:83 msgid "Invitation limit exceeded. Please contact your site administrator." msgstr "" @@ -5161,7 +5177,7 @@ msgstr "" msgid "%s : Message delivery failed." msgstr "" -#: Zotlabs/Module/Invite.php:91 +#: Zotlabs/Module/Invite.php:90 #, php-format msgid "%d message sent." msgid_plural "%d messages sent." @@ -5176,710 +5192,710 @@ msgstr "" msgid "You have no more invitations available" msgstr "" -#: Zotlabs/Module/Invite.php:152 +#: Zotlabs/Module/Invite.php:155 msgid "Send invitations" msgstr "" -#: Zotlabs/Module/Invite.php:153 +#: Zotlabs/Module/Invite.php:156 msgid "Enter email addresses, one per line:" msgstr "" -#: Zotlabs/Module/Invite.php:154 +#: Zotlabs/Module/Invite.php:157 msgid "Your message:" msgstr "" -#: Zotlabs/Module/Invite.php:155 +#: Zotlabs/Module/Invite.php:158 msgid "Please join my community on $Projectname." msgstr "" -#: Zotlabs/Module/Invite.php:157 +#: Zotlabs/Module/Invite.php:160 msgid "You will need to supply this invitation code:" msgstr "" -#: Zotlabs/Module/Invite.php:158 +#: Zotlabs/Module/Invite.php:161 msgid "1. Register at any $Projectname location (they are all inter-connected)" msgstr "" -#: Zotlabs/Module/Invite.php:160 +#: Zotlabs/Module/Invite.php:163 msgid "2. Enter my $Projectname network address into the site searchbar." msgstr "" -#: Zotlabs/Module/Invite.php:161 +#: Zotlabs/Module/Invite.php:164 msgid "or visit" msgstr "" -#: Zotlabs/Module/Invite.php:163 +#: Zotlabs/Module/Invite.php:166 msgid "3. Click [Connect]" msgstr "" -#: Zotlabs/Module/Sites.php:13 +#: Zotlabs/Module/Sites.php:17 msgid "" "This page provides information about related projects and websites that are " "currently known to this system. These are a small fraction of the thousands " "of websites and dozens of projects and providers which make up the fediverse." msgstr "" -#: Zotlabs/Module/Sites.php:42 +#: Zotlabs/Module/Sites.php:45 msgid "free" msgstr "" -#: Zotlabs/Module/Sites.php:44 +#: Zotlabs/Module/Sites.php:47 msgid "subscription" msgstr "" -#: Zotlabs/Module/Sites.php:46 +#: Zotlabs/Module/Sites.php:49 msgid "tiered service plans" msgstr "" -#: Zotlabs/Module/Sites.php:53 +#: Zotlabs/Module/Sites.php:57 msgid "Register (requires approval)" msgstr "" -#: Zotlabs/Module/Sites.php:98 +#: Zotlabs/Module/Sites.php:101 msgid "Project" msgstr "" -#: Zotlabs/Module/Sites.php:106 +#: Zotlabs/Module/Sites.php:109 msgid "Access type" msgstr "" -#: Zotlabs/Module/Sites.php:112 +#: Zotlabs/Module/Sites.php:115 msgid "Affiliated Sites" msgstr "" -#: Zotlabs/Module/Filer.php:50 +#: Zotlabs/Module/Filer.php:59 msgid "Enter a folder name" msgstr "" -#: Zotlabs/Module/Filer.php:50 +#: Zotlabs/Module/Filer.php:59 msgid "or select an existing folder (doubleclick)" msgstr "" -#: Zotlabs/Module/Filestorage.php:94 +#: Zotlabs/Module/Filestorage.php:98 msgid "Channel unavailable." msgstr "" -#: Zotlabs/Module/Filestorage.php:139 +#: Zotlabs/Module/Filestorage.php:142 msgid "File not found." msgstr "" -#: Zotlabs/Module/Filestorage.php:221 +#: Zotlabs/Module/Filestorage.php:225 msgid "Edit file permissions" msgstr "" -#: Zotlabs/Module/Filestorage.php:233 +#: Zotlabs/Module/Filestorage.php:237 msgid "Change filename to" msgstr "" -#: Zotlabs/Module/Filestorage.php:233 +#: Zotlabs/Module/Filestorage.php:237 msgid "Leave blank to keep the existing filename" msgstr "" -#: Zotlabs/Module/Filestorage.php:234 +#: Zotlabs/Module/Filestorage.php:238 msgid "Move to directory" msgstr "" -#: Zotlabs/Module/Filestorage.php:235 +#: Zotlabs/Module/Filestorage.php:239 #: extend/addon/a/flashcards/Mod_Flashcards.php:261 #: extend/addon/a/faces/Mod_Faces.php:179 msgid "Set/edit permissions" msgstr "" -#: Zotlabs/Module/Filestorage.php:236 +#: Zotlabs/Module/Filestorage.php:240 msgid "Include all files and sub folders" msgstr "" -#: Zotlabs/Module/Filestorage.php:237 +#: Zotlabs/Module/Filestorage.php:241 msgid "Return to file list" msgstr "" -#: Zotlabs/Module/Filestorage.php:239 +#: Zotlabs/Module/Filestorage.php:243 msgid "Copy/paste this code to attach file to a post" msgstr "" -#: Zotlabs/Module/Filestorage.php:240 +#: Zotlabs/Module/Filestorage.php:244 msgid "Copy/paste this URL to link file from a web page" msgstr "" -#: Zotlabs/Module/Filestorage.php:242 +#: Zotlabs/Module/Filestorage.php:246 msgid "Share this file" msgstr "" -#: Zotlabs/Module/Filestorage.php:243 +#: Zotlabs/Module/Filestorage.php:247 msgid "Show URL to this file" msgstr "" -#: Zotlabs/Module/Filestorage.php:244 Zotlabs/Storage/Browser.php:432 +#: Zotlabs/Module/Filestorage.php:248 Zotlabs/Storage/Browser.php:435 msgid "Show in your contacts shared folder" msgstr "" -#: Zotlabs/Module/Dircensor.php:42 +#: Zotlabs/Module/Dircensor.php:45 msgid "Entry censored" msgstr "" -#: Zotlabs/Module/Dircensor.php:45 +#: Zotlabs/Module/Dircensor.php:47 msgid "Entry uncensored" msgstr "" -#: Zotlabs/Module/Impel.php:43 include/bbcode.php:377 +#: Zotlabs/Module/Impel.php:56 include/bbcode.php:416 msgid "webpage" msgstr "" -#: Zotlabs/Module/Impel.php:48 include/bbcode.php:383 +#: Zotlabs/Module/Impel.php:61 include/bbcode.php:422 msgid "block" msgstr "" -#: Zotlabs/Module/Impel.php:53 include/bbcode.php:380 +#: Zotlabs/Module/Impel.php:66 include/bbcode.php:419 msgid "layout" msgstr "" -#: Zotlabs/Module/Impel.php:60 include/bbcode.php:386 +#: Zotlabs/Module/Impel.php:73 include/bbcode.php:425 msgid "menu" msgstr "" -#: Zotlabs/Module/Impel.php:185 +#: Zotlabs/Module/Impel.php:206 #, php-format msgid "%s element installed" msgstr "" -#: Zotlabs/Module/Impel.php:188 +#: Zotlabs/Module/Impel.php:208 #, php-format msgid "%s element installation failed" msgstr "" -#: Zotlabs/Module/Thing.php:133 +#: Zotlabs/Module/Thing.php:139 msgid "Thing updated" msgstr "" -#: Zotlabs/Module/Thing.php:191 +#: Zotlabs/Module/Thing.php:200 msgid "Object store: failed" msgstr "" -#: Zotlabs/Module/Thing.php:195 +#: Zotlabs/Module/Thing.php:204 msgid "Thing added" msgstr "" -#: Zotlabs/Module/Thing.php:221 +#: Zotlabs/Module/Thing.php:232 #, php-format msgid "OBJ: %1$s %2$s %3$s" msgstr "" -#: Zotlabs/Module/Thing.php:284 +#: Zotlabs/Module/Thing.php:301 msgid "Show Thing" msgstr "" -#: Zotlabs/Module/Thing.php:291 +#: Zotlabs/Module/Thing.php:307 msgid "item not found." msgstr "" -#: Zotlabs/Module/Thing.php:324 +#: Zotlabs/Module/Thing.php:341 msgid "Edit Thing" msgstr "" -#: Zotlabs/Module/Thing.php:326 Zotlabs/Module/Thing.php:383 +#: Zotlabs/Module/Thing.php:343 Zotlabs/Module/Thing.php:402 msgid "Select a profile" msgstr "" -#: Zotlabs/Module/Thing.php:330 Zotlabs/Module/Thing.php:386 +#: Zotlabs/Module/Thing.php:347 Zotlabs/Module/Thing.php:405 msgid "Post an activity" msgstr "" -#: Zotlabs/Module/Thing.php:330 Zotlabs/Module/Thing.php:386 +#: Zotlabs/Module/Thing.php:347 Zotlabs/Module/Thing.php:405 msgid "Only sends to viewers of the applicable profile" msgstr "" -#: Zotlabs/Module/Thing.php:332 Zotlabs/Module/Thing.php:388 +#: Zotlabs/Module/Thing.php:349 Zotlabs/Module/Thing.php:407 msgid "Name of thing e.g. something" msgstr "" -#: Zotlabs/Module/Thing.php:334 Zotlabs/Module/Thing.php:389 +#: Zotlabs/Module/Thing.php:351 Zotlabs/Module/Thing.php:408 msgid "URL of thing (optional)" msgstr "" -#: Zotlabs/Module/Thing.php:336 Zotlabs/Module/Thing.php:390 +#: Zotlabs/Module/Thing.php:353 Zotlabs/Module/Thing.php:409 msgid "URL for photo of thing (optional)" msgstr "" -#: Zotlabs/Module/Thing.php:381 +#: Zotlabs/Module/Thing.php:400 msgid "Add Thing to your Profile" msgstr "" -#: Zotlabs/Module/Chat.php:26 Zotlabs/Module/Channel.php:57 +#: Zotlabs/Module/Chat.php:30 Zotlabs/Module/Channel.php:58 msgid "You must be logged in to see this page." msgstr "" -#: Zotlabs/Module/Chat.php:187 +#: Zotlabs/Module/Chat.php:194 msgid "Room not found" msgstr "" -#: Zotlabs/Module/Chat.php:204 +#: Zotlabs/Module/Chat.php:211 msgid "Leave Room" msgstr "" -#: Zotlabs/Module/Chat.php:205 +#: Zotlabs/Module/Chat.php:212 msgid "Delete Room" msgstr "" -#: Zotlabs/Module/Chat.php:206 +#: Zotlabs/Module/Chat.php:213 msgid "I am away right now" msgstr "" -#: Zotlabs/Module/Chat.php:207 +#: Zotlabs/Module/Chat.php:214 msgid "I am online" msgstr "" -#: Zotlabs/Module/Chat.php:210 include/conversation.php:1267 +#: Zotlabs/Module/Chat.php:217 include/conversation.php:1287 msgid "Please enter a link URL:" msgstr "" -#: Zotlabs/Module/Chat.php:230 +#: Zotlabs/Module/Chat.php:237 msgid "New Chatroom" msgstr "" -#: Zotlabs/Module/Chat.php:231 +#: Zotlabs/Module/Chat.php:238 msgid "Chatroom name" msgstr "" -#: Zotlabs/Module/Chat.php:232 +#: Zotlabs/Module/Chat.php:239 msgid "Expiration of chats (minutes)" msgstr "" -#: Zotlabs/Module/Chat.php:248 +#: Zotlabs/Module/Chat.php:255 #, php-format msgid "%1$s's Chatrooms" msgstr "" -#: Zotlabs/Module/Chat.php:253 +#: Zotlabs/Module/Chat.php:260 msgid "No chatrooms available" msgstr "" -#: Zotlabs/Module/Chat.php:254 Zotlabs/Module/Manage.php:154 -#: Zotlabs/Module/Profiles.php:839 +#: Zotlabs/Module/Chat.php:261 Zotlabs/Module/Manage.php:154 +#: Zotlabs/Module/Profiles.php:879 msgid "Create New" msgstr "" -#: Zotlabs/Module/Chat.php:257 +#: Zotlabs/Module/Chat.php:264 msgid "Expiration" msgstr "" -#: Zotlabs/Module/Chat.php:258 +#: Zotlabs/Module/Chat.php:265 msgid "min" msgstr "" -#: Zotlabs/Module/Moderate.php:32 +#: Zotlabs/Module/Moderate.php:35 msgid "No entries." msgstr "" -#: Zotlabs/Module/Moderate.php:74 +#: Zotlabs/Module/Moderate.php:80 msgid "Comment approved" msgstr "" -#: Zotlabs/Module/Moderate.php:78 +#: Zotlabs/Module/Moderate.php:83 msgid "Comment deleted" msgstr "" -#: Zotlabs/Module/Setup.php:190 +#: Zotlabs/Module/Setup.php:192 msgid "$Projectname Server - Setup" msgstr "" -#: Zotlabs/Module/Setup.php:194 +#: Zotlabs/Module/Setup.php:196 msgid "Could not connect to database." msgstr "" -#: Zotlabs/Module/Setup.php:198 +#: Zotlabs/Module/Setup.php:200 msgid "" "Could not connect to specified site URL. Possible SSL certificate or DNS " "issue." msgstr "" -#: Zotlabs/Module/Setup.php:206 +#: Zotlabs/Module/Setup.php:208 msgid "Could not create table." msgstr "" -#: Zotlabs/Module/Setup.php:209 +#: Zotlabs/Module/Setup.php:211 msgid "Installation succeeded!" msgstr "" -#: Zotlabs/Module/Setup.php:211 +#: Zotlabs/Module/Setup.php:213 msgid "Your site database has been installed." msgstr "" -#: Zotlabs/Module/Setup.php:215 +#: Zotlabs/Module/Setup.php:217 msgid "Database install failed!" msgstr "" -#: Zotlabs/Module/Setup.php:217 +#: Zotlabs/Module/Setup.php:219 msgid "" "You may need to import the file \"install/schema_xxx.sql\" manually using a " "database client." msgstr "" -#: Zotlabs/Module/Setup.php:218 Zotlabs/Module/Setup.php:283 +#: Zotlabs/Module/Setup.php:220 Zotlabs/Module/Setup.php:285 msgid "Please see the file \"install/INSTALL.txt\"." msgstr "" -#: Zotlabs/Module/Setup.php:280 +#: Zotlabs/Module/Setup.php:282 msgid "System check" msgstr "" -#: Zotlabs/Module/Setup.php:285 +#: Zotlabs/Module/Setup.php:287 msgid "Check again" msgstr "" -#: Zotlabs/Module/Setup.php:310 +#: Zotlabs/Module/Setup.php:313 msgid "Database connection" msgstr "" -#: Zotlabs/Module/Setup.php:311 +#: Zotlabs/Module/Setup.php:314 msgid "" "In order to install this software we need to know how to connect to your " "database." msgstr "" -#: Zotlabs/Module/Setup.php:312 +#: Zotlabs/Module/Setup.php:315 msgid "" "Please contact your hosting provider or site administrator if you have " "questions about these settings." msgstr "" -#: Zotlabs/Module/Setup.php:313 +#: Zotlabs/Module/Setup.php:316 msgid "" "The database you specify below should already exist. If it does not, please " "create it before continuing." msgstr "" -#: Zotlabs/Module/Setup.php:317 +#: Zotlabs/Module/Setup.php:320 msgid "Database Server Name" msgstr "" -#: Zotlabs/Module/Setup.php:317 +#: Zotlabs/Module/Setup.php:320 msgid "Default is 127.0.0.1" msgstr "" -#: Zotlabs/Module/Setup.php:318 +#: Zotlabs/Module/Setup.php:321 msgid "Database Port" msgstr "" -#: Zotlabs/Module/Setup.php:318 +#: Zotlabs/Module/Setup.php:321 msgid "Communication port number - use 0 for default" msgstr "" -#: Zotlabs/Module/Setup.php:319 +#: Zotlabs/Module/Setup.php:322 msgid "Database Login Name" msgstr "" -#: Zotlabs/Module/Setup.php:320 +#: Zotlabs/Module/Setup.php:323 msgid "Database Login Password" msgstr "" -#: Zotlabs/Module/Setup.php:321 +#: Zotlabs/Module/Setup.php:324 msgid "Database Name" msgstr "" -#: Zotlabs/Module/Setup.php:322 +#: Zotlabs/Module/Setup.php:325 msgid "Database Type" msgstr "" -#: Zotlabs/Module/Setup.php:324 Zotlabs/Module/Setup.php:365 +#: Zotlabs/Module/Setup.php:327 Zotlabs/Module/Setup.php:369 msgid "Site administrator email address" msgstr "" -#: Zotlabs/Module/Setup.php:324 Zotlabs/Module/Setup.php:365 +#: Zotlabs/Module/Setup.php:327 Zotlabs/Module/Setup.php:369 msgid "" "Required. Your account email address must match this in order to use the web " "admin panel." msgstr "" -#: Zotlabs/Module/Setup.php:325 Zotlabs/Module/Setup.php:367 +#: Zotlabs/Module/Setup.php:328 Zotlabs/Module/Setup.php:371 msgid "Website URL" msgstr "" -#: Zotlabs/Module/Setup.php:325 Zotlabs/Module/Setup.php:367 +#: Zotlabs/Module/Setup.php:328 Zotlabs/Module/Setup.php:371 msgid "Required. Please use SSL (https) URL if available." msgstr "" -#: Zotlabs/Module/Setup.php:326 Zotlabs/Module/Setup.php:369 +#: Zotlabs/Module/Setup.php:329 Zotlabs/Module/Setup.php:373 msgid "Please select a default timezone for your website" msgstr "" -#: Zotlabs/Module/Setup.php:353 +#: Zotlabs/Module/Setup.php:357 msgid "Site settings" msgstr "" -#: Zotlabs/Module/Setup.php:409 +#: Zotlabs/Module/Setup.php:415 msgid "PHP version 7.1 or greater is required." msgstr "" -#: Zotlabs/Module/Setup.php:410 +#: Zotlabs/Module/Setup.php:416 msgid "PHP version" msgstr "" -#: Zotlabs/Module/Setup.php:427 +#: Zotlabs/Module/Setup.php:431 msgid "Could not find a command line version of PHP in the web server PATH." msgstr "" -#: Zotlabs/Module/Setup.php:428 +#: Zotlabs/Module/Setup.php:432 msgid "" "If you do not have a command line version of PHP installed on server, you " "will not be able to run background tasks - including message delivery." msgstr "" -#: Zotlabs/Module/Setup.php:432 +#: Zotlabs/Module/Setup.php:436 msgid "PHP executable path" msgstr "" -#: Zotlabs/Module/Setup.php:432 +#: Zotlabs/Module/Setup.php:436 msgid "" "Enter full path to php executable. You can leave this blank to continue the " "installation." msgstr "" -#: Zotlabs/Module/Setup.php:437 +#: Zotlabs/Module/Setup.php:441 msgid "Command line PHP" msgstr "" -#: Zotlabs/Module/Setup.php:448 +#: Zotlabs/Module/Setup.php:451 msgid "" "Unable to check command line PHP, as shell_exec() is disabled. This is " "required." msgstr "" -#: Zotlabs/Module/Setup.php:452 +#: Zotlabs/Module/Setup.php:455 msgid "" "The command line version of PHP on your system does not have " "\"register_argc_argv\" enabled." msgstr "" -#: Zotlabs/Module/Setup.php:453 +#: Zotlabs/Module/Setup.php:456 msgid "This is required for message delivery to work." msgstr "" -#: Zotlabs/Module/Setup.php:456 +#: Zotlabs/Module/Setup.php:459 msgid "PHP register_argc_argv" msgstr "" -#: Zotlabs/Module/Setup.php:475 +#: Zotlabs/Module/Setup.php:479 msgid "" "This is not sufficient to upload larger images or files. You should be able " "to upload at least 2MB (2097152 bytes) at once." msgstr "" -#: Zotlabs/Module/Setup.php:478 +#: Zotlabs/Module/Setup.php:483 #, php-format msgid "" "Your max allowed total upload size is set to %s. Maximum size of one file to " "upload is set to %s. You are allowed to upload up to %d files at once." msgstr "" -#: Zotlabs/Module/Setup.php:485 +#: Zotlabs/Module/Setup.php:490 msgid "You can adjust these settings in the server php.ini file." msgstr "" -#: Zotlabs/Module/Setup.php:487 +#: Zotlabs/Module/Setup.php:492 msgid "PHP upload limits" msgstr "" -#: Zotlabs/Module/Setup.php:510 +#: Zotlabs/Module/Setup.php:515 msgid "" "Error: the \"openssl_pkey_new\" function on this system is not able to " "generate encryption keys" msgstr "" -#: Zotlabs/Module/Setup.php:511 +#: Zotlabs/Module/Setup.php:516 msgid "" "If running under Windows, please see \"http://www.php.net/manual/en/openssl." "installation.php\"." msgstr "" -#: Zotlabs/Module/Setup.php:514 +#: Zotlabs/Module/Setup.php:519 msgid "Generate encryption keys" msgstr "" -#: Zotlabs/Module/Setup.php:532 +#: Zotlabs/Module/Setup.php:538 msgid "libCurl PHP module" msgstr "" -#: Zotlabs/Module/Setup.php:533 +#: Zotlabs/Module/Setup.php:539 msgid "GD graphics PHP module" msgstr "" -#: Zotlabs/Module/Setup.php:534 +#: Zotlabs/Module/Setup.php:540 msgid "OpenSSL PHP module" msgstr "" -#: Zotlabs/Module/Setup.php:535 +#: Zotlabs/Module/Setup.php:541 msgid "PDO database PHP module" msgstr "" -#: Zotlabs/Module/Setup.php:536 +#: Zotlabs/Module/Setup.php:542 msgid "mb_string PHP module" msgstr "" -#: Zotlabs/Module/Setup.php:537 +#: Zotlabs/Module/Setup.php:543 msgid "xml PHP module" msgstr "" -#: Zotlabs/Module/Setup.php:538 +#: Zotlabs/Module/Setup.php:544 msgid "zip PHP module" msgstr "" -#: Zotlabs/Module/Setup.php:542 Zotlabs/Module/Setup.php:545 +#: Zotlabs/Module/Setup.php:548 Zotlabs/Module/Setup.php:550 msgid "Apache mod_rewrite module" msgstr "" -#: Zotlabs/Module/Setup.php:542 +#: Zotlabs/Module/Setup.php:548 msgid "" "Error: Apache webserver mod-rewrite module is required but not installed." msgstr "" -#: Zotlabs/Module/Setup.php:549 Zotlabs/Module/Setup.php:552 +#: Zotlabs/Module/Setup.php:554 Zotlabs/Module/Setup.php:556 msgid "exec" msgstr "" -#: Zotlabs/Module/Setup.php:549 +#: Zotlabs/Module/Setup.php:554 msgid "" "Error: exec is required but is either not installed or has been disabled in " "php.ini" msgstr "" -#: Zotlabs/Module/Setup.php:555 Zotlabs/Module/Setup.php:558 +#: Zotlabs/Module/Setup.php:559 Zotlabs/Module/Setup.php:561 msgid "shell_exec" msgstr "" -#: Zotlabs/Module/Setup.php:555 +#: Zotlabs/Module/Setup.php:559 msgid "" "Error: shell_exec is required but is either not installed or has been " "disabled in php.ini" msgstr "" -#: Zotlabs/Module/Setup.php:563 +#: Zotlabs/Module/Setup.php:566 msgid "Error: libCURL PHP module required but not installed." msgstr "" -#: Zotlabs/Module/Setup.php:567 +#: Zotlabs/Module/Setup.php:570 msgid "" "Error: GD PHP module with JPEG support or ImageMagick graphics library " "required but not installed." msgstr "" -#: Zotlabs/Module/Setup.php:571 +#: Zotlabs/Module/Setup.php:574 msgid "Error: openssl PHP module required but not installed." msgstr "" -#: Zotlabs/Module/Setup.php:577 +#: Zotlabs/Module/Setup.php:580 msgid "" "Error: PDO database PHP module missing a driver for either mysql or pgsql." msgstr "" -#: Zotlabs/Module/Setup.php:582 +#: Zotlabs/Module/Setup.php:585 msgid "Error: PDO database PHP module required but not installed." msgstr "" -#: Zotlabs/Module/Setup.php:586 +#: Zotlabs/Module/Setup.php:589 msgid "Error: mb_string PHP module required but not installed." msgstr "" -#: Zotlabs/Module/Setup.php:590 +#: Zotlabs/Module/Setup.php:593 msgid "Error: xml PHP module required for DAV but not installed." msgstr "" -#: Zotlabs/Module/Setup.php:594 +#: Zotlabs/Module/Setup.php:597 msgid "Error: zip PHP module required but not installed." msgstr "" -#: Zotlabs/Module/Setup.php:613 Zotlabs/Module/Setup.php:622 +#: Zotlabs/Module/Setup.php:619 Zotlabs/Module/Setup.php:628 msgid ".htconfig.php is writable" msgstr "" -#: Zotlabs/Module/Setup.php:618 +#: Zotlabs/Module/Setup.php:624 msgid "" "The web installer needs to be able to create a file called \".htconfig.php\" " "in the top folder of your web server and it is unable to do so." msgstr "" -#: Zotlabs/Module/Setup.php:619 +#: Zotlabs/Module/Setup.php:625 msgid "" "This is most often a permission setting, as the web server may not be able " "to write files in your folder - even if you can." msgstr "" -#: Zotlabs/Module/Setup.php:620 +#: Zotlabs/Module/Setup.php:626 msgid "Please see install/INSTALL.txt for additional information." msgstr "" -#: Zotlabs/Module/Setup.php:638 +#: Zotlabs/Module/Setup.php:645 msgid "" "This software uses the Smarty3 template engine to render its web views. " "Smarty3 compiles templates to PHP to speed up rendering." msgstr "" -#: Zotlabs/Module/Setup.php:639 +#: Zotlabs/Module/Setup.php:646 #, php-format msgid "" "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." msgstr "" -#: Zotlabs/Module/Setup.php:640 Zotlabs/Module/Setup.php:660 +#: Zotlabs/Module/Setup.php:647 Zotlabs/Module/Setup.php:668 msgid "" "Please ensure that the user that your web server runs as (e.g. www-data) has " "write access to this folder." msgstr "" -#: Zotlabs/Module/Setup.php:643 +#: Zotlabs/Module/Setup.php:650 #, php-format msgid "%s is writable" msgstr "" -#: Zotlabs/Module/Setup.php:659 +#: Zotlabs/Module/Setup.php:667 msgid "" "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" msgstr "" -#: Zotlabs/Module/Setup.php:663 +#: Zotlabs/Module/Setup.php:671 msgid "store is writable" msgstr "" -#: Zotlabs/Module/Setup.php:695 +#: Zotlabs/Module/Setup.php:703 msgid "" "SSL certificate cannot be validated. Fix certificate or disable https access " "to this site." msgstr "" -#: Zotlabs/Module/Setup.php:696 +#: Zotlabs/Module/Setup.php:704 msgid "" "If you have https access to your website or allow connections to TCP port " "443 (the https: port), you MUST use a browser-valid certificate. You MUST " "NOT use self-signed certificates!" msgstr "" -#: Zotlabs/Module/Setup.php:697 +#: Zotlabs/Module/Setup.php:705 msgid "" "This restriction is incorporated because public posts from you may for " "example contain references to images on your own hub." msgstr "" -#: Zotlabs/Module/Setup.php:698 +#: Zotlabs/Module/Setup.php:706 msgid "" "If your certificate is not recognized, members of other sites (who may " "themselves have valid certificates) will get a warning message on their own " "site complaining about security issues." msgstr "" -#: Zotlabs/Module/Setup.php:699 +#: Zotlabs/Module/Setup.php:707 msgid "" "This can cause usability issues elsewhere (not just on your own site) so we " "must insist on this requirement." msgstr "" -#: Zotlabs/Module/Setup.php:700 +#: Zotlabs/Module/Setup.php:708 msgid "" "Providers are available that issue free certificates which are browser-valid." msgstr "" -#: Zotlabs/Module/Setup.php:702 +#: Zotlabs/Module/Setup.php:710 msgid "" "If you are confident that the certificate is valid and signed by a trusted " "authority, check to see if you have failed to install an intermediate cert. " @@ -5887,51 +5903,51 @@ msgid "" "server communications." msgstr "" -#: Zotlabs/Module/Setup.php:704 +#: Zotlabs/Module/Setup.php:712 msgid "SSL certificate validation" msgstr "" -#: Zotlabs/Module/Setup.php:710 +#: Zotlabs/Module/Setup.php:718 msgid "" "Url rewrite in .htaccess is not working. Check your server configuration." "Test: " msgstr "" -#: Zotlabs/Module/Setup.php:713 +#: Zotlabs/Module/Setup.php:721 msgid "Url rewrite is working" msgstr "" -#: Zotlabs/Module/Setup.php:728 +#: Zotlabs/Module/Setup.php:736 msgid "" "The database configuration file \".htconfig.php\" could not be written. " "Please use the enclosed text to create a configuration file in your web " "server root." msgstr "" -#: Zotlabs/Module/Setup.php:753 extend/addon/a/faces/faces.php:219 +#: Zotlabs/Module/Setup.php:762 extend/addon/a/faces/faces.php:219 msgid "Errors encountered creating database tables." msgstr "" -#: Zotlabs/Module/Setup.php:796 +#: Zotlabs/Module/Setup.php:807 msgid "registration page" msgstr "" -#: Zotlabs/Module/Setup.php:799 +#: Zotlabs/Module/Setup.php:810 msgid "

                    What next?

                    " msgstr "" -#: Zotlabs/Module/Setup.php:801 +#: Zotlabs/Module/Setup.php:812 msgid "" "IMPORTANT: You will need to [manually] setup a scheduled task for the poller." msgstr "" -#: Zotlabs/Module/Setup.php:803 +#: Zotlabs/Module/Setup.php:814 msgid "" "Please see the file \"install/INSTALL.txt\" for more information and " "instructions." msgstr "" -#: Zotlabs/Module/Setup.php:805 +#: Zotlabs/Module/Setup.php:816 #, php-format msgid "" "Go to your new hub %s and register as new member. Please use the same email " @@ -5939,184 +5955,177 @@ msgid "" "new account to enter the site admin panel." msgstr "" -#: Zotlabs/Module/Hcard.php:40 Zotlabs/Module/Profile.php:52 +#: Zotlabs/Module/Hcard.php:45 Zotlabs/Module/Profile.php:55 msgid "Posts and comments" msgstr "" -#: Zotlabs/Module/Hcard.php:47 Zotlabs/Module/Channel.php:84 -#: Zotlabs/Module/Profile.php:59 +#: Zotlabs/Module/Hcard.php:52 Zotlabs/Module/Channel.php:85 +#: Zotlabs/Module/Profile.php:62 msgid "Only posts" msgstr "" -#: Zotlabs/Module/Lists.php:105 +#: Zotlabs/Module/Lists.php:107 msgid "Access list created." msgstr "" -#: Zotlabs/Module/Lists.php:108 +#: Zotlabs/Module/Lists.php:109 msgid "Could not create access list." msgstr "" -#: Zotlabs/Module/Lists.php:127 Zotlabs/Module/Lists.php:311 -#: include/items.php:4375 +#: Zotlabs/Module/Lists.php:129 Zotlabs/Module/Lists.php:276 +#: include/items.php:4529 msgid "Access list not found." msgstr "" -#: Zotlabs/Module/Lists.php:144 +#: Zotlabs/Module/Lists.php:146 msgid "Access list updated." msgstr "" -#: Zotlabs/Module/Lists.php:173 Zotlabs/Module/Lists.php:201 -#: Zotlabs/Module/Import_items.php:123 Zotlabs/Module/Cloud.php:127 -#: Zotlabs/Module/Like.php:109 Zotlabs/Module/Share.php:70 -#: Zotlabs/Module/Dreport.php:13 Zotlabs/Module/Dreport.php:82 -#: Zotlabs/Module/Profperm.php:32 Zotlabs/Module/Subthread.php:92 -#: Zotlabs/Web/WebServer.php:70 include/items.php:476 +#: Zotlabs/Module/Lists.php:169 Zotlabs/Module/Lists.php:215 +#: Zotlabs/Module/Lists.php:244 Zotlabs/Module/Lists.php:287 +#: Zotlabs/Module/Import_items.php:133 Zotlabs/Module/Cloud.php:134 +#: Zotlabs/Module/Like.php:109 Zotlabs/Module/Share.php:81 +#: Zotlabs/Module/Dreport.php:16 Zotlabs/Module/Dreport.php:86 +#: Zotlabs/Module/Profperm.php:35 Zotlabs/Module/Subthread.php:97 +#: Zotlabs/Web/WebServer.php:69 include/items.php:490 msgid "Permission denied" msgstr "" -#: Zotlabs/Module/Lists.php:185 Zotlabs/Module/Lists.php:362 -msgid "List members" -msgstr "" - -#: Zotlabs/Module/Lists.php:191 -msgid "List not found" -msgstr "" - -#: Zotlabs/Module/Lists.php:229 Zotlabs/Module/Lists.php:240 +#: Zotlabs/Module/Lists.php:190 Zotlabs/Module/Lists.php:201 msgid "Access Lists" msgstr "" -#: Zotlabs/Module/Lists.php:230 +#: Zotlabs/Module/Lists.php:191 msgid "Create access list" msgstr "" -#: Zotlabs/Module/Lists.php:234 +#: Zotlabs/Module/Lists.php:195 msgid "Access list name" msgstr "" -#: Zotlabs/Module/Lists.php:235 Zotlabs/Module/Lists.php:350 +#: Zotlabs/Module/Lists.php:196 Zotlabs/Module/Lists.php:339 msgid "Members are visible to other channels" msgstr "" -#: Zotlabs/Module/Lists.php:242 +#: Zotlabs/Module/Lists.php:203 msgid "Members" msgstr "" -#: Zotlabs/Module/Lists.php:264 +#: Zotlabs/Module/Lists.php:232 msgid "Access list removed." msgstr "" -#: Zotlabs/Module/Lists.php:266 +#: Zotlabs/Module/Lists.php:234 msgid "Unable to remove access list." msgstr "" -#: Zotlabs/Module/Lists.php:345 +#: Zotlabs/Module/Lists.php:299 Zotlabs/Module/Lists.php:351 +msgid "List members" +msgstr "" + +#: Zotlabs/Module/Lists.php:334 #, php-format msgid "Access List: %s" msgstr "" -#: Zotlabs/Module/Lists.php:347 +#: Zotlabs/Module/Lists.php:336 msgid "Access list name: " msgstr "" -#: Zotlabs/Module/Lists.php:352 +#: Zotlabs/Module/Lists.php:341 msgid "Delete access list" msgstr "" -#: Zotlabs/Module/Lists.php:364 +#: Zotlabs/Module/Lists.php:353 msgid "Not in this list" msgstr "" -#: Zotlabs/Module/Lists.php:396 +#: Zotlabs/Module/Lists.php:386 msgid "Select a channel to toggle membership" msgstr "" -#: Zotlabs/Module/Layouts.php:190 include/text.php:2694 +#: Zotlabs/Module/Layouts.php:198 include/text.php:2852 msgid "Layouts" msgstr "" -#: Zotlabs/Module/Layouts.php:196 +#: Zotlabs/Module/Layouts.php:204 msgid "Layout Description" msgstr "" -#: Zotlabs/Module/Layouts.php:201 +#: Zotlabs/Module/Layouts.php:209 msgid "Download PDL file" msgstr "" -#: Zotlabs/Module/Channel.php:180 +#: Zotlabs/Module/Channel.php:176 #, php-format msgid "This is the home page of %s." msgstr "" -#: Zotlabs/Module/Channel.php:237 +#: Zotlabs/Module/Channel.php:233 msgid "Insufficient permissions. Request redirected to profile page." msgstr "" -#: Zotlabs/Module/Channel.php:254 Zotlabs/Module/Stream.php:215 +#: Zotlabs/Module/Channel.php:249 Zotlabs/Module/Stream.php:215 msgid "Search Results For:" msgstr "" -#: Zotlabs/Module/Channel.php:621 +#: Zotlabs/Module/Channel.php:604 #, php-format msgid "This post was published on the home page of %s." msgstr "" -#: Zotlabs/Module/Card_edit.php:119 Zotlabs/Module/Editblock.php:133 -#: Zotlabs/Module/Photos.php:724 Zotlabs/Module/Photos.php:1105 -#: include/conversation.php:1426 +#: Zotlabs/Module/Card_edit.php:126 Zotlabs/Module/Editblock.php:142 +#: Zotlabs/Module/Photos.php:732 Zotlabs/Module/Photos.php:1118 +#: include/conversation.php:1451 msgid "Title (optional)" msgstr "" -#: Zotlabs/Module/Card_edit.php:130 +#: Zotlabs/Module/Card_edit.php:137 msgid "Edit Card" msgstr "" -#: Zotlabs/Module/Import_items.php:48 Zotlabs/Module/Import.php:75 +#: Zotlabs/Module/Import_items.php:54 Zotlabs/Module/Import.php:73 msgid "Nothing to import." msgstr "" -#: Zotlabs/Module/Import_items.php:72 Zotlabs/Module/Import.php:91 -#: Zotlabs/Module/Import.php:107 +#: Zotlabs/Module/Import_items.php:79 Zotlabs/Module/Import.php:88 +#: Zotlabs/Module/Import.php:103 msgid "Unable to download data from old server" msgstr "" -#: Zotlabs/Module/Import_items.php:77 Zotlabs/Module/Import.php:114 +#: Zotlabs/Module/Import_items.php:85 Zotlabs/Module/Import.php:110 msgid "Imported file is empty." msgstr "" -#: Zotlabs/Module/Import_items.php:101 +#: Zotlabs/Module/Import_items.php:110 msgid "Data export format is not compatible with this software" msgstr "" -#: Zotlabs/Module/Import_items.php:111 +#: Zotlabs/Module/Import_items.php:120 msgid "Import completed" msgstr "" -#: Zotlabs/Module/Import_items.php:128 +#: Zotlabs/Module/Import_items.php:138 msgid "Import Items" msgstr "" -#: Zotlabs/Module/Import_items.php:129 +#: Zotlabs/Module/Import_items.php:139 msgid "Use this form to import existing posts and content from an export file." msgstr "" -#: Zotlabs/Module/Import_items.php:130 Zotlabs/Module/Import.php:693 +#: Zotlabs/Module/Import_items.php:140 Zotlabs/Module/Import.php:687 msgid "File to Upload" msgstr "" -#: Zotlabs/Module/Cloud.php:124 -msgid "Not found" -msgstr "" - -#: Zotlabs/Module/Cloud.php:130 +#: Zotlabs/Module/Cloud.php:136 msgid "Please refresh page" msgstr "" -#: Zotlabs/Module/Cloud.php:137 +#: Zotlabs/Module/Cloud.php:142 msgid "Unknown error" msgstr "" -#: Zotlabs/Module/Manage.php:147 Zotlabs/Module/New_channel.php:157 +#: Zotlabs/Module/Manage.php:148 Zotlabs/Module/New_channel.php:165 #, php-format msgid "You have created %1$.0f of %2$.0f allowed channels." msgstr "" @@ -6125,653 +6134,653 @@ msgstr "" msgid "Create a new channel" msgstr "" -#: Zotlabs/Module/Manage.php:184 +#: Zotlabs/Module/Manage.php:183 msgid "Current Channel" msgstr "" -#: Zotlabs/Module/Manage.php:186 +#: Zotlabs/Module/Manage.php:185 msgid "Switch to one of your channels by selecting it." msgstr "" -#: Zotlabs/Module/Manage.php:187 +#: Zotlabs/Module/Manage.php:186 msgid "Default Login Channel" msgstr "" -#: Zotlabs/Module/Manage.php:188 +#: Zotlabs/Module/Manage.php:187 msgid "Make Default" msgstr "" -#: Zotlabs/Module/Manage.php:189 Zotlabs/Module/Manage.php:190 +#: Zotlabs/Module/Manage.php:188 Zotlabs/Module/Manage.php:189 msgid "Add to menu" msgstr "" -#: Zotlabs/Module/Manage.php:193 +#: Zotlabs/Module/Manage.php:192 #, php-format msgid "%d new messages" msgstr "" -#: Zotlabs/Module/Manage.php:194 +#: Zotlabs/Module/Manage.php:193 #, php-format msgid "%d new introductions" msgstr "" -#: Zotlabs/Module/Manage.php:196 +#: Zotlabs/Module/Manage.php:195 msgid "Delegated Channel" msgstr "" -#: Zotlabs/Module/Lang.php:18 +#: Zotlabs/Module/Lang.php:21 msgid "Change UI language" msgstr "" -#: Zotlabs/Module/Mitem.php:35 Zotlabs/Module/Menu.php:211 +#: Zotlabs/Module/Mitem.php:41 Zotlabs/Module/Menu.php:213 msgid "Menu not found." msgstr "" -#: Zotlabs/Module/Mitem.php:67 +#: Zotlabs/Module/Mitem.php:75 msgid "Unable to create element." msgstr "" -#: Zotlabs/Module/Mitem.php:91 +#: Zotlabs/Module/Mitem.php:100 msgid "Unable to update menu element." msgstr "" -#: Zotlabs/Module/Mitem.php:107 +#: Zotlabs/Module/Mitem.php:114 msgid "Unable to add menu element." msgstr "" -#: Zotlabs/Module/Mitem.php:138 Zotlabs/Module/Menu.php:234 -#: Zotlabs/Module/Xchan.php:41 +#: Zotlabs/Module/Mitem.php:145 Zotlabs/Module/Menu.php:234 +#: Zotlabs/Module/Xchan.php:46 msgid "Not found." msgstr "" -#: Zotlabs/Module/Mitem.php:171 Zotlabs/Module/Mitem.php:250 +#: Zotlabs/Module/Mitem.php:179 Zotlabs/Module/Mitem.php:258 msgid "Menu Item Permissions" msgstr "" -#: Zotlabs/Module/Mitem.php:172 Zotlabs/Module/Mitem.php:251 -#: Zotlabs/Module/Settings/Channel.php:625 +#: Zotlabs/Module/Mitem.php:180 Zotlabs/Module/Mitem.php:259 +#: Zotlabs/Module/Settings/Channel.php:658 msgid "(click to open/close)" msgstr "" -#: Zotlabs/Module/Mitem.php:178 Zotlabs/Module/Mitem.php:195 +#: Zotlabs/Module/Mitem.php:186 Zotlabs/Module/Mitem.php:203 msgid "Link Name" msgstr "" -#: Zotlabs/Module/Mitem.php:179 Zotlabs/Module/Mitem.php:259 +#: Zotlabs/Module/Mitem.php:187 Zotlabs/Module/Mitem.php:267 msgid "Link or Submenu Target" msgstr "" -#: Zotlabs/Module/Mitem.php:179 +#: Zotlabs/Module/Mitem.php:187 msgid "Enter URL of the link or select a menu name to create a submenu" msgstr "" -#: Zotlabs/Module/Mitem.php:180 Zotlabs/Module/Mitem.php:260 +#: Zotlabs/Module/Mitem.php:188 Zotlabs/Module/Mitem.php:268 msgid "Use magic-auth if available" msgstr "" -#: Zotlabs/Module/Mitem.php:181 Zotlabs/Module/Mitem.php:261 +#: Zotlabs/Module/Mitem.php:189 Zotlabs/Module/Mitem.php:269 msgid "Open link in new window" msgstr "" -#: Zotlabs/Module/Mitem.php:182 Zotlabs/Module/Mitem.php:262 +#: Zotlabs/Module/Mitem.php:190 Zotlabs/Module/Mitem.php:270 msgid "Order in list" msgstr "" -#: Zotlabs/Module/Mitem.php:182 Zotlabs/Module/Mitem.php:262 +#: Zotlabs/Module/Mitem.php:190 Zotlabs/Module/Mitem.php:270 msgid "Higher numbers will sink to bottom of listing" msgstr "" -#: Zotlabs/Module/Mitem.php:183 +#: Zotlabs/Module/Mitem.php:191 msgid "Submit and finish" msgstr "" -#: Zotlabs/Module/Mitem.php:184 +#: Zotlabs/Module/Mitem.php:192 msgid "Submit and continue" msgstr "" -#: Zotlabs/Module/Mitem.php:193 +#: Zotlabs/Module/Mitem.php:201 msgid "Menu:" msgstr "" -#: Zotlabs/Module/Mitem.php:196 +#: Zotlabs/Module/Mitem.php:204 msgid "Link Target" msgstr "" -#: Zotlabs/Module/Mitem.php:199 +#: Zotlabs/Module/Mitem.php:207 msgid "Edit menu" msgstr "" -#: Zotlabs/Module/Mitem.php:202 +#: Zotlabs/Module/Mitem.php:210 msgid "Edit element" msgstr "" -#: Zotlabs/Module/Mitem.php:203 +#: Zotlabs/Module/Mitem.php:211 msgid "Drop element" msgstr "" -#: Zotlabs/Module/Mitem.php:204 +#: Zotlabs/Module/Mitem.php:212 msgid "New element" msgstr "" -#: Zotlabs/Module/Mitem.php:205 +#: Zotlabs/Module/Mitem.php:213 msgid "Edit this menu container" msgstr "" -#: Zotlabs/Module/Mitem.php:206 +#: Zotlabs/Module/Mitem.php:214 msgid "Add menu element" msgstr "" -#: Zotlabs/Module/Mitem.php:207 +#: Zotlabs/Module/Mitem.php:215 msgid "Delete this menu item" msgstr "" -#: Zotlabs/Module/Mitem.php:208 +#: Zotlabs/Module/Mitem.php:216 msgid "Edit this menu item" msgstr "" -#: Zotlabs/Module/Mitem.php:226 +#: Zotlabs/Module/Mitem.php:233 msgid "Menu item not found." msgstr "" -#: Zotlabs/Module/Mitem.php:239 +#: Zotlabs/Module/Mitem.php:246 msgid "Menu item deleted." msgstr "" -#: Zotlabs/Module/Mitem.php:241 +#: Zotlabs/Module/Mitem.php:248 msgid "Menu item could not be deleted." msgstr "" -#: Zotlabs/Module/Mitem.php:248 +#: Zotlabs/Module/Mitem.php:256 msgid "Edit Menu Element" msgstr "" -#: Zotlabs/Module/Mitem.php:258 +#: Zotlabs/Module/Mitem.php:266 msgid "Link text" msgstr "" -#: Zotlabs/Module/Finger.php:20 +#: Zotlabs/Module/Finger.php:22 msgid "Webfinger Diagnostic" msgstr "" -#: Zotlabs/Module/Finger.php:21 +#: Zotlabs/Module/Finger.php:23 msgid "Lookup address or URL" msgstr "" -#: Zotlabs/Module/Editblock.php:142 +#: Zotlabs/Module/Editblock.php:151 msgid "Edit Block" msgstr "" -#: Zotlabs/Module/Like.php:172 Zotlabs/Module/Subthread.php:119 -#: Zotlabs/Module/Tagger.php:76 include/text.php:2245 -#: include/conversation.php:127 +#: Zotlabs/Module/Like.php:172 Zotlabs/Module/Subthread.php:124 +#: Zotlabs/Module/Tagger.php:84 include/conversation.php:129 +#: include/text.php:2369 msgid "photo" msgstr "" -#: Zotlabs/Module/Like.php:172 Zotlabs/Module/Subthread.php:119 +#: Zotlabs/Module/Like.php:172 Zotlabs/Module/Subthread.php:124 msgid "status" msgstr "" -#: Zotlabs/Module/Like.php:215 +#: Zotlabs/Module/Like.php:217 msgid "Undo a previous action" msgstr "" -#: Zotlabs/Module/Like.php:226 +#: Zotlabs/Module/Like.php:227 #, php-format msgid "%1$s is attending %2$s's %3$s" msgstr "" -#: Zotlabs/Module/Like.php:229 +#: Zotlabs/Module/Like.php:230 #, php-format msgid "%1$s is not attending %2$s's %3$s" msgstr "" -#: Zotlabs/Module/Like.php:232 +#: Zotlabs/Module/Like.php:233 #, php-format msgid "%1$s may attend %2$s's %3$s" msgstr "" -#: Zotlabs/Module/Share.php:119 +#: Zotlabs/Module/Share.php:134 msgid "Post repeated" msgstr "" -#: Zotlabs/Module/Lostpass.php:19 +#: Zotlabs/Module/Lostpass.php:25 msgid "No valid account found." msgstr "" -#: Zotlabs/Module/Lostpass.php:33 +#: Zotlabs/Module/Lostpass.php:40 msgid "Password reset request issued. Check your email." msgstr "" -#: Zotlabs/Module/Lostpass.php:39 Zotlabs/Module/Lostpass.php:108 +#: Zotlabs/Module/Lostpass.php:47 Zotlabs/Module/Lostpass.php:119 #, php-format msgid "Site Member (%s)" msgstr "" -#: Zotlabs/Module/Lostpass.php:44 Zotlabs/Module/Lostpass.php:49 +#: Zotlabs/Module/Lostpass.php:52 Zotlabs/Module/Lostpass.php:57 #, php-format msgid "Password reset requested at %s" msgstr "" -#: Zotlabs/Module/Lostpass.php:68 +#: Zotlabs/Module/Lostpass.php:78 msgid "" "Request could not be verified. (You may have previously submitted it.) " "Password reset failed." msgstr "" -#: Zotlabs/Module/Lostpass.php:91 boot.php:1727 +#: Zotlabs/Module/Lostpass.php:102 boot.php:1735 msgid "Password Reset" msgstr "" -#: Zotlabs/Module/Lostpass.php:92 +#: Zotlabs/Module/Lostpass.php:103 msgid "Your password has been reset as requested." msgstr "" -#: Zotlabs/Module/Lostpass.php:93 +#: Zotlabs/Module/Lostpass.php:104 msgid "Your new password is" msgstr "" -#: Zotlabs/Module/Lostpass.php:94 +#: Zotlabs/Module/Lostpass.php:105 msgid "Save or copy your new password - and then" msgstr "" -#: Zotlabs/Module/Lostpass.php:95 +#: Zotlabs/Module/Lostpass.php:106 msgid "click here to login" msgstr "" -#: Zotlabs/Module/Lostpass.php:96 +#: Zotlabs/Module/Lostpass.php:107 msgid "" "Your password may be changed from the Settings page after " "successful login." msgstr "" -#: Zotlabs/Module/Lostpass.php:117 +#: Zotlabs/Module/Lostpass.php:127 #, php-format msgid "Your password has changed at %s" msgstr "" -#: Zotlabs/Module/Lostpass.php:130 +#: Zotlabs/Module/Lostpass.php:138 msgid "Forgot your Password?" msgstr "" -#: Zotlabs/Module/Lostpass.php:131 +#: Zotlabs/Module/Lostpass.php:139 msgid "" "Enter your email address and submit to have your password reset. Then check " "your email for further instructions." msgstr "" -#: Zotlabs/Module/Lostpass.php:132 Zotlabs/Module/Settings/Channel.php:600 +#: Zotlabs/Module/Lostpass.php:140 Zotlabs/Module/Settings/Channel.php:633 msgid "Email Address" msgstr "" -#: Zotlabs/Module/Menu.php:70 +#: Zotlabs/Module/Menu.php:76 msgid "Unable to update menu." msgstr "" -#: Zotlabs/Module/Menu.php:81 +#: Zotlabs/Module/Menu.php:86 msgid "Unable to create menu." msgstr "" -#: Zotlabs/Module/Menu.php:163 Zotlabs/Module/Menu.php:176 +#: Zotlabs/Module/Menu.php:166 Zotlabs/Module/Menu.php:179 msgid "Menu Name" msgstr "" -#: Zotlabs/Module/Menu.php:163 +#: Zotlabs/Module/Menu.php:166 msgid "Unique name (not visible on webpage) - required" msgstr "" -#: Zotlabs/Module/Menu.php:164 Zotlabs/Module/Menu.php:177 +#: Zotlabs/Module/Menu.php:167 Zotlabs/Module/Menu.php:180 msgid "Menu Title" msgstr "" -#: Zotlabs/Module/Menu.php:164 +#: Zotlabs/Module/Menu.php:167 msgid "Visible on webpage - leave empty for no title" msgstr "" -#: Zotlabs/Module/Menu.php:165 +#: Zotlabs/Module/Menu.php:168 msgid "Allow Bookmarks" msgstr "" -#: Zotlabs/Module/Menu.php:165 Zotlabs/Module/Menu.php:224 +#: Zotlabs/Module/Menu.php:168 Zotlabs/Module/Menu.php:226 msgid "Menu may be used to store saved bookmarks" msgstr "" -#: Zotlabs/Module/Menu.php:166 Zotlabs/Module/Menu.php:227 +#: Zotlabs/Module/Menu.php:169 Zotlabs/Module/Menu.php:229 msgid "Submit and proceed" msgstr "" -#: Zotlabs/Module/Menu.php:173 include/text.php:2693 +#: Zotlabs/Module/Menu.php:176 include/text.php:2851 msgid "Menus" msgstr "" -#: Zotlabs/Module/Menu.php:179 Zotlabs/Module/Locs.php:125 +#: Zotlabs/Module/Menu.php:182 Zotlabs/Module/Locs.php:135 msgid "Drop" msgstr "" -#: Zotlabs/Module/Menu.php:183 +#: Zotlabs/Module/Menu.php:186 msgid "Bookmarks allowed" msgstr "" -#: Zotlabs/Module/Menu.php:185 +#: Zotlabs/Module/Menu.php:188 msgid "Delete this menu" msgstr "" -#: Zotlabs/Module/Menu.php:186 Zotlabs/Module/Menu.php:221 +#: Zotlabs/Module/Menu.php:189 Zotlabs/Module/Menu.php:223 msgid "Edit menu contents" msgstr "" -#: Zotlabs/Module/Menu.php:187 +#: Zotlabs/Module/Menu.php:190 msgid "Edit this menu" msgstr "" -#: Zotlabs/Module/Menu.php:203 +#: Zotlabs/Module/Menu.php:204 msgid "Menu could not be deleted." msgstr "" -#: Zotlabs/Module/Menu.php:216 +#: Zotlabs/Module/Menu.php:218 msgid "Edit Menu" msgstr "" -#: Zotlabs/Module/Menu.php:220 +#: Zotlabs/Module/Menu.php:222 msgid "Add or remove entries to this menu" msgstr "" -#: Zotlabs/Module/Menu.php:222 +#: Zotlabs/Module/Menu.php:224 msgid "Menu name" msgstr "" -#: Zotlabs/Module/Menu.php:222 +#: Zotlabs/Module/Menu.php:224 msgid "Must be unique, only seen by you" msgstr "" -#: Zotlabs/Module/Menu.php:223 +#: Zotlabs/Module/Menu.php:225 msgid "Menu title" msgstr "" -#: Zotlabs/Module/Menu.php:223 +#: Zotlabs/Module/Menu.php:225 msgid "Menu title as seen by others" msgstr "" -#: Zotlabs/Module/Menu.php:224 +#: Zotlabs/Module/Menu.php:226 msgid "Allow bookmarks" msgstr "" -#: Zotlabs/Module/Pconfig.php:35 Zotlabs/Module/Pconfig.php:72 +#: Zotlabs/Module/Pconfig.php:37 Zotlabs/Module/Pconfig.php:73 msgid "This setting requires special processing and editing has been blocked." msgstr "" -#: Zotlabs/Module/Pconfig.php:61 +#: Zotlabs/Module/Pconfig.php:63 msgid "Configuration Editor" msgstr "" -#: Zotlabs/Module/Pconfig.php:62 +#: Zotlabs/Module/Pconfig.php:64 msgid "" "Warning: Changing some settings could render your channel inoperable. Please " "leave this page unless you are comfortable with and knowledgeable about how " "to correctly use this feature." msgstr "" -#: Zotlabs/Module/Editpost.php:40 +#: Zotlabs/Module/Editpost.php:44 msgid "Item is not editable" msgstr "" -#: Zotlabs/Module/Editpost.php:142 Zotlabs/Module/Rpost.php:246 +#: Zotlabs/Module/Editpost.php:147 Zotlabs/Module/Rpost.php:242 msgid "Edit post" msgstr "" -#: Zotlabs/Module/Search.php:342 +#: Zotlabs/Module/Search.php:336 #, php-format msgid "Items tagged with: %s" msgstr "" -#: Zotlabs/Module/Search.php:344 +#: Zotlabs/Module/Search.php:338 #, php-format msgid "Search results for: %s" msgstr "" -#: Zotlabs/Module/Hq.php:147 +#: Zotlabs/Module/Hq.php:160 msgid "Welcome to $Projectname!" msgstr "" -#: Zotlabs/Module/Hq.php:147 +#: Zotlabs/Module/Hq.php:160 msgid "You have got no unseen posts..." msgstr "" -#: Zotlabs/Module/Locs.php:28 Zotlabs/Module/Locs.php:65 +#: Zotlabs/Module/Locs.php:31 Zotlabs/Module/Locs.php:72 msgid "Location not found." msgstr "" -#: Zotlabs/Module/Locs.php:73 +#: Zotlabs/Module/Locs.php:81 msgid "Location lookup failed." msgstr "" -#: Zotlabs/Module/Locs.php:77 +#: Zotlabs/Module/Locs.php:85 msgid "" "Please select another location to become primary before removing the primary " "location." msgstr "" -#: Zotlabs/Module/Locs.php:106 +#: Zotlabs/Module/Locs.php:115 msgid "Pushing location info" msgstr "" -#: Zotlabs/Module/Locs.php:116 +#: Zotlabs/Module/Locs.php:126 msgid "No locations found." msgstr "" -#: Zotlabs/Module/Locs.php:121 +#: Zotlabs/Module/Locs.php:131 msgid "Manage Channel Locations" msgstr "" -#: Zotlabs/Module/Locs.php:127 +#: Zotlabs/Module/Locs.php:137 msgid "Publish these settings" msgstr "" -#: Zotlabs/Module/Locs.php:128 +#: Zotlabs/Module/Locs.php:138 msgid "Please wait several minutes between consecutive operations." msgstr "" -#: Zotlabs/Module/Locs.php:129 +#: Zotlabs/Module/Locs.php:139 msgid "" "When possible, drop a location by logging into that website/hub and removing " "your channel." msgstr "" -#: Zotlabs/Module/Locs.php:130 +#: Zotlabs/Module/Locs.php:140 msgid "Use this form to drop the location if the hub is no longer operating." msgstr "" -#: Zotlabs/Module/Viewsrc.php:45 +#: Zotlabs/Module/Viewsrc.php:48 msgid "item" msgstr "" -#: Zotlabs/Module/Viewsrc.php:55 +#: Zotlabs/Module/Viewsrc.php:58 msgid "inspect" msgstr "" -#: Zotlabs/Module/New_channel.php:166 +#: Zotlabs/Module/New_channel.php:173 msgid "Your real name is recommended." msgstr "" -#: Zotlabs/Module/New_channel.php:167 +#: Zotlabs/Module/New_channel.php:174 msgid "" "Examples: \"Bob Jameson\", \"Lisa and her Horses\", \"Soccer\", \"Aviation " "Group\"" msgstr "" -#: Zotlabs/Module/New_channel.php:172 +#: Zotlabs/Module/New_channel.php:179 msgid "" "This will be used to create a unique network address (like an email address)." msgstr "" -#: Zotlabs/Module/New_channel.php:174 +#: Zotlabs/Module/New_channel.php:181 msgid "Allowed characters are a-z 0-9, - and _" msgstr "" -#: Zotlabs/Module/New_channel.php:182 +#: Zotlabs/Module/New_channel.php:189 msgid "Channel name" msgstr "" -#: Zotlabs/Module/New_channel.php:185 +#: Zotlabs/Module/New_channel.php:192 msgid "" "Select a channel permission role compatible with your usage needs and " "privacy requirements." msgstr "" -#: Zotlabs/Module/New_channel.php:188 +#: Zotlabs/Module/New_channel.php:195 msgid "Create a Channel" msgstr "" -#: Zotlabs/Module/New_channel.php:189 +#: Zotlabs/Module/New_channel.php:196 msgid "" "A channel is a unique network identity. It can represent a person (social " "network profile), a forum (group), a business or celebrity page, a newsfeed, " "and many other things." msgstr "" -#: Zotlabs/Module/New_channel.php:190 +#: Zotlabs/Module/New_channel.php:197 msgid "" "or import an existing channel from another location." msgstr "" -#: Zotlabs/Module/New_channel.php:195 +#: Zotlabs/Module/New_channel.php:202 msgid "Validate" msgstr "" -#: Zotlabs/Module/Connect.php:54 Zotlabs/Module/Connect.php:102 +#: Zotlabs/Module/Connect.php:56 Zotlabs/Module/Connect.php:106 msgid "Continue" msgstr "" -#: Zotlabs/Module/Connect.php:83 +#: Zotlabs/Module/Connect.php:87 msgid "Premium Channel Setup" msgstr "" -#: Zotlabs/Module/Connect.php:85 +#: Zotlabs/Module/Connect.php:89 msgid "Enable premium channel connection restrictions" msgstr "" -#: Zotlabs/Module/Connect.php:86 +#: Zotlabs/Module/Connect.php:90 msgid "" "Please enter your restrictions or conditions, such as paypal receipt, usage " "guidelines, etc." msgstr "" -#: Zotlabs/Module/Connect.php:88 Zotlabs/Module/Connect.php:108 +#: Zotlabs/Module/Connect.php:92 Zotlabs/Module/Connect.php:112 msgid "" "This channel may require additional steps or acknowledgement of the " "following conditions prior to connecting:" msgstr "" -#: Zotlabs/Module/Connect.php:89 +#: Zotlabs/Module/Connect.php:93 msgid "" "Potential connections will then see the following text before proceeding:" msgstr "" -#: Zotlabs/Module/Connect.php:90 Zotlabs/Module/Connect.php:111 +#: Zotlabs/Module/Connect.php:94 Zotlabs/Module/Connect.php:115 msgid "" "By continuing, I certify that I have complied with any instructions provided " "on this page." msgstr "" -#: Zotlabs/Module/Connect.php:99 +#: Zotlabs/Module/Connect.php:102 msgid "(No specific instructions have been provided by the channel owner.)" msgstr "" -#: Zotlabs/Module/Connect.php:107 +#: Zotlabs/Module/Connect.php:111 msgid "Restricted or Premium Channel" msgstr "" -#: Zotlabs/Module/Notifications.php:60 Zotlabs/Module/Notify.php:70 +#: Zotlabs/Module/Notifications.php:67 Zotlabs/Module/Notify.php:73 msgid "No more system notifications." msgstr "" -#: Zotlabs/Module/Notifications.php:64 Zotlabs/Module/Notify.php:74 +#: Zotlabs/Module/Notifications.php:71 Zotlabs/Module/Notify.php:77 msgid "System Notifications" msgstr "" -#: Zotlabs/Module/Notify.php:38 +#: Zotlabs/Module/Notify.php:42 #, php-format msgid "A notification with that id was not found for channel '%s'" msgstr "" -#: Zotlabs/Module/Acl.php:277 +#: Zotlabs/Module/Acl.php:261 msgid "network" msgstr "" -#: Zotlabs/Module/Mood.php:75 include/conversation.php:291 +#: Zotlabs/Module/Mood.php:80 include/conversation.php:296 #, php-format msgctxt "mood" msgid "%1$s is %2$s" msgstr "" -#: Zotlabs/Module/Mood.php:134 Zotlabs/Module/Mood.php:154 +#: Zotlabs/Module/Mood.php:141 Zotlabs/Module/Mood.php:163 msgid "Set your current mood and tell your friends" msgstr "" -#: Zotlabs/Module/Vote.php:39 +#: Zotlabs/Module/Vote.php:41 msgid "Poll not found." msgstr "" -#: Zotlabs/Module/Vote.php:70 +#: Zotlabs/Module/Vote.php:72 msgid "Invalid response." msgstr "" -#: Zotlabs/Module/Vote.php:129 +#: Zotlabs/Module/Vote.php:131 msgid "Response submitted. Updates may not appear instantly." msgstr "" -#: Zotlabs/Module/Webpages.php:50 +#: Zotlabs/Module/Webpages.php:54 msgid "Provide managed web pages on your channel" msgstr "" -#: Zotlabs/Module/Webpages.php:70 +#: Zotlabs/Module/Webpages.php:74 msgid "Import Webpage Elements" msgstr "" -#: Zotlabs/Module/Webpages.php:71 +#: Zotlabs/Module/Webpages.php:75 msgid "Import selected" msgstr "" -#: Zotlabs/Module/Webpages.php:94 +#: Zotlabs/Module/Webpages.php:98 msgid "Export Webpage Elements" msgstr "" -#: Zotlabs/Module/Webpages.php:95 +#: Zotlabs/Module/Webpages.php:99 msgid "Export selected" msgstr "" -#: Zotlabs/Module/Webpages.php:264 +#: Zotlabs/Module/Webpages.php:273 msgid "Actions" msgstr "" -#: Zotlabs/Module/Webpages.php:265 +#: Zotlabs/Module/Webpages.php:274 msgid "Page Link" msgstr "" -#: Zotlabs/Module/Webpages.php:266 +#: Zotlabs/Module/Webpages.php:275 msgid "Page Title" msgstr "" -#: Zotlabs/Module/Webpages.php:296 +#: Zotlabs/Module/Webpages.php:305 msgid "Invalid file type." msgstr "" -#: Zotlabs/Module/Webpages.php:308 +#: Zotlabs/Module/Webpages.php:317 msgid "Error opening zip file" msgstr "" -#: Zotlabs/Module/Webpages.php:319 +#: Zotlabs/Module/Webpages.php:327 msgid "Invalid folder path." msgstr "" -#: Zotlabs/Module/Webpages.php:346 +#: Zotlabs/Module/Webpages.php:353 msgid "No webpage elements detected." msgstr "" -#: Zotlabs/Module/Webpages.php:421 Zotlabs/Import/Friendica.php:356 +#: Zotlabs/Module/Webpages.php:427 Zotlabs/Import/Friendica.php:360 msgid "Import complete." msgstr "" -#: Zotlabs/Module/Page.php:177 +#: Zotlabs/Module/Page.php:187 msgid "" "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod " "tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, " @@ -6781,737 +6790,737 @@ msgid "" "non proident, sunt in culpa qui officia deserunt mollit anim id est laborum." msgstr "" -#: Zotlabs/Module/Removeme.php:45 +#: Zotlabs/Module/Removeme.php:48 msgid "" "Channel removals are not allowed within 48 hours of changing the account " "password." msgstr "" -#: Zotlabs/Module/Removeme.php:67 +#: Zotlabs/Module/Removeme.php:71 msgid "Remove This Channel" msgstr "" -#: Zotlabs/Module/Removeme.php:68 +#: Zotlabs/Module/Removeme.php:72 msgid "This channel will be completely removed from this server. " msgstr "" -#: Zotlabs/Module/Removeme.php:68 Zotlabs/Module/Removeaccount.php:68 +#: Zotlabs/Module/Removeme.php:72 Zotlabs/Module/Removeaccount.php:72 msgid "This action is permanent and can not be undone!" msgstr "" -#: Zotlabs/Module/Removeme.php:70 Zotlabs/Module/Settings/Channel.php:711 +#: Zotlabs/Module/Removeme.php:74 Zotlabs/Module/Settings/Channel.php:744 msgid "Remove Channel" msgstr "" -#: Zotlabs/Module/Pdledit.php:21 +#: Zotlabs/Module/Pdledit.php:27 msgid "Layout updated." msgstr "" -#: Zotlabs/Module/Pdledit.php:42 Zotlabs/Module/Pdledit.php:97 +#: Zotlabs/Module/Pdledit.php:49 Zotlabs/Module/Pdledit.php:106 msgid "Edit System Page Description" msgstr "" -#: Zotlabs/Module/Pdledit.php:63 Zotlabs/Module/Pdledit.php:71 +#: Zotlabs/Module/Pdledit.php:71 Zotlabs/Module/Pdledit.php:79 msgid "(modified)" msgstr "" -#: Zotlabs/Module/Pdledit.php:92 +#: Zotlabs/Module/Pdledit.php:101 msgid "Layout not found." msgstr "" -#: Zotlabs/Module/Pdledit.php:98 +#: Zotlabs/Module/Pdledit.php:107 msgid "Module Name:" msgstr "" -#: Zotlabs/Module/Pdledit.php:99 +#: Zotlabs/Module/Pdledit.php:108 msgid "Layout Help" msgstr "" -#: Zotlabs/Module/Pdledit.php:100 +#: Zotlabs/Module/Pdledit.php:109 msgid "Edit another layout" msgstr "" -#: Zotlabs/Module/Pdledit.php:101 +#: Zotlabs/Module/Pdledit.php:110 msgid "System layout" msgstr "" -#: Zotlabs/Module/Dreport.php:33 +#: Zotlabs/Module/Dreport.php:35 msgid "Invalid message" msgstr "" -#: Zotlabs/Module/Dreport.php:59 +#: Zotlabs/Module/Dreport.php:63 msgid "Delivery Log" msgstr "" -#: Zotlabs/Module/Dreport.php:92 +#: Zotlabs/Module/Dreport.php:97 msgid "no results" msgstr "" -#: Zotlabs/Module/Dreport.php:105 +#: Zotlabs/Module/Dreport.php:109 msgid "channel sync processed" msgstr "" -#: Zotlabs/Module/Dreport.php:109 +#: Zotlabs/Module/Dreport.php:113 msgid "queued" msgstr "" -#: Zotlabs/Module/Dreport.php:113 +#: Zotlabs/Module/Dreport.php:117 msgid "site dead" msgstr "" -#: Zotlabs/Module/Dreport.php:117 +#: Zotlabs/Module/Dreport.php:121 msgid "site might be dead - deferred" msgstr "" -#: Zotlabs/Module/Dreport.php:121 +#: Zotlabs/Module/Dreport.php:125 msgid "posted" msgstr "" -#: Zotlabs/Module/Dreport.php:125 +#: Zotlabs/Module/Dreport.php:129 msgid "accepted for delivery" msgstr "" -#: Zotlabs/Module/Dreport.php:129 +#: Zotlabs/Module/Dreport.php:133 msgid "updated" msgstr "" -#: Zotlabs/Module/Dreport.php:132 +#: Zotlabs/Module/Dreport.php:136 msgid "update ignored" msgstr "" -#: Zotlabs/Module/Dreport.php:135 +#: Zotlabs/Module/Dreport.php:139 msgid "permission denied" msgstr "" -#: Zotlabs/Module/Dreport.php:139 +#: Zotlabs/Module/Dreport.php:143 msgid "recipient not found" msgstr "" -#: Zotlabs/Module/Dreport.php:142 +#: Zotlabs/Module/Dreport.php:146 msgid "mail recalled" msgstr "" -#: Zotlabs/Module/Dreport.php:145 +#: Zotlabs/Module/Dreport.php:149 msgid "duplicate mail received" msgstr "" -#: Zotlabs/Module/Dreport.php:148 +#: Zotlabs/Module/Dreport.php:152 msgid "mail delivered" msgstr "" -#: Zotlabs/Module/Dreport.php:152 +#: Zotlabs/Module/Dreport.php:156 msgid "delivery rejected" msgstr "" -#: Zotlabs/Module/Dreport.php:171 +#: Zotlabs/Module/Dreport.php:175 #, php-format msgid "Delivery report for %1$s" msgstr "" -#: Zotlabs/Module/Dreport.php:174 +#: Zotlabs/Module/Dreport.php:178 msgid "Options" msgstr "" -#: Zotlabs/Module/Dreport.php:175 +#: Zotlabs/Module/Dreport.php:179 msgid "Redeliver" msgstr "" -#: Zotlabs/Module/Profiles.php:28 Zotlabs/Module/Profiles.php:182 -#: Zotlabs/Module/Profiles.php:236 Zotlabs/Module/Profiles.php:663 +#: Zotlabs/Module/Profiles.php:32 Zotlabs/Module/Profiles.php:198 +#: Zotlabs/Module/Profiles.php:254 Zotlabs/Module/Profiles.php:701 msgid "Profile not found." msgstr "" -#: Zotlabs/Module/Profiles.php:47 +#: Zotlabs/Module/Profiles.php:53 msgid "Profile deleted." msgstr "" -#: Zotlabs/Module/Profiles.php:71 Zotlabs/Module/Profiles.php:108 +#: Zotlabs/Module/Profiles.php:76 Zotlabs/Module/Profiles.php:118 msgid "Profile-" msgstr "" -#: Zotlabs/Module/Profiles.php:93 Zotlabs/Module/Profiles.php:130 +#: Zotlabs/Module/Profiles.php:101 Zotlabs/Module/Profiles.php:142 msgid "New profile created." msgstr "" -#: Zotlabs/Module/Profiles.php:114 +#: Zotlabs/Module/Profiles.php:125 msgid "Profile unavailable to clone." msgstr "" -#: Zotlabs/Module/Profiles.php:149 +#: Zotlabs/Module/Profiles.php:162 msgid "Profile unavailable to export." msgstr "" -#: Zotlabs/Module/Profiles.php:247 +#: Zotlabs/Module/Profiles.php:265 msgid "Profile Name is required." msgstr "" -#: Zotlabs/Module/Profiles.php:456 +#: Zotlabs/Module/Profiles.php:484 msgid "Marital Status" msgstr "" -#: Zotlabs/Module/Profiles.php:460 +#: Zotlabs/Module/Profiles.php:488 msgid "Romantic Partner" msgstr "" -#: Zotlabs/Module/Profiles.php:464 Zotlabs/Module/Profiles.php:780 +#: Zotlabs/Module/Profiles.php:492 Zotlabs/Module/Profiles.php:821 msgid "Likes" msgstr "" -#: Zotlabs/Module/Profiles.php:468 Zotlabs/Module/Profiles.php:781 +#: Zotlabs/Module/Profiles.php:496 Zotlabs/Module/Profiles.php:822 msgid "Dislikes" msgstr "" -#: Zotlabs/Module/Profiles.php:472 Zotlabs/Module/Profiles.php:788 +#: Zotlabs/Module/Profiles.php:500 Zotlabs/Module/Profiles.php:829 msgid "Work/Employment" msgstr "" -#: Zotlabs/Module/Profiles.php:475 +#: Zotlabs/Module/Profiles.php:503 msgid "Religion" msgstr "" -#: Zotlabs/Module/Profiles.php:479 +#: Zotlabs/Module/Profiles.php:507 msgid "Political Views" msgstr "" -#: Zotlabs/Module/Profiles.php:483 +#: Zotlabs/Module/Profiles.php:511 msgid "Gender" msgstr "" -#: Zotlabs/Module/Profiles.php:487 +#: Zotlabs/Module/Profiles.php:515 msgid "Sexual Preference" msgstr "" -#: Zotlabs/Module/Profiles.php:491 +#: Zotlabs/Module/Profiles.php:519 msgid "Homepage" msgstr "" -#: Zotlabs/Module/Profiles.php:495 +#: Zotlabs/Module/Profiles.php:523 msgid "Interests" msgstr "" -#: Zotlabs/Module/Profiles.php:593 +#: Zotlabs/Module/Profiles.php:623 msgid "Profile updated." msgstr "" -#: Zotlabs/Module/Profiles.php:682 +#: Zotlabs/Module/Profiles.php:721 msgid "Hide your connections list from viewers of this profile" msgstr "" -#: Zotlabs/Module/Profiles.php:726 +#: Zotlabs/Module/Profiles.php:767 msgid "Edit Profile Details" msgstr "" -#: Zotlabs/Module/Profiles.php:728 +#: Zotlabs/Module/Profiles.php:769 msgid "View this profile" msgstr "" -#: Zotlabs/Module/Profiles.php:730 +#: Zotlabs/Module/Profiles.php:771 msgid "Profile Tools" msgstr "" -#: Zotlabs/Module/Profiles.php:731 +#: Zotlabs/Module/Profiles.php:772 msgid "Change cover photo" msgstr "" -#: Zotlabs/Module/Profiles.php:733 +#: Zotlabs/Module/Profiles.php:774 msgid "Create a new profile using these settings" msgstr "" -#: Zotlabs/Module/Profiles.php:734 +#: Zotlabs/Module/Profiles.php:775 msgid "Clone this profile" msgstr "" -#: Zotlabs/Module/Profiles.php:735 +#: Zotlabs/Module/Profiles.php:776 msgid "Delete this profile" msgstr "" -#: Zotlabs/Module/Profiles.php:736 +#: Zotlabs/Module/Profiles.php:777 msgid "Add profile things" msgstr "" -#: Zotlabs/Module/Profiles.php:737 +#: Zotlabs/Module/Profiles.php:778 msgid "Personal" msgstr "" -#: Zotlabs/Module/Profiles.php:739 +#: Zotlabs/Module/Profiles.php:780 msgid "Relationship" msgstr "" -#: Zotlabs/Module/Profiles.php:740 Zotlabs/Module/Settings/Channel.php:706 -#: Zotlabs/Widget/Newmember.php:49 include/datetime.php:58 +#: Zotlabs/Module/Profiles.php:781 Zotlabs/Module/Settings/Channel.php:739 +#: Zotlabs/Widget/Newmember.php:52 include/datetime.php:72 msgid "Miscellaneous" msgstr "" -#: Zotlabs/Module/Profiles.php:742 +#: Zotlabs/Module/Profiles.php:783 msgid "Import profile from file" msgstr "" -#: Zotlabs/Module/Profiles.php:743 +#: Zotlabs/Module/Profiles.php:784 msgid "Export profile to file" msgstr "" -#: Zotlabs/Module/Profiles.php:744 +#: Zotlabs/Module/Profiles.php:785 msgid "Your gender" msgstr "" -#: Zotlabs/Module/Profiles.php:745 +#: Zotlabs/Module/Profiles.php:786 msgid "Marital status" msgstr "" -#: Zotlabs/Module/Profiles.php:746 +#: Zotlabs/Module/Profiles.php:787 msgid "Sexual preference" msgstr "" -#: Zotlabs/Module/Profiles.php:747 +#: Zotlabs/Module/Profiles.php:788 msgid "Pronouns" msgstr "" -#: Zotlabs/Module/Profiles.php:750 +#: Zotlabs/Module/Profiles.php:791 msgid "Profile name" msgstr "" -#: Zotlabs/Module/Profiles.php:754 +#: Zotlabs/Module/Profiles.php:795 msgid "Your full name" msgstr "" -#: Zotlabs/Module/Profiles.php:755 +#: Zotlabs/Module/Profiles.php:796 msgid "Title/Description" msgstr "" -#: Zotlabs/Module/Profiles.php:758 +#: Zotlabs/Module/Profiles.php:799 msgid "Street address" msgstr "" -#: Zotlabs/Module/Profiles.php:759 +#: Zotlabs/Module/Profiles.php:800 msgid "Locality/City" msgstr "" -#: Zotlabs/Module/Profiles.php:760 +#: Zotlabs/Module/Profiles.php:801 msgid "Region/State" msgstr "" -#: Zotlabs/Module/Profiles.php:761 +#: Zotlabs/Module/Profiles.php:802 msgid "Postal/Zip code" msgstr "" -#: Zotlabs/Module/Profiles.php:768 +#: Zotlabs/Module/Profiles.php:809 msgid "Who (if applicable)" msgstr "" -#: Zotlabs/Module/Profiles.php:768 +#: Zotlabs/Module/Profiles.php:809 msgid "Examples: cathy123, Cathy Williams, cathy@example.com" msgstr "" -#: Zotlabs/Module/Profiles.php:769 +#: Zotlabs/Module/Profiles.php:810 msgid "Since (date)" msgstr "" -#: Zotlabs/Module/Profiles.php:774 +#: Zotlabs/Module/Profiles.php:815 msgid "Tell us about yourself" msgstr "" -#: Zotlabs/Module/Profiles.php:775 +#: Zotlabs/Module/Profiles.php:816 msgid "Homepage URL" msgstr "" -#: Zotlabs/Module/Profiles.php:776 +#: Zotlabs/Module/Profiles.php:817 msgid "Hometown" msgstr "" -#: Zotlabs/Module/Profiles.php:777 +#: Zotlabs/Module/Profiles.php:818 msgid "Political views" msgstr "" -#: Zotlabs/Module/Profiles.php:778 +#: Zotlabs/Module/Profiles.php:819 msgid "Religious views" msgstr "" -#: Zotlabs/Module/Profiles.php:779 +#: Zotlabs/Module/Profiles.php:820 msgid "Keywords used in directory listings" msgstr "" -#: Zotlabs/Module/Profiles.php:779 +#: Zotlabs/Module/Profiles.php:820 msgid "Example: fishing photography software" msgstr "" -#: Zotlabs/Module/Profiles.php:782 +#: Zotlabs/Module/Profiles.php:823 msgid "Musical interests" msgstr "" -#: Zotlabs/Module/Profiles.php:783 +#: Zotlabs/Module/Profiles.php:824 msgid "Books, literature" msgstr "" -#: Zotlabs/Module/Profiles.php:784 +#: Zotlabs/Module/Profiles.php:825 msgid "Television" msgstr "" -#: Zotlabs/Module/Profiles.php:785 +#: Zotlabs/Module/Profiles.php:826 msgid "Film/Dance/Culture/Entertainment" msgstr "" -#: Zotlabs/Module/Profiles.php:786 +#: Zotlabs/Module/Profiles.php:827 msgid "Hobbies/Interests" msgstr "" -#: Zotlabs/Module/Profiles.php:787 +#: Zotlabs/Module/Profiles.php:828 msgid "Love/Romance" msgstr "" -#: Zotlabs/Module/Profiles.php:789 +#: Zotlabs/Module/Profiles.php:830 msgid "School/Education" msgstr "" -#: Zotlabs/Module/Profiles.php:790 +#: Zotlabs/Module/Profiles.php:831 msgid "Contact information and social networks" msgstr "" -#: Zotlabs/Module/Profiles.php:791 +#: Zotlabs/Module/Profiles.php:832 msgid "My other channels" msgstr "" -#: Zotlabs/Module/Profiles.php:793 +#: Zotlabs/Module/Profiles.php:834 msgid "Communications" msgstr "" -#: Zotlabs/Module/Profiles.php:890 +#: Zotlabs/Module/Profiles.php:932 msgid " and " msgstr "" -#: Zotlabs/Module/Profiles.php:892 +#: Zotlabs/Module/Profiles.php:934 msgid ", " msgstr "" -#: Zotlabs/Module/Profiles.php:898 +#: Zotlabs/Module/Profiles.php:941 msgid "public profile" msgstr "" -#: Zotlabs/Module/Profiles.php:907 +#: Zotlabs/Module/Profiles.php:951 #, php-format msgid "%1$s changed %2$s to “%3$s”" msgstr "" -#: Zotlabs/Module/Profiles.php:908 +#: Zotlabs/Module/Profiles.php:952 #, php-format msgid "Visit %1$s's %2$s" msgstr "" -#: Zotlabs/Module/Profiles.php:911 +#: Zotlabs/Module/Profiles.php:954 #, php-format msgid "%1$s has an updated %2$s, changing %3$s." msgstr "" -#: Zotlabs/Module/Profiles.php:942 +#: Zotlabs/Module/Profiles.php:985 msgid "Currently Male" msgstr "" -#: Zotlabs/Module/Profiles.php:942 +#: Zotlabs/Module/Profiles.php:985 msgid "Currently Female" msgstr "" -#: Zotlabs/Module/Profiles.php:942 +#: Zotlabs/Module/Profiles.php:985 msgid "Mostly Male" msgstr "" -#: Zotlabs/Module/Profiles.php:942 +#: Zotlabs/Module/Profiles.php:985 msgid "Mostly Female" msgstr "" -#: Zotlabs/Module/Profiles.php:942 +#: Zotlabs/Module/Profiles.php:985 msgid "Transgender" msgstr "" -#: Zotlabs/Module/Profiles.php:942 +#: Zotlabs/Module/Profiles.php:985 msgid "Intersex" msgstr "" -#: Zotlabs/Module/Profiles.php:942 +#: Zotlabs/Module/Profiles.php:985 msgid "Transsexual" msgstr "" -#: Zotlabs/Module/Profiles.php:942 +#: Zotlabs/Module/Profiles.php:985 msgid "Hermaphrodite" msgstr "" -#: Zotlabs/Module/Profiles.php:942 +#: Zotlabs/Module/Profiles.php:985 msgid "Undecided" msgstr "" -#: Zotlabs/Module/Profiles.php:976 +#: Zotlabs/Module/Profiles.php:1021 msgid "He/Him" msgstr "" -#: Zotlabs/Module/Profiles.php:976 +#: Zotlabs/Module/Profiles.php:1021 msgid "She/Her" msgstr "" -#: Zotlabs/Module/Profiles.php:976 +#: Zotlabs/Module/Profiles.php:1021 msgid "They/Them" msgstr "" -#: Zotlabs/Module/Profiles.php:1008 Zotlabs/Module/Profiles.php:1027 +#: Zotlabs/Module/Profiles.php:1053 Zotlabs/Module/Profiles.php:1073 msgid "Males" msgstr "" -#: Zotlabs/Module/Profiles.php:1008 Zotlabs/Module/Profiles.php:1027 +#: Zotlabs/Module/Profiles.php:1053 Zotlabs/Module/Profiles.php:1073 msgid "Females" msgstr "" -#: Zotlabs/Module/Profiles.php:1008 +#: Zotlabs/Module/Profiles.php:1053 msgid "Gay" msgstr "" -#: Zotlabs/Module/Profiles.php:1008 +#: Zotlabs/Module/Profiles.php:1053 msgid "Lesbian" msgstr "" -#: Zotlabs/Module/Profiles.php:1008 +#: Zotlabs/Module/Profiles.php:1053 msgid "No Preference" msgstr "" -#: Zotlabs/Module/Profiles.php:1008 +#: Zotlabs/Module/Profiles.php:1053 msgid "Bisexual" msgstr "" -#: Zotlabs/Module/Profiles.php:1008 +#: Zotlabs/Module/Profiles.php:1053 msgid "Autosexual" msgstr "" -#: Zotlabs/Module/Profiles.php:1008 +#: Zotlabs/Module/Profiles.php:1053 msgid "Abstinent" msgstr "" -#: Zotlabs/Module/Profiles.php:1008 +#: Zotlabs/Module/Profiles.php:1053 msgid "Virgin" msgstr "" -#: Zotlabs/Module/Profiles.php:1008 +#: Zotlabs/Module/Profiles.php:1053 msgid "Deviant" msgstr "" -#: Zotlabs/Module/Profiles.php:1008 +#: Zotlabs/Module/Profiles.php:1053 msgid "Fetish" msgstr "" -#: Zotlabs/Module/Profiles.php:1008 +#: Zotlabs/Module/Profiles.php:1053 msgid "Oodles" msgstr "" -#: Zotlabs/Module/Profiles.php:1008 +#: Zotlabs/Module/Profiles.php:1053 msgid "Nonsexual" msgstr "" -#: Zotlabs/Module/Profiles.php:1046 Zotlabs/Module/Profiles.php:1063 +#: Zotlabs/Module/Profiles.php:1092 Zotlabs/Module/Profiles.php:1110 msgid "Single" msgstr "" -#: Zotlabs/Module/Profiles.php:1046 +#: Zotlabs/Module/Profiles.php:1092 msgid "Lonely" msgstr "" -#: Zotlabs/Module/Profiles.php:1046 +#: Zotlabs/Module/Profiles.php:1092 msgid "Available" msgstr "" -#: Zotlabs/Module/Profiles.php:1046 +#: Zotlabs/Module/Profiles.php:1092 msgid "Unavailable" msgstr "" -#: Zotlabs/Module/Profiles.php:1046 +#: Zotlabs/Module/Profiles.php:1092 msgid "Has crush" msgstr "" -#: Zotlabs/Module/Profiles.php:1046 +#: Zotlabs/Module/Profiles.php:1092 msgid "Infatuated" msgstr "" -#: Zotlabs/Module/Profiles.php:1046 Zotlabs/Module/Profiles.php:1063 +#: Zotlabs/Module/Profiles.php:1092 Zotlabs/Module/Profiles.php:1110 msgid "Dating" msgstr "" -#: Zotlabs/Module/Profiles.php:1046 +#: Zotlabs/Module/Profiles.php:1092 msgid "Unfaithful" msgstr "" -#: Zotlabs/Module/Profiles.php:1046 +#: Zotlabs/Module/Profiles.php:1092 msgid "Sex Addict" msgstr "" -#: Zotlabs/Module/Profiles.php:1046 +#: Zotlabs/Module/Profiles.php:1092 msgid "Friends/Benefits" msgstr "" -#: Zotlabs/Module/Profiles.php:1046 +#: Zotlabs/Module/Profiles.php:1092 msgid "Casual" msgstr "" -#: Zotlabs/Module/Profiles.php:1046 +#: Zotlabs/Module/Profiles.php:1092 msgid "Engaged" msgstr "" -#: Zotlabs/Module/Profiles.php:1046 Zotlabs/Module/Profiles.php:1063 +#: Zotlabs/Module/Profiles.php:1092 Zotlabs/Module/Profiles.php:1110 msgid "Married" msgstr "" -#: Zotlabs/Module/Profiles.php:1046 +#: Zotlabs/Module/Profiles.php:1092 msgid "Imaginarily married" msgstr "" -#: Zotlabs/Module/Profiles.php:1046 +#: Zotlabs/Module/Profiles.php:1092 msgid "Partners" msgstr "" -#: Zotlabs/Module/Profiles.php:1046 Zotlabs/Module/Profiles.php:1063 +#: Zotlabs/Module/Profiles.php:1092 Zotlabs/Module/Profiles.php:1110 msgid "Cohabiting" msgstr "" -#: Zotlabs/Module/Profiles.php:1046 +#: Zotlabs/Module/Profiles.php:1092 msgid "Common law" msgstr "" -#: Zotlabs/Module/Profiles.php:1046 +#: Zotlabs/Module/Profiles.php:1092 msgid "Happy" msgstr "" -#: Zotlabs/Module/Profiles.php:1046 +#: Zotlabs/Module/Profiles.php:1092 msgid "Not looking" msgstr "" -#: Zotlabs/Module/Profiles.php:1046 +#: Zotlabs/Module/Profiles.php:1092 msgid "Swinger" msgstr "" -#: Zotlabs/Module/Profiles.php:1046 +#: Zotlabs/Module/Profiles.php:1092 msgid "Betrayed" msgstr "" -#: Zotlabs/Module/Profiles.php:1046 Zotlabs/Module/Profiles.php:1063 +#: Zotlabs/Module/Profiles.php:1092 Zotlabs/Module/Profiles.php:1110 msgid "Separated" msgstr "" -#: Zotlabs/Module/Profiles.php:1046 +#: Zotlabs/Module/Profiles.php:1092 msgid "Unstable" msgstr "" -#: Zotlabs/Module/Profiles.php:1046 Zotlabs/Module/Profiles.php:1063 +#: Zotlabs/Module/Profiles.php:1092 Zotlabs/Module/Profiles.php:1110 msgid "Divorced" msgstr "" -#: Zotlabs/Module/Profiles.php:1046 +#: Zotlabs/Module/Profiles.php:1092 msgid "Imaginarily divorced" msgstr "" -#: Zotlabs/Module/Profiles.php:1046 Zotlabs/Module/Profiles.php:1063 +#: Zotlabs/Module/Profiles.php:1092 Zotlabs/Module/Profiles.php:1110 msgid "Widowed" msgstr "" -#: Zotlabs/Module/Profiles.php:1046 +#: Zotlabs/Module/Profiles.php:1092 msgid "Uncertain" msgstr "" -#: Zotlabs/Module/Profiles.php:1046 Zotlabs/Module/Profiles.php:1063 +#: Zotlabs/Module/Profiles.php:1092 Zotlabs/Module/Profiles.php:1110 msgid "It's complicated" msgstr "" -#: Zotlabs/Module/Profiles.php:1046 +#: Zotlabs/Module/Profiles.php:1092 msgid "Don't care" msgstr "" -#: Zotlabs/Module/Profiles.php:1046 +#: Zotlabs/Module/Profiles.php:1092 msgid "Ask me" msgstr "" -#: Zotlabs/Module/Stream.php:136 Zotlabs/Widget/Activity_filter.php:147 -#: include/network.php:1603 +#: Zotlabs/Module/Stream.php:139 Zotlabs/Widget/Activity_filter.php:149 +#: include/network.php:1693 msgid "Nomad" msgstr "" -#: Zotlabs/Module/Stream.php:140 Zotlabs/Widget/Activity_filter.php:150 -#: include/network.php:1599 +#: Zotlabs/Module/Stream.php:143 Zotlabs/Widget/Activity_filter.php:152 +#: include/network.php:1689 msgid "ActivityPub" msgstr "" -#: Zotlabs/Module/Stream.php:155 +#: Zotlabs/Module/Stream.php:158 msgid "Access list not found" msgstr "" -#: Zotlabs/Module/Stream.php:201 +#: Zotlabs/Module/Stream.php:202 msgid "No such channel" msgstr "" -#: Zotlabs/Module/Stream.php:290 +#: Zotlabs/Module/Stream.php:288 msgid "Access list is empty" msgstr "" -#: Zotlabs/Module/Stream.php:304 include/items.php:4399 +#: Zotlabs/Module/Stream.php:302 include/items.php:4552 #, php-format msgid "Access list: %s" msgstr "" -#: Zotlabs/Module/Stream.php:352 extend/addon/a/zotpost/Mod_zotpost.php:29 +#: Zotlabs/Module/Stream.php:345 extend/addon/a/zotpost/Mod_zotpost.php:29 msgid "Invalid channel." msgstr "" -#: Zotlabs/Module/Profile_photo.php:500 +#: Zotlabs/Module/Profile_photo.php:502 msgid "" "Your profile photo is visible to anybody on the internet and may be " "distributed to other websites." msgstr "" -#: Zotlabs/Module/Profile_photo.php:504 +#: Zotlabs/Module/Profile_photo.php:506 msgid "Use Photo for Profile" msgstr "" -#: Zotlabs/Module/Profile_photo.php:504 +#: Zotlabs/Module/Profile_photo.php:506 msgid "Change Profile Photo" msgstr "" -#: Zotlabs/Module/Poke.php:100 +#: Zotlabs/Module/Poke.php:104 #, php-format msgid "You %1$s %2$s" msgstr "" -#: Zotlabs/Module/Poke.php:116 +#: Zotlabs/Module/Poke.php:120 msgid "Poke App (Not Installed)" msgstr "" -#: Zotlabs/Module/Poke.php:117 +#: Zotlabs/Module/Poke.php:121 msgid "Poke or do something else to somebody" msgstr "" -#: Zotlabs/Module/Poke.php:136 +#: Zotlabs/Module/Poke.php:140 msgid "Poke, prod or do other things to somebody" msgstr "" -#: Zotlabs/Module/Poke.php:141 +#: Zotlabs/Module/Poke.php:145 msgid "Recipient" msgstr "" -#: Zotlabs/Module/Poke.php:142 +#: Zotlabs/Module/Poke.php:146 msgid "Choose your default action" msgstr "" -#: Zotlabs/Module/Profperm.php:38 Zotlabs/Module/Profperm.php:67 +#: Zotlabs/Module/Profperm.php:41 Zotlabs/Module/Profperm.php:75 msgid "Invalid profile identifier." msgstr "" -#: Zotlabs/Module/Profperm.php:115 +#: Zotlabs/Module/Profperm.php:129 msgid "Profile Visibility Editor" msgstr "" -#: Zotlabs/Module/Profperm.php:119 +#: Zotlabs/Module/Profperm.php:133 msgid "Click on a contact to add or remove." msgstr "" -#: Zotlabs/Module/Profperm.php:128 +#: Zotlabs/Module/Profperm.php:142 msgid "Visible To" msgstr "" -#: Zotlabs/Module/Affinity.php:26 +#: Zotlabs/Module/Affinity.php:31 msgid "Friend Zoom settings updated." msgstr "" -#: Zotlabs/Module/Affinity.php:37 +#: Zotlabs/Module/Affinity.php:41 msgid "" "This app (when installed) presents a slider control in your connection " "editor and also on your stream page. The slider represents your degree of " @@ -7519,911 +7528,911 @@ msgid "" "conversations from only your closest friends or everybody in your stream." msgstr "" -#: Zotlabs/Module/Affinity.php:45 +#: Zotlabs/Module/Affinity.php:49 msgid "" "The number below represents the default maximum slider position for your " "stream page as a percentage." msgstr "" -#: Zotlabs/Module/Affinity.php:71 +#: Zotlabs/Module/Affinity.php:74 msgid "Default friend zoom in/out" msgstr "" -#: Zotlabs/Module/Affinity.php:72 Zotlabs/Widget/Affinity.php:38 +#: Zotlabs/Module/Affinity.php:75 Zotlabs/Widget/Affinity.php:40 msgid "Refresh" msgstr "" -#: Zotlabs/Module/Affinity.php:83 +#: Zotlabs/Module/Affinity.php:86 msgid "Friend Zoom Settings" msgstr "" -#: Zotlabs/Module/Zot_probe.php:15 +#: Zotlabs/Module/Zot_probe.php:18 msgid "Zot6 Probe Diagnostic" msgstr "" -#: Zotlabs/Module/Zot_probe.php:16 Zotlabs/Module/Ap_probe.php:19 +#: Zotlabs/Module/Zot_probe.php:19 Zotlabs/Module/Ap_probe.php:21 msgid "Object URL" msgstr "" -#: Zotlabs/Module/Zot_probe.php:17 Zotlabs/Module/Ap_probe.php:20 +#: Zotlabs/Module/Zot_probe.php:20 Zotlabs/Module/Ap_probe.php:22 msgid "Authenticated fetch" msgstr "" -#: Zotlabs/Module/Home.php:159 +#: Zotlabs/Module/Home.php:161 #, php-format msgid "Welcome to %s" msgstr "" -#: Zotlabs/Module/Oexchange.php:25 +#: Zotlabs/Module/Oexchange.php:28 msgid "Unable to find your site." msgstr "" -#: Zotlabs/Module/Oexchange.php:39 +#: Zotlabs/Module/Oexchange.php:42 msgid "Post successful." msgstr "" -#: Zotlabs/Module/Regmod.php:15 +#: Zotlabs/Module/Regmod.php:19 msgid "Please login." msgstr "" -#: Zotlabs/Module/Removeaccount.php:46 +#: Zotlabs/Module/Removeaccount.php:49 msgid "" "Account removals are not allowed within 48 hours of changing the account " "password." msgstr "" -#: Zotlabs/Module/Removeaccount.php:67 +#: Zotlabs/Module/Removeaccount.php:71 msgid "Remove This Account" msgstr "" -#: Zotlabs/Module/Removeaccount.php:68 +#: Zotlabs/Module/Removeaccount.php:72 msgid "" "This account and all its channels will be completely removed from this " "server. " msgstr "" -#: Zotlabs/Module/Removeaccount.php:70 Zotlabs/Module/Settings/Account.php:106 +#: Zotlabs/Module/Removeaccount.php:74 Zotlabs/Module/Settings/Account.php:116 msgid "Remove Account" msgstr "" -#: Zotlabs/Module/Rmagic.php:39 +#: Zotlabs/Module/Rmagic.php:44 msgid "Authentication failed." msgstr "" -#: Zotlabs/Module/Rmagic.php:80 boot.php:1719 include/channel.php:2018 +#: Zotlabs/Module/Rmagic.php:84 boot.php:1727 include/channel.php:2142 msgid "Remote Authentication" msgstr "" -#: Zotlabs/Module/Rmagic.php:81 include/channel.php:2019 +#: Zotlabs/Module/Rmagic.php:85 include/channel.php:2143 msgid "Enter your channel address (e.g. channel@example.com)" msgstr "" -#: Zotlabs/Module/Rmagic.php:84 include/channel.php:2020 +#: Zotlabs/Module/Rmagic.php:88 include/channel.php:2144 msgid "Authenticate" msgstr "" -#: Zotlabs/Module/Ap_probe.php:18 +#: Zotlabs/Module/Ap_probe.php:20 msgid "ActivityPub Probe Diagnostic" msgstr "" -#: Zotlabs/Module/Tasks.php:102 +#: Zotlabs/Module/Tasks.php:106 msgid "This app provides a simple personal and task list." msgstr "" -#: Zotlabs/Module/Service_limits.php:23 +#: Zotlabs/Module/Service_limits.php:28 msgid "No service class restrictions found." msgstr "" -#: Zotlabs/Module/Settings/Featured.php:25 +#: Zotlabs/Module/Settings/Account.php:23 +msgid "Not valid email." +msgstr "" + +#: Zotlabs/Module/Settings/Account.php:27 +msgid "Protected email address. Cannot change to that email." +msgstr "" + +#: Zotlabs/Module/Settings/Account.php:37 +msgid "System failure storing new email. Please try again." +msgstr "" + +#: Zotlabs/Module/Settings/Account.php:55 +msgid "Password verification failed." +msgstr "" + +#: Zotlabs/Module/Settings/Account.php:62 +msgid "Passwords do not match. Password unchanged." +msgstr "" + +#: Zotlabs/Module/Settings/Account.php:66 +msgid "Empty passwords are not allowed. Password unchanged." +msgstr "" + +#: Zotlabs/Module/Settings/Account.php:81 +msgid "Password changed." +msgstr "" + +#: Zotlabs/Module/Settings/Account.php:83 +msgid "Password update failed. Please try again." +msgstr "" + +#: Zotlabs/Module/Settings/Account.php:110 +msgid "Account Settings" +msgstr "" + +#: Zotlabs/Module/Settings/Account.php:111 +msgid "Current Password" +msgstr "" + +#: Zotlabs/Module/Settings/Account.php:112 +msgid "Enter New Password" +msgstr "" + +#: Zotlabs/Module/Settings/Account.php:113 +msgid "Confirm New Password" +msgstr "" + +#: Zotlabs/Module/Settings/Account.php:113 +msgid "Leave password fields blank unless changing" +msgstr "" + +#: Zotlabs/Module/Settings/Account.php:115 +msgid "Email Address:" +msgstr "" + +#: Zotlabs/Module/Settings/Account.php:117 +msgid "Remove this account including all its channels" +msgstr "" + +#: Zotlabs/Module/Settings/Display.php:156 +#, php-format +msgid "%s - (Experimental)" +msgstr "" + +#: Zotlabs/Module/Settings/Display.php:199 +msgid "Display Settings" +msgstr "" + +#: Zotlabs/Module/Settings/Display.php:200 +msgid "Theme Settings" +msgstr "" + +#: Zotlabs/Module/Settings/Display.php:201 +msgid "Custom Theme Settings" +msgstr "" + +#: Zotlabs/Module/Settings/Display.php:202 +msgid "Content Settings" +msgstr "" + +#: Zotlabs/Module/Settings/Display.php:208 +msgid "Display Theme:" +msgstr "" + +#: Zotlabs/Module/Settings/Display.php:209 +msgid "Select scheme" +msgstr "" + +#: Zotlabs/Module/Settings/Display.php:211 +msgid "Preload images before rendering the page" +msgstr "" + +#: Zotlabs/Module/Settings/Display.php:211 +msgid "" +"The subjective page load time will be longer but the page will be ready when " +"displayed" +msgstr "" + +#: Zotlabs/Module/Settings/Display.php:212 +msgid "Enable user zoom on mobile devices" +msgstr "" + +#: Zotlabs/Module/Settings/Display.php:213 +msgid "Update notifications every xx seconds" +msgstr "" + +#: Zotlabs/Module/Settings/Display.php:213 +msgid "Minimum of 15 seconds, no maximum" +msgstr "" + +#: Zotlabs/Module/Settings/Display.php:214 +msgid "Maximum number of conversations to load at any time:" +msgstr "" + +#: Zotlabs/Module/Settings/Display.php:214 +msgid "Maximum of 100 items" +msgstr "" + +#: Zotlabs/Module/Settings/Display.php:215 +msgid "Show emoticons (smilies) as images" +msgstr "" + +#: Zotlabs/Module/Settings/Display.php:216 +msgid "Provide channel menu in navigation bar" +msgstr "" + +#: Zotlabs/Module/Settings/Display.php:216 +msgid "Default: channel menu located in app menu" +msgstr "" + +#: Zotlabs/Module/Settings/Display.php:217 +msgid "System Page Layout Editor - (advanced)" +msgstr "" + +#: Zotlabs/Module/Settings/Display.php:220 +msgid "Channel page max height of content (in pixels)" +msgstr "" + +#: Zotlabs/Module/Settings/Display.php:220 +#: Zotlabs/Module/Settings/Display.php:221 +msgid "click to expand content exceeding this height" +msgstr "" + +#: Zotlabs/Module/Settings/Display.php:221 +msgid "Stream page max height of content (in pixels)" +msgstr "" + +#: Zotlabs/Module/Settings/Display.php:222 +msgid "Indent threaded comments this many pixels from the parent" +msgstr "" + +#: Zotlabs/Module/Settings/Display.php:222 +msgid "0-20" +msgstr "" + +#: Zotlabs/Module/Settings/Featured.php:28 msgid "Affinity Slider settings updated." msgstr "" -#: Zotlabs/Module/Settings/Featured.php:40 +#: Zotlabs/Module/Settings/Featured.php:43 msgid "No feature settings configured" msgstr "" -#: Zotlabs/Module/Settings/Featured.php:47 +#: Zotlabs/Module/Settings/Featured.php:50 msgid "Default maximum affinity level" msgstr "" -#: Zotlabs/Module/Settings/Featured.php:47 +#: Zotlabs/Module/Settings/Featured.php:50 msgid "0-99 default 99" msgstr "" -#: Zotlabs/Module/Settings/Featured.php:52 +#: Zotlabs/Module/Settings/Featured.php:55 msgid "Default minimum affinity level" msgstr "" -#: Zotlabs/Module/Settings/Featured.php:52 +#: Zotlabs/Module/Settings/Featured.php:55 msgid "0-99 - default 0" msgstr "" -#: Zotlabs/Module/Settings/Featured.php:56 +#: Zotlabs/Module/Settings/Featured.php:59 msgid "Affinity Slider Settings" msgstr "" -#: Zotlabs/Module/Settings/Featured.php:69 +#: Zotlabs/Module/Settings/Featured.php:72 msgid "Addon Settings" msgstr "" -#: Zotlabs/Module/Settings/Featured.php:70 +#: Zotlabs/Module/Settings/Featured.php:73 msgid "Please save/submit changes to any panel before opening another." msgstr "" -#: Zotlabs/Module/Settings/Network.php:30 -#: Zotlabs/Module/Settings/Features.php:47 +#: Zotlabs/Module/Settings/Features.php:51 +#: Zotlabs/Module/Settings/Network.php:33 msgid "Off" msgstr "" -#: Zotlabs/Module/Settings/Network.php:30 -#: Zotlabs/Module/Settings/Features.php:47 +#: Zotlabs/Module/Settings/Features.php:51 +#: Zotlabs/Module/Settings/Network.php:33 msgid "On" msgstr "" -#: Zotlabs/Module/Settings/Network.php:38 +#: Zotlabs/Module/Settings/Features.php:59 +msgid "Additional Features" +msgstr "" + +#: Zotlabs/Module/Settings/Network.php:41 msgid "Activity Settings" msgstr "" -#: Zotlabs/Module/Settings/Network.php:52 include/features.php:375 +#: Zotlabs/Module/Settings/Network.php:56 include/features.php:383 msgid "Search by Date" msgstr "" -#: Zotlabs/Module/Settings/Network.php:53 include/features.php:376 +#: Zotlabs/Module/Settings/Network.php:57 include/features.php:384 msgid "Ability to select posts by date ranges" msgstr "" -#: Zotlabs/Module/Settings/Network.php:60 Zotlabs/Widget/Savedsearch.php:83 -#: include/features.php:385 +#: Zotlabs/Module/Settings/Network.php:64 Zotlabs/Widget/Savedsearch.php:94 +#: include/features.php:393 msgid "Saved Searches" msgstr "" -#: Zotlabs/Module/Settings/Network.php:61 include/features.php:386 +#: Zotlabs/Module/Settings/Network.php:65 include/features.php:394 msgid "Save search terms for re-use" msgstr "" -#: Zotlabs/Module/Settings/Network.php:68 include/features.php:394 +#: Zotlabs/Module/Settings/Network.php:72 include/features.php:402 msgid "Alternate Stream Order" msgstr "" -#: Zotlabs/Module/Settings/Network.php:69 include/features.php:395 +#: Zotlabs/Module/Settings/Network.php:73 include/features.php:403 msgid "" "Ability to order the stream by last post date, last comment date or " "unthreaded activities" msgstr "" -#: Zotlabs/Module/Settings/Network.php:76 include/features.php:403 +#: Zotlabs/Module/Settings/Network.php:80 include/features.php:411 msgid "Contact Filter" msgstr "" -#: Zotlabs/Module/Settings/Network.php:77 include/features.php:404 +#: Zotlabs/Module/Settings/Network.php:81 include/features.php:412 msgid "Ability to display only posts of a selected contact" msgstr "" -#: Zotlabs/Module/Settings/Network.php:84 include/features.php:412 +#: Zotlabs/Module/Settings/Network.php:88 include/features.php:420 msgid "Forum Filter" msgstr "" -#: Zotlabs/Module/Settings/Network.php:85 include/features.php:413 +#: Zotlabs/Module/Settings/Network.php:89 include/features.php:421 msgid "Ability to display only posts of a specific forum" msgstr "" -#: Zotlabs/Module/Settings/Network.php:92 include/features.php:421 +#: Zotlabs/Module/Settings/Network.php:96 include/features.php:429 msgid "Personal Posts Filter" msgstr "" -#: Zotlabs/Module/Settings/Network.php:93 include/features.php:422 +#: Zotlabs/Module/Settings/Network.php:97 include/features.php:430 msgid "Ability to display only posts that you've interacted on" msgstr "" -#: Zotlabs/Module/Settings/Network.php:100 include/features.php:430 +#: Zotlabs/Module/Settings/Network.php:104 include/features.php:438 msgid "Affinity Tool" msgstr "" -#: Zotlabs/Module/Settings/Network.php:101 include/features.php:431 +#: Zotlabs/Module/Settings/Network.php:105 include/features.php:439 msgid "Filter stream activity by depth of relationships" msgstr "" -#: Zotlabs/Module/Settings/Network.php:109 include/features.php:440 +#: Zotlabs/Module/Settings/Network.php:113 include/features.php:448 msgid "Show friend and connection suggestions" msgstr "" -#: Zotlabs/Module/Settings/Network.php:116 include/features.php:448 +#: Zotlabs/Module/Settings/Network.php:120 include/features.php:456 msgid "Connection Filtering" msgstr "" -#: Zotlabs/Module/Settings/Network.php:117 include/features.php:449 +#: Zotlabs/Module/Settings/Network.php:121 include/features.php:457 msgid "Filter incoming posts from connections based on keywords/content" msgstr "" -#: Zotlabs/Module/Settings/Oauth.php:35 +#: Zotlabs/Module/Settings/Oauth.php:37 msgid "Name is required" msgstr "" -#: Zotlabs/Module/Settings/Oauth.php:39 +#: Zotlabs/Module/Settings/Oauth.php:41 msgid "Key and Secret are required" msgstr "" -#: Zotlabs/Module/Settings/Oauth.php:87 Zotlabs/Module/Settings/Oauth.php:113 -#: Zotlabs/Module/Settings/Oauth.php:149 Zotlabs/Module/Settings/Oauth2.php:133 -#: Zotlabs/Module/Settings/Oauth2.php:195 +#: Zotlabs/Module/Settings/Oauth.php:94 Zotlabs/Module/Settings/Oauth.php:122 +#: Zotlabs/Module/Settings/Oauth.php:162 Zotlabs/Module/Settings/Oauth2.php:142 +#: Zotlabs/Module/Settings/Oauth2.php:210 msgid "Add application" msgstr "" -#: Zotlabs/Module/Settings/Oauth.php:90 Zotlabs/Module/Settings/Oauth2.php:107 -#: Zotlabs/Module/Settings/Oauth2.php:136 +#: Zotlabs/Module/Settings/Oauth.php:97 Zotlabs/Module/Settings/Oauth2.php:115 +#: Zotlabs/Module/Settings/Oauth2.php:145 msgid "Name of application" msgstr "" -#: Zotlabs/Module/Settings/Oauth.php:91 Zotlabs/Module/Settings/Oauth.php:117 +#: Zotlabs/Module/Settings/Oauth.php:98 Zotlabs/Module/Settings/Oauth.php:126 msgid "Consumer Key" msgstr "" -#: Zotlabs/Module/Settings/Oauth.php:91 Zotlabs/Module/Settings/Oauth.php:92 -#: Zotlabs/Module/Settings/Oauth2.php:108 -#: Zotlabs/Module/Settings/Oauth2.php:109 -#: Zotlabs/Module/Settings/Oauth2.php:137 -#: Zotlabs/Module/Settings/Oauth2.php:138 +#: Zotlabs/Module/Settings/Oauth.php:98 Zotlabs/Module/Settings/Oauth.php:99 +#: Zotlabs/Module/Settings/Oauth2.php:116 +#: Zotlabs/Module/Settings/Oauth2.php:117 +#: Zotlabs/Module/Settings/Oauth2.php:146 +#: Zotlabs/Module/Settings/Oauth2.php:147 msgid "Automatically generated - change if desired. Max length 20" msgstr "" -#: Zotlabs/Module/Settings/Oauth.php:92 Zotlabs/Module/Settings/Oauth.php:118 -#: Zotlabs/Module/Settings/Oauth2.php:109 -#: Zotlabs/Module/Settings/Oauth2.php:138 +#: Zotlabs/Module/Settings/Oauth.php:99 Zotlabs/Module/Settings/Oauth.php:127 +#: Zotlabs/Module/Settings/Oauth2.php:117 +#: Zotlabs/Module/Settings/Oauth2.php:147 msgid "Consumer Secret" msgstr "" -#: Zotlabs/Module/Settings/Oauth.php:93 Zotlabs/Module/Settings/Oauth.php:119 -#: Zotlabs/Module/Settings/Oauth2.php:110 -#: Zotlabs/Module/Settings/Oauth2.php:139 +#: Zotlabs/Module/Settings/Oauth.php:100 Zotlabs/Module/Settings/Oauth.php:128 +#: Zotlabs/Module/Settings/Oauth2.php:118 +#: Zotlabs/Module/Settings/Oauth2.php:148 msgid "Redirect" msgstr "" -#: Zotlabs/Module/Settings/Oauth.php:93 Zotlabs/Module/Settings/Oauth2.php:110 -#: Zotlabs/Module/Settings/Oauth2.php:139 +#: Zotlabs/Module/Settings/Oauth.php:100 Zotlabs/Module/Settings/Oauth2.php:118 +#: Zotlabs/Module/Settings/Oauth2.php:148 msgid "" "Redirect URI - leave blank unless your application specifically requires this" msgstr "" -#: Zotlabs/Module/Settings/Oauth.php:94 Zotlabs/Module/Settings/Oauth.php:120 +#: Zotlabs/Module/Settings/Oauth.php:101 Zotlabs/Module/Settings/Oauth.php:129 msgid "Icon url" msgstr "" -#: Zotlabs/Module/Settings/Oauth.php:94 Zotlabs/Module/Sources.php:121 -#: Zotlabs/Module/Sources.php:156 +#: Zotlabs/Module/Settings/Oauth.php:101 Zotlabs/Module/Sources.php:128 +#: Zotlabs/Module/Sources.php:164 msgid "Optional" msgstr "" -#: Zotlabs/Module/Settings/Oauth.php:105 +#: Zotlabs/Module/Settings/Oauth.php:114 msgid "Application not found." msgstr "" -#: Zotlabs/Module/Settings/Oauth.php:148 +#: Zotlabs/Module/Settings/Oauth.php:161 msgid "Connected Apps" msgstr "" -#: Zotlabs/Module/Settings/Oauth.php:152 Zotlabs/Module/Settings/Oauth2.php:198 +#: Zotlabs/Module/Settings/Oauth.php:165 Zotlabs/Module/Settings/Oauth2.php:213 msgid "Client key starts with" msgstr "" -#: Zotlabs/Module/Settings/Oauth.php:153 Zotlabs/Module/Settings/Oauth2.php:199 +#: Zotlabs/Module/Settings/Oauth.php:166 Zotlabs/Module/Settings/Oauth2.php:214 msgid "No name" msgstr "" -#: Zotlabs/Module/Settings/Oauth.php:154 Zotlabs/Module/Settings/Oauth2.php:200 +#: Zotlabs/Module/Settings/Oauth.php:167 Zotlabs/Module/Settings/Oauth2.php:215 msgid "Remove authorization" msgstr "" -#: Zotlabs/Module/Settings/Permcats.php:24 +#: Zotlabs/Module/Settings/Oauth2.php:51 +msgid "ID and Secret are required" +msgstr "" + +#: Zotlabs/Module/Settings/Oauth2.php:112 +msgid "Add OAuth2 application" +msgstr "" + +#: Zotlabs/Module/Settings/Oauth2.php:116 +#: Zotlabs/Module/Settings/Oauth2.php:146 +msgid "Consumer ID" +msgstr "" + +#: Zotlabs/Module/Settings/Oauth2.php:119 +#: Zotlabs/Module/Settings/Oauth2.php:149 +msgid "Grant Types" +msgstr "" + +#: Zotlabs/Module/Settings/Oauth2.php:119 +#: Zotlabs/Module/Settings/Oauth2.php:120 +#: Zotlabs/Module/Settings/Oauth2.php:149 +#: Zotlabs/Module/Settings/Oauth2.php:150 +msgid "leave blank unless your application specifically requires this" +msgstr "" + +#: Zotlabs/Module/Settings/Oauth2.php:120 +#: Zotlabs/Module/Settings/Oauth2.php:150 +msgid "Authorization scope" +msgstr "" + +#: Zotlabs/Module/Settings/Oauth2.php:133 +msgid "OAuth2 Application not found." +msgstr "" + +#: Zotlabs/Module/Settings/Oauth2.php:209 +msgid "Connected OAuth2 Apps" +msgstr "" + +#: Zotlabs/Module/Settings/Permcats.php:30 msgid "Permission Name is required." msgstr "" -#: Zotlabs/Module/Settings/Permcats.php:43 +#: Zotlabs/Module/Settings/Permcats.php:49 msgid "Permission category saved." msgstr "" -#: Zotlabs/Module/Settings/Permcats.php:67 +#: Zotlabs/Module/Settings/Permcats.php:76 msgid "" "Use this form to create permission rules for various classes of people or " "connections." msgstr "" -#: Zotlabs/Module/Settings/Permcats.php:108 +#: Zotlabs/Module/Settings/Permcats.php:119 msgid "Permission Name" msgstr "" -#: Zotlabs/Module/Settings/Oauth2.php:48 -msgid "ID and Secret are required" -msgstr "" - -#: Zotlabs/Module/Settings/Oauth2.php:104 -msgid "Add OAuth2 application" -msgstr "" - -#: Zotlabs/Module/Settings/Oauth2.php:108 -#: Zotlabs/Module/Settings/Oauth2.php:137 -msgid "Consumer ID" -msgstr "" - -#: Zotlabs/Module/Settings/Oauth2.php:111 -#: Zotlabs/Module/Settings/Oauth2.php:140 -msgid "Grant Types" -msgstr "" - -#: Zotlabs/Module/Settings/Oauth2.php:111 -#: Zotlabs/Module/Settings/Oauth2.php:112 -#: Zotlabs/Module/Settings/Oauth2.php:140 -#: Zotlabs/Module/Settings/Oauth2.php:141 -msgid "leave blank unless your application specifically requires this" -msgstr "" - -#: Zotlabs/Module/Settings/Oauth2.php:112 -#: Zotlabs/Module/Settings/Oauth2.php:141 -msgid "Authorization scope" -msgstr "" - -#: Zotlabs/Module/Settings/Oauth2.php:124 -msgid "OAuth2 Application not found." -msgstr "" - -#: Zotlabs/Module/Settings/Oauth2.php:194 -msgid "Connected OAuth2 Apps" -msgstr "" - -#: Zotlabs/Module/Settings/Account.php:19 -msgid "Not valid email." -msgstr "" - -#: Zotlabs/Module/Settings/Account.php:22 -msgid "Protected email address. Cannot change to that email." -msgstr "" - -#: Zotlabs/Module/Settings/Account.php:31 -msgid "System failure storing new email. Please try again." -msgstr "" - -#: Zotlabs/Module/Settings/Account.php:48 -msgid "Password verification failed." -msgstr "" - -#: Zotlabs/Module/Settings/Account.php:55 -msgid "Passwords do not match. Password unchanged." -msgstr "" - -#: Zotlabs/Module/Settings/Account.php:59 -msgid "Empty passwords are not allowed. Password unchanged." -msgstr "" - -#: Zotlabs/Module/Settings/Account.php:73 -msgid "Password changed." -msgstr "" - -#: Zotlabs/Module/Settings/Account.php:75 -msgid "Password update failed. Please try again." -msgstr "" - -#: Zotlabs/Module/Settings/Account.php:100 -msgid "Account Settings" -msgstr "" - -#: Zotlabs/Module/Settings/Account.php:101 -msgid "Current Password" -msgstr "" - -#: Zotlabs/Module/Settings/Account.php:102 -msgid "Enter New Password" -msgstr "" - -#: Zotlabs/Module/Settings/Account.php:103 -msgid "Confirm New Password" -msgstr "" - -#: Zotlabs/Module/Settings/Account.php:103 -msgid "Leave password fields blank unless changing" -msgstr "" - -#: Zotlabs/Module/Settings/Account.php:105 -msgid "Email Address:" -msgstr "" - -#: Zotlabs/Module/Settings/Account.php:107 -msgid "Remove this account including all its channels" -msgstr "" - -#: Zotlabs/Module/Settings/Features.php:55 -msgid "Additional Features" -msgstr "" - -#: Zotlabs/Module/Settings/Tokens.php:40 +#: Zotlabs/Module/Settings/Tokens.php:45 #, php-format msgid "This channel is limited to %d tokens" msgstr "" -#: Zotlabs/Module/Settings/Tokens.php:46 +#: Zotlabs/Module/Settings/Tokens.php:51 msgid "Name and Password are required." msgstr "" -#: Zotlabs/Module/Settings/Tokens.php:177 +#: Zotlabs/Module/Settings/Tokens.php:187 msgid "Token saved." msgstr "" -#: Zotlabs/Module/Settings/Tokens.php:242 +#: Zotlabs/Module/Settings/Tokens.php:258 msgid "" "Use this form to create temporary access identifiers to share things with " "non-members. These identities may be used in Access Control Lists and " "visitors may login using these credentials to access private content." msgstr "" -#: Zotlabs/Module/Settings/Tokens.php:244 +#: Zotlabs/Module/Settings/Tokens.php:260 msgid "" "You may also provide dropbox style access links to friends and " "associates by adding the Login Password to any specific site URL as shown. " "Examples:" msgstr "" -#: Zotlabs/Module/Settings/Tokens.php:277 +#: Zotlabs/Module/Settings/Tokens.php:295 msgid "Guest Access Tokens" msgstr "" -#: Zotlabs/Module/Settings/Tokens.php:285 +#: Zotlabs/Module/Settings/Tokens.php:303 msgid "Login Name" msgstr "" -#: Zotlabs/Module/Settings/Tokens.php:286 +#: Zotlabs/Module/Settings/Tokens.php:304 msgid "Login Password" msgstr "" -#: Zotlabs/Module/Settings/Tokens.php:287 +#: Zotlabs/Module/Settings/Tokens.php:305 msgid "Expires (yyyy-mm-dd)" msgstr "" -#: Zotlabs/Module/Settings/Display.php:142 -#, php-format -msgid "%s - (Experimental)" -msgstr "" - -#: Zotlabs/Module/Settings/Display.php:185 -msgid "Display Settings" -msgstr "" - -#: Zotlabs/Module/Settings/Display.php:186 -msgid "Theme Settings" -msgstr "" - -#: Zotlabs/Module/Settings/Display.php:187 -msgid "Custom Theme Settings" -msgstr "" - -#: Zotlabs/Module/Settings/Display.php:188 -msgid "Content Settings" -msgstr "" - -#: Zotlabs/Module/Settings/Display.php:194 -msgid "Display Theme:" -msgstr "" - -#: Zotlabs/Module/Settings/Display.php:195 -msgid "Select scheme" -msgstr "" - -#: Zotlabs/Module/Settings/Display.php:197 -msgid "Preload images before rendering the page" -msgstr "" - -#: Zotlabs/Module/Settings/Display.php:197 -msgid "" -"The subjective page load time will be longer but the page will be ready when " -"displayed" -msgstr "" - -#: Zotlabs/Module/Settings/Display.php:198 -msgid "Enable user zoom on mobile devices" -msgstr "" - -#: Zotlabs/Module/Settings/Display.php:199 -msgid "Update notifications every xx seconds" -msgstr "" - -#: Zotlabs/Module/Settings/Display.php:199 -msgid "Minimum of 15 seconds, no maximum" -msgstr "" - -#: Zotlabs/Module/Settings/Display.php:200 -msgid "Maximum number of conversations to load at any time:" -msgstr "" - -#: Zotlabs/Module/Settings/Display.php:200 -msgid "Maximum of 100 items" -msgstr "" - -#: Zotlabs/Module/Settings/Display.php:201 -msgid "Show emoticons (smilies) as images" -msgstr "" - -#: Zotlabs/Module/Settings/Display.php:202 -msgid "Provide channel menu in navigation bar" -msgstr "" - -#: Zotlabs/Module/Settings/Display.php:202 -msgid "Default: channel menu located in app menu" -msgstr "" - -#: Zotlabs/Module/Settings/Display.php:203 -msgid "System Page Layout Editor - (advanced)" -msgstr "" - -#: Zotlabs/Module/Settings/Display.php:206 -msgid "Channel page max height of content (in pixels)" -msgstr "" - -#: Zotlabs/Module/Settings/Display.php:206 -#: Zotlabs/Module/Settings/Display.php:207 -msgid "click to expand content exceeding this height" -msgstr "" - -#: Zotlabs/Module/Settings/Display.php:207 -msgid "Stream page max height of content (in pixels)" -msgstr "" - -#: Zotlabs/Module/Settings/Display.php:208 -msgid "Indent threaded comments this many pixels from the parent" -msgstr "" - -#: Zotlabs/Module/Settings/Display.php:208 -msgid "0-20" -msgstr "" - -#: Zotlabs/Module/Settings/Channel.php:397 include/conversation.php:1331 +#: Zotlabs/Module/Settings/Channel.php:433 include/conversation.php:1355 msgid "Restricted - from connections only" msgstr "" -#: Zotlabs/Module/Settings/Channel.php:398 include/conversation.php:1332 +#: Zotlabs/Module/Settings/Channel.php:434 include/conversation.php:1356 msgid "Semi-public - from anybody that can be identified" msgstr "" -#: Zotlabs/Module/Settings/Channel.php:399 include/conversation.php:1333 +#: Zotlabs/Module/Settings/Channel.php:435 include/conversation.php:1357 msgid "Public - from anybody on the internet" msgstr "" -#: Zotlabs/Module/Settings/Channel.php:479 +#: Zotlabs/Module/Settings/Channel.php:513 msgid "Publish your profile in the network directory" msgstr "" -#: Zotlabs/Module/Settings/Channel.php:484 +#: Zotlabs/Module/Settings/Channel.php:518 msgid "Allow us to suggest you as a potential friend to new members?" msgstr "" -#: Zotlabs/Module/Settings/Channel.php:488 -#: Zotlabs/Module/Settings/Channel.php:502 +#: Zotlabs/Module/Settings/Channel.php:522 +#: Zotlabs/Module/Settings/Channel.php:536 msgid "or" msgstr "" -#: Zotlabs/Module/Settings/Channel.php:494 +#: Zotlabs/Module/Settings/Channel.php:528 msgid "Your channel address is" msgstr "" -#: Zotlabs/Module/Settings/Channel.php:496 +#: Zotlabs/Module/Settings/Channel.php:530 msgid "" "Friends using compatible applications can use this address to connect with " "you." msgstr "" -#: Zotlabs/Module/Settings/Channel.php:498 +#: Zotlabs/Module/Settings/Channel.php:532 msgid "Your files/photos are accessible as a network drive at" msgstr "" -#: Zotlabs/Module/Settings/Channel.php:500 +#: Zotlabs/Module/Settings/Channel.php:534 msgid "(Windows)" msgstr "" -#: Zotlabs/Module/Settings/Channel.php:501 +#: Zotlabs/Module/Settings/Channel.php:535 msgid "(other platforms)" msgstr "" -#: Zotlabs/Module/Settings/Channel.php:547 +#: Zotlabs/Module/Settings/Channel.php:583 msgid "Automatic membership approval" msgstr "" -#: Zotlabs/Module/Settings/Channel.php:553 +#: Zotlabs/Module/Settings/Channel.php:588 msgid "Friend-of-friend conversations" msgstr "" -#: Zotlabs/Module/Settings/Channel.php:553 +#: Zotlabs/Module/Settings/Channel.php:588 msgid "" "Import public third-party conversations in which your connections " "participate." msgstr "" -#: Zotlabs/Module/Settings/Channel.php:557 +#: Zotlabs/Module/Settings/Channel.php:592 msgid "Enable ActivityPub protocol" msgstr "" -#: Zotlabs/Module/Settings/Channel.php:557 +#: Zotlabs/Module/Settings/Channel.php:592 msgid "ActivityPub is an emerging internet standard for social communications" msgstr "" -#: Zotlabs/Module/Settings/Channel.php:592 +#: Zotlabs/Module/Settings/Channel.php:625 msgid "Channel Settings" msgstr "" -#: Zotlabs/Module/Settings/Channel.php:598 +#: Zotlabs/Module/Settings/Channel.php:631 msgid "Basic Settings" msgstr "" -#: Zotlabs/Module/Settings/Channel.php:599 +#: Zotlabs/Module/Settings/Channel.php:632 msgid "Full name" msgstr "" -#: Zotlabs/Module/Settings/Channel.php:601 +#: Zotlabs/Module/Settings/Channel.php:634 msgid "Your timezone" msgstr "" -#: Zotlabs/Module/Settings/Channel.php:601 +#: Zotlabs/Module/Settings/Channel.php:634 msgid "This is important for showing the correct time on shared events" msgstr "" -#: Zotlabs/Module/Settings/Channel.php:602 +#: Zotlabs/Module/Settings/Channel.php:635 msgid "Default post location" msgstr "" -#: Zotlabs/Module/Settings/Channel.php:602 +#: Zotlabs/Module/Settings/Channel.php:635 msgid "Optional geographical location to display on your posts" msgstr "" -#: Zotlabs/Module/Settings/Channel.php:603 +#: Zotlabs/Module/Settings/Channel.php:636 msgid "Obtain post location from your web browser or device" msgstr "" -#: Zotlabs/Module/Settings/Channel.php:605 +#: Zotlabs/Module/Settings/Channel.php:638 msgid "Adult content" msgstr "" -#: Zotlabs/Module/Settings/Channel.php:605 +#: Zotlabs/Module/Settings/Channel.php:638 msgid "" "Enable to indicate if this channel frequently or regularly publishes adult " "content. (Please also tag any adult material and/or nudity with #NSFW)" msgstr "" -#: Zotlabs/Module/Settings/Channel.php:607 +#: Zotlabs/Module/Settings/Channel.php:640 msgid "Security and Privacy" msgstr "" -#: Zotlabs/Module/Settings/Channel.php:609 +#: Zotlabs/Module/Settings/Channel.php:642 msgid "Your permissions are already configured. Click to view/adjust" msgstr "" -#: Zotlabs/Module/Settings/Channel.php:611 +#: Zotlabs/Module/Settings/Channel.php:644 msgid "Hide my online presence" msgstr "" -#: Zotlabs/Module/Settings/Channel.php:611 +#: Zotlabs/Module/Settings/Channel.php:644 msgid "Prevents displaying in your profile that you are online" msgstr "" -#: Zotlabs/Module/Settings/Channel.php:612 +#: Zotlabs/Module/Settings/Channel.php:645 msgid "Allow others to view your friends and connections" msgstr "" -#: Zotlabs/Module/Settings/Channel.php:616 +#: Zotlabs/Module/Settings/Channel.php:649 msgid "Forbid indexing of your channel content by search engines" msgstr "" -#: Zotlabs/Module/Settings/Channel.php:617 +#: Zotlabs/Module/Settings/Channel.php:650 msgid "Disable acceptance of comments on my posts after this many days" msgstr "" -#: Zotlabs/Module/Settings/Channel.php:617 +#: Zotlabs/Module/Settings/Channel.php:650 msgid "Leave unset or enter 0 to allow comments indefinitely" msgstr "" -#: Zotlabs/Module/Settings/Channel.php:618 +#: Zotlabs/Module/Settings/Channel.php:651 msgid "Allow others to tag your posts" msgstr "" -#: Zotlabs/Module/Settings/Channel.php:618 +#: Zotlabs/Module/Settings/Channel.php:651 msgid "" "Often used by the community to retro-actively flag inappropriate content" msgstr "" -#: Zotlabs/Module/Settings/Channel.php:620 +#: Zotlabs/Module/Settings/Channel.php:653 msgid "Channel Permission Limits" msgstr "" -#: Zotlabs/Module/Settings/Channel.php:622 +#: Zotlabs/Module/Settings/Channel.php:655 msgid "Expire conversations you have not participated in after this many days" msgstr "" -#: Zotlabs/Module/Settings/Channel.php:622 +#: Zotlabs/Module/Settings/Channel.php:655 msgid "0 or blank to use the website limit." msgstr "" -#: Zotlabs/Module/Settings/Channel.php:622 +#: Zotlabs/Module/Settings/Channel.php:655 #, php-format msgid "This website expires after %d days." msgstr "" -#: Zotlabs/Module/Settings/Channel.php:622 +#: Zotlabs/Module/Settings/Channel.php:655 msgid "This website does not provide an expiration policy." msgstr "" -#: Zotlabs/Module/Settings/Channel.php:622 +#: Zotlabs/Module/Settings/Channel.php:655 msgid "The website limit takes precedence if lower than your limit." msgstr "" -#: Zotlabs/Module/Settings/Channel.php:623 +#: Zotlabs/Module/Settings/Channel.php:656 msgid "Maximum Friend Requests/Day:" msgstr "" -#: Zotlabs/Module/Settings/Channel.php:623 +#: Zotlabs/Module/Settings/Channel.php:656 msgid "May reduce spam activity" msgstr "" -#: Zotlabs/Module/Settings/Channel.php:624 +#: Zotlabs/Module/Settings/Channel.php:657 msgid "Default Access List" msgstr "" -#: Zotlabs/Module/Settings/Channel.php:626 +#: Zotlabs/Module/Settings/Channel.php:659 msgid "Use my default audience setting for the type of object published" msgstr "" -#: Zotlabs/Module/Settings/Channel.php:627 +#: Zotlabs/Module/Settings/Channel.php:660 msgid "Profile to assign new connections" msgstr "" -#: Zotlabs/Module/Settings/Channel.php:638 +#: Zotlabs/Module/Settings/Channel.php:671 msgid "Channel type and privacy" msgstr "" -#: Zotlabs/Module/Settings/Channel.php:639 +#: Zotlabs/Module/Settings/Channel.php:672 msgid "Default Permissions Group" msgstr "" -#: Zotlabs/Module/Settings/Channel.php:645 +#: Zotlabs/Module/Settings/Channel.php:678 msgid "Maximum direct messages per day from unknown people:" msgstr "" -#: Zotlabs/Module/Settings/Channel.php:645 +#: Zotlabs/Module/Settings/Channel.php:678 msgid "" "Useful to reduce spamming if you allow direct messages from unknown people" msgstr "" -#: Zotlabs/Module/Settings/Channel.php:654 +#: Zotlabs/Module/Settings/Channel.php:687 msgid "By default post a status message when:" msgstr "" -#: Zotlabs/Module/Settings/Channel.php:655 +#: Zotlabs/Module/Settings/Channel.php:688 msgid "accepting a friend request" msgstr "" -#: Zotlabs/Module/Settings/Channel.php:656 +#: Zotlabs/Module/Settings/Channel.php:689 msgid "joining a forum/community" msgstr "" -#: Zotlabs/Module/Settings/Channel.php:657 +#: Zotlabs/Module/Settings/Channel.php:690 msgid "making an interesting profile change" msgstr "" -#: Zotlabs/Module/Settings/Channel.php:658 +#: Zotlabs/Module/Settings/Channel.php:691 msgid "Send a notification email when:" msgstr "" -#: Zotlabs/Module/Settings/Channel.php:659 +#: Zotlabs/Module/Settings/Channel.php:692 msgid "You receive a connection request" msgstr "" -#: Zotlabs/Module/Settings/Channel.php:661 +#: Zotlabs/Module/Settings/Channel.php:694 msgid "Someone writes on your profile wall" msgstr "" -#: Zotlabs/Module/Settings/Channel.php:662 +#: Zotlabs/Module/Settings/Channel.php:695 msgid "Someone writes a followup comment" msgstr "" -#: Zotlabs/Module/Settings/Channel.php:663 +#: Zotlabs/Module/Settings/Channel.php:696 msgid "Someone shares a followed conversation" msgstr "" -#: Zotlabs/Module/Settings/Channel.php:664 +#: Zotlabs/Module/Settings/Channel.php:697 msgid "You receive a direct (private) message" msgstr "" -#: Zotlabs/Module/Settings/Channel.php:666 +#: Zotlabs/Module/Settings/Channel.php:699 msgid "You are tagged in a post" msgstr "" -#: Zotlabs/Module/Settings/Channel.php:669 +#: Zotlabs/Module/Settings/Channel.php:702 msgid "Someone likes your post/comment" msgstr "" -#: Zotlabs/Module/Settings/Channel.php:672 +#: Zotlabs/Module/Settings/Channel.php:705 msgid "Show visual notifications including:" msgstr "" -#: Zotlabs/Module/Settings/Channel.php:674 +#: Zotlabs/Module/Settings/Channel.php:707 msgid "Unseen stream activity" msgstr "" -#: Zotlabs/Module/Settings/Channel.php:675 +#: Zotlabs/Module/Settings/Channel.php:708 msgid "Unseen channel activity" msgstr "" -#: Zotlabs/Module/Settings/Channel.php:676 +#: Zotlabs/Module/Settings/Channel.php:709 msgid "Unseen direct messages" msgstr "" -#: Zotlabs/Module/Settings/Channel.php:676 -#: Zotlabs/Module/Settings/Channel.php:681 -#: Zotlabs/Module/Settings/Channel.php:682 -#: Zotlabs/Module/Settings/Channel.php:683 +#: Zotlabs/Module/Settings/Channel.php:709 +#: Zotlabs/Module/Settings/Channel.php:714 +#: Zotlabs/Module/Settings/Channel.php:715 +#: Zotlabs/Module/Settings/Channel.php:716 msgid "Recommended" msgstr "" -#: Zotlabs/Module/Settings/Channel.php:677 +#: Zotlabs/Module/Settings/Channel.php:710 msgid "Upcoming events" msgstr "" -#: Zotlabs/Module/Settings/Channel.php:678 +#: Zotlabs/Module/Settings/Channel.php:711 msgid "Events today" msgstr "" -#: Zotlabs/Module/Settings/Channel.php:679 +#: Zotlabs/Module/Settings/Channel.php:712 msgid "Upcoming birthdays" msgstr "" -#: Zotlabs/Module/Settings/Channel.php:679 +#: Zotlabs/Module/Settings/Channel.php:712 msgid "Not available in all themes" msgstr "" -#: Zotlabs/Module/Settings/Channel.php:680 +#: Zotlabs/Module/Settings/Channel.php:713 msgid "System (personal) notifications" msgstr "" -#: Zotlabs/Module/Settings/Channel.php:681 +#: Zotlabs/Module/Settings/Channel.php:714 msgid "System info messages" msgstr "" -#: Zotlabs/Module/Settings/Channel.php:682 +#: Zotlabs/Module/Settings/Channel.php:715 msgid "System critical alerts" msgstr "" -#: Zotlabs/Module/Settings/Channel.php:683 +#: Zotlabs/Module/Settings/Channel.php:716 msgid "New connections" msgstr "" -#: Zotlabs/Module/Settings/Channel.php:684 +#: Zotlabs/Module/Settings/Channel.php:717 msgid "System Registrations" msgstr "" -#: Zotlabs/Module/Settings/Channel.php:686 +#: Zotlabs/Module/Settings/Channel.php:719 msgid "Unseen public stream activity" msgstr "" -#: Zotlabs/Module/Settings/Channel.php:687 +#: Zotlabs/Module/Settings/Channel.php:720 msgid "Unseen likes and dislikes" msgstr "" -#: Zotlabs/Module/Settings/Channel.php:688 +#: Zotlabs/Module/Settings/Channel.php:721 msgid "Unseen forum posts" msgstr "" -#: Zotlabs/Module/Settings/Channel.php:689 +#: Zotlabs/Module/Settings/Channel.php:722 msgid "Reported content" msgstr "" -#: Zotlabs/Module/Settings/Channel.php:690 +#: Zotlabs/Module/Settings/Channel.php:723 msgid "" "Desktop notifications are unavailable because the required browser " "permission has not been granted" msgstr "" -#: Zotlabs/Module/Settings/Channel.php:691 +#: Zotlabs/Module/Settings/Channel.php:724 msgid "Grant permission" msgstr "" -#: Zotlabs/Module/Settings/Channel.php:692 +#: Zotlabs/Module/Settings/Channel.php:725 msgid "Email notifications sent from (hostname)" msgstr "" -#: Zotlabs/Module/Settings/Channel.php:692 +#: Zotlabs/Module/Settings/Channel.php:725 #, php-format msgid "" "If your channel is mirrored to multiple locations, set this to your " @@ -8431,237 +8440,237 @@ msgid "" "Example: %s" msgstr "" -#: Zotlabs/Module/Settings/Channel.php:693 +#: Zotlabs/Module/Settings/Channel.php:726 msgid "Show new wall posts, private messages and connections under Notices" msgstr "" -#: Zotlabs/Module/Settings/Channel.php:694 -msgid "Accept messages from strangers which mention me" +#: Zotlabs/Module/Settings/Channel.php:727 +msgid "Accept messages from strangers which mention you" msgstr "" -#: Zotlabs/Module/Settings/Channel.php:694 +#: Zotlabs/Module/Settings/Channel.php:727 msgid "This setting bypasses normal permissions" msgstr "" -#: Zotlabs/Module/Settings/Channel.php:695 +#: Zotlabs/Module/Settings/Channel.php:728 msgid "" "Accept messages from strangers which include any of the following hashtags" msgstr "" -#: Zotlabs/Module/Settings/Channel.php:695 +#: Zotlabs/Module/Settings/Channel.php:728 msgid "comma separated, do not include the #" msgstr "" -#: Zotlabs/Module/Settings/Channel.php:696 +#: Zotlabs/Module/Settings/Channel.php:729 msgid "Notify me of events this many days in advance" msgstr "" -#: Zotlabs/Module/Settings/Channel.php:696 +#: Zotlabs/Module/Settings/Channel.php:729 msgid "Must be greater than 0" msgstr "" -#: Zotlabs/Module/Settings/Channel.php:701 +#: Zotlabs/Module/Settings/Channel.php:734 msgid "Date and time" msgstr "" -#: Zotlabs/Module/Settings/Channel.php:702 +#: Zotlabs/Module/Settings/Channel.php:735 msgid "" "This section is reserved for use by optional addons and apps to provide " "additional settings." msgstr "" -#: Zotlabs/Module/Settings/Channel.php:703 +#: Zotlabs/Module/Settings/Channel.php:736 msgid "Advanced Account/Page Type Settings" msgstr "" -#: Zotlabs/Module/Settings/Channel.php:704 +#: Zotlabs/Module/Settings/Channel.php:737 msgid "Change the behaviour of this account for special situations" msgstr "" -#: Zotlabs/Module/Settings/Channel.php:707 +#: Zotlabs/Module/Settings/Channel.php:740 msgid "Default photo upload folder name" msgstr "" -#: Zotlabs/Module/Settings/Channel.php:707 -#: Zotlabs/Module/Settings/Channel.php:708 +#: Zotlabs/Module/Settings/Channel.php:740 +#: Zotlabs/Module/Settings/Channel.php:741 msgid "%Y - current year, %m - current month" msgstr "" -#: Zotlabs/Module/Settings/Channel.php:708 +#: Zotlabs/Module/Settings/Channel.php:741 msgid "Default file upload folder name" msgstr "" -#: Zotlabs/Module/Settings/Channel.php:710 +#: Zotlabs/Module/Settings/Channel.php:743 msgid "Personal menu to display in your channel pages" msgstr "" -#: Zotlabs/Module/Settings/Channel.php:712 +#: Zotlabs/Module/Settings/Channel.php:745 msgid "Remove this channel." msgstr "" -#: Zotlabs/Module/Settings/Channel.php:713 +#: Zotlabs/Module/Settings/Channel.php:746 msgid "Mentions should display" msgstr "" -#: Zotlabs/Module/Settings/Channel.php:713 +#: Zotlabs/Module/Settings/Channel.php:746 msgid "" "Changes to this setting are applied to new posts/comments only. It is not " "retroactive." msgstr "" -#: Zotlabs/Module/Settings/Channel.php:715 +#: Zotlabs/Module/Settings/Channel.php:748 msgid "the channel display name [example: @Barbara Jenkins]" msgstr "" -#: Zotlabs/Module/Settings/Channel.php:716 +#: Zotlabs/Module/Settings/Channel.php:749 msgid "the channel nickname [example: @barbara1976]" msgstr "" -#: Zotlabs/Module/Settings/Channel.php:717 +#: Zotlabs/Module/Settings/Channel.php:750 msgid "combined [example: @Barbara Jenkins (barbara1976)]" msgstr "" -#: Zotlabs/Module/Settings/Channel.php:718 +#: Zotlabs/Module/Settings/Channel.php:751 msgid "no preference, use the system default" msgstr "" -#: Zotlabs/Module/Settings/Channel.php:721 +#: Zotlabs/Module/Settings/Channel.php:754 msgid "Calendar week begins on" msgstr "" -#: Zotlabs/Module/Settings/Channel.php:721 +#: Zotlabs/Module/Settings/Channel.php:754 msgid "This varies by country/culture" msgstr "" -#: Zotlabs/Module/Settings/Channel.php:722 include/text.php:1352 -#: include/js_strings.php:90 +#: Zotlabs/Module/Settings/Channel.php:755 include/text.php:1427 +#: include/js_strings.php:93 msgid "Sunday" msgstr "" -#: Zotlabs/Module/Settings/Channel.php:723 include/text.php:1352 -#: include/js_strings.php:91 +#: Zotlabs/Module/Settings/Channel.php:756 include/text.php:1427 +#: include/js_strings.php:94 msgid "Monday" msgstr "" -#: Zotlabs/Module/Settings/Channel.php:724 include/text.php:1352 -#: include/js_strings.php:92 +#: Zotlabs/Module/Settings/Channel.php:757 include/text.php:1427 +#: include/js_strings.php:95 msgid "Tuesday" msgstr "" -#: Zotlabs/Module/Settings/Channel.php:725 include/text.php:1352 -#: include/js_strings.php:93 +#: Zotlabs/Module/Settings/Channel.php:758 include/text.php:1427 +#: include/js_strings.php:96 msgid "Wednesday" msgstr "" -#: Zotlabs/Module/Settings/Channel.php:726 include/text.php:1352 -#: include/js_strings.php:94 +#: Zotlabs/Module/Settings/Channel.php:759 include/text.php:1427 +#: include/js_strings.php:97 msgid "Thursday" msgstr "" -#: Zotlabs/Module/Settings/Channel.php:727 include/text.php:1352 -#: include/js_strings.php:95 +#: Zotlabs/Module/Settings/Channel.php:760 include/text.php:1427 +#: include/js_strings.php:98 msgid "Friday" msgstr "" -#: Zotlabs/Module/Settings/Channel.php:728 include/text.php:1352 -#: include/js_strings.php:96 +#: Zotlabs/Module/Settings/Channel.php:761 include/text.php:1427 +#: include/js_strings.php:99 msgid "Saturday" msgstr "" -#: Zotlabs/Module/Subthread.php:133 +#: Zotlabs/Module/Subthread.php:139 #, php-format msgid "%1$s is following %2$s's %3$s" msgstr "" -#: Zotlabs/Module/Subthread.php:136 +#: Zotlabs/Module/Subthread.php:142 #, php-format msgid "%1$s stopped following %2$s's %3$s" msgstr "" -#: Zotlabs/Module/Sharedwithme.php:108 +#: Zotlabs/Module/Sharedwithme.php:106 msgid "Files: shared with me" msgstr "" -#: Zotlabs/Module/Sharedwithme.php:110 +#: Zotlabs/Module/Sharedwithme.php:108 msgid "NEW" msgstr "" -#: Zotlabs/Module/Sharedwithme.php:111 Zotlabs/Storage/Browser.php:312 -#: include/text.php:1449 +#: Zotlabs/Module/Sharedwithme.php:109 Zotlabs/Storage/Browser.php:315 +#: include/text.php:1534 msgid "Size" msgstr "" -#: Zotlabs/Module/Sharedwithme.php:112 Zotlabs/Storage/Browser.php:313 +#: Zotlabs/Module/Sharedwithme.php:110 Zotlabs/Storage/Browser.php:316 msgid "Last Modified" msgstr "" -#: Zotlabs/Module/Sharedwithme.php:113 +#: Zotlabs/Module/Sharedwithme.php:111 msgid "Remove all files" msgstr "" -#: Zotlabs/Module/Sharedwithme.php:114 +#: Zotlabs/Module/Sharedwithme.php:112 msgid "Remove this file" msgstr "" -#: Zotlabs/Module/Siteinfo.php:29 +#: Zotlabs/Module/Siteinfo.php:34 msgid "About this site" msgstr "" -#: Zotlabs/Module/Siteinfo.php:31 +#: Zotlabs/Module/Siteinfo.php:36 msgid "Site Name" msgstr "" -#: Zotlabs/Module/Siteinfo.php:35 +#: Zotlabs/Module/Siteinfo.php:40 msgid "Administrator" msgstr "" -#: Zotlabs/Module/Siteinfo.php:38 +#: Zotlabs/Module/Siteinfo.php:43 msgid "Software and Project information" msgstr "" -#: Zotlabs/Module/Siteinfo.php:39 +#: Zotlabs/Module/Siteinfo.php:44 msgid "This site is powered by $Projectname" msgstr "" -#: Zotlabs/Module/Siteinfo.php:40 +#: Zotlabs/Module/Siteinfo.php:45 msgid "" "Federated and decentralised networking and identity services provided by " "Nomad" msgstr "" -#: Zotlabs/Module/Siteinfo.php:44 +#: Zotlabs/Module/Siteinfo.php:49 msgid "Protocols:" msgstr "" -#: Zotlabs/Module/Siteinfo.php:46 +#: Zotlabs/Module/Siteinfo.php:51 #, php-format msgid "Version %s" msgstr "" -#: Zotlabs/Module/Siteinfo.php:47 +#: Zotlabs/Module/Siteinfo.php:52 msgid "Project homepage" msgstr "" -#: Zotlabs/Module/Siteinfo.php:48 +#: Zotlabs/Module/Siteinfo.php:53 msgid "Developer homepage" msgstr "" -#: Zotlabs/Module/Uexport.php:61 Zotlabs/Module/Uexport.php:62 +#: Zotlabs/Module/Uexport.php:64 Zotlabs/Module/Uexport.php:65 msgid "Export Channel" msgstr "" -#: Zotlabs/Module/Uexport.php:63 +#: Zotlabs/Module/Uexport.php:66 msgid "" "Export your basic channel information to a file. This acts as a backup of " "your connections, permissions, profile and basic data, which can be used to " "import your data to a new server hub, but does not contain your content." msgstr "" -#: Zotlabs/Module/Uexport.php:64 +#: Zotlabs/Module/Uexport.php:67 msgid "Export Content" msgstr "" -#: Zotlabs/Module/Uexport.php:65 +#: Zotlabs/Module/Uexport.php:68 msgid "" "Export your channel information and recent content to a JSON backup that can " "be restored or imported to another server hub. This backs up all of your " @@ -8670,11 +8679,11 @@ msgid "" "this download to begin." msgstr "" -#: Zotlabs/Module/Uexport.php:67 +#: Zotlabs/Module/Uexport.php:70 msgid "Export your posts from a given year." msgstr "" -#: Zotlabs/Module/Uexport.php:69 +#: Zotlabs/Module/Uexport.php:72 msgid "" "You may also export your posts and conversations for a particular year or " "month. Adjust the date in your browser location bar to select other dates. " @@ -8682,21 +8691,21 @@ msgid "" "please try again selecting a more limited date range." msgstr "" -#: Zotlabs/Module/Uexport.php:70 +#: Zotlabs/Module/Uexport.php:73 #, php-format msgid "" "To select all posts for a given year, such as this year, visit %2$s" msgstr "" -#: Zotlabs/Module/Uexport.php:71 +#: Zotlabs/Module/Uexport.php:74 #, php-format msgid "" "To select all posts for a given month, such as January of this year, visit " "%2$s" msgstr "" -#: Zotlabs/Module/Uexport.php:72 +#: Zotlabs/Module/Uexport.php:75 #, php-format msgid "" "These content files may be imported or restored by visiting " @@ -8704,352 +8713,352 @@ msgid "" "or restore these in date order (oldest first)." msgstr "" -#: Zotlabs/Module/Sources.php:45 +#: Zotlabs/Module/Sources.php:49 msgid "Failed to create source. No channel selected." msgstr "" -#: Zotlabs/Module/Sources.php:61 +#: Zotlabs/Module/Sources.php:66 msgid "Source created." msgstr "" -#: Zotlabs/Module/Sources.php:74 +#: Zotlabs/Module/Sources.php:79 msgid "Source updated." msgstr "" -#: Zotlabs/Module/Sources.php:99 +#: Zotlabs/Module/Sources.php:106 msgid "*" msgstr "" -#: Zotlabs/Module/Sources.php:106 +#: Zotlabs/Module/Sources.php:113 msgid "Manage remote sources of content for your channel." msgstr "" -#: Zotlabs/Module/Sources.php:107 Zotlabs/Module/Sources.php:117 +#: Zotlabs/Module/Sources.php:114 Zotlabs/Module/Sources.php:124 msgid "New Source" msgstr "" -#: Zotlabs/Module/Sources.php:118 Zotlabs/Module/Sources.php:152 +#: Zotlabs/Module/Sources.php:125 Zotlabs/Module/Sources.php:160 msgid "" "Import all or selected content from the following channel into this channel " "and distribute it according to your channel settings." msgstr "" -#: Zotlabs/Module/Sources.php:119 Zotlabs/Module/Sources.php:153 +#: Zotlabs/Module/Sources.php:126 Zotlabs/Module/Sources.php:161 msgid "Only import content with these words (one per line)" msgstr "" -#: Zotlabs/Module/Sources.php:119 Zotlabs/Module/Sources.php:153 +#: Zotlabs/Module/Sources.php:126 Zotlabs/Module/Sources.php:161 msgid "Leave blank to import all public content" msgstr "" -#: Zotlabs/Module/Sources.php:120 Zotlabs/Module/Sources.php:159 +#: Zotlabs/Module/Sources.php:127 Zotlabs/Module/Sources.php:167 msgid "Channel Name" msgstr "" -#: Zotlabs/Module/Sources.php:121 Zotlabs/Module/Sources.php:156 +#: Zotlabs/Module/Sources.php:128 Zotlabs/Module/Sources.php:164 msgid "" "Add the following categories to posts imported from this source (comma " "separated)" msgstr "" -#: Zotlabs/Module/Sources.php:122 Zotlabs/Module/Sources.php:157 +#: Zotlabs/Module/Sources.php:129 Zotlabs/Module/Sources.php:165 msgid "Resend posts with this channel as author" msgstr "" -#: Zotlabs/Module/Sources.php:122 Zotlabs/Module/Sources.php:157 +#: Zotlabs/Module/Sources.php:129 Zotlabs/Module/Sources.php:165 msgid "Copyrights may apply" msgstr "" -#: Zotlabs/Module/Sources.php:142 Zotlabs/Module/Sources.php:172 +#: Zotlabs/Module/Sources.php:150 Zotlabs/Module/Sources.php:180 msgid "Source not found." msgstr "" -#: Zotlabs/Module/Sources.php:149 +#: Zotlabs/Module/Sources.php:157 msgid "Edit Source" msgstr "" -#: Zotlabs/Module/Sources.php:150 +#: Zotlabs/Module/Sources.php:158 msgid "Delete Source" msgstr "" -#: Zotlabs/Module/Sources.php:180 +#: Zotlabs/Module/Sources.php:189 msgid "Source removed" msgstr "" -#: Zotlabs/Module/Sources.php:182 +#: Zotlabs/Module/Sources.php:191 msgid "Unable to remove source." msgstr "" -#: Zotlabs/Module/Photos.php:80 Zotlabs/Module/Photos.php:98 +#: Zotlabs/Module/Photos.php:81 Zotlabs/Module/Photos.php:99 msgid "Album not found." msgstr "" -#: Zotlabs/Module/Photos.php:89 +#: Zotlabs/Module/Photos.php:90 msgid "Delete Album" msgstr "" -#: Zotlabs/Module/Photos.php:163 Zotlabs/Module/Photos.php:1120 +#: Zotlabs/Module/Photos.php:164 Zotlabs/Module/Photos.php:1133 msgid "Delete Photo" msgstr "" -#: Zotlabs/Module/Photos.php:356 +#: Zotlabs/Module/Photos.php:365 msgid "linked item not found." msgstr "" -#: Zotlabs/Module/Photos.php:549 +#: Zotlabs/Module/Photos.php:559 #, php-format msgid "%s: Unsupported photo type. Saved as file." msgstr "" -#: Zotlabs/Module/Photos.php:578 +#: Zotlabs/Module/Photos.php:587 msgid "No photos selected" msgstr "" -#: Zotlabs/Module/Photos.php:628 +#: Zotlabs/Module/Photos.php:637 msgid "Access to this item is restricted." msgstr "" -#: Zotlabs/Module/Photos.php:671 +#: Zotlabs/Module/Photos.php:680 #, php-format msgid "%1$.2f MB of %2$.2f MB photo storage used." msgstr "" -#: Zotlabs/Module/Photos.php:674 +#: Zotlabs/Module/Photos.php:682 #, php-format msgid "%1$.2f MB photo storage used." msgstr "" -#: Zotlabs/Module/Photos.php:716 +#: Zotlabs/Module/Photos.php:724 msgid "Upload Photos" msgstr "" -#: Zotlabs/Module/Photos.php:720 +#: Zotlabs/Module/Photos.php:728 msgid "Enter an album name" msgstr "" -#: Zotlabs/Module/Photos.php:721 +#: Zotlabs/Module/Photos.php:729 msgid "or select an existing album (doubleclick)" msgstr "" -#: Zotlabs/Module/Photos.php:722 +#: Zotlabs/Module/Photos.php:730 msgid "Create a status post for this upload" msgstr "" -#: Zotlabs/Module/Photos.php:722 +#: Zotlabs/Module/Photos.php:730 msgid "" "If multiple files are selected, the message will be repeated for each photo" msgstr "" -#: Zotlabs/Module/Photos.php:723 Zotlabs/Module/Photos.php:1104 +#: Zotlabs/Module/Photos.php:731 Zotlabs/Module/Photos.php:1117 msgid "Please briefly describe this photo for vision-impaired viewers" msgstr "" -#: Zotlabs/Module/Photos.php:725 Zotlabs/Module/Photos.php:1106 +#: Zotlabs/Module/Photos.php:733 Zotlabs/Module/Photos.php:1119 msgid "Your message (optional)" msgstr "" -#: Zotlabs/Module/Photos.php:814 +#: Zotlabs/Module/Photos.php:820 msgid "Date descending" msgstr "" -#: Zotlabs/Module/Photos.php:815 +#: Zotlabs/Module/Photos.php:821 msgid "Date ascending" msgstr "" -#: Zotlabs/Module/Photos.php:816 +#: Zotlabs/Module/Photos.php:822 msgid "Name ascending" msgstr "" -#: Zotlabs/Module/Photos.php:873 Zotlabs/Module/Photos.php:1422 +#: Zotlabs/Module/Photos.php:877 Zotlabs/Module/Photos.php:1430 msgid "View files" msgstr "" -#: Zotlabs/Module/Photos.php:877 Zotlabs/Module/Photos.php:1425 +#: Zotlabs/Module/Photos.php:881 Zotlabs/Module/Photos.php:1433 msgid "Add Photos" msgstr "" -#: Zotlabs/Module/Photos.php:879 +#: Zotlabs/Module/Photos.php:883 msgid "Sort" msgstr "" -#: Zotlabs/Module/Photos.php:923 +#: Zotlabs/Module/Photos.php:928 msgid "Permission denied. Access to this item may be restricted." msgstr "" -#: Zotlabs/Module/Photos.php:925 +#: Zotlabs/Module/Photos.php:930 msgid "Photo not available" msgstr "" -#: Zotlabs/Module/Photos.php:985 +#: Zotlabs/Module/Photos.php:994 msgid "Use as profile photo" msgstr "" -#: Zotlabs/Module/Photos.php:986 +#: Zotlabs/Module/Photos.php:995 msgid "Use as cover photo" msgstr "" -#: Zotlabs/Module/Photos.php:993 +#: Zotlabs/Module/Photos.php:1002 msgid "Private Photo" msgstr "" -#: Zotlabs/Module/Photos.php:1008 +#: Zotlabs/Module/Photos.php:1020 msgid "View Full Size" msgstr "" -#: Zotlabs/Module/Photos.php:1095 +#: Zotlabs/Module/Photos.php:1108 msgid "Edit photo" msgstr "" -#: Zotlabs/Module/Photos.php:1099 +#: Zotlabs/Module/Photos.php:1112 msgid "Move photo to album" msgstr "" -#: Zotlabs/Module/Photos.php:1100 +#: Zotlabs/Module/Photos.php:1113 msgid "Enter a new album name" msgstr "" -#: Zotlabs/Module/Photos.php:1101 +#: Zotlabs/Module/Photos.php:1114 msgid "or select an existing one (doubleclick)" msgstr "" -#: Zotlabs/Module/Photos.php:1106 +#: Zotlabs/Module/Photos.php:1119 msgid "" "This will only appear in the optional status post attached to this photo" msgstr "" -#: Zotlabs/Module/Photos.php:1107 +#: Zotlabs/Module/Photos.php:1120 msgid "Add a Tag" msgstr "" -#: Zotlabs/Module/Photos.php:1115 +#: Zotlabs/Module/Photos.php:1128 msgid "Example: @bob, @Barbara_Jensen, @jim@example.com" msgstr "" -#: Zotlabs/Module/Photos.php:1118 +#: Zotlabs/Module/Photos.php:1131 msgid "Flag as adult in album view" msgstr "" -#: Zotlabs/Module/Photos.php:1194 include/conversation.php:530 +#: Zotlabs/Module/Photos.php:1206 include/conversation.php:521 msgctxt "title" msgid "Likes" msgstr "" -#: Zotlabs/Module/Photos.php:1195 include/conversation.php:531 +#: Zotlabs/Module/Photos.php:1207 include/conversation.php:522 msgctxt "title" msgid "Dislikes" msgstr "" -#: Zotlabs/Module/Photos.php:1196 include/conversation.php:532 +#: Zotlabs/Module/Photos.php:1208 include/conversation.php:523 msgctxt "title" msgid "Attending" msgstr "" -#: Zotlabs/Module/Photos.php:1197 include/conversation.php:533 +#: Zotlabs/Module/Photos.php:1209 include/conversation.php:524 msgctxt "title" msgid "Not attending" msgstr "" -#: Zotlabs/Module/Photos.php:1198 include/conversation.php:534 +#: Zotlabs/Module/Photos.php:1210 include/conversation.php:525 msgctxt "title" msgid "Might attend" msgstr "" -#: Zotlabs/Module/Photos.php:1311 +#: Zotlabs/Module/Photos.php:1320 msgid "Photo Tools" msgstr "" -#: Zotlabs/Module/Photos.php:1321 +#: Zotlabs/Module/Photos.php:1330 msgid "In This Photo:" msgstr "" -#: Zotlabs/Module/Photos.php:1326 +#: Zotlabs/Module/Photos.php:1335 msgid "Map" msgstr "" -#: Zotlabs/Module/Photos.php:1406 Zotlabs/Module/Photos.php:1420 -#: Zotlabs/Module/Photos.php:1421 include/photos.php:746 +#: Zotlabs/Module/Photos.php:1417 Zotlabs/Module/Photos.php:1428 +#: Zotlabs/Module/Photos.php:1429 include/photos.php:756 msgid "Recent Photos" msgstr "" -#: Zotlabs/Module/Tagger.php:55 +#: Zotlabs/Module/Tagger.php:62 msgid "Post not found." msgstr "" -#: Zotlabs/Module/Tagger.php:86 include/text.php:2253 -#: include/conversation.php:160 +#: Zotlabs/Module/Tagger.php:94 include/conversation.php:163 +#: include/text.php:2377 msgid "comment" msgstr "" -#: Zotlabs/Module/Tagger.php:126 +#: Zotlabs/Module/Tagger.php:135 #, php-format msgid "%1$s tagged %2$s's %3$s with %4$s" msgstr "" -#: Zotlabs/Module/Tagrm.php:48 Zotlabs/Module/Tagrm.php:98 +#: Zotlabs/Module/Tagrm.php:59 Zotlabs/Module/Tagrm.php:111 msgid "Tag removed" msgstr "" -#: Zotlabs/Module/Tagrm.php:123 +#: Zotlabs/Module/Tagrm.php:137 msgid "Remove Item Tag" msgstr "" -#: Zotlabs/Module/Tagrm.php:125 +#: Zotlabs/Module/Tagrm.php:139 msgid "Select a tag to remove: " msgstr "" -#: Zotlabs/Module/Import.php:165 Zotlabs/Import/Friendica.php:60 +#: Zotlabs/Module/Import.php:160 Zotlabs/Import/Friendica.php:64 #, php-format msgid "Your service plan only allows %d channels." msgstr "" -#: Zotlabs/Module/Import.php:195 +#: Zotlabs/Module/Import.php:188 msgid "No channel. Import failed." msgstr "" -#: Zotlabs/Module/Import.php:640 +#: Zotlabs/Module/Import.php:633 msgid "Files and Posts imported." msgstr "" -#: Zotlabs/Module/Import.php:656 +#: Zotlabs/Module/Import.php:648 msgid "Import completed." msgstr "" -#: Zotlabs/Module/Import.php:686 +#: Zotlabs/Module/Import.php:680 msgid "You must be logged in to use this feature." msgstr "" -#: Zotlabs/Module/Import.php:691 +#: Zotlabs/Module/Import.php:685 msgid "Import Channel" msgstr "" -#: Zotlabs/Module/Import.php:692 +#: Zotlabs/Module/Import.php:686 msgid "" "Use this form to import an existing channel from a different server. You may " "retrieve the channel identity from the old server via the network or provide " "an export file." msgstr "" -#: Zotlabs/Module/Import.php:694 +#: Zotlabs/Module/Import.php:688 msgid "Or provide the old server details" msgstr "" -#: Zotlabs/Module/Import.php:695 +#: Zotlabs/Module/Import.php:689 msgid "Your old identity address (xyz@example.com)" msgstr "" -#: Zotlabs/Module/Import.php:696 +#: Zotlabs/Module/Import.php:690 msgid "Your old login email address" msgstr "" -#: Zotlabs/Module/Import.php:697 +#: Zotlabs/Module/Import.php:691 msgid "Your old login password" msgstr "" -#: Zotlabs/Module/Import.php:698 +#: Zotlabs/Module/Import.php:692 msgid "Import a few months of posts if possible (limited by available memory)" msgstr "" -#: Zotlabs/Module/Import.php:700 +#: Zotlabs/Module/Import.php:694 msgid "" "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 " @@ -9057,179 +9066,36 @@ msgid "" "location for files, photos, and media." msgstr "" -#: Zotlabs/Module/Import.php:702 +#: Zotlabs/Module/Import.php:696 msgid "Make this hub my primary location" msgstr "" -#: Zotlabs/Module/Import.php:703 +#: Zotlabs/Module/Import.php:697 msgid "Move this channel (disable all previous locations)" msgstr "" -#: Zotlabs/Module/Import.php:704 +#: Zotlabs/Module/Import.php:698 msgid "Use this channel nickname instead of the one provided" msgstr "" -#: Zotlabs/Module/Import.php:704 +#: Zotlabs/Module/Import.php:698 msgid "" "Leave blank to keep your existing channel nickname. You will be randomly " "assigned a similar nickname if either name is already allocated on this site." msgstr "" -#: Zotlabs/Module/Import.php:706 +#: Zotlabs/Module/Import.php:700 msgid "" "This process may take several minutes to complete and considerably longer if " "importing a large amount of posts and files. Please submit the form only " "once and leave this page open until finished." msgstr "" -#: Zotlabs/Module/Profile.php:112 +#: Zotlabs/Module/Profile.php:115 msgid "vcard" msgstr "" -#: Zotlabs/Module/Xchan.php:10 -msgid "Xchan Lookup" -msgstr "" - -#: Zotlabs/Module/Xchan.php:13 -msgid "Lookup xchan beginning with (or webbie): " -msgstr "" - -#: Zotlabs/Module/Comment_control.php:13 -msgid "" -"This app allows you to select the comment audience and set a length of time " -"that comments on a particular post will be accepted." -msgstr "" - -#: Zotlabs/Module/Comment_control.php:21 -msgid "" -"This app is installed. A button to control comments may be found below the " -"post editor." -msgstr "" - -#: Zotlabs/Module/Categories.php:29 -msgid "This app allows you to add categories to posts and events." -msgstr "" - -#: Zotlabs/Module/Zotfinger.php:23 -msgid "Zotfinger Diagnostic" -msgstr "" - -#: Zotlabs/Module/Zotfinger.php:24 -msgid "Lookup URL" -msgstr "" - -#: Zotlabs/Module/Photomap.php:12 -msgid "" -"This app provides a displayable map when viewing detail of photos that " -"contain location information." -msgstr "" - -#: Zotlabs/Module/Photomap.php:20 -msgid "This app is currently installed." -msgstr "" - -#: Zotlabs/Module/Viewconnections.php:62 -msgid "No connections." -msgstr "" - -#: Zotlabs/Module/Viewconnections.php:80 -#, php-format -msgid "Visit %1$s's profile [%2$s]" -msgstr "" - -#: Zotlabs/Module/Viewconnections.php:109 -msgid "View Connections" -msgstr "" - -#: Zotlabs/Module/Suggestions.php:39 -msgid "" -"This app (when installed) displays a small number of friend suggestions on " -"selected pages or you can run the app to display a full list of channel " -"suggestions." -msgstr "" - -#: Zotlabs/Module/Vlists.php:14 -msgid "" -"This app creates dynamic access lists corresponding to [1] all connections, " -"[2] all ActivityPub protocol connections, and [3] all Nomad or Zot/6 " -"protocol connections. These additional selections will be found within the " -"Permissions setting tool." -msgstr "" - -#: Zotlabs/Module/Vlists.php:21 -msgid "This app is installed. " -msgstr "" - -#: Zotlabs/Module/Clients.php:14 -msgid "" -"This app allows you to authorize mobile apps using OAuth and OpenID to " -"access your channel." -msgstr "" - -#: Zotlabs/Module/Superblock.php:34 -msgid "Blocking this site is not permitted." -msgstr "" - -#: Zotlabs/Module/Superblock.php:91 -msgid "Added by Superblock" -msgstr "" - -#: Zotlabs/Module/Superblock.php:191 -msgid "superblock settings updated" -msgstr "" - -#: Zotlabs/Module/Superblock.php:223 -msgid "Blocked channels" -msgstr "" - -#: Zotlabs/Module/Superblock.php:225 -msgid "No channels currently blocked" -msgstr "" - -#: Zotlabs/Module/Superblock.php:240 -msgid "Blocked servers" -msgstr "" - -#: Zotlabs/Module/Superblock.php:242 -msgid "No servers currently blocked" -msgstr "" - -#: Zotlabs/Module/Superblock.php:248 Zotlabs/Widget/Settings_menu.php:58 -msgid "Manage Blocks" -msgstr "" - -#: Zotlabs/Module/Fedi_id.php:23 -msgid "You are already connected with this channel." -msgstr "" - -#: Zotlabs/Module/Fedi_id.php:34 -msgid "Unknown or unreachable identifier" -msgstr "" - -#: Zotlabs/Module/Fedi_id.php:43 -msgid "Home instance" -msgstr "" - -#: Zotlabs/Module/Fedi_id.php:44 -msgid "Enter your channel address or fediverse ID (e.g. channel@example.com)" -msgstr "" - -#: Zotlabs/Module/Fedi_id.php:44 -msgid "" -"If you do not have a fediverse ID, please use your browser 'back' button to " -"return to the previous page" -msgstr "" - -#: Zotlabs/Module/Tagadelic.php:29 -msgid "This app displays a hashtag cloud on your channel homepage." -msgstr "" - -#: Zotlabs/Module/Tagadelic.php:37 -msgid "" -"This app is installed. It displays a hashtag cloud on your channel homepage." -msgstr "" - -#: Zotlabs/Module/Content_filter.php:25 +#: Zotlabs/Module/Content_filter.php:26 msgid "Content Filter settings updated." msgstr "" @@ -9257,7 +9123,40 @@ msgstr "" msgid "Content Filter Settings" msgstr "" -#: Zotlabs/Module/Future.php:13 +#: Zotlabs/Module/Secrets.php:15 +msgid "" +"This app allows you to protect messages with a secret passphrase. This only " +"works across selected platforms." +msgstr "" + +#: Zotlabs/Module/Secrets.php:23 +msgid "" +"This app is installed. A button to encrypt content may be found in the post " +"editor." +msgstr "" + +#: Zotlabs/Module/Xchan.php:13 +msgid "Xchan Lookup" +msgstr "" + +#: Zotlabs/Module/Xchan.php:16 +msgid "Lookup xchan beginning with (or webbie): " +msgstr "" + +#: Zotlabs/Module/Suggestions.php:44 +msgid "" +"This app (when installed) displays a small number of friend suggestions on " +"selected pages or you can run the app to display a full list of channel " +"suggestions." +msgstr "" + +#: Zotlabs/Module/Clients.php:16 +msgid "" +"This app allows you to authorize mobile apps using OAuth and OpenID to " +"access your channel." +msgstr "" + +#: Zotlabs/Module/Future.php:15 msgid "" "This app allows you to set an optional publish date/time for posts, which " "may be in the future. This must be at least ten minutes into the future to " @@ -9266,524 +9165,240 @@ msgid "" "editor to set the date/time." msgstr "" -#: Zotlabs/Module/Secrets.php:13 -msgid "" -"This app allows you to protect messages with a secret passphrase. This only " -"works across selected platforms." +#: Zotlabs/Module/Superblock.php:36 +msgid "Blocking this site is not permitted." msgstr "" -#: Zotlabs/Module/Secrets.php:21 -msgid "" -"This app is installed. A button to encrypt content may be found in the post " -"editor." +#: Zotlabs/Module/Superblock.php:93 +msgid "Added by Superblock" msgstr "" -#: Zotlabs/Module/Markup.php:14 +#: Zotlabs/Module/Superblock.php:199 +msgid "superblock settings updated" +msgstr "" + +#: Zotlabs/Module/Superblock.php:230 +msgid "Blocked channels" +msgstr "" + +#: Zotlabs/Module/Superblock.php:232 +msgid "No channels currently blocked" +msgstr "" + +#: Zotlabs/Module/Superblock.php:247 +msgid "Blocked servers" +msgstr "" + +#: Zotlabs/Module/Superblock.php:249 +msgid "No servers currently blocked" +msgstr "" + +#: Zotlabs/Module/Superblock.php:255 Zotlabs/Widget/Settings_menu.php:64 +msgid "Manage Blocks" +msgstr "" + +#: Zotlabs/Module/Viewconnections.php:66 +msgid "No connections." +msgstr "" + +#: Zotlabs/Module/Viewconnections.php:83 +#, php-format +msgid "Visit %1$s's profile [%2$s]" +msgstr "" + +#: Zotlabs/Module/Viewconnections.php:110 +msgid "View Connections" +msgstr "" + +#: Zotlabs/Module/Vlists.php:16 +msgid "" +"This app creates dynamic access lists corresponding to [1] all connections, " +"[2] all ActivityPub protocol connections, and [3] all Nomad or Zot/6 " +"protocol connections. These additional selections will be found within the " +"Permissions setting tool." +msgstr "" + +#: Zotlabs/Module/Vlists.php:23 +msgid "This app is installed. " +msgstr "" + +#: Zotlabs/Module/Categories.php:31 +msgid "This app allows you to add categories to posts and events." +msgstr "" + +#: Zotlabs/Module/Comment_control.php:15 +msgid "" +"This app allows you to select the comment audience and set a length of time " +"that comments on a particular post will be accepted." +msgstr "" + +#: Zotlabs/Module/Comment_control.php:23 +msgid "" +"This app is installed. A button to control comments may be found below the " +"post editor." +msgstr "" + +#: Zotlabs/Module/Tagadelic.php:31 +msgid "This app displays a hashtag cloud on your channel homepage." +msgstr "" + +#: Zotlabs/Module/Tagadelic.php:39 +msgid "" +"This app is installed. It displays a hashtag cloud on your channel homepage." +msgstr "" + +#: Zotlabs/Module/Zotfinger.php:26 +msgid "Zotfinger Diagnostic" +msgstr "" + +#: Zotlabs/Module/Zotfinger.php:27 +msgid "Lookup URL" +msgstr "" + +#: Zotlabs/Module/Markup.php:16 msgid "" "This app adds editor buttons for bold, italic, underline, quote, and " "possibly other common richtext constructs." msgstr "" -#: Zotlabs/Storage/Browser.php:117 Zotlabs/Storage/Browser.php:314 +#: Zotlabs/Module/Fedi_id.php:25 +msgid "You are already connected with this channel." +msgstr "" + +#: Zotlabs/Module/Fedi_id.php:37 +msgid "Unknown or unreachable identifier" +msgstr "" + +#: Zotlabs/Module/Fedi_id.php:48 +msgid "Home instance" +msgstr "" + +#: Zotlabs/Module/Fedi_id.php:49 +msgid "Enter your channel address or fediverse ID (e.g. channel@example.com)" +msgstr "" + +#: Zotlabs/Module/Fedi_id.php:49 +msgid "" +"If you do not have a fediverse ID, please use your browser 'back' button to " +"return to the previous page" +msgstr "" + +#: Zotlabs/Module/Photomap.php:15 +msgid "" +"This app provides a displayable map when viewing detail of photos that " +"contain location information." +msgstr "" + +#: Zotlabs/Module/Photomap.php:23 +msgid "This app is currently installed." +msgstr "" + +#: Zotlabs/Storage/Browser.php:120 Zotlabs/Storage/Browser.php:317 msgid "parent" msgstr "" -#: Zotlabs/Storage/Browser.php:148 +#: Zotlabs/Storage/Browser.php:151 msgid "Principal" msgstr "" -#: Zotlabs/Storage/Browser.php:151 +#: Zotlabs/Storage/Browser.php:154 msgid "Addressbook" msgstr "" -#: Zotlabs/Storage/Browser.php:157 +#: Zotlabs/Storage/Browser.php:160 msgid "Schedule Inbox" msgstr "" -#: Zotlabs/Storage/Browser.php:160 +#: Zotlabs/Storage/Browser.php:163 msgid "Schedule Outbox" msgstr "" -#: Zotlabs/Storage/Browser.php:296 +#: Zotlabs/Storage/Browser.php:299 msgid "Total" msgstr "" -#: Zotlabs/Storage/Browser.php:298 +#: Zotlabs/Storage/Browser.php:301 msgid "Shared" msgstr "" -#: Zotlabs/Storage/Browser.php:300 +#: Zotlabs/Storage/Browser.php:303 msgid "Add Files" msgstr "" -#: Zotlabs/Storage/Browser.php:307 +#: Zotlabs/Storage/Browser.php:310 msgid "View photos" msgstr "" -#: Zotlabs/Storage/Browser.php:311 +#: Zotlabs/Storage/Browser.php:314 msgid "Type" msgstr "" -#: Zotlabs/Storage/Browser.php:389 +#: Zotlabs/Storage/Browser.php:392 #, php-format msgid "You are using %1$s of your available file storage." msgstr "" -#: Zotlabs/Storage/Browser.php:393 +#: Zotlabs/Storage/Browser.php:396 #, php-format msgid "You are using %1$s of %2$s available file storage. (%3$s%)" msgstr "" -#: Zotlabs/Storage/Browser.php:404 +#: Zotlabs/Storage/Browser.php:407 msgid "WARNING:" msgstr "" -#: Zotlabs/Storage/Browser.php:416 +#: Zotlabs/Storage/Browser.php:419 msgid "Create new folder" msgstr "" -#: Zotlabs/Storage/Browser.php:418 +#: Zotlabs/Storage/Browser.php:421 msgid "Upload file" msgstr "" -#: Zotlabs/Storage/Browser.php:431 +#: Zotlabs/Storage/Browser.php:434 msgid "Drop files here to immediately upload" msgstr "" -#: Zotlabs/Update/_1239.php:23 +#: Zotlabs/Update/_1239.php:25 msgid "Added by superblock" msgstr "" -#: Zotlabs/Widget/Categories.php:78 Zotlabs/Widget/Categories.php:122 -#: Zotlabs/Widget/Categories.php:169 Zotlabs/Widget/Appcategories.php:46 -#: Zotlabs/Widget/Filer.php:31 -msgid "Everything" +#: Zotlabs/Widget/Rating.php:58 +msgid "Rating Tools" msgstr "" -#: Zotlabs/Widget/Photo_rand.php:58 Zotlabs/Widget/Photo.php:48 -msgid "photo/image" +#: Zotlabs/Widget/Rating.php:62 Zotlabs/Widget/Rating.php:64 +msgid "Rate Me" msgstr "" -#: Zotlabs/Widget/Archive.php:43 -msgid "Archives" +#: Zotlabs/Widget/Rating.php:68 +msgid "View Ratings" msgstr "" -#: Zotlabs/Widget/Bookmarkedchats.php:24 -msgid "Bookmarked Chatrooms" -msgstr "" - -#: Zotlabs/Widget/Chatroom_list.php:20 -msgid "Overview" -msgstr "" - -#: Zotlabs/Widget/Chatroom_members.php:11 -msgid "Chat Members" -msgstr "" - -#: Zotlabs/Widget/Cover_photo.php:85 -msgid "Click to show more" -msgstr "" - -#: Zotlabs/Widget/Findpeople.php:18 -#, php-format -msgid "%d invitation available" -msgid_plural "%d invitations available" -msgstr[0] "" -msgstr[1] "" - -#: Zotlabs/Widget/Findpeople.php:26 -msgid "Find Channels" -msgstr "" - -#: Zotlabs/Widget/Findpeople.php:27 -msgid "Enter name or interest" -msgstr "" - -#: Zotlabs/Widget/Findpeople.php:28 -msgid "Connect/Follow" -msgstr "" - -#: Zotlabs/Widget/Findpeople.php:29 -msgid "Examples: Robert Morgenstein, Fishing" -msgstr "" - -#: Zotlabs/Widget/Findpeople.php:34 -msgid "Affiliated sites" -msgstr "" - -#: Zotlabs/Widget/Findpeople.php:37 -msgid "Advanced example: name=fred and country=iceland" -msgstr "" - -#: Zotlabs/Widget/Eventstools.php:13 -msgid "Events Tools" -msgstr "" - -#: Zotlabs/Widget/Eventstools.php:14 -msgid "Export Calendar" -msgstr "" - -#: Zotlabs/Widget/Eventstools.php:15 -msgid "Import Calendar" -msgstr "" - -#: Zotlabs/Widget/Follow.php:26 +#: Zotlabs/Widget/Follow.php:29 #, php-format msgid "You have %1$.0f of %2$.0f allowed connections." msgstr "" -#: Zotlabs/Widget/Follow.php:33 +#: Zotlabs/Widget/Follow.php:35 msgid "Add New Connection" msgstr "" -#: Zotlabs/Widget/Follow.php:34 +#: Zotlabs/Widget/Follow.php:36 msgid "Enter channel address" msgstr "" -#: Zotlabs/Widget/Follow.php:35 Zotlabs/Widget/Sblock.php:18 +#: Zotlabs/Widget/Follow.php:37 Zotlabs/Widget/Sblock.php:20 msgid "Examples: bob@example.com, https://example.com/barbara" msgstr "" -#: Zotlabs/Widget/Settings_menu.php:38 -msgid "Account settings" -msgstr "" - -#: Zotlabs/Widget/Settings_menu.php:44 -msgid "Channel settings" -msgstr "" - -#: Zotlabs/Widget/Settings_menu.php:52 -msgid "Display settings" -msgstr "" - -#: Zotlabs/Widget/Settings_menu.php:66 -msgid "Manage locations" -msgstr "" - -#: Zotlabs/Widget/Settings_menu.php:73 -msgid "Export channel" -msgstr "" - -#: Zotlabs/Widget/Settings_menu.php:88 -msgid "Client apps" -msgstr "" - -#: Zotlabs/Widget/Activity_filter.php:34 -msgid "Direct Messages" -msgstr "" - -#: Zotlabs/Widget/Activity_filter.php:38 -msgid "Show direct (private) messages" -msgstr "" - -#: Zotlabs/Widget/Activity_filter.php:48 -msgid "Personal Posts" -msgstr "" - -#: Zotlabs/Widget/Activity_filter.php:52 -msgid "Show posts that mention or involve me" -msgstr "" - -#: Zotlabs/Widget/Activity_filter.php:61 -msgid "Saved Posts" -msgstr "" - -#: Zotlabs/Widget/Activity_filter.php:65 -msgid "Show posts that I have saved" -msgstr "" - -#: Zotlabs/Widget/Activity_filter.php:79 -msgid "Show drafts that I have saved" -msgstr "" - -#: Zotlabs/Widget/Activity_filter.php:89 -msgid "Videos" -msgstr "" - -#: Zotlabs/Widget/Activity_filter.php:93 -msgid "Show posts that include videos" -msgstr "" - -#: Zotlabs/Widget/Activity_filter.php:107 -msgid "Show posts that include events" -msgstr "" - -#: Zotlabs/Widget/Activity_filter.php:111 -msgid "Polls" -msgstr "" - -#: Zotlabs/Widget/Activity_filter.php:115 -msgid "Show posts that include polls" -msgstr "" - -#: Zotlabs/Widget/Activity_filter.php:136 -#: Zotlabs/Widget/Activity_filter.php:166 -#, php-format -msgid "Show posts related to the %s access list" -msgstr "" - -#: Zotlabs/Widget/Activity_filter.php:176 -msgid "Show my access lists" -msgstr "" - -#: Zotlabs/Widget/Activity_filter.php:194 -msgid "Show posts to this group" -msgstr "" - -#: Zotlabs/Widget/Activity_filter.php:196 -msgid "New post" -msgstr "" - -#: Zotlabs/Widget/Activity_filter.php:203 Zotlabs/Widget/Notifications.php:105 -#: Zotlabs/Widget/Notifications.php:106 Zotlabs/Widget/Groups.php:108 -msgid "Groups" -msgstr "" - -#: Zotlabs/Widget/Activity_filter.php:207 -msgid "Show groups" -msgstr "" - -#: Zotlabs/Widget/Activity_filter.php:229 -#, php-format -msgid "Show posts that I have filed to %s" -msgstr "" - -#: Zotlabs/Widget/Activity_filter.php:235 Zotlabs/Widget/Filer.php:28 -#: include/features.php:492 -msgid "Saved Folders" -msgstr "" - -#: Zotlabs/Widget/Activity_filter.php:239 -msgid "Show filed post categories" -msgstr "" - -#: Zotlabs/Widget/Activity_filter.php:259 -#, php-format -msgid "Show posts with hashtag %s" -msgstr "" - -#: Zotlabs/Widget/Activity_filter.php:265 -msgid "Followed Hashtags" -msgstr "" - -#: Zotlabs/Widget/Activity_filter.php:269 -msgid "Show followed hashtags" -msgstr "" - -#: Zotlabs/Widget/Activity_filter.php:307 -msgid "Remove active filter" -msgstr "" - -#: Zotlabs/Widget/Activity_filter.php:323 -msgid "Stream Filters" -msgstr "" - -#: Zotlabs/Widget/Hq_controls.php:14 -msgid "HQ Control Panel" -msgstr "" - -#: Zotlabs/Widget/Hq_controls.php:17 -msgid "Create a new post" -msgstr "" - -#: Zotlabs/Widget/Mailmenu.php:13 -msgid "Private Mail Menu" -msgstr "" - -#: Zotlabs/Widget/Mailmenu.php:15 -msgid "Combined View" -msgstr "" - -#: Zotlabs/Widget/Mailmenu.php:20 -msgid "Inbox" -msgstr "" - -#: Zotlabs/Widget/Mailmenu.php:25 -msgid "Outbox" -msgstr "" - -#: Zotlabs/Widget/Mailmenu.php:30 -msgid "New Message" -msgstr "" - -#: Zotlabs/Widget/Rating.php:51 -msgid "Rating Tools" -msgstr "" - -#: Zotlabs/Widget/Rating.php:55 Zotlabs/Widget/Rating.php:57 -msgid "Rate Me" -msgstr "" - -#: Zotlabs/Widget/Rating.php:60 -msgid "View Ratings" -msgstr "" - -#: Zotlabs/Widget/Savedsearch.php:75 -msgid "Remove term" -msgstr "" - -#: Zotlabs/Widget/Stream_order.php:96 -msgid "Commented Date" -msgstr "" - -#: Zotlabs/Widget/Stream_order.php:100 -msgid "Order by last commented date" -msgstr "" - -#: Zotlabs/Widget/Stream_order.php:103 -msgid "Posted Date" -msgstr "" - -#: Zotlabs/Widget/Stream_order.php:107 -msgid "Order by last posted date" -msgstr "" - -#: Zotlabs/Widget/Stream_order.php:110 -msgid "Date Unthreaded" -msgstr "" - -#: Zotlabs/Widget/Stream_order.php:114 -msgid "Order unthreaded by date" -msgstr "" - -#: Zotlabs/Widget/Suggestedchats.php:32 -msgid "Suggested Chatrooms" -msgstr "" - -#: Zotlabs/Widget/Tagcloud.php:22 include/taxonomy.php:322 -#: include/taxonomy.php:459 include/taxonomy.php:480 +#: Zotlabs/Widget/Tagcloud.php:25 include/taxonomy.php:364 +#: include/taxonomy.php:509 include/taxonomy.php:532 msgid "Tags" msgstr "" -#: Zotlabs/Widget/Common_friends.php:40 -msgid "Common Connections" -msgstr "" - -#: Zotlabs/Widget/Common_friends.php:45 -#, php-format -msgid "View all %d common connections" -msgstr "" - -#: Zotlabs/Widget/Notifications.php:17 -msgid "New Stream Activity" -msgstr "" - -#: Zotlabs/Widget/Notifications.php:18 -msgid "New Stream Activity Notifications" -msgstr "" - -#: Zotlabs/Widget/Notifications.php:21 -msgid "View your stream activity" -msgstr "" - -#: Zotlabs/Widget/Notifications.php:24 -msgid "Mark all notifications read" -msgstr "" - -#: Zotlabs/Widget/Notifications.php:27 Zotlabs/Widget/Notifications.php:46 -#: Zotlabs/Widget/Notifications.php:138 -msgid "Show new posts only" -msgstr "" - -#: Zotlabs/Widget/Notifications.php:28 Zotlabs/Widget/Notifications.php:47 -#: Zotlabs/Widget/Notifications.php:108 Zotlabs/Widget/Notifications.php:139 -msgid "Filter by name" -msgstr "" - -#: Zotlabs/Widget/Notifications.php:36 -msgid "New Home Activity" -msgstr "" - -#: Zotlabs/Widget/Notifications.php:37 -msgid "New Home Activity Notifications" -msgstr "" - -#: Zotlabs/Widget/Notifications.php:40 -msgid "View your home activity" -msgstr "" - -#: Zotlabs/Widget/Notifications.php:43 Zotlabs/Widget/Notifications.php:135 -msgid "Mark all notifications seen" -msgstr "" - -#: Zotlabs/Widget/Notifications.php:55 -msgid "New Events" -msgstr "" - -#: Zotlabs/Widget/Notifications.php:56 -msgid "New Events Notifications" -msgstr "" - -#: Zotlabs/Widget/Notifications.php:59 -msgid "View events" -msgstr "" - -#: Zotlabs/Widget/Notifications.php:62 -msgid "Mark all events seen" -msgstr "" - -#: Zotlabs/Widget/Notifications.php:71 -msgid "New Connections Notifications" -msgstr "" - -#: Zotlabs/Widget/Notifications.php:74 -msgid "View all connections" -msgstr "" - -#: Zotlabs/Widget/Notifications.php:82 -msgid "New Files" -msgstr "" - -#: Zotlabs/Widget/Notifications.php:83 -msgid "New Files Notifications" -msgstr "" - -#: Zotlabs/Widget/Notifications.php:90 Zotlabs/Widget/Notifications.php:91 -msgid "Notices" -msgstr "" - -#: Zotlabs/Widget/Notifications.php:94 -msgid "View all notices" -msgstr "" - -#: Zotlabs/Widget/Notifications.php:97 -msgid "Mark all notices seen" -msgstr "" - -#: Zotlabs/Widget/Notifications.php:118 -msgid "New Registrations" -msgstr "" - -#: Zotlabs/Widget/Notifications.php:119 -msgid "New Registrations Notifications" -msgstr "" - -#: Zotlabs/Widget/Notifications.php:129 -msgid "Public Stream Notifications" -msgstr "" - -#: Zotlabs/Widget/Notifications.php:132 -msgid "View the public stream" -msgstr "" - -#: Zotlabs/Widget/Notifications.php:147 -msgid "Sorry, you have got no notifications at the moment" -msgstr "" - -#: Zotlabs/Widget/Affinity.php:37 -msgid "Friend zoom in/out" -msgstr "" - -#: Zotlabs/Widget/Suggestions.php:49 -msgid "Ignore/Hide" -msgstr "" - -#: Zotlabs/Widget/Suggestions.php:54 -msgid "Suggestions" -msgstr "" - -#: Zotlabs/Widget/Suggestions.php:55 -msgid "See more..." +#: Zotlabs/Widget/Photo.php:55 Zotlabs/Widget/Photo_rand.php:72 +msgid "photo/image" msgstr "" #: Zotlabs/Widget/Activity.php:58 @@ -9791,221 +9406,615 @@ msgctxt "widget" msgid "Activity" msgstr "" -#: Zotlabs/Widget/Site_projects.php:33 -msgid "Projects" -msgstr "" - -#: Zotlabs/Widget/Site_projects.php:35 -msgid "All projects" -msgstr "" - -#: Zotlabs/Widget/Newmember.php:29 -msgid "Profile Creation" -msgstr "" - -#: Zotlabs/Widget/Newmember.php:31 -msgid "Upload profile photo" -msgstr "" - -#: Zotlabs/Widget/Newmember.php:32 -msgid "Upload cover photo" -msgstr "" - -#: Zotlabs/Widget/Newmember.php:33 include/nav.php:121 -msgid "Edit your profile" -msgstr "" - -#: Zotlabs/Widget/Newmember.php:36 -msgid "Find and Connect with others" -msgstr "" - -#: Zotlabs/Widget/Newmember.php:38 -msgid "View the directory" -msgstr "" - -#: Zotlabs/Widget/Newmember.php:39 -msgid "View friend suggestions" -msgstr "" - -#: Zotlabs/Widget/Newmember.php:40 -msgid "Manage your connections" -msgstr "" - -#: Zotlabs/Widget/Newmember.php:43 -msgid "Communicate" -msgstr "" - -#: Zotlabs/Widget/Newmember.php:45 -msgid "View your channel homepage" -msgstr "" - -#: Zotlabs/Widget/Newmember.php:46 -msgid "View your stream" -msgstr "" - -#: Zotlabs/Widget/Newmember.php:59 -msgid "View public stream" -msgstr "" - -#: Zotlabs/Widget/Newmember.php:63 include/features.php:60 -msgid "New Member Links" -msgstr "" - -#: Zotlabs/Widget/Sblock.php:16 -msgid "Block channel or site" -msgstr "" - -#: Zotlabs/Widget/Sblock.php:17 -msgid "Enter channel address or URL" -msgstr "" - -#: Zotlabs/Widget/Appstore.php:11 -msgid "App Collections" -msgstr "" - -#: Zotlabs/Widget/Cdav.php:37 +#: Zotlabs/Widget/Cdav.php:43 msgid "Select Channel" msgstr "" -#: Zotlabs/Widget/Cdav.php:42 +#: Zotlabs/Widget/Cdav.php:48 msgid "Read-write" msgstr "" -#: Zotlabs/Widget/Cdav.php:43 +#: Zotlabs/Widget/Cdav.php:49 msgid "Read-only" msgstr "" -#: Zotlabs/Widget/Cdav.php:127 +#: Zotlabs/Widget/Cdav.php:135 msgid "Channel Calendar" msgstr "" -#: Zotlabs/Widget/Cdav.php:131 +#: Zotlabs/Widget/Cdav.php:139 msgid "Shared CalDAV Calendars" msgstr "" -#: Zotlabs/Widget/Cdav.php:135 +#: Zotlabs/Widget/Cdav.php:143 msgid "Share this calendar" msgstr "" -#: Zotlabs/Widget/Cdav.php:137 +#: Zotlabs/Widget/Cdav.php:145 msgid "Calendar name and color" msgstr "" -#: Zotlabs/Widget/Cdav.php:139 +#: Zotlabs/Widget/Cdav.php:147 msgid "Create new CalDAV calendar" msgstr "" -#: Zotlabs/Widget/Cdav.php:141 +#: Zotlabs/Widget/Cdav.php:149 msgid "Calendar Name" msgstr "" -#: Zotlabs/Widget/Cdav.php:142 +#: Zotlabs/Widget/Cdav.php:150 msgid "Calendar Tools" msgstr "" -#: Zotlabs/Widget/Cdav.php:144 +#: Zotlabs/Widget/Cdav.php:152 msgid "Import calendar" msgstr "" -#: Zotlabs/Widget/Cdav.php:145 +#: Zotlabs/Widget/Cdav.php:153 msgid "Select a calendar to import to" msgstr "" -#: Zotlabs/Widget/Cdav.php:172 +#: Zotlabs/Widget/Cdav.php:178 msgid "Addressbooks" msgstr "" -#: Zotlabs/Widget/Cdav.php:174 +#: Zotlabs/Widget/Cdav.php:180 msgid "Addressbook name" msgstr "" -#: Zotlabs/Widget/Cdav.php:176 +#: Zotlabs/Widget/Cdav.php:182 msgid "Create new addressbook" msgstr "" -#: Zotlabs/Widget/Cdav.php:177 +#: Zotlabs/Widget/Cdav.php:183 msgid "Addressbook Name" msgstr "" -#: Zotlabs/Widget/Cdav.php:179 +#: Zotlabs/Widget/Cdav.php:185 msgid "Addressbook Tools" msgstr "" -#: Zotlabs/Widget/Cdav.php:180 +#: Zotlabs/Widget/Cdav.php:186 msgid "Import addressbook" msgstr "" -#: Zotlabs/Widget/Cdav.php:181 +#: Zotlabs/Widget/Cdav.php:187 msgid "Select an addressbook to import to" msgstr "" -#: Zotlabs/Widget/Admin.php:25 Zotlabs/Widget/Admin.php:62 +#: Zotlabs/Widget/Savedsearch.php:86 +msgid "Remove term" +msgstr "" + +#: Zotlabs/Widget/Activity_filter.php:36 +msgid "Direct Messages" +msgstr "" + +#: Zotlabs/Widget/Activity_filter.php:40 +msgid "Show direct (private) messages" +msgstr "" + +#: Zotlabs/Widget/Activity_filter.php:50 +msgid "Personal Posts" +msgstr "" + +#: Zotlabs/Widget/Activity_filter.php:54 +msgid "Show posts that mention or involve me" +msgstr "" + +#: Zotlabs/Widget/Activity_filter.php:63 +msgid "Saved Posts" +msgstr "" + +#: Zotlabs/Widget/Activity_filter.php:67 +msgid "Show posts that I have saved" +msgstr "" + +#: Zotlabs/Widget/Activity_filter.php:81 +msgid "Show drafts that I have saved" +msgstr "" + +#: Zotlabs/Widget/Activity_filter.php:91 +msgid "Videos" +msgstr "" + +#: Zotlabs/Widget/Activity_filter.php:95 +msgid "Show posts that include videos" +msgstr "" + +#: Zotlabs/Widget/Activity_filter.php:109 +msgid "Show posts that include events" +msgstr "" + +#: Zotlabs/Widget/Activity_filter.php:113 +msgid "Polls" +msgstr "" + +#: Zotlabs/Widget/Activity_filter.php:117 +msgid "Show posts that include polls" +msgstr "" + +#: Zotlabs/Widget/Activity_filter.php:138 +#: Zotlabs/Widget/Activity_filter.php:168 +#, php-format +msgid "Show posts related to the %s access list" +msgstr "" + +#: Zotlabs/Widget/Activity_filter.php:178 +msgid "Show my access lists" +msgstr "" + +#: Zotlabs/Widget/Activity_filter.php:196 +msgid "Show posts to this group" +msgstr "" + +#: Zotlabs/Widget/Activity_filter.php:198 +msgid "New post" +msgstr "" + +#: Zotlabs/Widget/Activity_filter.php:205 Zotlabs/Widget/Groups.php:114 +#: Zotlabs/Widget/Notifications.php:108 Zotlabs/Widget/Notifications.php:109 +msgid "Groups" +msgstr "" + +#: Zotlabs/Widget/Activity_filter.php:209 +msgid "Show groups" +msgstr "" + +#: Zotlabs/Widget/Activity_filter.php:232 +#, php-format +msgid "Show posts that I have filed to %s" +msgstr "" + +#: Zotlabs/Widget/Activity_filter.php:238 Zotlabs/Widget/Filer.php:34 +#: include/features.php:500 +msgid "Saved Folders" +msgstr "" + +#: Zotlabs/Widget/Activity_filter.php:242 +msgid "Show filed post categories" +msgstr "" + +#: Zotlabs/Widget/Activity_filter.php:262 +#, php-format +msgid "Show posts with hashtag %s" +msgstr "" + +#: Zotlabs/Widget/Activity_filter.php:268 +msgid "Followed Hashtags" +msgstr "" + +#: Zotlabs/Widget/Activity_filter.php:272 +msgid "Show followed hashtags" +msgstr "" + +#: Zotlabs/Widget/Activity_filter.php:308 +msgid "Remove active filter" +msgstr "" + +#: Zotlabs/Widget/Activity_filter.php:324 +msgid "Stream Filters" +msgstr "" + +#: Zotlabs/Widget/Appcategories.php:52 Zotlabs/Widget/Filer.php:37 +#: Zotlabs/Widget/Categories.php:88 Zotlabs/Widget/Categories.php:136 +#: Zotlabs/Widget/Categories.php:187 +msgid "Everything" +msgstr "" + +#: Zotlabs/Widget/Hq_controls.php:18 +msgid "HQ Control Panel" +msgstr "" + +#: Zotlabs/Widget/Hq_controls.php:21 +msgid "Create a new post" +msgstr "" + +#: Zotlabs/Widget/Mailmenu.php:16 +msgid "Private Mail Menu" +msgstr "" + +#: Zotlabs/Widget/Mailmenu.php:18 +msgid "Combined View" +msgstr "" + +#: Zotlabs/Widget/Mailmenu.php:23 +msgid "Inbox" +msgstr "" + +#: Zotlabs/Widget/Mailmenu.php:28 +msgid "Outbox" +msgstr "" + +#: Zotlabs/Widget/Mailmenu.php:33 +msgid "New Message" +msgstr "" + +#: Zotlabs/Widget/Sblock.php:18 +msgid "Block channel or site" +msgstr "" + +#: Zotlabs/Widget/Sblock.php:19 +msgid "Enter channel address or URL" +msgstr "" + +#: Zotlabs/Widget/Stream_order.php:104 +msgid "Commented Date" +msgstr "" + +#: Zotlabs/Widget/Stream_order.php:108 +msgid "Order by last commented date" +msgstr "" + +#: Zotlabs/Widget/Stream_order.php:111 +msgid "Posted Date" +msgstr "" + +#: Zotlabs/Widget/Stream_order.php:115 +msgid "Order by last posted date" +msgstr "" + +#: Zotlabs/Widget/Stream_order.php:118 +msgid "Date Unthreaded" +msgstr "" + +#: Zotlabs/Widget/Stream_order.php:122 +msgid "Order unthreaded by date" +msgstr "" + +#: Zotlabs/Widget/Suggestions.php:52 +msgid "Ignore/Hide" +msgstr "" + +#: Zotlabs/Widget/Suggestions.php:57 +msgid "Suggestions" +msgstr "" + +#: Zotlabs/Widget/Suggestions.php:58 +msgid "See more..." +msgstr "" + +#: Zotlabs/Widget/Admin.php:29 Zotlabs/Widget/Admin.php:66 msgid "Member registrations waiting for confirmation" msgstr "" -#: Zotlabs/Widget/Admin.php:31 +#: Zotlabs/Widget/Admin.php:35 msgid "Inspect queue" msgstr "" -#: Zotlabs/Widget/Admin.php:33 +#: Zotlabs/Widget/Admin.php:37 msgid "DB updates" msgstr "" -#: Zotlabs/Widget/Admin.php:57 include/nav.php:191 +#: Zotlabs/Widget/Admin.php:61 include/nav.php:190 msgid "Admin" msgstr "" -#: Zotlabs/Widget/Admin.php:58 +#: Zotlabs/Widget/Admin.php:62 msgid "Addon Features" msgstr "" -#: Zotlabs/Import/Friendica.php:192 include/channel.php:455 +#: Zotlabs/Widget/Appstore.php:12 +msgid "App Collections" +msgstr "" + +#: Zotlabs/Widget/Archive.php:49 +msgid "Archives" +msgstr "" + +#: Zotlabs/Widget/Bookmarkedchats.php:31 +msgid "Bookmarked Chatrooms" +msgstr "" + +#: Zotlabs/Widget/Chatroom_list.php:26 +msgid "Overview" +msgstr "" + +#: Zotlabs/Widget/Chatroom_members.php:13 +msgid "Chat Members" +msgstr "" + +#: Zotlabs/Widget/Common_friends.php:47 +msgid "Common Connections" +msgstr "" + +#: Zotlabs/Widget/Common_friends.php:52 +#, php-format +msgid "View all %d common connections" +msgstr "" + +#: Zotlabs/Widget/Cover_photo.php:95 +msgid "Click to show more" +msgstr "" + +#: Zotlabs/Widget/Eventstools.php:16 +msgid "Events Tools" +msgstr "" + +#: Zotlabs/Widget/Eventstools.php:17 +msgid "Export Calendar" +msgstr "" + +#: Zotlabs/Widget/Eventstools.php:18 +msgid "Import Calendar" +msgstr "" + +#: Zotlabs/Widget/Findpeople.php:21 +#, php-format +msgid "%d invitation available" +msgid_plural "%d invitations available" +msgstr[0] "" +msgstr[1] "" + +#: Zotlabs/Widget/Findpeople.php:29 +msgid "Find Channels" +msgstr "" + +#: Zotlabs/Widget/Findpeople.php:30 +msgid "Enter name or interest" +msgstr "" + +#: Zotlabs/Widget/Findpeople.php:31 +msgid "Connect/Follow" +msgstr "" + +#: Zotlabs/Widget/Findpeople.php:32 +msgid "Examples: Robert Morgenstein, Fishing" +msgstr "" + +#: Zotlabs/Widget/Findpeople.php:37 +msgid "Affiliated sites" +msgstr "" + +#: Zotlabs/Widget/Findpeople.php:40 +msgid "Advanced example: name=fred and country=iceland" +msgstr "" + +#: Zotlabs/Widget/Newmember.php:32 +msgid "Profile Creation" +msgstr "" + +#: Zotlabs/Widget/Newmember.php:34 +msgid "Upload profile photo" +msgstr "" + +#: Zotlabs/Widget/Newmember.php:35 +msgid "Upload cover photo" +msgstr "" + +#: Zotlabs/Widget/Newmember.php:36 include/nav.php:122 +msgid "Edit your profile" +msgstr "" + +#: Zotlabs/Widget/Newmember.php:39 +msgid "Find and Connect with others" +msgstr "" + +#: Zotlabs/Widget/Newmember.php:41 +msgid "View the directory" +msgstr "" + +#: Zotlabs/Widget/Newmember.php:42 +msgid "View friend suggestions" +msgstr "" + +#: Zotlabs/Widget/Newmember.php:43 +msgid "Manage your connections" +msgstr "" + +#: Zotlabs/Widget/Newmember.php:46 +msgid "Communicate" +msgstr "" + +#: Zotlabs/Widget/Newmember.php:48 +msgid "View your channel homepage" +msgstr "" + +#: Zotlabs/Widget/Newmember.php:49 +msgid "View your stream" +msgstr "" + +#: Zotlabs/Widget/Newmember.php:62 +msgid "View public stream" +msgstr "" + +#: Zotlabs/Widget/Newmember.php:66 include/features.php:68 +msgid "New Member Links" +msgstr "" + +#: Zotlabs/Widget/Settings_menu.php:44 +msgid "Account settings" +msgstr "" + +#: Zotlabs/Widget/Settings_menu.php:50 +msgid "Channel settings" +msgstr "" + +#: Zotlabs/Widget/Settings_menu.php:58 +msgid "Display settings" +msgstr "" + +#: Zotlabs/Widget/Settings_menu.php:72 +msgid "Manage locations" +msgstr "" + +#: Zotlabs/Widget/Settings_menu.php:79 +msgid "Export channel" +msgstr "" + +#: Zotlabs/Widget/Settings_menu.php:94 +msgid "Client apps" +msgstr "" + +#: Zotlabs/Widget/Site_projects.php:32 +msgid "Projects" +msgstr "" + +#: Zotlabs/Widget/Site_projects.php:34 +msgid "All projects" +msgstr "" + +#: Zotlabs/Widget/Suggestedchats.php:38 +msgid "Suggested Chatrooms" +msgstr "" + +#: Zotlabs/Widget/Notifications.php:20 +msgid "New Stream Activity" +msgstr "" + +#: Zotlabs/Widget/Notifications.php:21 +msgid "New Stream Activity Notifications" +msgstr "" + +#: Zotlabs/Widget/Notifications.php:24 +msgid "View your stream activity" +msgstr "" + +#: Zotlabs/Widget/Notifications.php:27 +msgid "Mark all notifications read" +msgstr "" + +#: Zotlabs/Widget/Notifications.php:30 Zotlabs/Widget/Notifications.php:49 +#: Zotlabs/Widget/Notifications.php:141 +msgid "Show new posts only" +msgstr "" + +#: Zotlabs/Widget/Notifications.php:31 Zotlabs/Widget/Notifications.php:50 +#: Zotlabs/Widget/Notifications.php:111 Zotlabs/Widget/Notifications.php:142 +msgid "Filter by name" +msgstr "" + +#: Zotlabs/Widget/Notifications.php:39 +msgid "New Home Activity" +msgstr "" + +#: Zotlabs/Widget/Notifications.php:40 +msgid "New Home Activity Notifications" +msgstr "" + +#: Zotlabs/Widget/Notifications.php:43 +msgid "View your home activity" +msgstr "" + +#: Zotlabs/Widget/Notifications.php:46 Zotlabs/Widget/Notifications.php:138 +msgid "Mark all notifications seen" +msgstr "" + +#: Zotlabs/Widget/Notifications.php:58 +msgid "New Events" +msgstr "" + +#: Zotlabs/Widget/Notifications.php:59 +msgid "New Events Notifications" +msgstr "" + +#: Zotlabs/Widget/Notifications.php:62 +msgid "View events" +msgstr "" + +#: Zotlabs/Widget/Notifications.php:65 +msgid "Mark all events seen" +msgstr "" + +#: Zotlabs/Widget/Notifications.php:74 +msgid "New Connections Notifications" +msgstr "" + +#: Zotlabs/Widget/Notifications.php:77 +msgid "View all connections" +msgstr "" + +#: Zotlabs/Widget/Notifications.php:85 +msgid "New Files" +msgstr "" + +#: Zotlabs/Widget/Notifications.php:86 +msgid "New Files Notifications" +msgstr "" + +#: Zotlabs/Widget/Notifications.php:93 Zotlabs/Widget/Notifications.php:94 +msgid "Notices" +msgstr "" + +#: Zotlabs/Widget/Notifications.php:97 +msgid "View all notices" +msgstr "" + +#: Zotlabs/Widget/Notifications.php:100 +msgid "Mark all notices seen" +msgstr "" + +#: Zotlabs/Widget/Notifications.php:121 +msgid "New Registrations" +msgstr "" + +#: Zotlabs/Widget/Notifications.php:122 +msgid "New Registrations Notifications" +msgstr "" + +#: Zotlabs/Widget/Notifications.php:132 +msgid "Public Stream Notifications" +msgstr "" + +#: Zotlabs/Widget/Notifications.php:135 +msgid "View the public stream" +msgstr "" + +#: Zotlabs/Widget/Notifications.php:150 +msgid "Sorry, you have got no notifications at the moment" +msgstr "" + +#: Zotlabs/Widget/Affinity.php:39 +msgid "Friend zoom in/out" +msgstr "" + +#: Zotlabs/Import/Friendica.php:195 include/channel.php:480 msgid "Default Profile" msgstr "" -#: boot.php:1697 +#: boot.php:1705 msgid "Create an account to access services and applications" msgstr "" -#: boot.php:1717 include/nav.php:113 include/nav.php:142 include/nav.php:161 +#: boot.php:1725 include/nav.php:114 include/nav.php:141 include/nav.php:159 msgid "Logout" msgstr "" -#: boot.php:1721 +#: boot.php:1729 msgid "Login/Email" msgstr "" -#: boot.php:1722 +#: boot.php:1730 msgid "Password" msgstr "" -#: boot.php:1723 +#: boot.php:1731 msgid "Remember me" msgstr "" -#: boot.php:1726 +#: boot.php:1734 msgid "Forgot your password?" msgstr "" -#: boot.php:2507 +#: boot.php:2541 #, php-format msgid "[$Projectname] Website SSL error for %s" msgstr "" -#: boot.php:2512 +#: boot.php:2546 msgid "Website SSL certificate is not valid. Please correct." msgstr "" -#: boot.php:2556 +#: boot.php:2590 #, php-format msgid "[$Projectname] Cron tasks not running on %s" msgstr "" -#: boot.php:2561 +#: boot.php:2595 msgid "Cron/Scheduled tasks not running." msgstr "" -#: boot.php:2562 include/datetime.php:240 +#: boot.php:2596 include/datetime.php:277 msgid "never" msgstr "" @@ -10428,547 +10437,39 @@ msgstr "" msgid "Photo Gallery" msgstr "" -#: include/features.php:56 -msgid "General Features" -msgstr "" - -#: include/features.php:61 -msgid "Display new member quick links menu" -msgstr "" - -#: include/features.php:69 -msgid "Advanced Profiles" -msgstr "" - -#: include/features.php:70 -msgid "Additional profile sections and selections" -msgstr "" - -#: include/features.php:116 -msgid "Private Notes" -msgstr "" - -#: include/features.php:117 -msgid "Enables a tool to store notes and reminders (note: not encrypted)" -msgstr "" - -#: include/features.php:137 -msgid "Create interactive articles" -msgstr "" - -#: include/features.php:154 -msgid "Photo Location" -msgstr "" - -#: include/features.php:155 -msgid "If location data is available on uploaded photos, link this to a map." -msgstr "" - -#: include/features.php:183 -msgid "Event Timezone Selection" -msgstr "" - -#: include/features.php:184 -msgid "Allow event creation in timezones other than your own." -msgstr "" - -#: include/features.php:202 -msgid "Advanced Directory Search" -msgstr "" - -#: include/features.php:203 -msgid "Allows creation of complex directory search queries" -msgstr "" - -#: include/features.php:211 -msgid "Advanced Theme and Layout Settings" -msgstr "" - -#: include/features.php:212 -msgid "Allows fine tuning of themes and page layouts" -msgstr "" - -#: include/features.php:221 -msgid "Access Control and Permissions" -msgstr "" - -#: include/features.php:225 -msgid "Privacy Groups" -msgstr "" - -#: include/features.php:226 -msgid "Enable management and selection of privacy groups" -msgstr "" - -#: include/features.php:262 -msgid "OAuth2 Clients" -msgstr "" - -#: include/features.php:263 -msgid "Manage OAuth2 authenticatication tokens for mobile and remote apps." -msgstr "" - -#: include/features.php:283 -msgid "Post Composition Features" -msgstr "" - -#: include/features.php:305 -msgid "Browser Encryption" -msgstr "" - -#: include/features.php:306 -msgid "" -"Provide optional browser-to-browser encryption of content with a shared " -"secret key" -msgstr "" - -#: include/features.php:350 -msgid "Suppress Duplicate Posts/Comments" -msgstr "" - -#: include/features.php:351 -msgid "" -"Prevent posts with identical content to be published with less than two " -"minutes in between submissions." -msgstr "" - -#: include/features.php:359 -msgid "Auto-save drafts of posts and comments" -msgstr "" - -#: include/features.php:360 -msgid "" -"Automatically saves post and comment drafts in local browser storage to help " -"prevent accidental loss of compositions" -msgstr "" - -#: include/features.php:371 -msgid "Network and Stream Filtering" -msgstr "" - -#: include/features.php:461 -msgid "Post/Comment Tools" -msgstr "" - -#: include/features.php:465 -msgid "Community Tagging" -msgstr "" - -#: include/features.php:466 -msgid "Ability to tag existing posts" -msgstr "" - -#: include/features.php:474 -msgid "Post Categories" -msgstr "" - -#: include/features.php:475 -msgid "Add categories to your posts" -msgstr "" - -#: include/features.php:483 -msgid "Emoji Reactions" -msgstr "" - -#: include/features.php:484 -msgid "Add emoji reaction ability to posts" -msgstr "" - -#: include/features.php:493 -msgid "Ability to file posts under folders" -msgstr "" - -#: include/features.php:501 -msgid "Dislike Posts" -msgstr "" - -#: include/features.php:502 -msgid "Ability to dislike posts/comments" -msgstr "" - -#: include/features.php:519 -msgid "Tag Cloud" -msgstr "" - -#: include/features.php:520 -msgid "Provide a personal tag cloud on your channel page" -msgstr "" - -#: include/bbcode.php:283 -msgid "Encrypted content" -msgstr "" - -#: include/bbcode.php:323 +#: include/dba/dba_driver.php:190 #, php-format -msgid "(Embedded app '%s' could not be displayed)." +msgid "Cannot locate DNS info for database server '%s'" msgstr "" -#: include/bbcode.php:363 -#, php-format -msgid "Install %1$s element %2$s" -msgstr "" - -#: include/bbcode.php:367 -#, php-format -msgid "" -"This post contains an installable %s element, however you lack permissions " -"to install it on this site." -msgstr "" - -#: include/bbcode.php:624 -msgid "card" -msgstr "" - -#: include/bbcode.php:626 -msgid "article" -msgstr "" - -#: include/bbcode.php:709 include/bbcode.php:717 -msgid "Click to open/close" -msgstr "" - -#: include/bbcode.php:717 -msgid "spoiler" -msgstr "" - -#: include/bbcode.php:1310 include/bbcode.php:1580 -msgid "Different viewers will see this text differently" -msgstr "" - -#: include/bbcode.php:1906 -msgid "$1 wrote:" -msgstr "" - -#: include/auth.php:196 -msgid "Delegation session ended." -msgstr "" - -#: include/auth.php:200 -msgid "Logged out." -msgstr "" - -#: include/auth.php:296 -msgid "Email validation is incomplete. Please check your email." -msgstr "" - -#: include/auth.php:312 -msgid "Failed authentication" -msgstr "" - -#: include/auth.php:322 -msgid "Login failed." -msgstr "" - -#: include/security.php:619 +#: include/security.php:643 msgid "" "The form security token was not correct. This probably happened because the " "form has been opened for too long (>3 hours) before submitting it." msgstr "" -#: include/dba/dba_driver.php:179 -#, php-format -msgid "Cannot locate DNS info for database server '%s'" -msgstr "" - -#: include/account.php:31 -msgid "Not a valid email address" -msgstr "" - -#: include/account.php:34 -msgid "Your email domain is not among those allowed on this site" -msgstr "" - -#: include/account.php:41 -msgid "Your email address is already registered at this site." -msgstr "" - -#: include/account.php:74 -msgid "An invitation is required." -msgstr "" - -#: include/account.php:78 -msgid "Invitation could not be verified." -msgstr "" - -#: include/account.php:157 -msgid "Please enter the required information." -msgstr "" - -#: include/account.php:221 -msgid "Failed to store account information." -msgstr "" - -#: include/account.php:310 -#, php-format -msgid "Registration confirmation for %s" -msgstr "" - -#: include/account.php:386 -#, php-format -msgid "Registration request at %s" -msgstr "" - -#: include/account.php:410 -msgid "your registration password" -msgstr "" - -#: include/account.php:416 include/account.php:480 -#, php-format -msgid "Registration details for %s" -msgstr "" - -#: include/account.php:492 -msgid "Account approved." -msgstr "" - -#: include/account.php:534 -#, php-format -msgid "Registration revoked for %s" -msgstr "" - -#: include/account.php:821 include/account.php:823 -msgid "Click here to upgrade." -msgstr "" - -#: include/account.php:829 -msgid "This action exceeds the limits set by your subscription plan." -msgstr "" - -#: include/account.php:834 -msgid "This action is not available under your subscription plan." -msgstr "" - -#: include/channel.php:47 -msgid "Unable to obtain identity information from database" -msgstr "" - -#: include/channel.php:80 -msgid "Empty name" -msgstr "" - -#: include/channel.php:83 -msgid "Name too long" -msgstr "" - -#: include/channel.php:256 -msgid "No account identifier" -msgstr "" - -#: include/channel.php:268 -msgid "Nickname is required." -msgstr "" - -#: include/channel.php:356 -msgid "Unable to retrieve created identity" -msgstr "" - -#: include/channel.php:647 include/channel.php:737 -msgid "Unable to retrieve modified identity" -msgstr "" - -#: include/taxonomy.php:322 -msgid "Popular Tags" -msgstr "" - -#: include/taxonomy.php:566 -msgid "Keywords" -msgstr "" - -#: include/taxonomy.php:587 -msgid "have" -msgstr "" - -#: include/taxonomy.php:587 -msgid "has" -msgstr "" - -#: include/taxonomy.php:588 -msgid "want" -msgstr "" - -#: include/taxonomy.php:588 -msgid "wants" -msgstr "" - -#: include/taxonomy.php:589 -msgid "like" -msgstr "" - -#: include/taxonomy.php:589 -msgid "likes" -msgstr "" - -#: include/taxonomy.php:590 -msgid "dislike" -msgstr "" - -#: include/taxonomy.php:590 -msgid "dislikes" -msgstr "" - -#: include/oembed.php:150 -msgid "View PDF" -msgstr "" - -#: include/oembed.php:357 -msgid " by " -msgstr "" - -#: include/oembed.php:358 -msgid " on " -msgstr "" - -#: include/oembed.php:387 -msgid "Embedded content" -msgstr "" - -#: include/oembed.php:396 -msgid "Embedding disabled" -msgstr "" - -#: include/datetime.php:142 -msgid "Birthday" -msgstr "" - -#: include/datetime.php:142 -msgid "Age: " -msgstr "" - -#: include/datetime.php:142 -msgid "YYYY-MM-DD or MM-DD" -msgstr "" - -#: include/datetime.php:244 -msgid "from now" -msgstr "" - -#: include/datetime.php:248 -msgid "ago" -msgstr "" - -#: include/datetime.php:253 -#, php-format -msgid "less than a second %s" -msgstr "" - -#: include/datetime.php:271 -#, php-format -msgctxt "e.g. 22 hours ago, 1 minute ago" -msgid "%1$d %2$s %3$s" -msgstr "" - -#: include/datetime.php:282 -msgctxt "relative_date" -msgid "year" -msgid_plural "years" -msgstr[0] "" -msgstr[1] "" - -#: include/datetime.php:285 -msgctxt "relative_date" -msgid "month" -msgid_plural "months" -msgstr[0] "" -msgstr[1] "" - -#: include/datetime.php:288 -msgctxt "relative_date" -msgid "week" -msgid_plural "weeks" -msgstr[0] "" -msgstr[1] "" - -#: include/datetime.php:291 -msgctxt "relative_date" -msgid "day" -msgid_plural "days" -msgstr[0] "" -msgstr[1] "" - -#: include/datetime.php:294 -msgctxt "relative_date" -msgid "hour" -msgid_plural "hours" -msgstr[0] "" -msgstr[1] "" - -#: include/datetime.php:297 -msgctxt "relative_date" -msgid "minute" -msgid_plural "minutes" -msgstr[0] "" -msgstr[1] "" - -#: include/datetime.php:300 -msgctxt "relative_date" -msgid "second" -msgid_plural "seconds" -msgstr[0] "" -msgstr[1] "" - -#: include/datetime.php:529 -#, php-format -msgid "%1$s's birthday" -msgstr "" - -#: include/datetime.php:530 -#, php-format -msgid "Happy Birthday %1$s" -msgstr "" - -#: include/photos.php:153 -#, php-format -msgid "Image exceeds website size limit of %lu bytes" -msgstr "" - -#: include/photos.php:164 -msgid "Image file is empty." -msgstr "" - -#: include/photos.php:347 -msgid "Photo storage failed." -msgstr "" - -#: include/photos.php:397 -msgid "a new photo" -msgstr "" - -#: include/photos.php:401 -#, php-format -msgctxt "photo_upload" -msgid "%1$s posted %2$s to %3$s" -msgstr "" - -#: include/photos.php:745 include/nav.php:424 include/conversation.php:1944 -msgid "Photo Albums" -msgstr "" - -#: include/photos.php:750 -msgid "Upload New Photos" -msgstr "" - -#: include/acl_selectors.php:93 +#: include/acl_selectors.php:94 msgid "(List)" msgstr "" -#: include/acl_selectors.php:99 +#: include/acl_selectors.php:100 msgid "My connections" msgstr "" -#: include/acl_selectors.php:99 include/acl_selectors.php:102 -#: include/acl_selectors.php:105 +#: include/acl_selectors.php:100 include/acl_selectors.php:103 +#: include/acl_selectors.php:106 msgid "(Virtual List)" msgstr "" -#: include/acl_selectors.php:102 +#: include/acl_selectors.php:103 msgid "My ActivityPub connections" msgstr "" -#: include/acl_selectors.php:105 +#: include/acl_selectors.php:106 msgid "My Nomad connections" msgstr "" -#: include/acl_selectors.php:116 +#: include/acl_selectors.php:115 msgid "(Group)" msgstr "" @@ -10998,759 +10499,108 @@ msgstr "" msgid "Don't show" msgstr "" -#: include/acl_selectors.php:179 +#: include/acl_selectors.php:180 msgid "" "Post permissions cannot be changed after a post is shared.
                    These " "permissions set who is allowed to view the post." msgstr "" -#: include/import.php:33 -msgid "Unable to import a removed channel." +#: include/bbcode.php:316 +msgid "Encrypted content" msgstr "" -#: include/import.php:60 +#: include/bbcode.php:359 +#, php-format +msgid "(Embedded app '%s' could not be displayed)." +msgstr "" + +#: include/bbcode.php:402 +#, php-format +msgid "Install %1$s element %2$s" +msgstr "" + +#: include/bbcode.php:405 +#, php-format msgid "" -"A channel with these settings was discovered and is not usable as it was " -"removed or reserved for system use. Import failed." +"This post contains an installable %s element, however you lack permissions " +"to install it on this site." msgstr "" -#: include/import.php:69 -msgid "" -"Cannot create a duplicate channel identifier on this system. Import failed." +#: include/bbcode.php:673 +msgid "card" msgstr "" -#: include/import.php:90 -msgid "Unable to create a unique channel address. Import failed." +#: include/bbcode.php:675 +msgid "article" msgstr "" -#: include/import.php:136 -msgid "Cloned channel not found. Import failed." +#: include/bbcode.php:771 include/bbcode.php:780 +msgid "Click to open/close" msgstr "" -#: include/attach.php:275 include/attach.php:396 -msgid "Item was not found." +#: include/bbcode.php:780 +msgid "spoiler" msgstr "" -#: include/attach.php:292 -msgid "Unknown error." +#: include/bbcode.php:1412 include/bbcode.php:1683 +msgid "Different viewers will see this text differently" msgstr "" -#: include/attach.php:585 -msgid "No source file." +#: include/bbcode.php:2010 +msgid "$1 wrote:" msgstr "" -#: include/attach.php:607 -msgid "Cannot locate file to replace" +#: include/taxonomy.php:364 +msgid "Popular Tags" msgstr "" -#: include/attach.php:626 -msgid "Cannot locate file to revise/update" +#: include/taxonomy.php:626 +msgid "Keywords" msgstr "" -#: include/attach.php:781 -#, php-format -msgid "File exceeds size limit of %d" +#: include/taxonomy.php:648 +msgid "have" msgstr "" -#: include/attach.php:802 -#, php-format -msgid "You have reached your limit of %1$.0f Mbytes attachment storage." +#: include/taxonomy.php:648 +msgid "has" msgstr "" -#: include/attach.php:993 -msgid "File upload failed. Possible system limit or action terminated." +#: include/taxonomy.php:649 +msgid "want" msgstr "" -#: include/attach.php:1022 -msgid "Stored file could not be verified. Upload failed." +#: include/taxonomy.php:649 +msgid "wants" msgstr "" -#: include/attach.php:1097 include/attach.php:1114 -msgid "Path not available." +#: include/taxonomy.php:650 +msgid "like" msgstr "" -#: include/attach.php:1163 include/attach.php:1332 -msgid "Empty pathname" +#: include/taxonomy.php:650 +msgid "likes" msgstr "" -#: include/attach.php:1189 -msgid "duplicate filename or path" +#: include/taxonomy.php:651 +msgid "dislike" msgstr "" -#: include/attach.php:1214 -msgid "Path not found." +#: include/taxonomy.php:651 +msgid "dislikes" msgstr "" -#: include/attach.php:1285 -msgid "mkdir failed." -msgstr "" - -#: include/attach.php:1289 -msgid "database storage failed." -msgstr "" - -#: include/attach.php:1338 -msgid "Empty path" -msgstr "" - -#: include/nav.php:93 -msgid "Remote authentication" -msgstr "" - -#: include/nav.php:93 -msgid "Click to authenticate to your home hub" -msgstr "" - -#: include/nav.php:99 -msgid "Manage your channels" -msgstr "" - -#: include/nav.php:102 -msgid "Manage your access lists" -msgstr "" - -#: include/nav.php:104 -msgid "Account/Channel Settings" -msgstr "" - -#: include/nav.php:106 -msgid "(is on)" -msgstr "" - -#: include/nav.php:106 -msgid "(is off)" -msgstr "" - -#: include/nav.php:106 -msgid "Content filtering" -msgstr "" - -#: include/nav.php:113 include/nav.php:142 -msgid "End this session" -msgstr "" - -#: include/nav.php:116 -msgid "Your profile page" -msgstr "" - -#: include/nav.php:119 -msgid "Manage/Edit profiles" -msgstr "" - -#: include/nav.php:128 include/nav.php:132 -msgid "Sign in" -msgstr "" - -#: include/nav.php:159 -msgid "Take me home" -msgstr "" - -#: include/nav.php:161 -msgid "Log me out of this site" -msgstr "" - -#: include/nav.php:166 -msgid "Create an account" -msgstr "" - -#: include/nav.php:178 -msgid "Help and documentation" -msgstr "" - -#: include/nav.php:185 -msgid "Search site @name, #tag, ?doc, content" -msgstr "" - -#: include/nav.php:191 -msgid "Site Setup and Configuration" -msgstr "" - -#: include/nav.php:299 -msgid "Powered by $Projectname" -msgstr "" - -#: include/nav.php:310 -msgid "@name, #tag, ?doc, content" -msgstr "" - -#: include/nav.php:311 -msgid "Please wait..." -msgstr "" - -#: include/nav.php:319 -msgid "Arrange Apps" -msgstr "" - -#: include/nav.php:320 -msgid "Toggle System Apps" -msgstr "" - -#: include/nav.php:401 include/conversation.php:1921 -msgid "Status Messages and Posts" -msgstr "" - -#: include/nav.php:411 include/conversation.php:1931 -msgid "About" -msgstr "" - -#: include/nav.php:414 include/conversation.php:1934 -msgid "Profile Details" -msgstr "" - -#: include/nav.php:432 include/conversation.php:1952 -msgid "Files and Storage" -msgstr "" - -#: include/nav.php:467 include/conversation.php:1989 -msgid "Bookmarks" -msgstr "" - -#: include/nav.php:470 include/conversation.php:1992 -msgid "Saved Bookmarks" -msgstr "" - -#: include/nav.php:481 include/conversation.php:2003 -msgid "View Cards" -msgstr "" - -#: include/nav.php:492 include/conversation.php:2014 -msgid "View Articles" -msgstr "" - -#: include/nav.php:504 include/conversation.php:2025 -msgid "View Webpages" -msgstr "" - -#: include/nav.php:513 include/conversation.php:2035 -msgid "Wikis" -msgstr "" - -#: include/text.php:506 -msgid "prev" -msgstr "" - -#: include/text.php:508 -msgid "first" -msgstr "" - -#: include/text.php:537 -msgid "last" -msgstr "" - -#: include/text.php:540 -msgid "next" -msgstr "" - -#: include/text.php:551 -msgid "older" -msgstr "" - -#: include/text.php:553 -msgid "newer" -msgstr "" - -#: include/text.php:1121 include/text.php:1125 -msgid "poke" -msgstr "" - -#: include/text.php:1121 include/text.php:1125 include/conversation.php:274 -msgid "poked" -msgstr "" - -#: include/text.php:1126 -msgid "ping" -msgstr "" - -#: include/text.php:1126 -msgid "pinged" -msgstr "" - -#: include/text.php:1127 -msgid "prod" -msgstr "" - -#: include/text.php:1127 -msgid "prodded" -msgstr "" - -#: include/text.php:1128 -msgid "slap" -msgstr "" - -#: include/text.php:1128 -msgid "slapped" -msgstr "" - -#: include/text.php:1129 -msgid "finger" -msgstr "" - -#: include/text.php:1129 -msgid "fingered" -msgstr "" - -#: include/text.php:1130 -msgid "rebuff" -msgstr "" - -#: include/text.php:1130 -msgid "rebuffed" -msgstr "" - -#: include/text.php:1153 -msgid "happy" -msgstr "" - -#: include/text.php:1154 -msgid "sad" -msgstr "" - -#: include/text.php:1155 -msgid "mellow" -msgstr "" - -#: include/text.php:1156 -msgid "tired" -msgstr "" - -#: include/text.php:1157 -msgid "perky" -msgstr "" - -#: include/text.php:1158 -msgid "angry" -msgstr "" - -#: include/text.php:1159 -msgid "stupefied" -msgstr "" - -#: include/text.php:1160 -msgid "puzzled" -msgstr "" - -#: include/text.php:1161 -msgid "interested" -msgstr "" - -#: include/text.php:1162 -msgid "bitter" -msgstr "" - -#: include/text.php:1163 -msgid "cheerful" -msgstr "" - -#: include/text.php:1164 -msgid "alive" -msgstr "" - -#: include/text.php:1165 -msgid "annoyed" -msgstr "" - -#: include/text.php:1166 -msgid "anxious" -msgstr "" - -#: include/text.php:1167 -msgid "cranky" -msgstr "" - -#: include/text.php:1168 -msgid "disturbed" -msgstr "" - -#: include/text.php:1169 -msgid "frustrated" -msgstr "" - -#: include/text.php:1170 -msgid "depressed" -msgstr "" - -#: include/text.php:1171 -msgid "motivated" -msgstr "" - -#: include/text.php:1172 -msgid "relaxed" -msgstr "" - -#: include/text.php:1173 -msgid "surprised" -msgstr "" - -#: include/text.php:1356 include/js_strings.php:66 -msgid "January" -msgstr "" - -#: include/text.php:1356 include/js_strings.php:67 -msgid "February" -msgstr "" - -#: include/text.php:1356 include/js_strings.php:68 -msgid "March" -msgstr "" - -#: include/text.php:1356 include/js_strings.php:69 -msgid "April" -msgstr "" - -#: include/text.php:1356 -msgid "May" -msgstr "" - -#: include/text.php:1356 include/js_strings.php:71 -msgid "June" -msgstr "" - -#: include/text.php:1356 include/js_strings.php:72 -msgid "July" -msgstr "" - -#: include/text.php:1356 include/js_strings.php:73 -msgid "August" -msgstr "" - -#: include/text.php:1356 include/js_strings.php:74 -msgid "September" -msgstr "" - -#: include/text.php:1356 include/js_strings.php:75 -msgid "October" -msgstr "" - -#: include/text.php:1356 include/js_strings.php:76 -msgid "November" -msgstr "" - -#: include/text.php:1356 include/js_strings.php:77 -msgid "December" -msgstr "" - -#: include/text.php:1446 -msgid "Unknown Attachment" -msgstr "" - -#: include/text.php:1449 -msgid "unknown" -msgstr "" - -#: include/text.php:1502 -msgid "remove category" -msgstr "" - -#: include/text.php:1634 -msgid "remove from file" -msgstr "" - -#: include/text.php:1784 -msgid "Added to your calendar" -msgstr "" - -#: include/text.php:1866 -msgid "Link" -msgstr "" - -#: include/text.php:1952 -msgid "Poll has ended." -msgstr "" - -#: include/text.php:1955 -#, php-format -msgid "Poll ends: %1$s (%2$s)" -msgstr "" - -#: include/text.php:1960 -msgid "vote" -msgstr "" - -#: include/text.php:1972 -msgid "Download binary/encrypted content" -msgstr "" - -#: include/text.php:2097 -msgid "Page layout" -msgstr "" - -#: include/text.php:2097 -msgid "You can create your own with the layouts tool" -msgstr "" - -#: include/text.php:2107 -msgid "BBcode" -msgstr "" - -#: include/text.php:2108 -msgid "HTML" -msgstr "" - -#: include/text.php:2109 -msgid "Markdown" -msgstr "" - -#: include/text.php:2110 -msgid "Text" -msgstr "" - -#: include/text.php:2111 -msgid "Comanche Layout" -msgstr "" - -#: include/text.php:2116 -msgid "PHP" -msgstr "" - -#: include/text.php:2125 -msgid "Page content type" -msgstr "" - -#: include/text.php:2258 -msgid "activity" -msgstr "" - -#: include/text.php:2362 -msgid "a-z, 0-9, -, and _ only" -msgstr "" - -#: include/text.php:2689 -msgid "Design Tools" -msgstr "" - -#: include/text.php:2695 -msgid "Pages" -msgstr "" - -#: include/text.php:2717 -msgid "Import website..." -msgstr "" - -#: include/text.php:2718 -msgid "Select folder to import" -msgstr "" - -#: include/text.php:2719 -msgid "Import from a zipped folder:" -msgstr "" - -#: include/text.php:2720 -msgid "Import from cloud files:" -msgstr "" - -#: include/text.php:2721 -msgid "/cloud/channel/path/to/folder" -msgstr "" - -#: include/text.php:2722 -msgid "Enter path to website files" -msgstr "" - -#: include/text.php:2723 -msgid "Select folder" -msgstr "" - -#: include/text.php:2724 -msgid "Export website..." -msgstr "" - -#: include/text.php:2725 -msgid "Export to a zip file" -msgstr "" - -#: include/text.php:2726 -msgid "website.zip" -msgstr "" - -#: include/text.php:2727 -msgid "Enter a name for the zip file." -msgstr "" - -#: include/text.php:2728 -msgid "Export to cloud files" -msgstr "" - -#: include/text.php:2729 -msgid "/path/to/export/folder" -msgstr "" - -#: include/text.php:2730 -msgid "Enter a path to a cloud files destination." -msgstr "" - -#: include/text.php:2731 -msgid "Specify folder" -msgstr "" - -#: include/items.php:1026 -msgid "(Unknown)" -msgstr "" - -#: include/items.php:1210 -msgid "Visible to anybody on the internet." -msgstr "" - -#: include/items.php:1212 -msgid "Visible to you only." -msgstr "" - -#: include/items.php:1214 -msgid "Visible to anybody in this network." -msgstr "" - -#: include/items.php:1216 -msgid "Visible to anybody authenticated." -msgstr "" - -#: include/items.php:1218 -#, php-format -msgid "Visible to anybody on %s." -msgstr "" - -#: include/items.php:1220 -msgid "Visible to all connections." -msgstr "" - -#: include/items.php:1222 -msgid "Visible to approved connections." -msgstr "" - -#: include/items.php:1224 -msgid "Visible to specific connections." -msgstr "" - -#: include/items.php:4392 -msgid "Privacy group is empty." -msgstr "" - -#: include/items.php:4773 -msgid "profile photo" -msgstr "" - -#: include/items.php:4977 -#, php-format -msgid "[Edited %s]" -msgstr "" - -#: include/items.php:4977 -msgctxt "edit_activity" -msgid "Post" -msgstr "" - -#: include/items.php:4977 -msgctxt "edit_activity" -msgid "Comment" -msgstr "" - -#: include/connections.php:158 -msgid "New window" -msgstr "" - -#: include/connections.php:159 -msgid "Open the selected location in a different window or browser tab" -msgstr "" - -#: include/connections.php:792 include/event.php:1379 -msgid "Home, Voice" -msgstr "" - -#: include/connections.php:793 include/event.php:1380 -msgid "Home, Fax" -msgstr "" - -#: include/connections.php:795 include/event.php:1382 -msgid "Work, Voice" -msgstr "" - -#: include/connections.php:796 include/event.php:1383 -msgid "Work, Fax" -msgstr "" - -#: include/connections.php:895 -msgid "No connections" -msgstr "" - -#: include/connections.php:927 -#, php-format -msgid "View all %s connections" -msgstr "" - -#: include/connections.php:960 -#, php-format -msgid "Network: %s" -msgstr "" - -#: include/api_auth.php:184 -msgid "This api method requires authentication" -msgstr "" - -#: include/network.php:473 -msgid "url: " -msgstr "" - -#: include/network.php:474 -msgid "error_code: " -msgstr "" - -#: include/network.php:475 -msgid "error_string: " -msgstr "" - -#: include/network.php:476 -msgid "content-type: " -msgstr "" - -#: include/network.php:1594 include/network.php:1595 -msgid "Friendica" -msgstr "" - -#: include/network.php:1596 -msgid "OStatus" -msgstr "" - -#: include/network.php:1597 -msgid "GNU-Social" -msgstr "" - -#: include/network.php:1598 -msgid "RSS/Atom" -msgstr "" - -#: include/network.php:1601 -msgid "Diaspora" -msgstr "" - -#: include/network.php:1602 -msgid "Facebook" -msgstr "" - -#: include/network.php:1604 -msgid "Zot" -msgstr "" - -#: include/network.php:1605 -msgid "LinkedIn" -msgstr "" - -#: include/network.php:1606 -msgid "XMPP/IM" -msgstr "" - -#: include/network.php:1607 -msgid "MySpace" -msgstr "" - -#: include/conversation.php:180 +#: include/conversation.php:182 #, php-format msgid "%1$s repeated %2$s's %3$s" msgstr "" -#: include/conversation.php:186 +#: include/conversation.php:188 #, php-format msgid "likes %1$s's %2$s" msgstr "" -#: include/conversation.php:189 +#: include/conversation.php:190 #, php-format msgid "doesn't like %1$s's %2$s" msgstr "" @@ -11760,58 +10610,62 @@ msgstr "" msgid "repeated %1$s's %2$s" msgstr "" -#: include/conversation.php:235 +#: include/conversation.php:234 #, php-format msgid "%1$s is now connected with %2$s" msgstr "" -#: include/conversation.php:270 +#: include/conversation.php:274 #, php-format msgid "%1$s poked %2$s" msgstr "" -#: include/conversation.php:612 +#: include/conversation.php:278 include/text.php:1182 include/text.php:1186 +msgid "poked" +msgstr "" + +#: include/conversation.php:604 msgid "Toggle Star Status" msgstr "" -#: include/conversation.php:659 +#: include/conversation.php:657 #, php-format msgid "View %s's profile @ %s" msgstr "" -#: include/conversation.php:680 +#: include/conversation.php:679 msgid "Categories:" msgstr "" -#: include/conversation.php:681 +#: include/conversation.php:680 msgid "Filed under:" msgstr "" -#: include/conversation.php:710 +#: include/conversation.php:709 msgid "View Conversation" msgstr "" -#: include/conversation.php:807 +#: include/conversation.php:799 msgid "remove" msgstr "" -#: include/conversation.php:811 +#: include/conversation.php:803 msgid "Loading..." msgstr "" -#: include/conversation.php:812 +#: include/conversation.php:804 msgid "Delete Selected Items" msgstr "" -#: include/conversation.php:855 +#: include/conversation.php:850 msgid "View Source" msgstr "" -#: include/conversation.php:865 +#: include/conversation.php:860 msgid "Follow Thread" msgstr "" -#: include/conversation.php:874 +#: include/conversation.php:869 msgid "Unfollow Thread" msgstr "" @@ -11831,608 +10685,1763 @@ msgstr "" msgid "Block author" msgstr "" -#: include/conversation.php:1129 +#: include/conversation.php:1135 #, php-format msgid "%s likes this." msgstr "" -#: include/conversation.php:1129 +#: include/conversation.php:1135 #, php-format msgid "%s doesn't like this." msgstr "" -#: include/conversation.php:1133 +#: include/conversation.php:1139 #, php-format msgid "%2$d people like this." msgid_plural "%2$d people like this." msgstr[0] "" msgstr[1] "" -#: include/conversation.php:1135 +#: include/conversation.php:1141 #, php-format msgid "%2$d people don't like this." msgid_plural "%2$d people don't like this." msgstr[0] "" msgstr[1] "" -#: include/conversation.php:1141 +#: include/conversation.php:1148 msgid "and" msgstr "" -#: include/conversation.php:1144 +#: include/conversation.php:1152 #, php-format msgid ", and %d other people" msgid_plural ", and %d other people" msgstr[0] "" msgstr[1] "" -#: include/conversation.php:1145 +#: include/conversation.php:1154 #, php-format msgid "%s like this." msgstr "" -#: include/conversation.php:1145 +#: include/conversation.php:1154 #, php-format msgid "%s don't like this." msgstr "" -#: include/conversation.php:1205 +#: include/conversation.php:1220 msgid "Set your location" msgstr "" -#: include/conversation.php:1206 +#: include/conversation.php:1221 msgid "Clear browser location" msgstr "" -#: include/conversation.php:1224 +#: include/conversation.php:1242 msgid "Embed (existing) photo from your photo albums" msgstr "" -#: include/conversation.php:1268 +#: include/conversation.php:1288 msgid "Tag term:" msgstr "" -#: include/conversation.php:1269 +#: include/conversation.php:1289 msgid "Where are you right now?" msgstr "" -#: include/conversation.php:1274 +#: include/conversation.php:1294 msgid "Choose a different album..." msgstr "" -#: include/conversation.php:1279 include/js_strings.php:6 +#: include/conversation.php:1299 include/js_strings.php:9 msgid "Delete this item?" msgstr "" -#: include/conversation.php:1348 +#: include/conversation.php:1373 msgid "Accept delivery of comments and likes on this post from" msgstr "" -#: include/conversation.php:1385 +#: include/conversation.php:1410 msgid "Page link name" msgstr "" -#: include/conversation.php:1388 +#: include/conversation.php:1413 msgid "Post as" msgstr "" -#: include/conversation.php:1390 +#: include/conversation.php:1415 msgid "Text styles" msgstr "" -#: include/conversation.php:1398 +#: include/conversation.php:1423 msgid "Please enter a link location (URL)" msgstr "" -#: include/conversation.php:1399 +#: include/conversation.php:1424 msgid "Insert link only" msgstr "" -#: include/conversation.php:1399 +#: include/conversation.php:1424 msgid "Embed content if possible" msgstr "" -#: include/conversation.php:1401 +#: include/conversation.php:1426 msgid "Embed an image from your albums" msgstr "" -#: include/conversation.php:1405 +#: include/conversation.php:1430 msgid "Toggle poll" msgstr "" -#: include/conversation.php:1406 +#: include/conversation.php:1431 msgid "Option" msgstr "" -#: include/conversation.php:1407 +#: include/conversation.php:1432 msgid "Add option" msgstr "" -#: include/conversation.php:1408 +#: include/conversation.php:1433 msgid "Minutes" msgstr "" -#: include/conversation.php:1408 +#: include/conversation.php:1433 msgid "Hours" msgstr "" -#: include/conversation.php:1408 +#: include/conversation.php:1433 msgid "Days" msgstr "" -#: include/conversation.php:1409 +#: include/conversation.php:1434 msgid "Allow multiple answers" msgstr "" -#: include/conversation.php:1412 +#: include/conversation.php:1437 msgid "Disable comments" msgstr "" -#: include/conversation.php:1413 +#: include/conversation.php:1438 msgid "Toggle comments" msgstr "" -#: include/conversation.php:1414 +#: include/conversation.php:1439 msgid "Allow comments on this post" msgstr "" -#: include/conversation.php:1420 +#: include/conversation.php:1445 msgid "Optional: disable comments after (date)" msgstr "" -#: include/conversation.php:1429 +#: include/conversation.php:1454 msgid "Categories (optional, comma-separated list)" msgstr "" -#: include/conversation.php:1453 +#: include/conversation.php:1478 msgid "Other networks and post services" msgstr "" -#: include/conversation.php:1454 +#: include/conversation.php:1479 msgid "Collections" msgstr "" -#: include/conversation.php:1457 +#: include/conversation.php:1482 msgid "Set expiration date" msgstr "" -#: include/conversation.php:1462 +#: include/conversation.php:1487 msgid "Set publish date" msgstr "" -#: include/conversation.php:1480 +#: include/conversation.php:1505 msgid "Load remote media players" msgstr "" -#: include/conversation.php:1481 +#: include/conversation.php:1506 msgid "This may subject viewers of this post to behaviour tracking" msgstr "" -#: include/conversation.php:1483 +#: include/conversation.php:1508 msgid "Find shareable objects (Zot)" msgstr "" -#: include/conversation.php:1508 +#: include/conversation.php:1535 msgid "Post to Collections" msgstr "" -#: include/conversation.php:2011 +#: include/conversation.php:1981 include/nav.php:403 +msgid "Status Messages and Posts" +msgstr "" + +#: include/conversation.php:1991 include/nav.php:413 +msgid "About" +msgstr "" + +#: include/conversation.php:1994 include/nav.php:416 +msgid "Profile Details" +msgstr "" + +#: include/conversation.php:2004 include/photos.php:755 include/nav.php:426 +msgid "Photo Albums" +msgstr "" + +#: include/conversation.php:2012 include/nav.php:434 +msgid "Files and Storage" +msgstr "" + +#: include/conversation.php:2049 include/nav.php:469 +msgid "Bookmarks" +msgstr "" + +#: include/conversation.php:2052 include/nav.php:472 +msgid "Saved Bookmarks" +msgstr "" + +#: include/conversation.php:2063 include/nav.php:483 +msgid "View Cards" +msgstr "" + +#: include/conversation.php:2071 msgid "articles" msgstr "" -#: include/conversation.php:2104 +#: include/conversation.php:2074 include/nav.php:494 +msgid "View Articles" +msgstr "" + +#: include/conversation.php:2085 include/nav.php:506 +msgid "View Webpages" +msgstr "" + +#: include/conversation.php:2095 include/nav.php:515 +msgid "Wikis" +msgstr "" + +#: include/conversation.php:2165 msgctxt "noun" msgid "Attending" msgid_plural "Attending" msgstr[0] "" msgstr[1] "" -#: include/conversation.php:2107 +#: include/conversation.php:2168 msgctxt "noun" msgid "Not Attending" msgid_plural "Not Attending" msgstr[0] "" msgstr[1] "" -#: include/conversation.php:2110 +#: include/conversation.php:2171 msgctxt "noun" msgid "Undecided" msgid_plural "Undecided" msgstr[0] "" msgstr[1] "" -#: include/conversation.php:2113 +#: include/conversation.php:2174 msgctxt "noun" msgid "Agree" msgid_plural "Agrees" msgstr[0] "" msgstr[1] "" -#: include/conversation.php:2116 +#: include/conversation.php:2177 msgctxt "noun" msgid "Disagree" msgid_plural "Disagrees" msgstr[0] "" msgstr[1] "" -#: include/conversation.php:2119 +#: include/conversation.php:2180 msgctxt "noun" msgid "Abstain" msgid_plural "Abstains" msgstr[0] "" msgstr[1] "" -#: include/zid.php:376 +#: include/account.php:34 +msgid "Not a valid email address" +msgstr "" + +#: include/account.php:36 +msgid "Your email domain is not among those allowed on this site" +msgstr "" + +#: include/account.php:43 +msgid "Your email address is already registered at this site." +msgstr "" + +#: include/account.php:78 +msgid "An invitation is required." +msgstr "" + +#: include/account.php:82 +msgid "Invitation could not be verified." +msgstr "" + +#: include/account.php:164 +msgid "Please enter the required information." +msgstr "" + +#: include/account.php:228 +msgid "Failed to store account information." +msgstr "" + +#: include/account.php:321 +#, php-format +msgid "Registration confirmation for %s" +msgstr "" + +#: include/account.php:398 +#, php-format +msgid "Registration request at %s" +msgstr "" + +#: include/account.php:422 +msgid "your registration password" +msgstr "" + +#: include/account.php:428 include/account.php:498 +#, php-format +msgid "Registration details for %s" +msgstr "" + +#: include/account.php:510 +msgid "Account approved." +msgstr "" + +#: include/account.php:557 +#, php-format +msgid "Registration revoked for %s" +msgstr "" + +#: include/account.php:862 include/account.php:864 +msgid "Click here to upgrade." +msgstr "" + +#: include/account.php:872 +msgid "This action exceeds the limits set by your subscription plan." +msgstr "" + +#: include/account.php:878 +msgid "This action is not available under your subscription plan." +msgstr "" + +#: include/api_auth.php:184 +msgid "This api method requires authentication" +msgstr "" + +#: include/attach.php:282 include/attach.php:408 +msgid "Item was not found." +msgstr "" + +#: include/attach.php:300 +msgid "Unknown error." +msgstr "" + +#: include/attach.php:603 +msgid "No source file." +msgstr "" + +#: include/attach.php:627 +msgid "Cannot locate file to replace" +msgstr "" + +#: include/attach.php:648 +msgid "Cannot locate file to revise/update" +msgstr "" + +#: include/attach.php:807 +#, php-format +msgid "File exceeds size limit of %d" +msgstr "" + +#: include/attach.php:830 +#, php-format +msgid "You have reached your limit of %1$.0f Mbytes attachment storage." +msgstr "" + +#: include/attach.php:1037 +msgid "File upload failed. Possible system limit or action terminated." +msgstr "" + +#: include/attach.php:1068 +msgid "Stored file could not be verified. Upload failed." +msgstr "" + +#: include/attach.php:1146 include/attach.php:1163 +msgid "Path not available." +msgstr "" + +#: include/attach.php:1213 include/attach.php:1384 +msgid "Empty pathname" +msgstr "" + +#: include/attach.php:1241 +msgid "duplicate filename or path" +msgstr "" + +#: include/attach.php:1266 +msgid "Path not found." +msgstr "" + +#: include/attach.php:1337 +msgid "mkdir failed." +msgstr "" + +#: include/attach.php:1340 +msgid "database storage failed." +msgstr "" + +#: include/attach.php:1390 +msgid "Empty path" +msgstr "" + +#: include/photos.php:154 +#, php-format +msgid "Image exceeds website size limit of %lu bytes" +msgstr "" + +#: include/photos.php:165 +msgid "Image file is empty." +msgstr "" + +#: include/photos.php:359 +msgid "Photo storage failed." +msgstr "" + +#: include/photos.php:408 +msgid "a new photo" +msgstr "" + +#: include/photos.php:416 +#, php-format +msgctxt "photo_upload" +msgid "%1$s posted %2$s to %3$s" +msgstr "" + +#: include/photos.php:760 +msgid "Upload New Photos" +msgstr "" + +#: include/network.php:502 +msgid "url: " +msgstr "" + +#: include/network.php:503 +msgid "error_code: " +msgstr "" + +#: include/network.php:504 +msgid "error_string: " +msgstr "" + +#: include/network.php:505 +msgid "content-type: " +msgstr "" + +#: include/network.php:1684 include/network.php:1685 +msgid "Friendica" +msgstr "" + +#: include/network.php:1686 +msgid "OStatus" +msgstr "" + +#: include/network.php:1687 +msgid "GNU-Social" +msgstr "" + +#: include/network.php:1688 +msgid "RSS/Atom" +msgstr "" + +#: include/network.php:1691 +msgid "Diaspora" +msgstr "" + +#: include/network.php:1692 +msgid "Facebook" +msgstr "" + +#: include/network.php:1694 +msgid "Zot" +msgstr "" + +#: include/network.php:1695 +msgid "LinkedIn" +msgstr "" + +#: include/network.php:1696 +msgid "XMPP/IM" +msgstr "" + +#: include/network.php:1697 +msgid "MySpace" +msgstr "" + +#: include/auth.php:207 +msgid "Delegation session ended." +msgstr "" + +#: include/auth.php:210 +msgid "Logged out." +msgstr "" + +#: include/auth.php:305 +msgid "Email validation is incomplete. Please check your email." +msgstr "" + +#: include/auth.php:318 +msgid "Failed authentication" +msgstr "" + +#: include/auth.php:329 +msgid "Login failed." +msgstr "" + +#: include/datetime.php:165 +msgid "Birthday" +msgstr "" + +#: include/datetime.php:165 +msgid "Age: " +msgstr "" + +#: include/datetime.php:165 +msgid "YYYY-MM-DD or MM-DD" +msgstr "" + +#: include/datetime.php:281 +msgid "from now" +msgstr "" + +#: include/datetime.php:284 +msgid "ago" +msgstr "" + +#: include/datetime.php:289 +#, php-format +msgid "less than a second %s" +msgstr "" + +#: include/datetime.php:307 +#, php-format +msgctxt "e.g. 22 hours ago, 1 minute ago" +msgid "%1$d %2$s %3$s" +msgstr "" + +#: include/datetime.php:319 +msgctxt "relative_date" +msgid "year" +msgid_plural "years" +msgstr[0] "" +msgstr[1] "" + +#: include/datetime.php:322 +msgctxt "relative_date" +msgid "month" +msgid_plural "months" +msgstr[0] "" +msgstr[1] "" + +#: include/datetime.php:325 +msgctxt "relative_date" +msgid "week" +msgid_plural "weeks" +msgstr[0] "" +msgstr[1] "" + +#: include/datetime.php:328 +msgctxt "relative_date" +msgid "day" +msgid_plural "days" +msgstr[0] "" +msgstr[1] "" + +#: include/datetime.php:331 +msgctxt "relative_date" +msgid "hour" +msgid_plural "hours" +msgstr[0] "" +msgstr[1] "" + +#: include/datetime.php:334 +msgctxt "relative_date" +msgid "minute" +msgid_plural "minutes" +msgstr[0] "" +msgstr[1] "" + +#: include/datetime.php:337 +msgctxt "relative_date" +msgid "second" +msgid_plural "seconds" +msgstr[0] "" +msgstr[1] "" + +#: include/datetime.php:592 +#, php-format +msgid "%1$s's birthday" +msgstr "" + +#: include/datetime.php:593 +#, php-format +msgid "Happy Birthday %1$s" +msgstr "" + +#: include/event.php:34 include/event.php:121 +msgid "l F d, Y \\@ g:i A" +msgstr "" + +#: include/event.php:42 include/event.php:127 +msgid "Starts:" +msgstr "" + +#: include/event.php:60 include/event.php:131 +msgid "Finishes:" +msgstr "" + +#: include/event.php:1190 +msgid "This event has been added to your calendar." +msgstr "" + +#: include/event.php:1421 +msgid "Not specified" +msgstr "" + +#: include/event.php:1422 +msgid "Needs Action" +msgstr "" + +#: include/event.php:1423 +msgid "Completed" +msgstr "" + +#: include/event.php:1424 +msgid "In Process" +msgstr "" + +#: include/event.php:1425 +msgid "Cancelled" +msgstr "" + +#: include/event.php:1516 include/connections.php:827 +msgid "Home, Voice" +msgstr "" + +#: include/event.php:1517 include/connections.php:828 +msgid "Home, Fax" +msgstr "" + +#: include/event.php:1519 include/connections.php:830 +msgid "Work, Voice" +msgstr "" + +#: include/event.php:1520 include/connections.php:831 +msgid "Work, Fax" +msgstr "" + +#: include/features.php:64 +msgid "General Features" +msgstr "" + +#: include/features.php:69 +msgid "Display new member quick links menu" +msgstr "" + +#: include/features.php:77 +msgid "Advanced Profiles" +msgstr "" + +#: include/features.php:78 +msgid "Additional profile sections and selections" +msgstr "" + +#: include/features.php:124 +msgid "Private Notes" +msgstr "" + +#: include/features.php:125 +msgid "Enables a tool to store notes and reminders (note: not encrypted)" +msgstr "" + +#: include/features.php:145 +msgid "Create interactive articles" +msgstr "" + +#: include/features.php:162 +msgid "Photo Location" +msgstr "" + +#: include/features.php:163 +msgid "If location data is available on uploaded photos, link this to a map." +msgstr "" + +#: include/features.php:191 +msgid "Event Timezone Selection" +msgstr "" + +#: include/features.php:192 +msgid "Allow event creation in timezones other than your own." +msgstr "" + +#: include/features.php:210 +msgid "Advanced Directory Search" +msgstr "" + +#: include/features.php:211 +msgid "Allows creation of complex directory search queries" +msgstr "" + +#: include/features.php:219 +msgid "Advanced Theme and Layout Settings" +msgstr "" + +#: include/features.php:220 +msgid "Allows fine tuning of themes and page layouts" +msgstr "" + +#: include/features.php:229 +msgid "Access Control and Permissions" +msgstr "" + +#: include/features.php:233 +msgid "Privacy Groups" +msgstr "" + +#: include/features.php:234 +msgid "Enable management and selection of privacy groups" +msgstr "" + +#: include/features.php:270 +msgid "OAuth2 Clients" +msgstr "" + +#: include/features.php:271 +msgid "Manage OAuth2 authenticatication tokens for mobile and remote apps." +msgstr "" + +#: include/features.php:291 +msgid "Post Composition Features" +msgstr "" + +#: include/features.php:313 +msgid "Browser Encryption" +msgstr "" + +#: include/features.php:314 +msgid "" +"Provide optional browser-to-browser encryption of content with a shared " +"secret key" +msgstr "" + +#: include/features.php:358 +msgid "Suppress Duplicate Posts/Comments" +msgstr "" + +#: include/features.php:359 +msgid "" +"Prevent posts with identical content to be published with less than two " +"minutes in between submissions." +msgstr "" + +#: include/features.php:367 +msgid "Auto-save drafts of posts and comments" +msgstr "" + +#: include/features.php:368 +msgid "" +"Automatically saves post and comment drafts in local browser storage to help " +"prevent accidental loss of compositions" +msgstr "" + +#: include/features.php:379 +msgid "Network and Stream Filtering" +msgstr "" + +#: include/features.php:469 +msgid "Post/Comment Tools" +msgstr "" + +#: include/features.php:473 +msgid "Community Tagging" +msgstr "" + +#: include/features.php:474 +msgid "Ability to tag existing posts" +msgstr "" + +#: include/features.php:482 +msgid "Post Categories" +msgstr "" + +#: include/features.php:483 +msgid "Add categories to your posts" +msgstr "" + +#: include/features.php:491 +msgid "Emoji Reactions" +msgstr "" + +#: include/features.php:492 +msgid "Add emoji reaction ability to posts" +msgstr "" + +#: include/features.php:501 +msgid "Ability to file posts under folders" +msgstr "" + +#: include/features.php:509 +msgid "Dislike Posts" +msgstr "" + +#: include/features.php:510 +msgid "Ability to dislike posts/comments" +msgstr "" + +#: include/features.php:527 +msgid "Tag Cloud" +msgstr "" + +#: include/features.php:528 +msgid "Provide a personal tag cloud on your channel page" +msgstr "" + +#: include/connections.php:172 +msgid "New window" +msgstr "" + +#: include/connections.php:173 +msgid "Open the selected location in a different window or browser tab" +msgstr "" + +#: include/connections.php:942 +msgid "No connections" +msgstr "" + +#: include/connections.php:973 +#, php-format +msgid "View all %s connections" +msgstr "" + +#: include/connections.php:1010 +#, php-format +msgid "Network: %s" +msgstr "" + +#: include/text.php:522 +msgid "prev" +msgstr "" + +#: include/text.php:525 +msgid "first" +msgstr "" + +#: include/text.php:556 +msgid "last" +msgstr "" + +#: include/text.php:559 +msgid "next" +msgstr "" + +#: include/text.php:572 +msgid "older" +msgstr "" + +#: include/text.php:575 +msgid "newer" +msgstr "" + +#: include/text.php:1182 include/text.php:1186 +msgid "poke" +msgstr "" + +#: include/text.php:1187 +msgid "ping" +msgstr "" + +#: include/text.php:1187 +msgid "pinged" +msgstr "" + +#: include/text.php:1188 +msgid "prod" +msgstr "" + +#: include/text.php:1188 +msgid "prodded" +msgstr "" + +#: include/text.php:1189 +msgid "slap" +msgstr "" + +#: include/text.php:1189 +msgid "slapped" +msgstr "" + +#: include/text.php:1190 +msgid "finger" +msgstr "" + +#: include/text.php:1190 +msgid "fingered" +msgstr "" + +#: include/text.php:1191 +msgid "rebuff" +msgstr "" + +#: include/text.php:1191 +msgid "rebuffed" +msgstr "" + +#: include/text.php:1215 +msgid "happy" +msgstr "" + +#: include/text.php:1216 +msgid "sad" +msgstr "" + +#: include/text.php:1217 +msgid "mellow" +msgstr "" + +#: include/text.php:1218 +msgid "tired" +msgstr "" + +#: include/text.php:1219 +msgid "perky" +msgstr "" + +#: include/text.php:1220 +msgid "angry" +msgstr "" + +#: include/text.php:1221 +msgid "stupefied" +msgstr "" + +#: include/text.php:1222 +msgid "puzzled" +msgstr "" + +#: include/text.php:1223 +msgid "interested" +msgstr "" + +#: include/text.php:1224 +msgid "bitter" +msgstr "" + +#: include/text.php:1225 +msgid "cheerful" +msgstr "" + +#: include/text.php:1226 +msgid "alive" +msgstr "" + +#: include/text.php:1227 +msgid "annoyed" +msgstr "" + +#: include/text.php:1228 +msgid "anxious" +msgstr "" + +#: include/text.php:1229 +msgid "cranky" +msgstr "" + +#: include/text.php:1230 +msgid "disturbed" +msgstr "" + +#: include/text.php:1231 +msgid "frustrated" +msgstr "" + +#: include/text.php:1232 +msgid "depressed" +msgstr "" + +#: include/text.php:1233 +msgid "motivated" +msgstr "" + +#: include/text.php:1234 +msgid "relaxed" +msgstr "" + +#: include/text.php:1235 +msgid "surprised" +msgstr "" + +#: include/text.php:1433 include/js_strings.php:69 +msgid "January" +msgstr "" + +#: include/text.php:1433 include/js_strings.php:70 +msgid "February" +msgstr "" + +#: include/text.php:1433 include/js_strings.php:71 +msgid "March" +msgstr "" + +#: include/text.php:1433 include/js_strings.php:72 +msgid "April" +msgstr "" + +#: include/text.php:1433 +msgid "May" +msgstr "" + +#: include/text.php:1433 include/js_strings.php:74 +msgid "June" +msgstr "" + +#: include/text.php:1433 include/js_strings.php:75 +msgid "July" +msgstr "" + +#: include/text.php:1433 include/js_strings.php:76 +msgid "August" +msgstr "" + +#: include/text.php:1433 include/js_strings.php:77 +msgid "September" +msgstr "" + +#: include/text.php:1433 include/js_strings.php:78 +msgid "October" +msgstr "" + +#: include/text.php:1433 include/js_strings.php:79 +msgid "November" +msgstr "" + +#: include/text.php:1433 include/js_strings.php:80 +msgid "December" +msgstr "" + +#: include/text.php:1531 +msgid "Unknown Attachment" +msgstr "" + +#: include/text.php:1534 +msgid "unknown" +msgstr "" + +#: include/text.php:1588 +msgid "remove category" +msgstr "" + +#: include/text.php:1727 +msgid "remove from file" +msgstr "" + +#: include/text.php:1873 +msgid "Added to your calendar" +msgstr "" + +#: include/text.php:1961 +msgid "Link" +msgstr "" + +#: include/text.php:2044 +msgid "Poll has ended." +msgstr "" + +#: include/text.php:2046 +#, php-format +msgid "Poll ends: %1$s (%2$s)" +msgstr "" + +#: include/text.php:2051 +msgid "vote" +msgstr "" + +#: include/text.php:2062 +msgid "Download binary/encrypted content" +msgstr "" + +#: include/text.php:2194 +msgid "Page layout" +msgstr "" + +#: include/text.php:2194 +msgid "You can create your own with the layouts tool" +msgstr "" + +#: include/text.php:2205 +msgid "BBcode" +msgstr "" + +#: include/text.php:2206 +msgid "HTML" +msgstr "" + +#: include/text.php:2207 +msgid "Markdown" +msgstr "" + +#: include/text.php:2208 +msgid "Text" +msgstr "" + +#: include/text.php:2209 +msgid "Comanche Layout" +msgstr "" + +#: include/text.php:2214 +msgid "PHP" +msgstr "" + +#: include/text.php:2223 +msgid "Page content type" +msgstr "" + +#: include/text.php:2383 +msgid "activity" +msgstr "" + +#: include/text.php:2497 +msgid "a-z, 0-9, -, and _ only" +msgstr "" + +#: include/text.php:2847 +msgid "Design Tools" +msgstr "" + +#: include/text.php:2853 +msgid "Pages" +msgstr "" + +#: include/text.php:2876 +msgid "Import website..." +msgstr "" + +#: include/text.php:2877 +msgid "Select folder to import" +msgstr "" + +#: include/text.php:2878 +msgid "Import from a zipped folder:" +msgstr "" + +#: include/text.php:2879 +msgid "Import from cloud files:" +msgstr "" + +#: include/text.php:2880 +msgid "/cloud/channel/path/to/folder" +msgstr "" + +#: include/text.php:2881 +msgid "Enter path to website files" +msgstr "" + +#: include/text.php:2882 +msgid "Select folder" +msgstr "" + +#: include/text.php:2883 +msgid "Export website..." +msgstr "" + +#: include/text.php:2884 +msgid "Export to a zip file" +msgstr "" + +#: include/text.php:2885 +msgid "website.zip" +msgstr "" + +#: include/text.php:2886 +msgid "Enter a name for the zip file." +msgstr "" + +#: include/text.php:2887 +msgid "Export to cloud files" +msgstr "" + +#: include/text.php:2888 +msgid "/path/to/export/folder" +msgstr "" + +#: include/text.php:2889 +msgid "Enter a path to a cloud files destination." +msgstr "" + +#: include/text.php:2890 +msgid "Specify folder" +msgstr "" + +#: include/items.php:1058 +msgid "(Unknown)" +msgstr "" + +#: include/items.php:1261 +msgid "Visible to anybody on the internet." +msgstr "" + +#: include/items.php:1264 +msgid "Visible to you only." +msgstr "" + +#: include/items.php:1267 +msgid "Visible to anybody in this network." +msgstr "" + +#: include/items.php:1270 +msgid "Visible to anybody authenticated." +msgstr "" + +#: include/items.php:1273 +#, php-format +msgid "Visible to anybody on %s." +msgstr "" + +#: include/items.php:1276 +msgid "Visible to all connections." +msgstr "" + +#: include/items.php:1279 +msgid "Visible to approved connections." +msgstr "" + +#: include/items.php:1282 +msgid "Visible to specific connections." +msgstr "" + +#: include/items.php:4545 +msgid "Privacy group is empty." +msgstr "" + +#: include/items.php:4920 +msgid "profile photo" +msgstr "" + +#: include/items.php:5135 +#, php-format +msgid "[Edited %s]" +msgstr "" + +#: include/items.php:5135 +msgctxt "edit_activity" +msgid "Post" +msgstr "" + +#: include/items.php:5135 +msgctxt "edit_activity" +msgid "Comment" +msgstr "" + +#: include/oembed.php:159 +msgid "View PDF" +msgstr "" + +#: include/oembed.php:375 +msgid " by " +msgstr "" + +#: include/oembed.php:378 +msgid " on " +msgstr "" + +#: include/oembed.php:409 +msgid "Embedded content" +msgstr "" + +#: include/oembed.php:418 +msgid "Embedding disabled" +msgstr "" + +#: include/zid.php:398 #, php-format msgid "OpenWebAuth: %1$s welcomes %2$s" msgstr "" -#: include/js_strings.php:9 +#: include/channel.php:49 +msgid "Unable to obtain identity information from database" +msgstr "" + +#: include/channel.php:83 +msgid "Empty name" +msgstr "" + +#: include/channel.php:87 +msgid "Name too long" +msgstr "" + +#: include/channel.php:272 +msgid "No account identifier" +msgstr "" + +#: include/channel.php:284 +msgid "Nickname is required." +msgstr "" + +#: include/channel.php:375 +msgid "Unable to retrieve created identity" +msgstr "" + +#: include/channel.php:675 include/channel.php:773 +msgid "Unable to retrieve modified identity" +msgstr "" + +#: include/import.php:33 +msgid "Unable to import a removed channel." +msgstr "" + +#: include/import.php:63 +msgid "" +"A channel with these settings was discovered and is not usable as it was " +"removed or reserved for system use. Import failed." +msgstr "" + +#: include/import.php:72 +msgid "" +"Cannot create a duplicate channel identifier on this system. Import failed." +msgstr "" + +#: include/import.php:93 +msgid "Unable to create a unique channel address. Import failed." +msgstr "" + +#: include/import.php:140 +msgid "Cloned channel not found. Import failed." +msgstr "" + +#: include/nav.php:95 +msgid "Remote authentication" +msgstr "" + +#: include/nav.php:95 +msgid "Click to authenticate to your home hub" +msgstr "" + +#: include/nav.php:100 +msgid "Manage your channels" +msgstr "" + +#: include/nav.php:103 +msgid "Manage your access lists" +msgstr "" + +#: include/nav.php:105 +msgid "Account/Channel Settings" +msgstr "" + +#: include/nav.php:107 +msgid "(is on)" +msgstr "" + +#: include/nav.php:107 +msgid "(is off)" +msgstr "" + +#: include/nav.php:107 +msgid "Content filtering" +msgstr "" + +#: include/nav.php:114 include/nav.php:141 +msgid "End this session" +msgstr "" + +#: include/nav.php:117 +msgid "Your profile page" +msgstr "" + +#: include/nav.php:120 +msgid "Manage/Edit profiles" +msgstr "" + +#: include/nav.php:128 include/nav.php:131 +msgid "Sign in" +msgstr "" + +#: include/nav.php:157 +msgid "Take me home" +msgstr "" + +#: include/nav.php:159 +msgid "Log me out of this site" +msgstr "" + +#: include/nav.php:164 +msgid "Create an account" +msgstr "" + +#: include/nav.php:177 +msgid "Help and documentation" +msgstr "" + +#: include/nav.php:184 +msgid "Search site @name, #tag, ?doc, content" +msgstr "" + +#: include/nav.php:190 +msgid "Site Setup and Configuration" +msgstr "" + +#: include/nav.php:296 +msgid "Powered by $Projectname" +msgstr "" + +#: include/nav.php:307 +msgid "@name, #tag, ?doc, content" +msgstr "" + +#: include/nav.php:308 +msgid "Please wait..." +msgstr "" + +#: include/nav.php:316 +msgid "Arrange Apps" +msgstr "" + +#: include/nav.php:317 +msgid "Toggle System Apps" +msgstr "" + +#: include/js_strings.php:12 #, php-format msgid "%s show less" msgstr "" -#: include/js_strings.php:10 +#: include/js_strings.php:13 #, php-format msgid "%s expand" msgstr "" -#: include/js_strings.php:11 +#: include/js_strings.php:14 #, php-format msgid "%s collapse" msgstr "" -#: include/js_strings.php:12 +#: include/js_strings.php:15 msgid "Password too short" msgstr "" -#: include/js_strings.php:13 +#: include/js_strings.php:16 msgid "Passwords do not match" msgstr "" -#: include/js_strings.php:14 +#: include/js_strings.php:17 msgid "everybody" msgstr "" -#: include/js_strings.php:15 +#: include/js_strings.php:18 msgid "Secret Passphrase" msgstr "" -#: include/js_strings.php:16 +#: include/js_strings.php:19 msgid "Passphrase hint" msgstr "" -#: include/js_strings.php:17 +#: include/js_strings.php:20 msgid "Notice: Permissions have changed but have not yet been submitted." msgstr "" -#: include/js_strings.php:18 +#: include/js_strings.php:21 msgid "close all" msgstr "" -#: include/js_strings.php:19 +#: include/js_strings.php:22 msgid "Nothing new here" msgstr "" -#: include/js_strings.php:20 +#: include/js_strings.php:23 msgid "Rate This Channel (this is public)" msgstr "" -#: include/js_strings.php:22 +#: include/js_strings.php:25 msgid "Describe (optional)" msgstr "" -#: include/js_strings.php:24 +#: include/js_strings.php:27 msgid "Please enter a link URL" msgstr "" -#: include/js_strings.php:25 +#: include/js_strings.php:28 msgid "Unsaved changes. Are you sure you wish to leave this page?" msgstr "" -#: include/js_strings.php:27 +#: include/js_strings.php:30 msgid "lovely" msgstr "" -#: include/js_strings.php:28 +#: include/js_strings.php:31 msgid "wonderful" msgstr "" -#: include/js_strings.php:29 +#: include/js_strings.php:32 msgid "fantastic" msgstr "" -#: include/js_strings.php:30 +#: include/js_strings.php:33 msgid "great" msgstr "" -#: include/js_strings.php:31 +#: include/js_strings.php:34 msgid "" "Your chosen nickname was either already taken or not valid. Please use our " "suggestion (" msgstr "" -#: include/js_strings.php:32 +#: include/js_strings.php:35 msgid ") or enter a new one." msgstr "" -#: include/js_strings.php:33 +#: include/js_strings.php:36 msgid "Thank you, this nickname is valid." msgstr "" -#: include/js_strings.php:34 +#: include/js_strings.php:37 msgid "A channel name is required." msgstr "" -#: include/js_strings.php:35 +#: include/js_strings.php:38 msgid "This is a " msgstr "" -#: include/js_strings.php:36 +#: include/js_strings.php:39 msgid " channel name" msgstr "" -#: include/js_strings.php:37 +#: include/js_strings.php:40 msgid "Pinned" msgstr "" -#: include/js_strings.php:40 +#: include/js_strings.php:43 msgid "Please accept terms to continue" msgstr "" -#: include/js_strings.php:46 +#: include/js_strings.php:49 msgid "timeago.prefixAgo" msgstr "" -#: include/js_strings.php:47 +#: include/js_strings.php:50 msgid "timeago.prefixFromNow" msgstr "" -#: include/js_strings.php:48 +#: include/js_strings.php:51 msgid "timeago.suffixAgo" msgstr "" -#: include/js_strings.php:49 +#: include/js_strings.php:52 msgid "timeago.suffixFromNow" msgstr "" -#: include/js_strings.php:52 +#: include/js_strings.php:55 msgid "less than a minute" msgstr "" -#: include/js_strings.php:53 +#: include/js_strings.php:56 msgid "about a minute" msgstr "" -#: include/js_strings.php:54 +#: include/js_strings.php:57 #, php-format msgid "%d minutes" msgstr "" -#: include/js_strings.php:55 +#: include/js_strings.php:58 msgid "about an hour" msgstr "" -#: include/js_strings.php:56 +#: include/js_strings.php:59 #, php-format msgid "about %d hours" msgstr "" -#: include/js_strings.php:57 +#: include/js_strings.php:60 msgid "a day" msgstr "" -#: include/js_strings.php:58 +#: include/js_strings.php:61 #, php-format msgid "%d days" msgstr "" -#: include/js_strings.php:59 +#: include/js_strings.php:62 msgid "about a month" msgstr "" -#: include/js_strings.php:60 +#: include/js_strings.php:63 #, php-format msgid "%d months" msgstr "" -#: include/js_strings.php:61 +#: include/js_strings.php:64 msgid "about a year" msgstr "" -#: include/js_strings.php:62 +#: include/js_strings.php:65 #, php-format msgid "%d years" msgstr "" -#: include/js_strings.php:63 +#: include/js_strings.php:66 msgid " " msgstr "" -#: include/js_strings.php:64 +#: include/js_strings.php:67 msgid "timeago.numbers" msgstr "" -#: include/js_strings.php:70 +#: include/js_strings.php:73 msgctxt "long" msgid "May" msgstr "" -#: include/js_strings.php:78 +#: include/js_strings.php:81 msgid "Jan" msgstr "" -#: include/js_strings.php:79 +#: include/js_strings.php:82 msgid "Feb" msgstr "" -#: include/js_strings.php:80 +#: include/js_strings.php:83 msgid "Mar" msgstr "" -#: include/js_strings.php:81 +#: include/js_strings.php:84 msgid "Apr" msgstr "" -#: include/js_strings.php:82 +#: include/js_strings.php:85 msgctxt "short" msgid "May" msgstr "" -#: include/js_strings.php:83 +#: include/js_strings.php:86 msgid "Jun" msgstr "" -#: include/js_strings.php:84 +#: include/js_strings.php:87 msgid "Jul" msgstr "" -#: include/js_strings.php:85 +#: include/js_strings.php:88 msgid "Aug" msgstr "" -#: include/js_strings.php:86 +#: include/js_strings.php:89 msgid "Sep" msgstr "" -#: include/js_strings.php:87 +#: include/js_strings.php:90 msgid "Oct" msgstr "" -#: include/js_strings.php:88 +#: include/js_strings.php:91 msgid "Nov" msgstr "" -#: include/js_strings.php:89 +#: include/js_strings.php:92 msgid "Dec" msgstr "" -#: include/js_strings.php:97 +#: include/js_strings.php:100 msgid "Sun" msgstr "" -#: include/js_strings.php:98 +#: include/js_strings.php:101 msgid "Mon" msgstr "" -#: include/js_strings.php:99 +#: include/js_strings.php:102 msgid "Tue" msgstr "" -#: include/js_strings.php:100 +#: include/js_strings.php:103 msgid "Wed" msgstr "" -#: include/js_strings.php:101 +#: include/js_strings.php:104 msgid "Thu" msgstr "" -#: include/js_strings.php:102 +#: include/js_strings.php:105 msgid "Fri" msgstr "" -#: include/js_strings.php:103 -msgid "Sat" -msgstr "" - -#: include/js_strings.php:104 -msgctxt "calendar" -msgid "today" -msgstr "" - -#: include/js_strings.php:105 -msgctxt "calendar" -msgid "month" -msgstr "" - #: include/js_strings.php:106 -msgctxt "calendar" -msgid "week" +msgid "Sat" msgstr "" #: include/js_strings.php:107 msgctxt "calendar" -msgid "day" +msgid "today" msgstr "" #: include/js_strings.php:108 msgctxt "calendar" -msgid "All day" +msgid "month" msgstr "" #: include/js_strings.php:109 +msgctxt "calendar" +msgid "week" +msgstr "" + +#: include/js_strings.php:110 +msgctxt "calendar" +msgid "day" +msgstr "" + +#: include/js_strings.php:111 +msgctxt "calendar" +msgid "All day" +msgstr "" + +#: include/js_strings.php:112 msgid "" "A social networking profile that is public by default and private if desired" msgstr "" -#: include/js_strings.php:110 +#: include/js_strings.php:113 msgid "" "A social networking profile where content is private to your [Friends] " "Access List by default but can be made public if desired" msgstr "" -#: include/js_strings.php:111 +#: include/js_strings.php:114 msgid "A public group where members are allowed to upload media by default" msgstr "" -#: include/js_strings.php:112 +#: include/js_strings.php:115 msgid "A private group with no upload permission" msgstr "" -#: include/js_strings.php:113 +#: include/js_strings.php:116 msgid "" "A public group where posts are moderated by the owner. The [moderated] " "permission may be removed from any group member once trust is established" msgstr "" -#: include/js_strings.php:114 +#: include/js_strings.php:117 msgid "" "A sub-channel of your main channel - often devoted to a specific language or " "topic. Replies are sent back to your main channel" msgstr "" -#: include/js_strings.php:115 +#: include/js_strings.php:118 msgid "" "A private sub-channel of your main channel - often devoted to a specific " "language or topic. Replies are sent back to your main channel" msgstr "" -#: include/event.php:33 include/event.php:102 -msgid "l F d, Y \\@ g:i A" -msgstr "" - -#: include/event.php:41 include/event.php:108 -msgid "Starts:" -msgstr "" - -#: include/event.php:51 include/event.php:112 -msgid "Finishes:" -msgstr "" - -#: include/event.php:1066 -msgid "This event has been added to your calendar." -msgstr "" - -#: include/event.php:1291 -msgid "Not specified" -msgstr "" - -#: include/event.php:1292 -msgid "Needs Action" -msgstr "" - -#: include/event.php:1293 -msgid "Completed" -msgstr "" - -#: include/event.php:1294 -msgid "In Process" -msgstr "" - -#: include/event.php:1295 -msgid "Cancelled" -msgstr "" - #: view/theme/redbasic/php/config.php:16 view/theme/redbasic/php/config.php:19 msgid "Focus (Hubzilla default)" msgstr "" diff --git a/util/pconfig b/util/pconfig index a30e69960..69192534f 100755 --- a/util/pconfig +++ b/util/pconfig @@ -82,7 +82,7 @@ if($argc == 3) { } if($argc == 2) { - $r = q("select * from pconfig where uid = " . intval($argv[1])); + $r = q('select * from pconfig where uid = ' . intval($argv[1])); if($r) { foreach($r as $rr) { echo "pconfig[{$rr['uid']}][{$rr['cat']}][{$rr['k']}] = " . printable_config($rr['v']) . "\n"; @@ -91,7 +91,7 @@ if($argc == 2) { } if($argc == 1) { - $r = q("select channel_id, channel_name from channel where channel_removed = 0"); + $r = q('select channel_id, channel_name from channel where channel_removed = 0'); if($r) { foreach($r as $rr) { echo sprintf('%4u %s', $rr['channel_id'], $rr['channel_name']) . PHP_EOL; diff --git a/util/php2po.php b/util/php2po.php index 46e7f9069..906e04704 100644 --- a/util/php2po.php +++ b/util/php2po.php @@ -1,85 +1,86 @@ \n\n"; - return; - } +if ($argc != 2) { + print 'Usage: ' . $argv[0] . " \n\n"; + return; +} $phpfile = $argv[1]; - $pofile = dirname($phpfile)."/messages.po"; + $pofile = dirname($phpfile) . '/messages.po'; - if (!file_exists($phpfile)){ - print "Unable to find '$phpfile'\n"; - return; - } +if (!file_exists($phpfile)) { + print "Unable to find '$phpfile'\n"; + return; +} include_once($phpfile); print "Out to '$pofile'\n"; - $out = ""; + $out = ''; $infile = file($pofile); - $k = ""; - $c = ""; - $ink = False; - foreach ($infile as $l) { - - $l = trim($l, " "); - if (!preg_match("/^msgstr\[[1-9]/",$l)) { - if ($k!="" && (substr($l,0,7)=="msgstr " || substr($l,0,8)=="msgstr[0")){ - $ink = False; - $k = stripcslashes($k); - $v = ""; - if (isset(App::$strings[$k])) { - $v = App::$strings[$k]; - } else { - $k = "__ctx:".$c."__ ".$k; - if (isset(App::$strings[$k])) { - $v = App::$strings[$k]; - $c = ""; - }; - } - if (!empty($v)) { - if (is_array($v)) { - $l = ""; - $n = 0; - foreach ($v as &$value) { - $l .= "msgstr[".$n."] \"".addcslashes($value,"\"\n")."\"\n"; - $n++; - } - } else { - $l = "msgstr \"".addcslashes($v,"\"\n")."\"\n"; - } - } - } - - if (substr($l,0,6)=="msgid_" || substr($l,0,7)=="msgstr[") $ink = False; - - if ($ink) { - preg_match('/^"(.*)"$/',$l,$m); - $k .= $m[1]; - } - - if (substr($l,0,6)=="msgid ") { - preg_match('/^msgid "(.*)"$/',$l,$m); - $k = $m[1]; - $ink = True; - } - - if (substr($l,0,8)=="msgctxt ") { - preg_match('/^msgctxt "(.*)"$/',$l,$m); - $c = $m[1]; - } - - $out .= $l; + $k = ''; + $c = ''; + $ink = false; +foreach ($infile as $l) { + $l = trim($l, ' '); + if (!preg_match('/^msgstr\[[1-9]/', $l)) { + if ($k != '' && (substr($l, 0, 7) == 'msgstr ' || substr($l, 0, 8) == 'msgstr[0')) { + $ink = false; + $k = stripcslashes($k); + $v = ''; + if (isset(App::$strings[$k])) { + $v = App::$strings[$k]; + } else { + $k = '__ctx:' . $c . '__ ' . $k; + if (isset(App::$strings[$k])) { + $v = App::$strings[$k]; + $c = ''; } + } + if (!empty($v)) { + if (is_array($v)) { + $l = ''; + $n = 0; + foreach ($v as &$value) { + $l .= 'msgstr[' . $n . "] \"" . addcslashes($value, "\"\n") . "\"\n"; + $n++; + } + } else { + $l = "msgstr \"" . addcslashes($v, "\"\n") . "\"\n"; + } + } } + + if (substr($l, 0, 6) == 'msgid_' || substr($l, 0, 7) == 'msgstr[') { + $ink = false; + } + + if ($ink) { + preg_match('/^"(.*)"$/', $l, $m); + $k .= $m[1]; + } + + if (substr($l, 0, 6) == 'msgid ') { + preg_match('/^msgid "(.*)"$/', $l, $m); + $k = $m[1]; + $ink = true; + } + + if (substr($l, 0, 8) == 'msgctxt ') { + preg_match('/^msgctxt "(.*)"$/', $l, $m); + $c = $m[1]; + } + + $out .= $l; + } +} file_put_contents($pofile, $out); -?> diff --git a/util/phplogtime b/util/phplogtime index 0fb8a8e2b..d774ecaef 100755 --- a/util/phplogtime +++ b/util/phplogtime @@ -33,8 +33,8 @@ if (! $lines) { if ($lines) { foreach ($lines as $line) { - if (substr($line,0,1) === "[") { - $ts = rtrim(substr($line,1,strpos($line,"]")),"]"); + if (substr($line,0,1) === '[') { + $ts = rtrim(substr($line,1,strpos($line, ']')), ']'); if ($ts) { $arr = explode(' ', $ts); if (count($arr) === 3) { @@ -42,8 +42,8 @@ if ($lines) { $d = new DateTime($arr[1] . ' ' . $arr[2]); $to = new DateTimeZone($dest_tz); $d->setTimeZone($to); - $o = $d->format("Y-m-d H:i:s"); - echo "[" . $o . "]" . substr($line,strlen($ts) + 2); + $o = $d->format('Y-m-d H:i:s'); + echo '[' . $o . ']' . substr($line,strlen($ts) + 2); } } } diff --git a/util/po2php.php b/util/po2php.php index 6bb54f2e9..775b850d4 100644 --- a/util/po2php.php +++ b/util/po2php.php @@ -1,152 +1,173 @@ \n\n"; - return; - } + if ($argc < 2) { + print 'Usage: ' . $argv[0] . " \n\n"; + return; + } - $rtl = false; + $rtl = false; - $pofile = $argv[1]; - $outfile = dirname($pofile)."/strings.php"; + $pofile = $argv[1]; + $outfile = dirname($pofile) . '/strings.php'; - if($argc > 2) { - if($argv[2] === 'rtl') - $rtl = true; - } + if ($argc > 2) { + if ($argv[2] === 'rtl') { + $rtl = true; + } + } - if(strstr($outfile,'util')) - $lang = 'en'; - else - $lang = str_replace('-','_',basename(dirname($pofile))); + if (strstr($outfile, 'util')) { + $lang = 'en'; + } else { + $lang = str_replace('-', '_', basename(dirname($pofile))); + } - if (!file_exists($pofile)){ - print "Unable to find '$pofile'\n"; - return; - } - - print "Out to '$outfile'\n"; - - $out=" " - .preg_replace_callback($escape_s_exp,'escape_s',$match[2]) .",\n"; - } - - if (substr($l,0,6)=="msgid_") { - $ink = False; - $out .= 'App::$strings["'.$k.'"] = '; - } + $infile = file($pofile); + $k = ''; + $v = ''; + $ctx = ''; + $arr = false; + $ink = false; + $inv = false; + $escape_s_exp = '|[^\\\\]\$[a-z]|'; + + function escape_s($match) + { + return str_replace('$', '\$', $match[0]); + } + + foreach ($infile as $l) { + $l = str_replace(array('$projectname','$Projectname'), array('\$projectname','\$Projectname'), $l); + $len = strlen($l); + if ($l[0] == '#') { + $l = ''; + } + if (substr($l, 0, 15) == '"Plural-Forms: ') { + $match = array(); + preg_match("|nplurals=([0-9]*); *plural=(.*)[;\\\\]|", $l, $match); + $cond = str_replace('n', '$n', $match[2]); + $out .= 'if(! function_exists("' . 'string_plural_select_' . $lang . '")) {' . "\n"; + $out .= 'function string_plural_select_' . $lang . '($n){' . "\n"; + $out .= ' return ' . $cond . ';' . "\n"; + $out .= '}}' . "\n"; + + $out .= 'App::$rtl = ' . intval($rtl) ; + } + + if ($k != '' && substr($l, 0, 7) == 'msgstr ') { + if ($ink) { + $ink = false; + $out .= 'App::$strings["' . $k . '"] = '; + } + if ($inv) { + $inv = false; + $out .= '"' . $v . '"'; + } + + $v = substr($l, 8, $len - 10); + $v = preg_replace_callback($escape_s_exp, 'escape_s', $v); + $inv = true; + //$out .= $v; + } + if ($k != '' && substr($l, 0, 7) == 'msgstr[') { + if ($ink) { + $ink = false; + $out .= 'App::$strings["' . $k . '"] = '; + } + if ($inv) { + $inv = false; + $out .= '"' . $v . '"'; + } + if (!$arr) { + $arr = true; + $out .= "[\n"; + } + $match = array(); + preg_match('|\[([0-9]*)\] (.*)|', $l, $match); + $out .= "\t" . + preg_replace_callback($escape_s_exp, 'escape_s', $match[1]) + . ' => ' + . preg_replace_callback($escape_s_exp, 'escape_s', $match[2]) . ",\n"; + } + + if (substr($l, 0, 6) == 'msgid_') { + $ink = false; + $out .= 'App::$strings["' . $k . '"] = '; + } - if ($ink) { - $k .= trim_message($l); - $k = preg_replace_callback($escape_s_exp,'escape_s',$k); - //$out .= 'App::$strings['.$k.'] = '; - } - - if (substr($l,0,6)=="msgid "){ - if ($inv) { $inv = False; $out .= '"'.$v.'"'; } - if ($k!="") $out .= $arr?"];\n":";\n"; - $arr=False; - $k = str_replace("msgid ","",$l); - $k = trim_message($k); - $k = $ctx.$k; - // echo $ctx ? $ctx."\nX\n":""; - $k = preg_replace_callback($escape_s_exp,'escape_s',$k); - $ctx = ""; - $ink = True; - } - - if ($inv && substr($l,0,6)!="msgstr" && substr($l,0,7)!="msgctxt") { - $v .= trim_message($l); - $v = preg_replace_callback($escape_s_exp,'escape_s',$v); - //$out .= 'App::$strings['.$k.'] = '; - } + if ($ink) { + $k .= trim_message($l); + $k = preg_replace_callback($escape_s_exp, 'escape_s', $k); + //$out .= 'App::$strings['.$k.'] = '; + } - if (substr($l,0,7)=="msgctxt") { - $ctx = str_replace("msgctxt ","",$l); - $ctx = trim_message($ctx); - $ctx = "__ctx:".$ctx."__ "; - $ctx = preg_replace_callback($escape_s_exp,'escape_s',$ctx); - } + if (substr($l, 0, 6) == 'msgid ') { + if ($inv) { + $inv = false; + $out .= '"' . $v . '"'; + } + if ($k != '') { + $out .= $arr ? "];\n" : ";\n"; + } + $arr = false; + $k = str_replace('msgid ', '', $l); + $k = trim_message($k); + $k = $ctx . $k; + // echo $ctx ? $ctx."\nX\n":""; + $k = preg_replace_callback($escape_s_exp, 'escape_s', $k); + $ctx = ''; + $ink = true; + } - } + if ($inv && substr($l, 0, 6) != 'msgstr' && substr($l, 0, 7) != 'msgctxt') { + $v .= trim_message($l); + $v = preg_replace_callback($escape_s_exp, 'escape_s', $v); + //$out .= 'App::$strings['.$k.'] = '; + } - if ($inv) { $inv = False; $out .= '"'.$v.'"'; } - if ($k!="") $out .= $arr?"];\n":";\n"; - - file_put_contents($outfile, $out); - + if (substr($l, 0, 7) == 'msgctxt') { + $ctx = str_replace('msgctxt ', '', $l); + $ctx = trim_message($ctx); + $ctx = '__ctx:' . $ctx . '__ '; + $ctx = preg_replace_callback($escape_s_exp, 'escape_s', $ctx); + } + } + + if ($inv) { + $inv = false; + $out .= '"' . $v . '"'; + } + if ($k != '') { + $out .= $arr ? "];\n" : ";\n"; + } + + file_put_contents($outfile, $out); } -function trim_message($str) { - // Almost same as trim("\"\r\n") except that escaped quotes are preserved - $str = trim($str, "\r\n"); - $str = ltrim($str, "\""); - $str = preg_replace('/(?setTemplateDir($folders); - -$s->setCompileDir(TEMPLATE_BUILD_PATH . '/compiled/'); -$s->setConfigDir(TEMPLATE_BUILD_PATH . '/config/'); -$s->setCacheDir(TEMPLATE_BUILD_PATH . '/cache/'); - -$s->left_delimiter = "{{"; -$s->right_delimiter = "}}"; - -$s->compileAllTemplates('.tpl',true); \ No newline at end of file +setTemplateDir($folders); + +$s->setCompileDir(TEMPLATE_BUILD_PATH . '/compiled/'); +$s->setConfigDir(TEMPLATE_BUILD_PATH . '/config/'); +$s->setCacheDir(TEMPLATE_BUILD_PATH . '/cache/'); + +$s->left_delimiter = '{{'; +$s->right_delimiter = '}}'; + +$s->compileAllTemplates('.tpl', true); diff --git a/util/schemaspy b/util/schemaspy index fdae74965..29a631300 100755 --- a/util/schemaspy +++ b/util/schemaspy @@ -2,7 +2,7 @@ 0"); + $x = q('SELECT COUNT(id) AS qty FROM photo WHERE resource_id IN (SELECT DISTINCT resource_id FROM photo WHERE photo_usage = 0 and os_storage = 1) AND imgscale > 0'); echo 'Thumbnails total: ' . $x[0]['qty'] . PHP_EOL; - $x = q("SELECT COUNT(id) AS qty FROM photo WHERE resource_id IN (SELECT DISTINCT resource_id FROM photo WHERE photo_usage = 0 and os_storage = 1) AND os_storage != %d AND imgscale > 0", + $x = q('SELECT COUNT(id) AS qty FROM photo WHERE resource_id IN (SELECT DISTINCT resource_id FROM photo WHERE photo_usage = 0 and os_storage = 1) AND os_storage != %d AND imgscale > 0', $storage ); echo 'Thumbnails to convert: ' . $x[0]['qty'] . PHP_EOL; @@ -45,7 +45,7 @@ if($argc == 2) { break; } - $x = q("SELECT resource_id, content FROM photo WHERE photo_usage = 0 AND os_storage = 1 AND imgscale = 0"); + $x = q('SELECT resource_id, content FROM photo WHERE photo_usage = 0 AND os_storage = 1 AND imgscale = 0'); if($x) { foreach($x as $xx) { @@ -88,7 +88,7 @@ if($argc == 2) { break; } - $x = q("SELECT resource_id FROM photo WHERE photo_usage = 0 AND os_storage = 1 AND imgscale = 0"); + $x = q('SELECT resource_id FROM photo WHERE photo_usage = 0 AND os_storage = 1 AND imgscale = 0'); if($x) { foreach($x as $xx) { diff --git a/util/strings.php b/util/strings.php index d8c571b63..f368ed7d6 100644 --- a/util/strings.php +++ b/util/strings.php @@ -1,2743 +1,2742 @@ may apply to this list and any future members. If this is not what you intended, please create another list with a different name."] = ""; -App::$strings["Add new connections to this access list"] = ""; -App::$strings["edit"] = ""; -App::$strings["Edit list"] = ""; -App::$strings["Create new list"] = ""; -App::$strings["Channels not in any access list"] = ""; -App::$strings["add"] = ""; -App::$strings["Source code of failed update: "] = ""; -App::$strings["Update Error at %s"] = ""; -App::$strings["Update %s failed. See error logs."] = ""; -App::$strings["__ctx:permcat__ default"] = ""; -App::$strings["__ctx:permcat__ follower"] = ""; -App::$strings["__ctx:permcat__ contributor"] = ""; -App::$strings["__ctx:permcat__ publisher"] = ""; -App::$strings["%1\$s wrote the following %2\$s %3\$s"] = ""; -App::$strings["post"] = ""; -App::$strings["Private Message"] = ""; -App::$strings["Privacy conflict. Discretion advised."] = ""; -App::$strings["Admin Delete"] = ""; -App::$strings["Select"] = ""; -App::$strings["Save to Folder"] = ""; -App::$strings["I will attend"] = ""; -App::$strings["I will not attend"] = ""; -App::$strings["I might attend"] = ""; -App::$strings["I agree"] = ""; -App::$strings["I disagree"] = ""; -App::$strings["I abstain"] = ""; -App::$strings["View all"] = ""; -App::$strings["__ctx:noun__ Like"] = [ - 0 => "", - 1 => "", +App::$strings['Social Networking'] = ''; +App::$strings['Social - Normal'] = ''; +App::$strings['Social - Restricted'] = ''; +App::$strings['Community Group'] = ''; +App::$strings['Group - Normal'] = ''; +App::$strings['Group - Restricted'] = ''; +App::$strings['Group - Moderated'] = ''; +App::$strings['Collection'] = ''; +App::$strings['Collection - Normal'] = ''; +App::$strings['Collection - Restricted'] = ''; +App::$strings['Grant viewing access to and delivery of your channel stream and posts'] = ''; +App::$strings['Grant viewing access to your default channel profile'] = ''; +App::$strings['Grant viewing access to your address book (connections)'] = ''; +App::$strings['Grant viewing access to your file storage and photos'] = ''; +App::$strings['Grant permission to post on your channel (wall) page'] = ''; +App::$strings['Accept delivery of their posts and all comments to their posts'] = ''; +App::$strings['Accept delivery of comments and likes on your posts'] = ''; +App::$strings['Accept delivery of their likes of your profile'] = ''; +App::$strings['Grant upload permissions to your file storage and photos'] = ''; +App::$strings['Grant permission to republish/mirror your posts'] = ''; +App::$strings['Accept comments and wall posts only after approval (moderation)'] = ''; +App::$strings['Grant channel administration (delegation) permission'] = ''; +App::$strings['Missing room name'] = ''; +App::$strings['Duplicate room name'] = ''; +App::$strings['Invalid room specifier.'] = ''; +App::$strings['Room not found.'] = ''; +App::$strings['Permission denied.'] = ''; +App::$strings['Room is full'] = ''; +App::$strings['Apps'] = ''; +App::$strings['Friend Zoom'] = ''; +App::$strings['Articles'] = ''; +App::$strings['Cards'] = ''; +App::$strings['Calendar'] = ''; +App::$strings['Categories'] = ''; +App::$strings['Clients'] = ''; +App::$strings['Site Admin'] = ''; +App::$strings['Content Filter'] = ''; +App::$strings['Content Import'] = ''; +App::$strings['Report Bug'] = ''; +App::$strings['View Bookmarks'] = ''; +App::$strings['Chatrooms'] = ''; +App::$strings['Connections'] = ''; +App::$strings['Expire Posts'] = ''; +App::$strings['Future Posting'] = ''; +App::$strings['Remote Diagnostics'] = ''; +App::$strings['Suggest Channels'] = ''; +App::$strings['Login'] = ''; +App::$strings['Channel Manager'] = ''; +App::$strings['Notes'] = ''; +App::$strings['Stream'] = ''; +App::$strings['Settings'] = ''; +App::$strings['Files'] = ''; +App::$strings['Webpages'] = ''; +App::$strings['Wiki'] = ''; +App::$strings['Channel Home'] = ''; +App::$strings['View Profile'] = ''; +App::$strings['Photos'] = ''; +App::$strings['Photomap'] = ''; +App::$strings['Events'] = ''; +App::$strings['Tasks'] = ''; +App::$strings['No Comment'] = ''; +App::$strings['Directory'] = ''; +App::$strings['Help'] = ''; +App::$strings['Mail'] = ''; +App::$strings['Mood'] = ''; +App::$strings['Poke'] = ''; +App::$strings['Chat'] = ''; +App::$strings['Search'] = ''; +App::$strings['Stream Order'] = ''; +App::$strings['Probe'] = ''; +App::$strings['Suggest'] = ''; +App::$strings['Random Channel'] = ''; +App::$strings['Invite'] = ''; +App::$strings['Features'] = ''; +App::$strings['Language'] = ''; +App::$strings['Post'] = ''; +App::$strings['ZotPost'] = ''; +App::$strings['Profile Photo'] = ''; +App::$strings['Profile'] = ''; +App::$strings['Profiles'] = ''; +App::$strings['Lists'] = ''; +App::$strings['Notifications'] = ''; +App::$strings['Order Apps'] = ''; +App::$strings['CalDAV'] = ''; +App::$strings['CardDAV'] = ''; +App::$strings['Channel Sources'] = ''; +App::$strings['Gallery'] = ''; +App::$strings['Guest Access'] = ''; +App::$strings['OAuth Apps Manager'] = ''; +App::$strings['OAuth2 Apps Manager'] = ''; +App::$strings['PDL Editor'] = ''; +App::$strings['Permission Categories'] = ''; +App::$strings['Premium Channel'] = ''; +App::$strings['Public Stream'] = ''; +App::$strings['My Chatrooms'] = ''; +App::$strings['Update'] = ''; +App::$strings['Install'] = ''; +App::$strings['Purchase'] = ''; +App::$strings['Edit'] = ''; +App::$strings['Delete'] = ''; +App::$strings['Undelete'] = ''; +App::$strings['Add to app-tray'] = ''; +App::$strings['Remove from app-tray'] = ''; +App::$strings['Pin to navbar'] = ''; +App::$strings['Unpin from navbar'] = ''; +App::$strings['Unknown'] = ''; +App::$strings['Visible to your default audience'] = ''; +App::$strings['Only me'] = ''; +App::$strings['Public'] = ''; +App::$strings["Anybody in the \$Projectname network"] = ''; +App::$strings['Any account on %s'] = ''; +App::$strings['Any of my connections'] = ''; +App::$strings['Only connections I specifically allow'] = ''; +App::$strings['Anybody authenticated (could include visitors from other networks)'] = ''; +App::$strings["Any connections including those who haven't yet been approved"] = ''; +App::$strings['This is your default setting for the audience of your normal stream, and posts.'] = ''; +App::$strings['This is your default setting for who can view your default channel profile'] = ''; +App::$strings['This is your default setting for who can view your connections'] = ''; +App::$strings['This is your default setting for who can view your file storage and photos'] = ''; +App::$strings['This is your default setting for the audience of your webpages'] = ''; +App::$strings['Channel is blocked on this site.'] = ''; +App::$strings['Channel location missing.'] = ''; +App::$strings['Remote channel or protocol unavailable.'] = ''; +App::$strings['Channel discovery failed.'] = ''; +App::$strings['Protocol not supported'] = ''; +App::$strings['Cannot connect to yourself.'] = ''; +App::$strings['error saving data'] = ''; +App::$strings['A deleted list with this name was revived. Existing item permissions may apply to this list and any future members. If this is not what you intended, please create another list with a different name.'] = ''; +App::$strings['Add new connections to this access list'] = ''; +App::$strings['edit'] = ''; +App::$strings['Edit list'] = ''; +App::$strings['Create new list'] = ''; +App::$strings['Channels not in any access list'] = ''; +App::$strings['add'] = ''; +App::$strings['Source code of failed update: '] = ''; +App::$strings['Update Error at %s'] = ''; +App::$strings['Update %s failed. See error logs.'] = ''; +App::$strings['__ctx:permcat__ default'] = ''; +App::$strings['__ctx:permcat__ follower'] = ''; +App::$strings['__ctx:permcat__ contributor'] = ''; +App::$strings['__ctx:permcat__ publisher'] = ''; +App::$strings["%1\$s wrote the following %2\$s %3\$s"] = ''; +App::$strings['post'] = ''; +App::$strings['Private Message'] = ''; +App::$strings['Privacy conflict. Discretion advised.'] = ''; +App::$strings['Admin Delete'] = ''; +App::$strings['Select'] = ''; +App::$strings['Save to Folder'] = ''; +App::$strings['I will attend'] = ''; +App::$strings['I will not attend'] = ''; +App::$strings['I might attend'] = ''; +App::$strings['I agree'] = ''; +App::$strings['I disagree'] = ''; +App::$strings['I abstain'] = ''; +App::$strings['View all'] = ''; +App::$strings['__ctx:noun__ Like'] = [ + 0 => '', + 1 => '', ]; -App::$strings["__ctx:noun__ Likes"] = ""; -App::$strings["__ctx:noun__ Dislike"] = [ - 0 => "", - 1 => "", +App::$strings['__ctx:noun__ Likes'] = ''; +App::$strings['__ctx:noun__ Dislike'] = [ + 0 => '', + 1 => '', ]; -App::$strings["__ctx:noun__ Dislikes"] = ""; -App::$strings["Save"] = ""; -App::$strings["Message signature validated"] = ""; -App::$strings["Message signature incorrect"] = ""; -App::$strings["Add Tag"] = ""; -App::$strings["I like this (toggle)"] = ""; -App::$strings["like"] = ""; -App::$strings["I don't like this (toggle)"] = ""; -App::$strings["dislike"] = ""; -App::$strings["Repeat This"] = ""; -App::$strings["Share this"] = ""; -App::$strings["Delivery Report"] = ""; -App::$strings["%d comment"] = [ - 0 => "", - 1 => "", +App::$strings['__ctx:noun__ Dislikes'] = ''; +App::$strings['Save'] = ''; +App::$strings['Message signature validated'] = ''; +App::$strings['Message signature incorrect'] = ''; +App::$strings['Add Tag'] = ''; +App::$strings['I like this (toggle)'] = ''; +App::$strings['like'] = ''; +App::$strings["I don't like this (toggle)"] = ''; +App::$strings['dislike'] = ''; +App::$strings['Repeat This'] = ''; +App::$strings['Share this'] = ''; +App::$strings['Delivery Report'] = ''; +App::$strings['%d comment'] = [ + 0 => '', + 1 => '', ]; -App::$strings["%d unseen"] = ""; -App::$strings["View %s's profile - %s"] = ""; -App::$strings["to"] = ""; -App::$strings["via"] = ""; -App::$strings["Wall-to-Wall"] = ""; -App::$strings["via Wall-To-Wall:"] = ""; -App::$strings["from %s"] = ""; -App::$strings["last edited: %s"] = ""; -App::$strings["Expires: %s"] = ""; -App::$strings["Attend"] = ""; -App::$strings["Attendance Options"] = ""; -App::$strings["Vote"] = ""; -App::$strings["Voting Options"] = ""; -App::$strings["Reply"] = ""; -App::$strings["Pinned post"] = ""; -App::$strings["Unpin this post"] = ""; -App::$strings["Pin this post"] = ""; -App::$strings["Save Bookmarks"] = ""; -App::$strings["Add to Calendar"] = ""; -App::$strings["Mark all seen"] = ""; -App::$strings["Close"] = ""; -App::$strings["This is an unsaved preview"] = ""; -App::$strings["Please wait"] = ""; -App::$strings["%s show all"] = ""; -App::$strings["This is you"] = ""; -App::$strings["Comment"] = ""; -App::$strings["Submit"] = ""; -App::$strings["Bold"] = ""; -App::$strings["Italic"] = ""; -App::$strings["Underline"] = ""; -App::$strings["Quote"] = ""; -App::$strings["Code"] = ""; -App::$strings["Image"] = ""; -App::$strings["Attach/Upload file"] = ""; -App::$strings["Insert Link"] = ""; -App::$strings["Video"] = ""; -App::$strings["Preview"] = ""; -App::$strings["Reset"] = ""; -App::$strings["Encrypt text"] = ""; -App::$strings["Your full name (required)"] = ""; -App::$strings["Your email address (required)"] = ""; -App::$strings["Your website URL (optional)"] = ""; -App::$strings["Likes %1\$s's %2\$s"] = ""; -App::$strings["Doesn't like %1\$s's %2\$s"] = ""; -App::$strings["Will attend %s's event"] = ""; -App::$strings["Will not attend %s's event"] = ""; -App::$strings["May attend %s's event"] = ""; -App::$strings["May not attend %s's event"] = ""; -App::$strings["🔁 Repeated %1\$s's %2\$s"] = ""; -App::$strings["%1\$s (%2\$s)"] = ""; -App::$strings["\$Projectname Notification"] = ""; -App::$strings["\$projectname"] = ""; -App::$strings["Thank You,"] = ""; -App::$strings["%s Administrator"] = ""; -App::$strings["This email was sent by %1\$s at %2\$s."] = ""; -App::$strings["\$Projectname"] = ""; -App::$strings["To stop receiving these messages, please adjust your Notification Settings at %s"] = ""; -App::$strings["To stop receiving these messages, please adjust your %s."] = ""; -App::$strings["Notification Settings"] = ""; -App::$strings["%s "] = ""; -App::$strings["[\$Projectname:Notify] New mail received at %s"] = ""; -App::$strings["%1\$s sent you a new private message at %2\$s."] = ""; -App::$strings["%1\$s sent you %2\$s."] = ""; -App::$strings["a private message"] = ""; -App::$strings["Please visit %s to view and/or reply to your private messages."] = ""; -App::$strings["commented on"] = ""; -App::$strings["liked"] = ""; -App::$strings["disliked"] = ""; -App::$strings["%1\$s %2\$s [zrl=%3\$s]a %4\$s[/zrl]"] = ""; -App::$strings["%1\$s %2\$s [zrl=%3\$s]%4\$s's %5\$s[/zrl]"] = ""; -App::$strings["%1\$s %2\$s [zrl=%3\$s]your %4\$s[/zrl]"] = ""; -App::$strings["[\$Projectname:Notify] Moderated Comment to conversation #%1\$d by %2\$s"] = ""; -App::$strings["[\$Projectname:Notify] Comment to conversation #%1\$d by %2\$s"] = ""; -App::$strings["%1\$s commented on an item/conversation you have been following."] = ""; -App::$strings["(Moderated)"] = ""; -App::$strings["Please visit %s to view and/or reply to the conversation."] = ""; -App::$strings["Please visit %s to approve or reject this comment."] = ""; -App::$strings["%1\$s liked [zrl=%2\$s]your %3\$s[/zrl]"] = ""; -App::$strings["[\$Projectname:Notify] Like received to conversation #%1\$d by %2\$s"] = ""; -App::$strings["%1\$s liked an item/conversation you created."] = ""; -App::$strings["[\$Projectname:Notify] %s posted to your profile wall"] = ""; -App::$strings["%1\$s posted to your profile wall at %2\$s"] = ""; -App::$strings["%1\$s posted to [zrl=%2\$s]your wall[/zrl]"] = ""; -App::$strings[" - "] = ""; -App::$strings["Moderated"] = ""; -App::$strings["Please visit %s to approve or reject this post."] = ""; -App::$strings["[\$Projectname:Notify] %s tagged you"] = ""; -App::$strings["%1\$s tagged you at %2\$s"] = ""; -App::$strings["%1\$s [zrl=%2\$s]tagged you[/zrl]."] = ""; -App::$strings["[\$Projectname:Notify] %1\$s poked you"] = ""; -App::$strings["%1\$s poked you at %2\$s"] = ""; -App::$strings["%1\$s [zrl=%2\$s]poked you[/zrl]."] = ""; -App::$strings["[\$Projectname:Notify] %s tagged your post"] = ""; -App::$strings["%1\$s tagged your post at %2\$s"] = ""; -App::$strings["%1\$s tagged [zrl=%2\$s]your post[/zrl]"] = ""; -App::$strings["[\$Projectname:Notify] Introduction received"] = ""; -App::$strings["You've received an new connection request from '%1\$s' at %2\$s"] = ""; -App::$strings["You've received [zrl=%1\$s]a new connection request[/zrl] from %2\$s."] = ""; -App::$strings["You may visit their profile at %s"] = ""; -App::$strings["Please visit %s to approve or reject the connection request."] = ""; -App::$strings["[\$Projectname:Notify] Friend suggestion received"] = ""; -App::$strings["You've received a friend suggestion from '%1\$s' at %2\$s"] = ""; -App::$strings["You've received [zrl=%1\$s]a friend suggestion[/zrl] for %2\$s from %3\$s."] = ""; -App::$strings["Name:"] = ""; -App::$strings["Photo:"] = ""; -App::$strings["Please visit %s to approve or reject the suggestion."] = ""; -App::$strings["[\$Projectname:Notify]"] = ""; -App::$strings["created a new post"] = ""; -App::$strings["reacted to %s's conversation"] = ""; -App::$strings["shared %s's post"] = ""; -App::$strings["edited a post dated %s"] = ""; -App::$strings["edited a comment dated %s"] = ""; -App::$strings["Unable to verify site signature for %s"] = ""; -App::$strings["Requested channel is not available."] = ""; -App::$strings["Requested profile is not available."] = ""; -App::$strings["Change profile photo"] = ""; -App::$strings["Edit Profiles"] = ""; -App::$strings["Create New Profile"] = ""; -App::$strings["Edit Profile"] = ""; -App::$strings["Profile Image"] = ""; -App::$strings["Visible to everybody"] = ""; -App::$strings["Edit visibility"] = ""; -App::$strings["Connect"] = ""; -App::$strings["Location:"] = ""; -App::$strings["Gender:"] = ""; -App::$strings["Status:"] = ""; -App::$strings["Homepage:"] = ""; -App::$strings["Change your profile photo"] = ""; -App::$strings["Female"] = ""; -App::$strings["Male"] = ""; -App::$strings["Trans"] = ""; -App::$strings["Inter"] = ""; -App::$strings["Neuter"] = ""; -App::$strings["Non-specific"] = ""; -App::$strings["Full Name:"] = ""; -App::$strings["Like this channel"] = ""; -App::$strings["j F, Y"] = ""; -App::$strings["j F"] = ""; -App::$strings["Birthday:"] = ""; -App::$strings["Age:"] = ""; -App::$strings["for %1\$d %2\$s"] = ""; -App::$strings["Tags:"] = ""; -App::$strings["Sexual Preference:"] = ""; -App::$strings["Hometown:"] = ""; -App::$strings["Political Views:"] = ""; -App::$strings["Religion:"] = ""; -App::$strings["About:"] = ""; -App::$strings["Hobbies/Interests:"] = ""; -App::$strings["Likes:"] = ""; -App::$strings["Dislikes:"] = ""; -App::$strings["Contact information and Social Networks:"] = ""; -App::$strings["My other channels:"] = ""; -App::$strings["Musical interests:"] = ""; -App::$strings["Books, literature:"] = ""; -App::$strings["Television:"] = ""; -App::$strings["Film/dance/culture/entertainment:"] = ""; -App::$strings["Love/Romance:"] = ""; -App::$strings["Work/employment:"] = ""; -App::$strings["School/education:"] = ""; -App::$strings["Like this thing"] = ""; -App::$strings["Export"] = ""; -App::$strings["Unable to verify channel signature"] = ""; -App::$strings["default"] = ""; -App::$strings["Select an alternate language"] = ""; -App::$strings["Directory Options"] = ""; -App::$strings["Safe Mode"] = ""; -App::$strings["No"] = ""; -App::$strings["Yes"] = ""; -App::$strings["Public Groups Only"] = ""; -App::$strings["Collections Only"] = ""; -App::$strings["This Website Only"] = ""; -App::$strings["Remote privacy information not available."] = ""; -App::$strings["Visible to:"] = ""; -App::$strings["__ctx:acl__ Profile"] = ""; -App::$strings["Event can not end before it has started."] = ""; -App::$strings["Unable to generate preview."] = ""; -App::$strings["Event title and start time are required."] = ""; -App::$strings["Event not found."] = ""; -App::$strings["event"] = ""; -App::$strings["Edit event"] = ""; -App::$strings["Delete event"] = ""; -App::$strings["Link to source"] = ""; -App::$strings["calendar"] = ""; -App::$strings["Failed to remove event"] = ""; -App::$strings["sent you a private message"] = ""; -App::$strings["added your channel"] = ""; -App::$strings["requires approval"] = ""; -App::$strings["g A l F d"] = ""; -App::$strings["[today]"] = ""; -App::$strings["posted an event"] = ""; -App::$strings["shared a file with you"] = ""; -App::$strings["reported content"] = ""; -App::$strings["Private group"] = ""; -App::$strings["Public group"] = ""; -App::$strings["Off"] = ""; -App::$strings["On"] = ""; -App::$strings["Lock feature %s"] = ""; -App::$strings["Manage Additional Features"] = ""; -App::$strings["Log settings updated."] = ""; -App::$strings["Administration"] = ""; -App::$strings["Logs"] = ""; -App::$strings["Clear"] = ""; -App::$strings["Debugging"] = ""; -App::$strings["Log file"] = ""; -App::$strings["Must be writable by web server. Relative to your top-level webserver directory."] = ""; -App::$strings["Log level"] = ""; -App::$strings["New Profile Field"] = ""; -App::$strings["Field nickname"] = ""; -App::$strings["System name of field"] = ""; -App::$strings["Input type"] = ""; -App::$strings["Field Name"] = ""; -App::$strings["Label on profile pages"] = ""; -App::$strings["Help text"] = ""; -App::$strings["Additional info (optional)"] = ""; -App::$strings["Field definition not found"] = ""; -App::$strings["Edit Profile Field"] = ""; -App::$strings["Profile Fields"] = ""; -App::$strings["Basic Profile Fields"] = ""; -App::$strings["Advanced Profile Fields"] = ""; -App::$strings["(In addition to basic fields)"] = ""; -App::$strings["All available fields"] = ""; -App::$strings["Custom Fields"] = ""; -App::$strings["Create Custom Field"] = ""; -App::$strings["Queue Statistics"] = ""; -App::$strings["Total Entries"] = ""; -App::$strings["Priority"] = ""; -App::$strings["Destination URL"] = ""; -App::$strings["Mark hub permanently offline"] = ""; -App::$strings["Empty queue for this hub"] = ""; -App::$strings["Last known contact"] = ""; -App::$strings["Theme settings updated."] = ""; -App::$strings["No themes found."] = ""; -App::$strings["Item not found."] = ""; -App::$strings["Disable"] = ""; -App::$strings["Enable"] = ""; -App::$strings["Screenshot"] = ""; -App::$strings["Themes"] = ""; -App::$strings["Toggle"] = ""; -App::$strings["Author: "] = ""; -App::$strings["Maintainer: "] = ""; -App::$strings["[Experimental]"] = ""; -App::$strings["[Unsupported]"] = ""; -App::$strings["Update has been marked successful"] = ""; -App::$strings["Verification of update %s failed. Check system logs."] = ""; -App::$strings["Update %s was successfully applied."] = ""; -App::$strings["Verifying update %s did not return a status. Unknown if it succeeded."] = ""; -App::$strings["Update %s does not contain a verification function."] = ""; -App::$strings["Update function %s could not be found."] = ""; -App::$strings["Executing update procedure %s failed. Check system logs."] = ""; -App::$strings["Update %s did not return a status. It cannot be determined if it was successful."] = ""; -App::$strings["Failed Updates"] = ""; -App::$strings["Mark success (if update was manually applied)"] = ""; -App::$strings["Attempt to verify this update if a verification procedure exists"] = ""; -App::$strings["Attempt to execute this update step automatically"] = ""; -App::$strings["No failed updates."] = ""; -App::$strings["Password changed for account %d."] = ""; -App::$strings["Account settings updated."] = ""; -App::$strings["Account not found."] = ""; -App::$strings["Account Edit"] = ""; -App::$strings["New Password"] = ""; -App::$strings["New Password again"] = ""; -App::$strings["Account language (for emails)"] = ""; -App::$strings["Service class"] = ""; -App::$strings["%s account blocked/unblocked"] = [ - 0 => "", - 1 => "", +App::$strings['%d unseen'] = ''; +App::$strings["View %s's profile - %s"] = ''; +App::$strings['to'] = ''; +App::$strings['via'] = ''; +App::$strings['Wall-to-Wall'] = ''; +App::$strings['via Wall-To-Wall:'] = ''; +App::$strings['from %s'] = ''; +App::$strings['last edited: %s'] = ''; +App::$strings['Expires: %s'] = ''; +App::$strings['Attend'] = ''; +App::$strings['Attendance Options'] = ''; +App::$strings['Vote'] = ''; +App::$strings['Voting Options'] = ''; +App::$strings['Reply'] = ''; +App::$strings['Pinned post'] = ''; +App::$strings['Unpin this post'] = ''; +App::$strings['Pin this post'] = ''; +App::$strings['Save Bookmarks'] = ''; +App::$strings['Add to Calendar'] = ''; +App::$strings['Mark all seen'] = ''; +App::$strings['Close'] = ''; +App::$strings['This is an unsaved preview'] = ''; +App::$strings['Please wait'] = ''; +App::$strings['%s show all'] = ''; +App::$strings['This is you'] = ''; +App::$strings['Comment'] = ''; +App::$strings['Submit'] = ''; +App::$strings['Bold'] = ''; +App::$strings['Italic'] = ''; +App::$strings['Underline'] = ''; +App::$strings['Quote'] = ''; +App::$strings['Code'] = ''; +App::$strings['Image'] = ''; +App::$strings['Attach/Upload file'] = ''; +App::$strings['Insert Link'] = ''; +App::$strings['Video'] = ''; +App::$strings['Preview'] = ''; +App::$strings['Reset'] = ''; +App::$strings['Encrypt text'] = ''; +App::$strings['Your full name (required)'] = ''; +App::$strings['Your email address (required)'] = ''; +App::$strings['Your website URL (optional)'] = ''; +App::$strings["Likes %1\$s's %2\$s"] = ''; +App::$strings["Doesn't like %1\$s's %2\$s"] = ''; +App::$strings["Will attend %s's event"] = ''; +App::$strings["Will not attend %s's event"] = ''; +App::$strings["May attend %s's event"] = ''; +App::$strings["May not attend %s's event"] = ''; +App::$strings["🔁 Repeated %1\$s's %2\$s"] = ''; +App::$strings["%1\$s (%2\$s)"] = ''; +App::$strings["\$Projectname Notification"] = ''; +App::$strings["\$projectname"] = ''; +App::$strings['Thank You,'] = ''; +App::$strings['%s Administrator'] = ''; +App::$strings["This email was sent by %1\$s at %2\$s."] = ''; +App::$strings["\$Projectname"] = ''; +App::$strings['To stop receiving these messages, please adjust your Notification Settings at %s'] = ''; +App::$strings['To stop receiving these messages, please adjust your %s.'] = ''; +App::$strings['Notification Settings'] = ''; +App::$strings['%s '] = ''; +App::$strings["[\$Projectname:Notify] New mail received at %s"] = ''; +App::$strings["%1\$s sent you a new private message at %2\$s."] = ''; +App::$strings["%1\$s sent you %2\$s."] = ''; +App::$strings['a private message'] = ''; +App::$strings['Please visit %s to view and/or reply to your private messages.'] = ''; +App::$strings['commented on'] = ''; +App::$strings['liked'] = ''; +App::$strings['disliked'] = ''; +App::$strings["%1\$s %2\$s [zrl=%3\$s]a %4\$s[/zrl]"] = ''; +App::$strings["%1\$s %2\$s [zrl=%3\$s]%4\$s's %5\$s[/zrl]"] = ''; +App::$strings["%1\$s %2\$s [zrl=%3\$s]your %4\$s[/zrl]"] = ''; +App::$strings["[\$Projectname:Notify] Moderated Comment to conversation #%1\$d by %2\$s"] = ''; +App::$strings["[\$Projectname:Notify] Comment to conversation #%1\$d by %2\$s"] = ''; +App::$strings["%1\$s commented on an item/conversation you have been following."] = ''; +App::$strings['(Moderated)'] = ''; +App::$strings['Please visit %s to view and/or reply to the conversation.'] = ''; +App::$strings['Please visit %s to approve or reject this comment.'] = ''; +App::$strings["%1\$s liked [zrl=%2\$s]your %3\$s[/zrl]"] = ''; +App::$strings["[\$Projectname:Notify] Like received to conversation #%1\$d by %2\$s"] = ''; +App::$strings["%1\$s liked an item/conversation you created."] = ''; +App::$strings["[\$Projectname:Notify] %s posted to your profile wall"] = ''; +App::$strings["%1\$s posted to your profile wall at %2\$s"] = ''; +App::$strings["%1\$s posted to [zrl=%2\$s]your wall[/zrl]"] = ''; +App::$strings[' - '] = ''; +App::$strings['Moderated'] = ''; +App::$strings['Please visit %s to approve or reject this post.'] = ''; +App::$strings["[\$Projectname:Notify] %s tagged you"] = ''; +App::$strings["%1\$s tagged you at %2\$s"] = ''; +App::$strings["%1\$s [zrl=%2\$s]tagged you[/zrl]."] = ''; +App::$strings["[\$Projectname:Notify] %1\$s poked you"] = ''; +App::$strings["%1\$s poked you at %2\$s"] = ''; +App::$strings["%1\$s [zrl=%2\$s]poked you[/zrl]."] = ''; +App::$strings["[\$Projectname:Notify] %s tagged your post"] = ''; +App::$strings["%1\$s tagged your post at %2\$s"] = ''; +App::$strings["%1\$s tagged [zrl=%2\$s]your post[/zrl]"] = ''; +App::$strings["[\$Projectname:Notify] Introduction received"] = ''; +App::$strings["You've received an new connection request from '%1\$s' at %2\$s"] = ''; +App::$strings["You've received [zrl=%1\$s]a new connection request[/zrl] from %2\$s."] = ''; +App::$strings['You may visit their profile at %s'] = ''; +App::$strings['Please visit %s to approve or reject the connection request.'] = ''; +App::$strings["[\$Projectname:Notify] Friend suggestion received"] = ''; +App::$strings["You've received a friend suggestion from '%1\$s' at %2\$s"] = ''; +App::$strings["You've received [zrl=%1\$s]a friend suggestion[/zrl] for %2\$s from %3\$s."] = ''; +App::$strings['Name:'] = ''; +App::$strings['Photo:'] = ''; +App::$strings['Please visit %s to approve or reject the suggestion.'] = ''; +App::$strings["[\$Projectname:Notify]"] = ''; +App::$strings['created a new post'] = ''; +App::$strings["reacted to %s's conversation"] = ''; +App::$strings["shared %s's post"] = ''; +App::$strings['edited a post dated %s'] = ''; +App::$strings['edited a comment dated %s'] = ''; +App::$strings['Unable to verify site signature for %s'] = ''; +App::$strings['Requested channel is not available.'] = ''; +App::$strings['Requested profile is not available.'] = ''; +App::$strings['Change profile photo'] = ''; +App::$strings['Edit Profiles'] = ''; +App::$strings['Create New Profile'] = ''; +App::$strings['Edit Profile'] = ''; +App::$strings['Profile Image'] = ''; +App::$strings['Visible to everybody'] = ''; +App::$strings['Edit visibility'] = ''; +App::$strings['Connect'] = ''; +App::$strings['Location:'] = ''; +App::$strings['Gender:'] = ''; +App::$strings['Status:'] = ''; +App::$strings['Homepage:'] = ''; +App::$strings['Change your profile photo'] = ''; +App::$strings['Female'] = ''; +App::$strings['Male'] = ''; +App::$strings['Trans'] = ''; +App::$strings['Inter'] = ''; +App::$strings['Neuter'] = ''; +App::$strings['Non-specific'] = ''; +App::$strings['Full Name:'] = ''; +App::$strings['Like this channel'] = ''; +App::$strings['j F, Y'] = ''; +App::$strings['j F'] = ''; +App::$strings['Birthday:'] = ''; +App::$strings['Age:'] = ''; +App::$strings["for %1\$d %2\$s"] = ''; +App::$strings['Tags:'] = ''; +App::$strings['Sexual Preference:'] = ''; +App::$strings['Hometown:'] = ''; +App::$strings['Political Views:'] = ''; +App::$strings['Religion:'] = ''; +App::$strings['About:'] = ''; +App::$strings['Hobbies/Interests:'] = ''; +App::$strings['Likes:'] = ''; +App::$strings['Dislikes:'] = ''; +App::$strings['Contact information and Social Networks:'] = ''; +App::$strings['My other channels:'] = ''; +App::$strings['Musical interests:'] = ''; +App::$strings['Books, literature:'] = ''; +App::$strings['Television:'] = ''; +App::$strings['Film/dance/culture/entertainment:'] = ''; +App::$strings['Love/Romance:'] = ''; +App::$strings['Work/employment:'] = ''; +App::$strings['School/education:'] = ''; +App::$strings['Like this thing'] = ''; +App::$strings['Export'] = ''; +App::$strings['Unable to verify channel signature'] = ''; +App::$strings['default'] = ''; +App::$strings['Select an alternate language'] = ''; +App::$strings['Directory Options'] = ''; +App::$strings['Safe Mode'] = ''; +App::$strings['No'] = ''; +App::$strings['Yes'] = ''; +App::$strings['Public Groups Only'] = ''; +App::$strings['Collections Only'] = ''; +App::$strings['This Website Only'] = ''; +App::$strings['Remote privacy information not available.'] = ''; +App::$strings['Visible to:'] = ''; +App::$strings['__ctx:acl__ Profile'] = ''; +App::$strings['Event can not end before it has started.'] = ''; +App::$strings['Unable to generate preview.'] = ''; +App::$strings['Event title and start time are required.'] = ''; +App::$strings['Event not found.'] = ''; +App::$strings['event'] = ''; +App::$strings['Edit event'] = ''; +App::$strings['Delete event'] = ''; +App::$strings['Link to source'] = ''; +App::$strings['calendar'] = ''; +App::$strings['Failed to remove event'] = ''; +App::$strings['sent you a private message'] = ''; +App::$strings['added your channel'] = ''; +App::$strings['requires approval'] = ''; +App::$strings['g A l F d'] = ''; +App::$strings['[today]'] = ''; +App::$strings['posted an event'] = ''; +App::$strings['shared a file with you'] = ''; +App::$strings['reported content'] = ''; +App::$strings['Private group'] = ''; +App::$strings['Public group'] = ''; +App::$strings['Off'] = ''; +App::$strings['On'] = ''; +App::$strings['Lock feature %s'] = ''; +App::$strings['Manage Additional Features'] = ''; +App::$strings['Log settings updated.'] = ''; +App::$strings['Administration'] = ''; +App::$strings['Logs'] = ''; +App::$strings['Clear'] = ''; +App::$strings['Debugging'] = ''; +App::$strings['Log file'] = ''; +App::$strings['Must be writable by web server. Relative to your top-level webserver directory.'] = ''; +App::$strings['Log level'] = ''; +App::$strings['New Profile Field'] = ''; +App::$strings['Field nickname'] = ''; +App::$strings['System name of field'] = ''; +App::$strings['Input type'] = ''; +App::$strings['Field Name'] = ''; +App::$strings['Label on profile pages'] = ''; +App::$strings['Help text'] = ''; +App::$strings['Additional info (optional)'] = ''; +App::$strings['Field definition not found'] = ''; +App::$strings['Edit Profile Field'] = ''; +App::$strings['Profile Fields'] = ''; +App::$strings['Basic Profile Fields'] = ''; +App::$strings['Advanced Profile Fields'] = ''; +App::$strings['(In addition to basic fields)'] = ''; +App::$strings['All available fields'] = ''; +App::$strings['Custom Fields'] = ''; +App::$strings['Create Custom Field'] = ''; +App::$strings['Queue Statistics'] = ''; +App::$strings['Total Entries'] = ''; +App::$strings['Priority'] = ''; +App::$strings['Destination URL'] = ''; +App::$strings['Mark hub permanently offline'] = ''; +App::$strings['Empty queue for this hub'] = ''; +App::$strings['Last known contact'] = ''; +App::$strings['Theme settings updated.'] = ''; +App::$strings['No themes found.'] = ''; +App::$strings['Item not found.'] = ''; +App::$strings['Disable'] = ''; +App::$strings['Enable'] = ''; +App::$strings['Screenshot'] = ''; +App::$strings['Themes'] = ''; +App::$strings['Toggle'] = ''; +App::$strings['Author: '] = ''; +App::$strings['Maintainer: '] = ''; +App::$strings['[Experimental]'] = ''; +App::$strings['[Unsupported]'] = ''; +App::$strings['Update has been marked successful'] = ''; +App::$strings['Verification of update %s failed. Check system logs.'] = ''; +App::$strings['Update %s was successfully applied.'] = ''; +App::$strings['Verifying update %s did not return a status. Unknown if it succeeded.'] = ''; +App::$strings['Update %s does not contain a verification function.'] = ''; +App::$strings['Update function %s could not be found.'] = ''; +App::$strings['Executing update procedure %s failed. Check system logs.'] = ''; +App::$strings['Update %s did not return a status. It cannot be determined if it was successful.'] = ''; +App::$strings['Failed Updates'] = ''; +App::$strings['Mark success (if update was manually applied)'] = ''; +App::$strings['Attempt to verify this update if a verification procedure exists'] = ''; +App::$strings['Attempt to execute this update step automatically'] = ''; +App::$strings['No failed updates.'] = ''; +App::$strings['Password changed for account %d.'] = ''; +App::$strings['Account settings updated.'] = ''; +App::$strings['Account not found.'] = ''; +App::$strings['Account Edit'] = ''; +App::$strings['New Password'] = ''; +App::$strings['New Password again'] = ''; +App::$strings['Account language (for emails)'] = ''; +App::$strings['Service class'] = ''; +App::$strings['%s account blocked/unblocked'] = [ + 0 => '', + 1 => '', ]; -App::$strings["%s account deleted"] = [ - 0 => "", - 1 => "", +App::$strings['%s account deleted'] = [ + 0 => '', + 1 => '', ]; -App::$strings["Account not found"] = ""; -App::$strings["Account '%s' deleted"] = ""; -App::$strings["Account '%s' blocked"] = ""; -App::$strings["Account '%s' unblocked"] = ""; -App::$strings["Accounts"] = ""; -App::$strings["select all"] = ""; -App::$strings["Registrations waiting for confirm"] = ""; -App::$strings["Request date"] = ""; -App::$strings["Email"] = ""; -App::$strings["No registrations."] = ""; -App::$strings["Approve"] = ""; -App::$strings["Deny"] = ""; -App::$strings["Block"] = ""; -App::$strings["Unblock"] = ""; -App::$strings["ID"] = ""; -App::$strings["All Channels"] = ""; -App::$strings["Register date"] = ""; -App::$strings["Last login"] = ""; -App::$strings["Expires"] = ""; -App::$strings["Service Class"] = ""; -App::$strings["Selected accounts will be deleted!\\n\\nEverything these accounts had posted on this site will be permanently deleted!\\n\\nAre you sure?"] = ""; -App::$strings["The account {0} will be deleted!\\n\\nEverything this account has posted on this site will be permanently deleted!\\n\\nAre you sure?"] = ""; -App::$strings["Plugin %s disabled."] = ""; -App::$strings["Plugin %s enabled."] = ""; -App::$strings["Addons"] = ""; -App::$strings["Minimum project version: "] = ""; -App::$strings["Maximum project version: "] = ""; -App::$strings["Minimum PHP version: "] = ""; -App::$strings["Compatible Server Roles: "] = ""; -App::$strings["Requires: "] = ""; -App::$strings["Disabled - version incompatibility"] = ""; -App::$strings["Enter the public git repository URL of the addon repo."] = ""; -App::$strings["Addon repo git URL"] = ""; -App::$strings["Custom repo name"] = ""; -App::$strings["(optional)"] = ""; -App::$strings["Download Addon Repo"] = ""; -App::$strings["Install new repo"] = ""; -App::$strings["Cancel"] = ""; -App::$strings["Manage Repos"] = ""; -App::$strings["Installed Addon Repositories"] = ""; -App::$strings["Install a New Addon Repository"] = ""; -App::$strings["Switch branch"] = ""; -App::$strings["Remove"] = ""; -App::$strings["Site settings updated."] = ""; -App::$strings["Default"] = ""; -App::$strings["%s - (Incompatible)"] = ""; -App::$strings["mobile"] = ""; -App::$strings["experimental"] = ""; -App::$strings["unsupported"] = ""; -App::$strings["Yes - with approval"] = ""; -App::$strings["My site is not a public server"] = ""; -App::$strings["My site provides free public access"] = ""; -App::$strings["My site provides paid public access"] = ""; -App::$strings["My site provides free public access and premium paid plans"] = ""; -App::$strings["Default permission role for new accounts"] = ""; -App::$strings["This role will be used for the first channel created after registration."] = ""; -App::$strings["Site"] = ""; -App::$strings["Site Configuration"] = ""; -App::$strings["Registration"] = ""; -App::$strings["File upload"] = ""; -App::$strings["Policies"] = ""; -App::$strings["Advanced"] = ""; -App::$strings["Site name"] = ""; -App::$strings["Banner/Logo"] = ""; -App::$strings["Unfiltered HTML/CSS/JS is allowed"] = ""; -App::$strings["Administrator Information"] = ""; -App::$strings["Contact information for site administrators. Displayed on siteinfo page. BBCode may be used here."] = ""; -App::$strings["Site Information"] = ""; -App::$strings["Publicly visible description of this site. Displayed on siteinfo page. BBCode may be used here."] = ""; -App::$strings["System language"] = ""; -App::$strings["System theme"] = ""; -App::$strings["Default system theme - may be over-ridden by user profiles -
                    change theme settings"] = ""; -App::$strings["Allow ActivityPub Connections"] = ""; -App::$strings["Provides access to software supporting the ActivityPub protocol."] = ""; -App::$strings["Maximum image size"] = ""; -App::$strings["Maximum size in bytes of uploaded images. Default is 0, which means no limits."] = ""; -App::$strings["Cache all public images"] = ""; -App::$strings["By default proxy non-SSL images, but do not cache"] = ""; -App::$strings["Does this site allow new member registration?"] = ""; -App::$strings["Invitation only"] = ""; -App::$strings["Only allow new member registrations with an invitation code. New member registration must be allowed for this to work."] = ""; -App::$strings["Minimum age"] = ""; -App::$strings["Minimum age (in years) for who may register on this site."] = ""; -App::$strings["Which best describes the types of account offered by this hub?"] = ""; -App::$strings["If a public server policy is selected, this information may be displayed on the public server site list."] = ""; -App::$strings["Register text"] = ""; -App::$strings["Will be displayed prominently on the registration page."] = ""; -App::$strings["Site homepage to show visitors (default: login box)"] = ""; -App::$strings["example: 'public' to show public stream, 'page/sys/home' to show a system webpage called 'home' or 'include:home.html' to include a file."] = ""; -App::$strings["Preserve site homepage URL"] = ""; -App::$strings["Present the site homepage in a frame at the original location instead of redirecting"] = ""; -App::$strings["Accounts abandoned after x days"] = ""; -App::$strings["Will not waste system resources polling external sites for abandonded accounts. Enter 0 for no time limit."] = ""; -App::$strings["Verify Email Addresses"] = ""; -App::$strings["Check to verify email addresses used in account registration (recommended)."] = ""; -App::$strings["Force publish"] = ""; -App::$strings["Check to force all profiles on this site to be listed in the site directory."] = ""; -App::$strings["Import Public Streams"] = ""; -App::$strings["Import and allow access to public content pulled from other sites. Warning: this content is unmoderated."] = ""; -App::$strings["Site only Public Streams"] = ""; -App::$strings["Allow access to public content originating only from this site if Imported Public Streams are disabled."] = ""; -App::$strings["Allow anybody on the internet to access the Public streams"] = ""; -App::$strings["Default is to only allow viewing by site members. Warning: this content is unmoderated."] = ""; -App::$strings["Show numbers of likes and dislikes in conversations"] = ""; -App::$strings["If disabled, the presence of likes and dislikes will be shown, but without totals."] = ""; -App::$strings["Only import Public stream posts with this text"] = ""; -App::$strings["words one per line or #tags or /patterns/ or lang=xx, leave blank to import all posts"] = ""; -App::$strings["Do not import Public stream posts with this text"] = ""; -App::$strings["Login on Homepage"] = ""; -App::$strings["Present a login box to visitors on the home page if no other content has been configured."] = ""; -App::$strings["Enable context help"] = ""; -App::$strings["Display contextual help for the current page when the help button is pressed."] = ""; -App::$strings["Reply-to email address for system generated email."] = ""; -App::$strings["Sender (From) email address for system generated email."] = ""; -App::$strings["Name of email sender for system generated email."] = ""; -App::$strings["Directory Server URL"] = ""; -App::$strings["Default directory server"] = ""; -App::$strings["Proxy user"] = ""; -App::$strings["Proxy URL"] = ""; -App::$strings["Network timeout"] = ""; -App::$strings["Value is in seconds. Set to 0 for unlimited (not recommended)."] = ""; -App::$strings["Delivery interval"] = ""; -App::$strings["Delay background delivery processes by this many seconds to reduce system load. Recommend: 4-5 for shared hosts, 2-3 for virtual private servers. 0-1 for large dedicated servers."] = ""; -App::$strings["Deliveries per process"] = ""; -App::$strings["Number of deliveries to attempt in a single operating system process. Adjust if necessary to tune system performance. Recommend: 1-5."] = ""; -App::$strings["Queue Threshold"] = ""; -App::$strings["Always defer immediate delivery if queue contains more than this number of entries."] = ""; -App::$strings["Poll interval"] = ""; -App::$strings["Delay background polling processes by this many seconds to reduce system load. If 0, use delivery interval."] = ""; -App::$strings["Path to ImageMagick convert program"] = ""; -App::$strings["If set, use this program to generate photo thumbnails for huge images ( > 4000 pixels in either dimension), otherwise memory exhaustion may occur. Example: /usr/bin/convert"] = ""; -App::$strings["Maximum Load Average"] = ""; -App::$strings["Maximum system load before delivery and poll processes are deferred - default 50."] = ""; -App::$strings["Expiration period in days for imported streams and cached images"] = ""; -App::$strings["0 for no expiration of imported content"] = ""; -App::$strings["Do not expire any posts which have comments less than this many days ago"] = ""; -App::$strings["Public servers: Optional landing (marketing) webpage for new registrants"] = ""; -App::$strings["Create this page first. Default is %s/register"] = ""; -App::$strings["Page to display after creating a new channel"] = ""; -App::$strings["Default: profiles"] = ""; -App::$strings["Optional: site location"] = ""; -App::$strings["Region or country"] = ""; -App::$strings["%s channel censored/uncensored"] = [ - 0 => "", - 1 => "", +App::$strings['Account not found'] = ''; +App::$strings["Account '%s' deleted"] = ''; +App::$strings["Account '%s' blocked"] = ''; +App::$strings["Account '%s' unblocked"] = ''; +App::$strings['Accounts'] = ''; +App::$strings['select all'] = ''; +App::$strings['Registrations waiting for confirm'] = ''; +App::$strings['Request date'] = ''; +App::$strings['Email'] = ''; +App::$strings['No registrations.'] = ''; +App::$strings['Approve'] = ''; +App::$strings['Deny'] = ''; +App::$strings['Block'] = ''; +App::$strings['Unblock'] = ''; +App::$strings['ID'] = ''; +App::$strings['All Channels'] = ''; +App::$strings['Register date'] = ''; +App::$strings['Last login'] = ''; +App::$strings['Expires'] = ''; +App::$strings['Service Class'] = ''; +App::$strings["Selected accounts will be deleted!\\n\\nEverything these accounts had posted on this site will be permanently deleted!\\n\\nAre you sure?"] = ''; +App::$strings["The account {0} will be deleted!\\n\\nEverything this account has posted on this site will be permanently deleted!\\n\\nAre you sure?"] = ''; +App::$strings['Plugin %s disabled.'] = ''; +App::$strings['Plugin %s enabled.'] = ''; +App::$strings['Addons'] = ''; +App::$strings['Minimum project version: '] = ''; +App::$strings['Maximum project version: '] = ''; +App::$strings['Minimum PHP version: '] = ''; +App::$strings['Compatible Server Roles: '] = ''; +App::$strings['Requires: '] = ''; +App::$strings['Disabled - version incompatibility'] = ''; +App::$strings['Enter the public git repository URL of the addon repo.'] = ''; +App::$strings['Addon repo git URL'] = ''; +App::$strings['Custom repo name'] = ''; +App::$strings['(optional)'] = ''; +App::$strings['Download Addon Repo'] = ''; +App::$strings['Install new repo'] = ''; +App::$strings['Cancel'] = ''; +App::$strings['Manage Repos'] = ''; +App::$strings['Installed Addon Repositories'] = ''; +App::$strings['Install a New Addon Repository'] = ''; +App::$strings['Switch branch'] = ''; +App::$strings['Remove'] = ''; +App::$strings['Site settings updated.'] = ''; +App::$strings['Default'] = ''; +App::$strings['%s - (Incompatible)'] = ''; +App::$strings['mobile'] = ''; +App::$strings['experimental'] = ''; +App::$strings['unsupported'] = ''; +App::$strings['Yes - with approval'] = ''; +App::$strings['My site is not a public server'] = ''; +App::$strings['My site provides free public access'] = ''; +App::$strings['My site provides paid public access'] = ''; +App::$strings['My site provides free public access and premium paid plans'] = ''; +App::$strings['Default permission role for new accounts'] = ''; +App::$strings['This role will be used for the first channel created after registration.'] = ''; +App::$strings['Site'] = ''; +App::$strings['Site Configuration'] = ''; +App::$strings['Registration'] = ''; +App::$strings['File upload'] = ''; +App::$strings['Policies'] = ''; +App::$strings['Advanced'] = ''; +App::$strings['Site name'] = ''; +App::$strings['Banner/Logo'] = ''; +App::$strings['Unfiltered HTML/CSS/JS is allowed'] = ''; +App::$strings['Administrator Information'] = ''; +App::$strings['Contact information for site administrators. Displayed on siteinfo page. BBCode may be used here.'] = ''; +App::$strings['Site Information'] = ''; +App::$strings['Publicly visible description of this site. Displayed on siteinfo page. BBCode may be used here.'] = ''; +App::$strings['System language'] = ''; +App::$strings['System theme'] = ''; +App::$strings["Default system theme - may be over-ridden by user profiles - change theme settings"] = ''; +App::$strings['Allow ActivityPub Connections'] = ''; +App::$strings['Provides access to software supporting the ActivityPub protocol.'] = ''; +App::$strings['Maximum image size'] = ''; +App::$strings['Maximum size in bytes of uploaded images. Default is 0, which means no limits.'] = ''; +App::$strings['Cache all public images'] = ''; +App::$strings['By default proxy non-SSL images, but do not cache'] = ''; +App::$strings['Does this site allow new member registration?'] = ''; +App::$strings['Invitation only'] = ''; +App::$strings['Only allow new member registrations with an invitation code. New member registration must be allowed for this to work.'] = ''; +App::$strings['Minimum age'] = ''; +App::$strings['Minimum age (in years) for who may register on this site.'] = ''; +App::$strings['Which best describes the types of account offered by this hub?'] = ''; +App::$strings['If a public server policy is selected, this information may be displayed on the public server site list.'] = ''; +App::$strings['Register text'] = ''; +App::$strings['Will be displayed prominently on the registration page.'] = ''; +App::$strings['Site homepage to show visitors (default: login box)'] = ''; +App::$strings["example: 'public' to show public stream, 'page/sys/home' to show a system webpage called 'home' or 'include:home.html' to include a file."] = ''; +App::$strings['Preserve site homepage URL'] = ''; +App::$strings['Present the site homepage in a frame at the original location instead of redirecting'] = ''; +App::$strings['Accounts abandoned after x days'] = ''; +App::$strings['Will not waste system resources polling external sites for abandonded accounts. Enter 0 for no time limit.'] = ''; +App::$strings['Verify Email Addresses'] = ''; +App::$strings['Check to verify email addresses used in account registration (recommended).'] = ''; +App::$strings['Force publish'] = ''; +App::$strings['Check to force all profiles on this site to be listed in the site directory.'] = ''; +App::$strings['Import Public Streams'] = ''; +App::$strings['Import and allow access to public content pulled from other sites. Warning: this content is unmoderated.'] = ''; +App::$strings['Site only Public Streams'] = ''; +App::$strings['Allow access to public content originating only from this site if Imported Public Streams are disabled.'] = ''; +App::$strings['Allow anybody on the internet to access the Public streams'] = ''; +App::$strings['Default is to only allow viewing by site members. Warning: this content is unmoderated.'] = ''; +App::$strings['Show numbers of likes and dislikes in conversations'] = ''; +App::$strings['If disabled, the presence of likes and dislikes will be shown, but without totals.'] = ''; +App::$strings['Only import Public stream posts with this text'] = ''; +App::$strings['words one per line or #tags or /patterns/ or lang=xx, leave blank to import all posts'] = ''; +App::$strings['Do not import Public stream posts with this text'] = ''; +App::$strings['Login on Homepage'] = ''; +App::$strings['Present a login box to visitors on the home page if no other content has been configured.'] = ''; +App::$strings['Enable context help'] = ''; +App::$strings['Display contextual help for the current page when the help button is pressed.'] = ''; +App::$strings['Reply-to email address for system generated email.'] = ''; +App::$strings['Sender (From) email address for system generated email.'] = ''; +App::$strings['Name of email sender for system generated email.'] = ''; +App::$strings['Directory Server URL'] = ''; +App::$strings['Default directory server'] = ''; +App::$strings['Proxy user'] = ''; +App::$strings['Proxy URL'] = ''; +App::$strings['Network timeout'] = ''; +App::$strings['Value is in seconds. Set to 0 for unlimited (not recommended).'] = ''; +App::$strings['Delivery interval'] = ''; +App::$strings['Delay background delivery processes by this many seconds to reduce system load. Recommend: 4-5 for shared hosts, 2-3 for virtual private servers. 0-1 for large dedicated servers.'] = ''; +App::$strings['Deliveries per process'] = ''; +App::$strings['Number of deliveries to attempt in a single operating system process. Adjust if necessary to tune system performance. Recommend: 1-5.'] = ''; +App::$strings['Queue Threshold'] = ''; +App::$strings['Always defer immediate delivery if queue contains more than this number of entries.'] = ''; +App::$strings['Poll interval'] = ''; +App::$strings['Delay background polling processes by this many seconds to reduce system load. If 0, use delivery interval.'] = ''; +App::$strings['Path to ImageMagick convert program'] = ''; +App::$strings['If set, use this program to generate photo thumbnails for huge images ( > 4000 pixels in either dimension), otherwise memory exhaustion may occur. Example: /usr/bin/convert'] = ''; +App::$strings['Maximum Load Average'] = ''; +App::$strings['Maximum system load before delivery and poll processes are deferred - default 50.'] = ''; +App::$strings['Expiration period in days for imported streams and cached images'] = ''; +App::$strings['0 for no expiration of imported content'] = ''; +App::$strings['Do not expire any posts which have comments less than this many days ago'] = ''; +App::$strings['Public servers: Optional landing (marketing) webpage for new registrants'] = ''; +App::$strings['Create this page first. Default is %s/register'] = ''; +App::$strings['Page to display after creating a new channel'] = ''; +App::$strings['Default: profiles'] = ''; +App::$strings['Optional: site location'] = ''; +App::$strings['Region or country'] = ''; +App::$strings['%s channel censored/uncensored'] = [ + 0 => '', + 1 => '', ]; -App::$strings["%s channel code allowed/disallowed"] = [ - 0 => "", - 1 => "", +App::$strings['%s channel code allowed/disallowed'] = [ + 0 => '', + 1 => '', ]; -App::$strings["%s channel deleted"] = [ - 0 => "", - 1 => "", +App::$strings['%s channel deleted'] = [ + 0 => '', + 1 => '', ]; -App::$strings["Channel not found"] = ""; -App::$strings["Channel '%s' deleted"] = ""; -App::$strings["Channel '%s' censored"] = ""; -App::$strings["Channel '%s' uncensored"] = ""; -App::$strings["Channel '%s' code allowed"] = ""; -App::$strings["Channel '%s' code disallowed"] = ""; -App::$strings["Channels"] = ""; -App::$strings["Censor"] = ""; -App::$strings["Uncensor"] = ""; -App::$strings["Allow Code"] = ""; -App::$strings["Disallow Code"] = ""; -App::$strings["Channel"] = ""; -App::$strings["UID"] = ""; -App::$strings["Name"] = ""; -App::$strings["Address"] = ""; -App::$strings["Selected channels will be deleted!\\n\\nEverything that was posted in these channels on this site will be permanently deleted!\\n\\nAre you sure?"] = ""; -App::$strings["The channel {0} will be deleted!\\n\\nEverything that was posted in this channel on this site will be permanently deleted!\\n\\nAre you sure?"] = ""; -App::$strings["By default, unfiltered HTML is allowed in embedded media. This is inherently insecure."] = ""; -App::$strings["The recommended setting is to only allow unfiltered HTML from the following sites:"] = ""; -App::$strings["https://youtube.com/
                    https://www.youtube.com/
                    https://youtu.be/
                    https://vimeo.com/
                    https://soundcloud.com/
                    "] = ""; -App::$strings["All other embedded content will be filtered, unless embedded content from that site is explicitly blocked."] = ""; -App::$strings["Security"] = ""; -App::$strings["Block public"] = ""; -App::$strings["Check to block public access to all otherwise public personal pages on this site unless you are currently authenticated."] = ""; -App::$strings["Block public search"] = ""; -App::$strings["Prevent access to search content unless you are currently authenticated."] = ""; -App::$strings["Hide local directory"] = ""; -App::$strings["Only use the global directory"] = ""; -App::$strings["Provide a cloud root directory"] = ""; -App::$strings["The cloud root directory lists all channel names which provide public files"] = ""; -App::$strings["Show total disk space available to cloud uploads"] = ""; -App::$strings["Allow SVG thumbnails in file browser"] = ""; -App::$strings["WARNING: SVG images may contain malicious code."] = ""; -App::$strings["Allow embedded (inline) PDF files"] = ""; -App::$strings["Set \"Transport Security\" HTTP header"] = ""; -App::$strings["Set \"Content Security Policy\" HTTP header"] = ""; -App::$strings["Allowed email domains"] = ""; -App::$strings["Comma separated list of domains which are allowed in email addresses for registrations to this site. Wildcards are accepted. Empty to allow any domains"] = ""; -App::$strings["Not allowed email domains"] = ""; -App::$strings["Comma separated list of domains which are not allowed in email addresses for registrations to this site. Wildcards are accepted. Empty to allow any domains, unless allowed domains have been defined."] = ""; -App::$strings["Allow communications only from these sites"] = ""; -App::$strings["One site per line. Leave empty to allow communication from anywhere by default"] = ""; -App::$strings["Block communications from these sites"] = ""; -App::$strings["Allow communications only from these channels"] = ""; -App::$strings["One channel (hash) per line. Leave empty to allow from any channel by default"] = ""; -App::$strings["Block communications from these channels"] = ""; -App::$strings["Allow public stream communications only from these sites"] = ""; -App::$strings["Block public stream communications from these sites"] = ""; -App::$strings["Allow public stream communications only from these channels"] = ""; -App::$strings["Block public stream communications from these channels"] = ""; -App::$strings["Only allow embeds from secure (SSL) websites and links."] = ""; -App::$strings["Allow unfiltered embedded HTML content only from these domains"] = ""; -App::$strings["One site per line. By default embedded content is filtered."] = ""; -App::$strings["Block embedded HTML from these domains"] = ""; -App::$strings["You must be logged in to see this page."] = ""; -App::$strings["Posts and comments"] = ""; -App::$strings["Only posts"] = ""; -App::$strings["This is the home page of %s."] = ""; -App::$strings["Insufficient permissions. Request redirected to profile page."] = ""; -App::$strings["Search Results For:"] = ""; -App::$strings["Reset form"] = ""; -App::$strings["You must enable javascript for your browser to be able to view this content."] = ""; -App::$strings["Authorize application connection"] = ""; -App::$strings["Return to your app and insert this Security Code:"] = ""; -App::$strings["Please login to continue."] = ""; -App::$strings["Do you want to authorize this application to access your posts and contacts, and/or create new posts for you?"] = ""; -App::$strings["Change Order of Pinned Navbar Apps"] = ""; -App::$strings["Change Order of App Tray Apps"] = ""; -App::$strings["Use arrows to move the corresponding app left (top) or right (bottom) in the navbar"] = ""; -App::$strings["Use arrows to move the corresponding app up or down in the app tray"] = ""; -App::$strings["Available Apps"] = ""; -App::$strings["Installed Apps"] = ""; -App::$strings["Manage apps"] = ""; -App::$strings["Create Custom App"] = ""; -App::$strings["No channel."] = ""; -App::$strings["No connections in common."] = ""; -App::$strings["View Common Connections"] = ""; -App::$strings["Item not available."] = ""; -App::$strings["Unknown App"] = ""; -App::$strings["Authorize"] = ""; -App::$strings["Do you authorize the app %s to access your channel data?"] = ""; -App::$strings["Allow"] = ""; -App::$strings["Channel not found."] = ""; -App::$strings["Permissions denied."] = ""; -App::$strings["l, F j"] = ""; -App::$strings["Link to Source"] = ""; -App::$strings["Edit Event"] = ""; -App::$strings["Create Event"] = ""; -App::$strings["Previous"] = ""; -App::$strings["Next"] = ""; -App::$strings["Import"] = ""; -App::$strings["Today"] = ""; -App::$strings["Expiration settings updated."] = ""; -App::$strings["This app allows you to set an optional expiration date/time for posts, after which they will be deleted. This must be at least fifteen minutes into the future. You may also choose to automatically delete all your posts after a set number of days"] = ""; -App::$strings["Expire and delete all my posts after this many days"] = ""; -App::$strings["Leave at 0 if you wish to manually control expiration of specific posts."] = ""; -App::$strings["Automatic Expiration Settings"] = ""; -App::$strings["Blocked accounts"] = ""; -App::$strings["Expired accounts"] = ""; -App::$strings["Expiring accounts"] = ""; -App::$strings["Primary"] = ""; -App::$strings["Clones"] = ""; -App::$strings["Message queues"] = ""; -App::$strings["Your software should be updated"] = ""; -App::$strings["Summary"] = ""; -App::$strings["Registered accounts"] = ""; -App::$strings["Pending registrations"] = ""; -App::$strings["Registered channels"] = ""; -App::$strings["Active addons"] = ""; -App::$strings["Version"] = ""; -App::$strings["Repository version (release)"] = ""; -App::$strings["Repository version (dev)"] = ""; -App::$strings["Invalid item."] = ""; -App::$strings["Page not found."] = ""; -App::$strings["Create personal planning cards"] = ""; -App::$strings["Add Card"] = ""; -App::$strings["Create"] = ""; -App::$strings["Block Name"] = ""; -App::$strings["Blocks"] = ""; -App::$strings["Block Title"] = ""; -App::$strings["Created"] = ""; -App::$strings["Edited"] = ""; -App::$strings["Share"] = ""; -App::$strings["View"] = ""; -App::$strings["Calendar entries imported."] = ""; -App::$strings["No calendar entries found."] = ""; -App::$strings["INVALID EVENT DISMISSED!"] = ""; -App::$strings["Summary: "] = ""; -App::$strings["Date: "] = ""; -App::$strings["Reason: "] = ""; -App::$strings["INVALID CARD DISMISSED!"] = ""; -App::$strings["Name: "] = ""; -App::$strings["CardDAV App"] = ""; -App::$strings["Not Installed"] = ""; -App::$strings["CalDAV capable addressbook"] = ""; -App::$strings["Event title"] = ""; -App::$strings["Start date and time"] = ""; -App::$strings["End date and time"] = ""; -App::$strings["Description"] = ""; -App::$strings["Location"] = ""; -App::$strings["Month"] = ""; -App::$strings["Week"] = ""; -App::$strings["Day"] = ""; -App::$strings["List month"] = ""; -App::$strings["List week"] = ""; -App::$strings["List day"] = ""; -App::$strings["More"] = ""; -App::$strings["Less"] = ""; -App::$strings["Select calendar"] = ""; -App::$strings["Channel Calendars"] = ""; -App::$strings["CalDAV Calendars"] = ""; -App::$strings["Delete all"] = ""; -App::$strings["Sorry! Editing of recurrent events is not yet implemented."] = ""; -App::$strings["Organisation"] = ""; -App::$strings["Title"] = ""; -App::$strings["Phone"] = ""; -App::$strings["Instant messenger"] = ""; -App::$strings["Website"] = ""; -App::$strings["Note"] = ""; -App::$strings["Mobile"] = ""; -App::$strings["Home"] = ""; -App::$strings["Work"] = ""; -App::$strings["Other"] = ""; -App::$strings["Add Contact"] = ""; -App::$strings["Add Field"] = ""; -App::$strings["P.O. Box"] = ""; -App::$strings["Additional"] = ""; -App::$strings["Street"] = ""; -App::$strings["Locality"] = ""; -App::$strings["Region"] = ""; -App::$strings["ZIP Code"] = ""; -App::$strings["Country"] = ""; -App::$strings["Default Calendar"] = ""; -App::$strings["Default Addressbook"] = ""; -App::$strings["Channel name changes are not allowed within 48 hours of changing the account password."] = ""; -App::$strings["Reserved nickname. Please choose another."] = ""; -App::$strings["Nickname has unsupported characters or is already being used on this site."] = ""; -App::$strings["Feature has been disabled"] = ""; -App::$strings["Change channel nickname/address"] = ""; -App::$strings["WARNING: "] = ""; -App::$strings["Any/all connections on other networks will be lost!"] = ""; -App::$strings["Please enter your password for verification:"] = ""; -App::$strings["New channel address"] = ""; -App::$strings["Rename Channel"] = ""; -App::$strings["toggle full screen mode"] = ""; -App::$strings["Documentation Search"] = ""; -App::$strings["\$Projectname Documentation"] = ""; -App::$strings["Contents"] = ""; -App::$strings["Away"] = ""; -App::$strings["Online"] = ""; -App::$strings["Could not access contact record."] = ""; -App::$strings["Could not locate selected profile."] = ""; -App::$strings["Connection updated."] = ""; -App::$strings["Failed to update connection record."] = ""; -App::$strings["is now connected to"] = ""; -App::$strings["Could not access address book record."] = ""; -App::$strings["Refresh failed - channel is currently unavailable."] = ""; -App::$strings["Added by Connedit"] = ""; -App::$strings["Unable to set address book parameters."] = ""; -App::$strings["Connection has been removed."] = ""; -App::$strings["View %s's profile"] = ""; -App::$strings["Refresh Permissions"] = ""; -App::$strings["Fetch updated permissions"] = ""; -App::$strings["Refresh Photo"] = ""; -App::$strings["Fetch updated photo"] = ""; -App::$strings["Recent Activity"] = ""; -App::$strings["View recent posts and comments"] = ""; -App::$strings["Block (or Unblock) all communications with this connection"] = ""; -App::$strings["This connection is blocked!"] = ""; -App::$strings["Unignore"] = ""; -App::$strings["Ignore"] = ""; -App::$strings["Ignore (or Unignore) all inbound communications from this connection"] = ""; -App::$strings["This connection is ignored!"] = ""; -App::$strings["Censor (or Uncensor) images from this connection"] = ""; -App::$strings["This connection is censored!"] = ""; -App::$strings["Unarchive"] = ""; -App::$strings["Archive"] = ""; -App::$strings["Archive (or Unarchive) this connection - mark channel dead but keep content"] = ""; -App::$strings["This connection is archived!"] = ""; -App::$strings["Unhide"] = ""; -App::$strings["Hide"] = ""; -App::$strings["Hide or Unhide this connection from your other connections"] = ""; -App::$strings["This connection is hidden!"] = ""; -App::$strings["Delete this connection"] = ""; -App::$strings["Fetch Vcard"] = ""; -App::$strings["Fetch electronic calling card for this connection"] = ""; -App::$strings["Permissions"] = ""; -App::$strings["Open Individual Permissions section by default"] = ""; -App::$strings["Open Friend Zoom section by default"] = ""; -App::$strings["Me"] = ""; -App::$strings["Family"] = ""; -App::$strings["Friends"] = ""; -App::$strings["Peers"] = ""; -App::$strings["All"] = ""; -App::$strings["Filter"] = ""; -App::$strings["Open Custom Filter section by default"] = ""; -App::$strings["Approve this connection"] = ""; -App::$strings["Accept connection to allow communication"] = ""; -App::$strings["Set Friend Zoom"] = ""; -App::$strings["Set Profile"] = ""; -App::$strings["Set Friend Zoom & Profile"] = ""; -App::$strings["This connection is unreachable from this location."] = ""; -App::$strings["This connection may be unreachable from other channel locations."] = ""; -App::$strings["Location independence is not supported by their network."] = ""; -App::$strings["This connection is unreachable from this location. Location independence is not supported by their network."] = ""; -App::$strings["Connection Default Permissions"] = ""; -App::$strings["Connection: %s"] = ""; -App::$strings["Apply these permissions automatically"] = ""; -App::$strings["Connection requests will be approved without your interaction"] = ""; -App::$strings["Permission role"] = ""; -App::$strings["Loading"] = ""; -App::$strings["Add permission role"] = ""; -App::$strings["This connection's primary address is"] = ""; -App::$strings["Available locations:"] = ""; -App::$strings["The permissions indicated on this page will be applied to all new connections."] = ""; -App::$strings["Connection Tools"] = ""; -App::$strings["Slide to adjust your degree of friendship"] = ""; -App::$strings["Rating"] = ""; -App::$strings["Slide to adjust your rating"] = ""; -App::$strings["Optionally explain your rating"] = ""; -App::$strings["Custom Filter"] = ""; -App::$strings["Only import posts with this text"] = ""; -App::$strings["words one per line or #tags, \$categories, /patterns/, or lang=xx, leave blank to import all posts"] = ""; -App::$strings["Do not import posts with this text"] = ""; -App::$strings["Nickname"] = ""; -App::$strings["optional - allows you to search by a name that you have chosen"] = ""; -App::$strings["This information is public!"] = ""; -App::$strings["Connection Pending Approval"] = ""; -App::$strings["inherited"] = ""; -App::$strings["Please choose the profile you would like to display to %s when viewing your profile securely."] = ""; -App::$strings["Their Settings"] = ""; -App::$strings["My Settings"] = ""; -App::$strings["Individual Permissions"] = ""; -App::$strings["Some permissions may be inherited from your channel's privacy settings, which have higher priority than individual settings. You can not change those settings here."] = ""; -App::$strings["Some permissions may be inherited from your channel's privacy settings, which have higher priority than individual settings. You can change those settings here but they wont have any impact unless the inherited setting changes."] = ""; -App::$strings["Last update:"] = ""; -App::$strings["Details"] = ""; -App::$strings["Active"] = ""; -App::$strings["Blocked"] = ""; -App::$strings["Ignored"] = ""; -App::$strings["Hidden"] = ""; -App::$strings["Archived/Unreachable"] = ""; -App::$strings["New"] = ""; -App::$strings["Active Connections"] = ""; -App::$strings["Show active connections"] = ""; -App::$strings["New Connections"] = ""; -App::$strings["Show pending (new) connections"] = ""; -App::$strings["Only show blocked connections"] = ""; -App::$strings["Only show ignored connections"] = ""; -App::$strings["Only show archived/unreachable connections"] = ""; -App::$strings["Only show hidden connections"] = ""; -App::$strings["All Connections"] = ""; -App::$strings["Show all connections"] = ""; -App::$strings["Order by name"] = ""; -App::$strings["Recent"] = ""; -App::$strings["Order by recent"] = ""; -App::$strings["Order by date"] = ""; -App::$strings["Pending approval"] = ""; -App::$strings["Archived"] = ""; -App::$strings["Not connected at this location"] = ""; -App::$strings["%1\$s [%2\$s]"] = ""; -App::$strings["Edit connection"] = ""; -App::$strings["Delete connection"] = ""; -App::$strings["Channel address"] = ""; -App::$strings["Network"] = ""; -App::$strings["Call"] = ""; -App::$strings["Status"] = ""; -App::$strings["Connected"] = ""; -App::$strings["Approve connection"] = ""; -App::$strings["Ignore connection"] = ""; -App::$strings["Recent activity"] = ""; -App::$strings["Filter by"] = ""; -App::$strings["Sort by"] = ""; -App::$strings["Search your connections"] = ""; -App::$strings["Connections search"] = ""; -App::$strings["Find"] = ""; -App::$strings["network"] = ""; -App::$strings["App installed."] = ""; -App::$strings["Malformed app."] = ""; -App::$strings["Embed code"] = ""; -App::$strings["Edit App"] = ""; -App::$strings["Create App"] = ""; -App::$strings["Name of app"] = ""; -App::$strings["Required"] = ""; -App::$strings["Location (URL) of app"] = ""; -App::$strings["Photo icon URL"] = ""; -App::$strings["80 x 80 pixels - optional"] = ""; -App::$strings["Categories (optional, comma separated list)"] = ""; -App::$strings["Version ID"] = ""; -App::$strings["Price of app"] = ""; -App::$strings["Location (URL) to purchase app"] = ""; -App::$strings["Image uploaded but image cropping failed."] = ""; -App::$strings["Cover Photos"] = ""; -App::$strings["Image resize failed."] = ""; -App::$strings["Unable to process image"] = ""; -App::$strings["female"] = ""; -App::$strings["%1\$s updated her %2\$s"] = ""; -App::$strings["male"] = ""; -App::$strings["%1\$s updated his %2\$s"] = ""; -App::$strings["%1\$s updated their %2\$s"] = ""; -App::$strings["cover photo"] = ""; -App::$strings["Photo not available."] = ""; -App::$strings["Your cover photo may be visible to anybody on the internet"] = ""; -App::$strings["Upload File:"] = ""; -App::$strings["Select a profile:"] = ""; -App::$strings["Change Cover Photo"] = ""; -App::$strings["Upload"] = ""; -App::$strings["Use a photo from your albums"] = ""; -App::$strings["OK"] = ""; -App::$strings["Choose images to embed"] = ""; -App::$strings["Choose an album"] = ""; -App::$strings["Choose a different album"] = ""; -App::$strings["Error getting album list"] = ""; -App::$strings["Error getting photo link"] = ""; -App::$strings["Error getting album"] = ""; -App::$strings["Select previously uploaded photo"] = ""; -App::$strings["Crop Image"] = ""; -App::$strings["Please adjust the image cropping for optimum viewing."] = ""; -App::$strings["Done Editing"] = ""; -App::$strings["Settings updated."] = ""; -App::$strings["If enabled, connection requests will be approved without your interaction"] = ""; -App::$strings["Automatic approval settings"] = ""; -App::$strings["Some individual permissions may have been preset or locked based on your channel type and privacy settings."] = ""; -App::$strings["Invalid request."] = ""; -App::$strings["channel"] = ""; -App::$strings["thing"] = ""; -App::$strings["Permission denied"] = ""; -App::$strings["photo"] = ""; -App::$strings["status"] = ""; -App::$strings["%1\$s likes %2\$s's %3\$s"] = ""; -App::$strings["%1\$s doesn't like %2\$s's %3\$s"] = ""; -App::$strings["%1\$s is attending %2\$s's %3\$s"] = ""; -App::$strings["%1\$s is not attending %2\$s's %3\$s"] = ""; -App::$strings["%1\$s may attend %2\$s's %3\$s"] = ""; -App::$strings["Item not found"] = ""; -App::$strings["Item is not editable"] = ""; -App::$strings["Edit post"] = ""; -App::$strings["day(s)"] = ""; -App::$strings["week(s)"] = ""; -App::$strings["month(s)"] = ""; -App::$strings["year(s)"] = ""; -App::$strings["Edit event title"] = ""; -App::$strings["Categories (comma-separated list)"] = ""; -App::$strings["Edit Category"] = ""; -App::$strings["Category"] = ""; -App::$strings["Edit start date and time"] = ""; -App::$strings["Finish date and time are not known or not relevant"] = ""; -App::$strings["Edit finish date and time"] = ""; -App::$strings["Finish date and time"] = ""; -App::$strings["Adjust for viewer timezone"] = ""; -App::$strings["Important for events that happen in a particular place. Not practical for global holidays."] = ""; -App::$strings["Edit Description"] = ""; -App::$strings["Edit Location"] = ""; -App::$strings["Permission settings"] = ""; -App::$strings["Timezone:"] = ""; -App::$strings["Advanced Options"] = ""; -App::$strings["Event repeat"] = ""; -App::$strings["Repeat frequency"] = ""; -App::$strings["Repeat every"] = ""; -App::$strings["Number of total repeats"] = ""; -App::$strings["Event removed"] = ""; -App::$strings["Layout Name"] = ""; -App::$strings["Layout Description (Optional)"] = ""; -App::$strings["Edit Layout"] = ""; -App::$strings["This app allows you to create private notes for your personal use."] = ""; -App::$strings["This app is installed. The Notes tool can be found on your stream page."] = ""; -App::$strings["Page link"] = ""; -App::$strings["Insert web link"] = ""; -App::$strings["Edit Webpage"] = ""; -App::$strings["Token verification failed."] = ""; -App::$strings["Email verification resent"] = ""; -App::$strings["Unable to resend email verification message."] = ""; -App::$strings["Email Verification Required"] = ""; -App::$strings["A verification token was sent to your email address [%s]. Enter that token here to complete the account verification step. Please allow a few minutes for delivery, and check your spam folder if you do not see the message."] = ""; -App::$strings["Resend Email"] = ""; -App::$strings["Validation token"] = ""; -App::$strings["Image/photo"] = ""; -App::$strings["View Photo"] = ""; -App::$strings["Edit Album"] = ""; -App::$strings["Nothing to import."] = ""; -App::$strings["Unable to download data from old server"] = ""; -App::$strings["Imported file is empty."] = ""; -App::$strings["Your service plan only allows %d channels."] = ""; -App::$strings["No channel. Import failed."] = ""; -App::$strings["Import completed."] = ""; -App::$strings["You must be logged in to use this feature."] = ""; -App::$strings["Import Channel"] = ""; -App::$strings["Use this form to import an existing channel from a different server. You may retrieve the channel identity from the old server via the network or provide an export file."] = ""; -App::$strings["File to Upload"] = ""; -App::$strings["Or provide the old server details"] = ""; -App::$strings["Your old identity address (xyz@example.com)"] = ""; -App::$strings["Your old login email address"] = ""; -App::$strings["Your old login password"] = ""; -App::$strings["Import a few months of posts if possible (limited by available memory"] = ""; -App::$strings["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."] = ""; -App::$strings["Make this hub my primary location"] = ""; -App::$strings["Move this channel (disable all previous locations)"] = ""; -App::$strings["Use this channel nickname instead of the one provided"] = ""; -App::$strings["Leave blank to keep your existing channel nickname. You will be randomly assigned a similar nickname if either name is already allocated on this site."] = ""; -App::$strings["This process may take several minutes to complete. Please submit the form only once and leave this page open until finished."] = ""; -App::$strings["Total invitation limit exceeded."] = ""; -App::$strings["%s : Not a valid email address."] = ""; -App::$strings["Please join us on \$Projectname"] = ""; -App::$strings["Invitation limit exceeded. Please contact your site administrator."] = ""; -App::$strings["%s : Message delivery failed."] = ""; -App::$strings["%d message sent."] = [ - 0 => "", - 1 => "", +App::$strings['Channel not found'] = ''; +App::$strings["Channel '%s' deleted"] = ''; +App::$strings["Channel '%s' censored"] = ''; +App::$strings["Channel '%s' uncensored"] = ''; +App::$strings["Channel '%s' code allowed"] = ''; +App::$strings["Channel '%s' code disallowed"] = ''; +App::$strings['Channels'] = ''; +App::$strings['Censor'] = ''; +App::$strings['Uncensor'] = ''; +App::$strings['Allow Code'] = ''; +App::$strings['Disallow Code'] = ''; +App::$strings['Channel'] = ''; +App::$strings['UID'] = ''; +App::$strings['Name'] = ''; +App::$strings['Address'] = ''; +App::$strings["Selected channels will be deleted!\\n\\nEverything that was posted in these channels on this site will be permanently deleted!\\n\\nAre you sure?"] = ''; +App::$strings["The channel {0} will be deleted!\\n\\nEverything that was posted in this channel on this site will be permanently deleted!\\n\\nAre you sure?"] = ''; +App::$strings['By default, unfiltered HTML is allowed in embedded media. This is inherently insecure.'] = ''; +App::$strings['The recommended setting is to only allow unfiltered HTML from the following sites:'] = ''; +App::$strings['https://youtube.com/
                    https://www.youtube.com/
                    https://youtu.be/
                    https://vimeo.com/
                    https://soundcloud.com/
                    '] = ''; +App::$strings['All other embedded content will be filtered, unless embedded content from that site is explicitly blocked.'] = ''; +App::$strings['Security'] = ''; +App::$strings['Block public'] = ''; +App::$strings['Check to block public access to all otherwise public personal pages on this site unless you are currently authenticated.'] = ''; +App::$strings['Block public search'] = ''; +App::$strings['Prevent access to search content unless you are currently authenticated.'] = ''; +App::$strings['Hide local directory'] = ''; +App::$strings['Only use the global directory'] = ''; +App::$strings['Provide a cloud root directory'] = ''; +App::$strings['The cloud root directory lists all channel names which provide public files'] = ''; +App::$strings['Show total disk space available to cloud uploads'] = ''; +App::$strings['Allow SVG thumbnails in file browser'] = ''; +App::$strings['WARNING: SVG images may contain malicious code.'] = ''; +App::$strings['Allow embedded (inline) PDF files'] = ''; +App::$strings["Set \"Transport Security\" HTTP header"] = ''; +App::$strings["Set \"Content Security Policy\" HTTP header"] = ''; +App::$strings['Allowed email domains'] = ''; +App::$strings['Comma separated list of domains which are allowed in email addresses for registrations to this site. Wildcards are accepted. Empty to allow any domains'] = ''; +App::$strings['Not allowed email domains'] = ''; +App::$strings['Comma separated list of domains which are not allowed in email addresses for registrations to this site. Wildcards are accepted. Empty to allow any domains, unless allowed domains have been defined.'] = ''; +App::$strings['Allow communications only from these sites'] = ''; +App::$strings['One site per line. Leave empty to allow communication from anywhere by default'] = ''; +App::$strings['Block communications from these sites'] = ''; +App::$strings['Allow communications only from these channels'] = ''; +App::$strings['One channel (hash) per line. Leave empty to allow from any channel by default'] = ''; +App::$strings['Block communications from these channels'] = ''; +App::$strings['Allow public stream communications only from these sites'] = ''; +App::$strings['Block public stream communications from these sites'] = ''; +App::$strings['Allow public stream communications only from these channels'] = ''; +App::$strings['Block public stream communications from these channels'] = ''; +App::$strings['Only allow embeds from secure (SSL) websites and links.'] = ''; +App::$strings['Allow unfiltered embedded HTML content only from these domains'] = ''; +App::$strings['One site per line. By default embedded content is filtered.'] = ''; +App::$strings['Block embedded HTML from these domains'] = ''; +App::$strings['You must be logged in to see this page.'] = ''; +App::$strings['Posts and comments'] = ''; +App::$strings['Only posts'] = ''; +App::$strings['This is the home page of %s.'] = ''; +App::$strings['Insufficient permissions. Request redirected to profile page.'] = ''; +App::$strings['Search Results For:'] = ''; +App::$strings['Reset form'] = ''; +App::$strings['You must enable javascript for your browser to be able to view this content.'] = ''; +App::$strings['Authorize application connection'] = ''; +App::$strings['Return to your app and insert this Security Code:'] = ''; +App::$strings['Please login to continue.'] = ''; +App::$strings['Do you want to authorize this application to access your posts and contacts, and/or create new posts for you?'] = ''; +App::$strings['Change Order of Pinned Navbar Apps'] = ''; +App::$strings['Change Order of App Tray Apps'] = ''; +App::$strings['Use arrows to move the corresponding app left (top) or right (bottom) in the navbar'] = ''; +App::$strings['Use arrows to move the corresponding app up or down in the app tray'] = ''; +App::$strings['Available Apps'] = ''; +App::$strings['Installed Apps'] = ''; +App::$strings['Manage apps'] = ''; +App::$strings['Create Custom App'] = ''; +App::$strings['No channel.'] = ''; +App::$strings['No connections in common.'] = ''; +App::$strings['View Common Connections'] = ''; +App::$strings['Item not available.'] = ''; +App::$strings['Unknown App'] = ''; +App::$strings['Authorize'] = ''; +App::$strings['Do you authorize the app %s to access your channel data?'] = ''; +App::$strings['Allow'] = ''; +App::$strings['Channel not found.'] = ''; +App::$strings['Permissions denied.'] = ''; +App::$strings['l, F j'] = ''; +App::$strings['Link to Source'] = ''; +App::$strings['Edit Event'] = ''; +App::$strings['Create Event'] = ''; +App::$strings['Previous'] = ''; +App::$strings['Next'] = ''; +App::$strings['Import'] = ''; +App::$strings['Today'] = ''; +App::$strings['Expiration settings updated.'] = ''; +App::$strings['This app allows you to set an optional expiration date/time for posts, after which they will be deleted. This must be at least fifteen minutes into the future. You may also choose to automatically delete all your posts after a set number of days'] = ''; +App::$strings['Expire and delete all my posts after this many days'] = ''; +App::$strings['Leave at 0 if you wish to manually control expiration of specific posts.'] = ''; +App::$strings['Automatic Expiration Settings'] = ''; +App::$strings['Blocked accounts'] = ''; +App::$strings['Expired accounts'] = ''; +App::$strings['Expiring accounts'] = ''; +App::$strings['Primary'] = ''; +App::$strings['Clones'] = ''; +App::$strings['Message queues'] = ''; +App::$strings['Your software should be updated'] = ''; +App::$strings['Summary'] = ''; +App::$strings['Registered accounts'] = ''; +App::$strings['Pending registrations'] = ''; +App::$strings['Registered channels'] = ''; +App::$strings['Active addons'] = ''; +App::$strings['Version'] = ''; +App::$strings['Repository version (release)'] = ''; +App::$strings['Repository version (dev)'] = ''; +App::$strings['Invalid item.'] = ''; +App::$strings['Page not found.'] = ''; +App::$strings['Create personal planning cards'] = ''; +App::$strings['Add Card'] = ''; +App::$strings['Create'] = ''; +App::$strings['Block Name'] = ''; +App::$strings['Blocks'] = ''; +App::$strings['Block Title'] = ''; +App::$strings['Created'] = ''; +App::$strings['Edited'] = ''; +App::$strings['Share'] = ''; +App::$strings['View'] = ''; +App::$strings['Calendar entries imported.'] = ''; +App::$strings['No calendar entries found.'] = ''; +App::$strings['INVALID EVENT DISMISSED!'] = ''; +App::$strings['Summary: '] = ''; +App::$strings['Date: '] = ''; +App::$strings['Reason: '] = ''; +App::$strings['INVALID CARD DISMISSED!'] = ''; +App::$strings['Name: '] = ''; +App::$strings['CardDAV App'] = ''; +App::$strings['Not Installed'] = ''; +App::$strings['CalDAV capable addressbook'] = ''; +App::$strings['Event title'] = ''; +App::$strings['Start date and time'] = ''; +App::$strings['End date and time'] = ''; +App::$strings['Description'] = ''; +App::$strings['Location'] = ''; +App::$strings['Month'] = ''; +App::$strings['Week'] = ''; +App::$strings['Day'] = ''; +App::$strings['List month'] = ''; +App::$strings['List week'] = ''; +App::$strings['List day'] = ''; +App::$strings['More'] = ''; +App::$strings['Less'] = ''; +App::$strings['Select calendar'] = ''; +App::$strings['Channel Calendars'] = ''; +App::$strings['CalDAV Calendars'] = ''; +App::$strings['Delete all'] = ''; +App::$strings['Sorry! Editing of recurrent events is not yet implemented.'] = ''; +App::$strings['Organisation'] = ''; +App::$strings['Title'] = ''; +App::$strings['Phone'] = ''; +App::$strings['Instant messenger'] = ''; +App::$strings['Website'] = ''; +App::$strings['Note'] = ''; +App::$strings['Mobile'] = ''; +App::$strings['Home'] = ''; +App::$strings['Work'] = ''; +App::$strings['Other'] = ''; +App::$strings['Add Contact'] = ''; +App::$strings['Add Field'] = ''; +App::$strings['P.O. Box'] = ''; +App::$strings['Additional'] = ''; +App::$strings['Street'] = ''; +App::$strings['Locality'] = ''; +App::$strings['Region'] = ''; +App::$strings['ZIP Code'] = ''; +App::$strings['Country'] = ''; +App::$strings['Default Calendar'] = ''; +App::$strings['Default Addressbook'] = ''; +App::$strings['Channel name changes are not allowed within 48 hours of changing the account password.'] = ''; +App::$strings['Reserved nickname. Please choose another.'] = ''; +App::$strings['Nickname has unsupported characters or is already being used on this site.'] = ''; +App::$strings['Feature has been disabled'] = ''; +App::$strings['Change channel nickname/address'] = ''; +App::$strings['WARNING: '] = ''; +App::$strings['Any/all connections on other networks will be lost!'] = ''; +App::$strings['Please enter your password for verification:'] = ''; +App::$strings['New channel address'] = ''; +App::$strings['Rename Channel'] = ''; +App::$strings['toggle full screen mode'] = ''; +App::$strings['Documentation Search'] = ''; +App::$strings["\$Projectname Documentation"] = ''; +App::$strings['Contents'] = ''; +App::$strings['Away'] = ''; +App::$strings['Online'] = ''; +App::$strings['Could not access contact record.'] = ''; +App::$strings['Could not locate selected profile.'] = ''; +App::$strings['Connection updated.'] = ''; +App::$strings['Failed to update connection record.'] = ''; +App::$strings['is now connected to'] = ''; +App::$strings['Could not access address book record.'] = ''; +App::$strings['Refresh failed - channel is currently unavailable.'] = ''; +App::$strings['Added by Connedit'] = ''; +App::$strings['Unable to set address book parameters.'] = ''; +App::$strings['Connection has been removed.'] = ''; +App::$strings["View %s's profile"] = ''; +App::$strings['Refresh Permissions'] = ''; +App::$strings['Fetch updated permissions'] = ''; +App::$strings['Refresh Photo'] = ''; +App::$strings['Fetch updated photo'] = ''; +App::$strings['Recent Activity'] = ''; +App::$strings['View recent posts and comments'] = ''; +App::$strings['Block (or Unblock) all communications with this connection'] = ''; +App::$strings['This connection is blocked!'] = ''; +App::$strings['Unignore'] = ''; +App::$strings['Ignore'] = ''; +App::$strings['Ignore (or Unignore) all inbound communications from this connection'] = ''; +App::$strings['This connection is ignored!'] = ''; +App::$strings['Censor (or Uncensor) images from this connection'] = ''; +App::$strings['This connection is censored!'] = ''; +App::$strings['Unarchive'] = ''; +App::$strings['Archive'] = ''; +App::$strings['Archive (or Unarchive) this connection - mark channel dead but keep content'] = ''; +App::$strings['This connection is archived!'] = ''; +App::$strings['Unhide'] = ''; +App::$strings['Hide'] = ''; +App::$strings['Hide or Unhide this connection from your other connections'] = ''; +App::$strings['This connection is hidden!'] = ''; +App::$strings['Delete this connection'] = ''; +App::$strings['Fetch Vcard'] = ''; +App::$strings['Fetch electronic calling card for this connection'] = ''; +App::$strings['Permissions'] = ''; +App::$strings['Open Individual Permissions section by default'] = ''; +App::$strings['Open Friend Zoom section by default'] = ''; +App::$strings['Me'] = ''; +App::$strings['Family'] = ''; +App::$strings['Friends'] = ''; +App::$strings['Peers'] = ''; +App::$strings['All'] = ''; +App::$strings['Filter'] = ''; +App::$strings['Open Custom Filter section by default'] = ''; +App::$strings['Approve this connection'] = ''; +App::$strings['Accept connection to allow communication'] = ''; +App::$strings['Set Friend Zoom'] = ''; +App::$strings['Set Profile'] = ''; +App::$strings['Set Friend Zoom & Profile'] = ''; +App::$strings['This connection is unreachable from this location.'] = ''; +App::$strings['This connection may be unreachable from other channel locations.'] = ''; +App::$strings['Location independence is not supported by their network.'] = ''; +App::$strings['This connection is unreachable from this location. Location independence is not supported by their network.'] = ''; +App::$strings['Connection Default Permissions'] = ''; +App::$strings['Connection: %s'] = ''; +App::$strings['Apply these permissions automatically'] = ''; +App::$strings['Connection requests will be approved without your interaction'] = ''; +App::$strings['Permission role'] = ''; +App::$strings['Loading'] = ''; +App::$strings['Add permission role'] = ''; +App::$strings["This connection's primary address is"] = ''; +App::$strings['Available locations:'] = ''; +App::$strings['The permissions indicated on this page will be applied to all new connections.'] = ''; +App::$strings['Connection Tools'] = ''; +App::$strings['Slide to adjust your degree of friendship'] = ''; +App::$strings['Rating'] = ''; +App::$strings['Slide to adjust your rating'] = ''; +App::$strings['Optionally explain your rating'] = ''; +App::$strings['Custom Filter'] = ''; +App::$strings['Only import posts with this text'] = ''; +App::$strings["words one per line or #tags, \$categories, /patterns/, or lang=xx, leave blank to import all posts"] = ''; +App::$strings['Do not import posts with this text'] = ''; +App::$strings['Nickname'] = ''; +App::$strings['optional - allows you to search by a name that you have chosen'] = ''; +App::$strings['This information is public!'] = ''; +App::$strings['Connection Pending Approval'] = ''; +App::$strings['inherited'] = ''; +App::$strings['Please choose the profile you would like to display to %s when viewing your profile securely.'] = ''; +App::$strings['Their Settings'] = ''; +App::$strings['My Settings'] = ''; +App::$strings['Individual Permissions'] = ''; +App::$strings["Some permissions may be inherited from your channel's privacy settings, which have higher priority than individual settings. You can not change those settings here."] = ''; +App::$strings["Some permissions may be inherited from your channel's privacy settings, which have higher priority than individual settings. You can change those settings here but they wont have any impact unless the inherited setting changes."] = ''; +App::$strings['Last update:'] = ''; +App::$strings['Details'] = ''; +App::$strings['Active'] = ''; +App::$strings['Blocked'] = ''; +App::$strings['Ignored'] = ''; +App::$strings['Hidden'] = ''; +App::$strings['Archived/Unreachable'] = ''; +App::$strings['New'] = ''; +App::$strings['Active Connections'] = ''; +App::$strings['Show active connections'] = ''; +App::$strings['New Connections'] = ''; +App::$strings['Show pending (new) connections'] = ''; +App::$strings['Only show blocked connections'] = ''; +App::$strings['Only show ignored connections'] = ''; +App::$strings['Only show archived/unreachable connections'] = ''; +App::$strings['Only show hidden connections'] = ''; +App::$strings['All Connections'] = ''; +App::$strings['Show all connections'] = ''; +App::$strings['Order by name'] = ''; +App::$strings['Recent'] = ''; +App::$strings['Order by recent'] = ''; +App::$strings['Order by date'] = ''; +App::$strings['Pending approval'] = ''; +App::$strings['Archived'] = ''; +App::$strings['Not connected at this location'] = ''; +App::$strings["%1\$s [%2\$s]"] = ''; +App::$strings['Edit connection'] = ''; +App::$strings['Delete connection'] = ''; +App::$strings['Channel address'] = ''; +App::$strings['Network'] = ''; +App::$strings['Call'] = ''; +App::$strings['Status'] = ''; +App::$strings['Connected'] = ''; +App::$strings['Approve connection'] = ''; +App::$strings['Ignore connection'] = ''; +App::$strings['Recent activity'] = ''; +App::$strings['Filter by'] = ''; +App::$strings['Sort by'] = ''; +App::$strings['Search your connections'] = ''; +App::$strings['Connections search'] = ''; +App::$strings['Find'] = ''; +App::$strings['network'] = ''; +App::$strings['App installed.'] = ''; +App::$strings['Malformed app.'] = ''; +App::$strings['Embed code'] = ''; +App::$strings['Edit App'] = ''; +App::$strings['Create App'] = ''; +App::$strings['Name of app'] = ''; +App::$strings['Required'] = ''; +App::$strings['Location (URL) of app'] = ''; +App::$strings['Photo icon URL'] = ''; +App::$strings['80 x 80 pixels - optional'] = ''; +App::$strings['Categories (optional, comma separated list)'] = ''; +App::$strings['Version ID'] = ''; +App::$strings['Price of app'] = ''; +App::$strings['Location (URL) to purchase app'] = ''; +App::$strings['Image uploaded but image cropping failed.'] = ''; +App::$strings['Cover Photos'] = ''; +App::$strings['Image resize failed.'] = ''; +App::$strings['Unable to process image'] = ''; +App::$strings['female'] = ''; +App::$strings["%1\$s updated her %2\$s"] = ''; +App::$strings['male'] = ''; +App::$strings["%1\$s updated his %2\$s"] = ''; +App::$strings["%1\$s updated their %2\$s"] = ''; +App::$strings['cover photo'] = ''; +App::$strings['Photo not available.'] = ''; +App::$strings['Your cover photo may be visible to anybody on the internet'] = ''; +App::$strings['Upload File:'] = ''; +App::$strings['Select a profile:'] = ''; +App::$strings['Change Cover Photo'] = ''; +App::$strings['Upload'] = ''; +App::$strings['Use a photo from your albums'] = ''; +App::$strings['OK'] = ''; +App::$strings['Choose images to embed'] = ''; +App::$strings['Choose an album'] = ''; +App::$strings['Choose a different album'] = ''; +App::$strings['Error getting album list'] = ''; +App::$strings['Error getting photo link'] = ''; +App::$strings['Error getting album'] = ''; +App::$strings['Select previously uploaded photo'] = ''; +App::$strings['Crop Image'] = ''; +App::$strings['Please adjust the image cropping for optimum viewing.'] = ''; +App::$strings['Done Editing'] = ''; +App::$strings['Settings updated.'] = ''; +App::$strings['If enabled, connection requests will be approved without your interaction'] = ''; +App::$strings['Automatic approval settings'] = ''; +App::$strings['Some individual permissions may have been preset or locked based on your channel type and privacy settings.'] = ''; +App::$strings['Invalid request.'] = ''; +App::$strings['channel'] = ''; +App::$strings['thing'] = ''; +App::$strings['Permission denied'] = ''; +App::$strings['photo'] = ''; +App::$strings['status'] = ''; +App::$strings["%1\$s likes %2\$s's %3\$s"] = ''; +App::$strings["%1\$s doesn't like %2\$s's %3\$s"] = ''; +App::$strings["%1\$s is attending %2\$s's %3\$s"] = ''; +App::$strings["%1\$s is not attending %2\$s's %3\$s"] = ''; +App::$strings["%1\$s may attend %2\$s's %3\$s"] = ''; +App::$strings['Item not found'] = ''; +App::$strings['Item is not editable'] = ''; +App::$strings['Edit post'] = ''; +App::$strings['day(s)'] = ''; +App::$strings['week(s)'] = ''; +App::$strings['month(s)'] = ''; +App::$strings['year(s)'] = ''; +App::$strings['Edit event title'] = ''; +App::$strings['Categories (comma-separated list)'] = ''; +App::$strings['Edit Category'] = ''; +App::$strings['Category'] = ''; +App::$strings['Edit start date and time'] = ''; +App::$strings['Finish date and time are not known or not relevant'] = ''; +App::$strings['Edit finish date and time'] = ''; +App::$strings['Finish date and time'] = ''; +App::$strings['Adjust for viewer timezone'] = ''; +App::$strings['Important for events that happen in a particular place. Not practical for global holidays.'] = ''; +App::$strings['Edit Description'] = ''; +App::$strings['Edit Location'] = ''; +App::$strings['Permission settings'] = ''; +App::$strings['Timezone:'] = ''; +App::$strings['Advanced Options'] = ''; +App::$strings['Event repeat'] = ''; +App::$strings['Repeat frequency'] = ''; +App::$strings['Repeat every'] = ''; +App::$strings['Number of total repeats'] = ''; +App::$strings['Event removed'] = ''; +App::$strings['Layout Name'] = ''; +App::$strings['Layout Description (Optional)'] = ''; +App::$strings['Edit Layout'] = ''; +App::$strings['This app allows you to create private notes for your personal use.'] = ''; +App::$strings['This app is installed. The Notes tool can be found on your stream page.'] = ''; +App::$strings['Page link'] = ''; +App::$strings['Insert web link'] = ''; +App::$strings['Edit Webpage'] = ''; +App::$strings['Token verification failed.'] = ''; +App::$strings['Email verification resent'] = ''; +App::$strings['Unable to resend email verification message.'] = ''; +App::$strings['Email Verification Required'] = ''; +App::$strings['A verification token was sent to your email address [%s]. Enter that token here to complete the account verification step. Please allow a few minutes for delivery, and check your spam folder if you do not see the message.'] = ''; +App::$strings['Resend Email'] = ''; +App::$strings['Validation token'] = ''; +App::$strings['Image/photo'] = ''; +App::$strings['View Photo'] = ''; +App::$strings['Edit Album'] = ''; +App::$strings['Nothing to import.'] = ''; +App::$strings['Unable to download data from old server'] = ''; +App::$strings['Imported file is empty.'] = ''; +App::$strings['Your service plan only allows %d channels.'] = ''; +App::$strings['No channel. Import failed.'] = ''; +App::$strings['Import completed.'] = ''; +App::$strings['You must be logged in to use this feature.'] = ''; +App::$strings['Import Channel'] = ''; +App::$strings['Use this form to import an existing channel from a different server. You may retrieve the channel identity from the old server via the network or provide an export file.'] = ''; +App::$strings['File to Upload'] = ''; +App::$strings['Or provide the old server details'] = ''; +App::$strings['Your old identity address (xyz@example.com)'] = ''; +App::$strings['Your old login email address'] = ''; +App::$strings['Your old login password'] = ''; +App::$strings['Import a few months of posts if possible (limited by available memory'] = ''; +App::$strings['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.'] = ''; +App::$strings['Make this hub my primary location'] = ''; +App::$strings['Move this channel (disable all previous locations)'] = ''; +App::$strings['Use this channel nickname instead of the one provided'] = ''; +App::$strings['Leave blank to keep your existing channel nickname. You will be randomly assigned a similar nickname if either name is already allocated on this site.'] = ''; +App::$strings['This process may take several minutes to complete. Please submit the form only once and leave this page open until finished.'] = ''; +App::$strings['Total invitation limit exceeded.'] = ''; +App::$strings['%s : Not a valid email address.'] = ''; +App::$strings["Please join us on \$Projectname"] = ''; +App::$strings['Invitation limit exceeded. Please contact your site administrator.'] = ''; +App::$strings['%s : Message delivery failed.'] = ''; +App::$strings['%d message sent.'] = [ + 0 => '', + 1 => '', ]; -App::$strings["Send email invitations to join this network"] = ""; -App::$strings["You have no more invitations available"] = ""; -App::$strings["Send invitations"] = ""; -App::$strings["Enter email addresses, one per line:"] = ""; -App::$strings["Your message:"] = ""; -App::$strings["Please join my community on \$Projectname."] = ""; -App::$strings["You will need to supply this invitation code:"] = ""; -App::$strings["1. Register at any \$Projectname location (they are all inter-connected)"] = ""; -App::$strings["2. Enter my \$Projectname network address into the site searchbar."] = ""; -App::$strings["or visit"] = ""; -App::$strings["3. Click [Connect]"] = ""; -App::$strings["This app allows you to disable comments on individual posts."] = ""; -App::$strings["This app is installed. A button to control the ability to comment may be found below the post editor."] = ""; -App::$strings["Enter a folder name"] = ""; -App::$strings["or select an existing folder (doubleclick)"] = ""; -App::$strings["File not found."] = ""; -App::$strings["Permission Denied."] = ""; -App::$strings["Edit file permissions"] = ""; -App::$strings["Set/edit permissions"] = ""; -App::$strings["Include all files and sub folders"] = ""; -App::$strings["Return to file list"] = ""; -App::$strings["Copy/paste this code to attach file to a post"] = ""; -App::$strings["Copy/paste this URL to link file from a web page"] = ""; -App::$strings["Share this file"] = ""; -App::$strings["Show URL to this file"] = ""; -App::$strings["Show in your contacts shared folder"] = ""; -App::$strings["Entry censored"] = ""; -App::$strings["Entry uncensored"] = ""; -App::$strings["webpage"] = ""; -App::$strings["block"] = ""; -App::$strings["layout"] = ""; -App::$strings["menu"] = ""; -App::$strings["%s element installed"] = ""; -App::$strings["%s element installation failed"] = ""; -App::$strings["Thing updated"] = ""; -App::$strings["Object store: failed"] = ""; -App::$strings["Thing added"] = ""; -App::$strings["OBJ: %1\$s %2\$s %3\$s"] = ""; -App::$strings["Show Thing"] = ""; -App::$strings["item not found."] = ""; -App::$strings["Edit Thing"] = ""; -App::$strings["Select a profile"] = ""; -App::$strings["Post an activity"] = ""; -App::$strings["Only sends to viewers of the applicable profile"] = ""; -App::$strings["Name of thing e.g. something"] = ""; -App::$strings["URL of thing (optional)"] = ""; -App::$strings["URL for photo of thing (optional)"] = ""; -App::$strings["Add Thing to your Profile"] = ""; -App::$strings["Room not found"] = ""; -App::$strings["Leave Room"] = ""; -App::$strings["Delete Room"] = ""; -App::$strings["I am away right now"] = ""; -App::$strings["I am online"] = ""; -App::$strings["Please enter a link URL:"] = ""; -App::$strings["New Chatroom"] = ""; -App::$strings["Chatroom name"] = ""; -App::$strings["Expiration of chats (minutes)"] = ""; -App::$strings["%1\$s's Chatrooms"] = ""; -App::$strings["No chatrooms available"] = ""; -App::$strings["Create New"] = ""; -App::$strings["Expiration"] = ""; -App::$strings["min"] = ""; -App::$strings["No entries."] = ""; -App::$strings["Comment approved"] = ""; -App::$strings["Comment deleted"] = ""; -App::$strings["Welcome to Hubzilla!"] = ""; -App::$strings["You have got no unseen posts..."] = ""; -App::$strings["Access list created."] = ""; -App::$strings["Could not create access list."] = ""; -App::$strings["Access list not found."] = ""; -App::$strings["Access list updated."] = ""; -App::$strings["List members"] = ""; -App::$strings["List not found"] = ""; -App::$strings["Access Lists"] = ""; -App::$strings["Create access list"] = ""; -App::$strings["Access list name"] = ""; -App::$strings["Members are visible to other channels"] = ""; -App::$strings["Members"] = ""; -App::$strings["Access list removed."] = ""; -App::$strings["Unable to remove access list."] = ""; -App::$strings["Access List: %s"] = ""; -App::$strings["Access list name: "] = ""; -App::$strings["Delete access list"] = ""; -App::$strings["Not in this list"] = ""; -App::$strings["Select a channel to toggle membership"] = ""; -App::$strings["Layouts"] = ""; -App::$strings["Layout Description"] = ""; -App::$strings["Download PDL file"] = ""; -App::$strings["No such group"] = ""; -App::$strings["No such channel"] = ""; -App::$strings["Access list is empty"] = ""; -App::$strings["Access list: "] = ""; -App::$strings["Invalid channel."] = ""; -App::$strings["Title (optional)"] = ""; -App::$strings["Edit Card"] = ""; -App::$strings["Warning: Database versions differ by %1\$d updates."] = ""; -App::$strings["Import completed"] = ""; -App::$strings["Import Items"] = ""; -App::$strings["Use this form to import existing posts and content from an export file."] = ""; -App::$strings["Not found"] = ""; -App::$strings["Please refresh page"] = ""; -App::$strings["Unknown error"] = ""; -App::$strings["Group"] = ""; -App::$strings["You have created %1$.0f of %2$.0f allowed channels."] = ""; -App::$strings["Create a new channel"] = ""; -App::$strings["Current Channel"] = ""; -App::$strings["Switch to one of your channels by selecting it."] = ""; -App::$strings["Default Login Channel"] = ""; -App::$strings["Make Default"] = ""; -App::$strings["Add to menu"] = ""; -App::$strings["%d new messages"] = ""; -App::$strings["%d new introductions"] = ""; -App::$strings["Delegated Channel"] = ""; -App::$strings["Change UI language"] = ""; -App::$strings["Menu not found."] = ""; -App::$strings["Unable to create element."] = ""; -App::$strings["Unable to update menu element."] = ""; -App::$strings["Unable to add menu element."] = ""; -App::$strings["Not found."] = ""; -App::$strings["Menu Item Permissions"] = ""; -App::$strings["(click to open/close)"] = ""; -App::$strings["Link Name"] = ""; -App::$strings["Link or Submenu Target"] = ""; -App::$strings["Enter URL of the link or select a menu name to create a submenu"] = ""; -App::$strings["Use magic-auth if available"] = ""; -App::$strings["Open link in new window"] = ""; -App::$strings["Order in list"] = ""; -App::$strings["Higher numbers will sink to bottom of listing"] = ""; -App::$strings["Submit and finish"] = ""; -App::$strings["Submit and continue"] = ""; -App::$strings["Menu:"] = ""; -App::$strings["Link Target"] = ""; -App::$strings["Edit menu"] = ""; -App::$strings["Edit element"] = ""; -App::$strings["Drop element"] = ""; -App::$strings["New element"] = ""; -App::$strings["Edit this menu container"] = ""; -App::$strings["Add menu element"] = ""; -App::$strings["Delete this menu item"] = ""; -App::$strings["Edit this menu item"] = ""; -App::$strings["Menu item not found."] = ""; -App::$strings["Menu item deleted."] = ""; -App::$strings["Menu item could not be deleted."] = ""; -App::$strings["Edit Menu Element"] = ""; -App::$strings["Link text"] = ""; -App::$strings["Webfinger Diagnostic"] = ""; -App::$strings["Lookup address or URL"] = ""; -App::$strings["Edit Block"] = ""; -App::$strings["Invalid message"] = ""; -App::$strings["no results"] = ""; -App::$strings["channel sync processed"] = ""; -App::$strings["queued"] = ""; -App::$strings["posted"] = ""; -App::$strings["accepted for delivery"] = ""; -App::$strings["updated"] = ""; -App::$strings["update ignored"] = ""; -App::$strings["permission denied"] = ""; -App::$strings["recipient not found"] = ""; -App::$strings["mail recalled"] = ""; -App::$strings["duplicate mail received"] = ""; -App::$strings["mail delivered"] = ""; -App::$strings["Delivery report for %1\$s"] = ""; -App::$strings["Options"] = ""; -App::$strings["Redeliver"] = ""; -App::$strings["Post repeated"] = ""; -App::$strings["No valid account found."] = ""; -App::$strings["Password reset request issued. Check your email."] = ""; -App::$strings["Site Member (%s)"] = ""; -App::$strings["Password reset requested at %s"] = ""; -App::$strings["Request could not be verified. (You may have previously submitted it.) Password reset failed."] = ""; -App::$strings["Password Reset"] = ""; -App::$strings["Your password has been reset as requested."] = ""; -App::$strings["Your new password is"] = ""; -App::$strings["Save or copy your new password - and then"] = ""; -App::$strings["click here to login"] = ""; -App::$strings["Your password may be changed from the Settings page after successful login."] = ""; -App::$strings["Your password has changed at %s"] = ""; -App::$strings["Forgot your Password?"] = ""; -App::$strings["Enter your email address and submit to have your password reset. Then check your email for further instructions."] = ""; -App::$strings["Email Address"] = ""; -App::$strings["Unable to update menu."] = ""; -App::$strings["Unable to create menu."] = ""; -App::$strings["Menu Name"] = ""; -App::$strings["Unique name (not visible on webpage) - required"] = ""; -App::$strings["Menu Title"] = ""; -App::$strings["Visible on webpage - leave empty for no title"] = ""; -App::$strings["Allow Bookmarks"] = ""; -App::$strings["Menu may be used to store saved bookmarks"] = ""; -App::$strings["Submit and proceed"] = ""; -App::$strings["Menus"] = ""; -App::$strings["Drop"] = ""; -App::$strings["Bookmarks allowed"] = ""; -App::$strings["Delete this menu"] = ""; -App::$strings["Edit menu contents"] = ""; -App::$strings["Edit this menu"] = ""; -App::$strings["Menu could not be deleted."] = ""; -App::$strings["Edit Menu"] = ""; -App::$strings["Add or remove entries to this menu"] = ""; -App::$strings["Menu name"] = ""; -App::$strings["Must be unique, only seen by you"] = ""; -App::$strings["Menu title"] = ""; -App::$strings["Menu title as seen by others"] = ""; -App::$strings["Allow bookmarks"] = ""; -App::$strings["This setting requires special processing and editing has been blocked."] = ""; -App::$strings["Configuration Editor"] = ""; -App::$strings["Warning: Changing some settings could render your channel inoperable. Please leave this page unless you are comfortable with and knowledgeable about how to correctly use this feature."] = ""; -App::$strings["Maximum daily site registrations exceeded. Please try again tomorrow."] = ""; -App::$strings["Please indicate acceptance of the Terms of Service. Registration failed."] = ""; -App::$strings["Passwords do not match."] = ""; -App::$strings["Registration successful. Continue to create your first channel..."] = ""; -App::$strings["Registration successful. Please check your email for validation instructions."] = ""; -App::$strings["Your registration is pending approval by the site owner."] = ""; -App::$strings["Your registration can not be processed."] = ""; -App::$strings["Registration on this hub is disabled."] = ""; -App::$strings["Registration on this hub is by approval only."] = ""; -App::$strings["Register at another affiliated hub."] = ""; -App::$strings["Registration on this hub is by invitation only."] = ""; -App::$strings["This site has exceeded the number of allowed daily account registrations. Please try again tomorrow."] = ""; -App::$strings["Terms of Service"] = ""; -App::$strings["I accept the %s for this website"] = ""; -App::$strings["I am over %s years of age and accept the %s for this website"] = ""; -App::$strings["Your email address"] = ""; -App::$strings["Choose a password"] = ""; -App::$strings["Please re-enter your password"] = ""; -App::$strings["Please enter your invitation code"] = ""; -App::$strings["Your Name"] = ""; -App::$strings["Real names are preferred."] = ""; -App::$strings["Choose a short nickname"] = ""; -App::$strings["Your nickname will be used to create an easy to remember channel address e.g. nickname%s"] = ""; -App::$strings["Channel role and privacy"] = ""; -App::$strings["Select a channel permission role for your usage needs and privacy requirements."] = ""; -App::$strings["no"] = ""; -App::$strings["yes"] = ""; -App::$strings["Register"] = ""; -App::$strings["This site requires email verification. After completing this form, please check your email for further instructions."] = ""; -App::$strings["Poll not found."] = ""; -App::$strings["Invalid response."] = ""; -App::$strings["Response submitted. Updates may not appear instantly."] = ""; -App::$strings["Location not found."] = ""; -App::$strings["Location lookup failed."] = ""; -App::$strings["Please select another location to become primary before removing the primary location."] = ""; -App::$strings["Pushing location info"] = ""; -App::$strings["No locations found."] = ""; -App::$strings["Manage Channel Locations"] = ""; -App::$strings["Publish these settings"] = ""; -App::$strings["Please wait several minutes between consecutive operations."] = ""; -App::$strings["When possible, drop a location by logging into that website/hub and removing your channel."] = ""; -App::$strings["Use this form to drop the location if the hub is no longer operating."] = ""; -App::$strings["item"] = ""; -App::$strings["inspect"] = ""; -App::$strings["Your real name is recommended."] = ""; -App::$strings["Examples: \"Bob Jameson\", \"Lisa and her Horses\", \"Soccer\", \"Aviation Group\""] = ""; -App::$strings["This will be used to create a unique network address (like an email address)."] = ""; -App::$strings["Allowed characters are a-z 0-9, - and _"] = ""; -App::$strings["Channel name"] = ""; -App::$strings["Select a channel permission role compatible with your usage needs and privacy requirements."] = ""; -App::$strings["Create a Channel"] = ""; -App::$strings["A channel is a unique network identity. It can represent a person (social network profile), a forum (group), a business or celebrity page, a newsfeed, and many other things."] = ""; -App::$strings["or import an existing channel from another location."] = ""; -App::$strings["Validate"] = ""; -App::$strings["Continue"] = ""; -App::$strings["Premium Channel Setup"] = ""; -App::$strings["Enable premium channel connection restrictions"] = ""; -App::$strings["Please enter your restrictions or conditions, such as paypal receipt, usage guidelines, etc."] = ""; -App::$strings["This channel may require additional steps or acknowledgement of the following conditions prior to connecting:"] = ""; -App::$strings["Potential connections will then see the following text before proceeding:"] = ""; -App::$strings["By continuing, I certify that I have complied with any instructions provided on this page."] = ""; -App::$strings["(No specific instructions have been provided by the channel owner.)"] = ""; -App::$strings["Restricted or Premium Channel"] = ""; -App::$strings["No more system notifications."] = ""; -App::$strings["System Notifications"] = ""; -App::$strings["Unable to locate original post."] = ""; -App::$strings["Comment may be moderated."] = ""; -App::$strings["Empty post discarded."] = ""; -App::$strings["Duplicate post suppressed."] = ""; -App::$strings["System error. Post not saved."] = ""; -App::$strings["Your post/comment is awaiting approval."] = ""; -App::$strings["Unable to obtain post information from database."] = ""; -App::$strings["You have reached your limit of %1$.0f top level posts."] = ""; -App::$strings["You have reached your limit of %1$.0f webpages."] = ""; -App::$strings["__ctx:mood__ %1\$s is %2\$s"] = ""; -App::$strings["Set your current mood and tell your friends"] = ""; -App::$strings["Unable to find your hub."] = ""; -App::$strings["Post successful."] = ""; -App::$strings["Public Hubs"] = ""; -App::$strings["The listed hubs allow public registration for the \$Projectname network. All hubs in the network are interlinked so membership on any of them conveys membership in the network as a whole. Some hubs may require subscription or provide tiered service plans. The hub itself may provide additional details."] = ""; -App::$strings["Hub URL"] = ""; -App::$strings["Access Type"] = ""; -App::$strings["Registration Policy"] = ""; -App::$strings["Software"] = ""; -App::$strings["Provide managed web pages on your channel"] = ""; -App::$strings["Import Webpage Elements"] = ""; -App::$strings["Import selected"] = ""; -App::$strings["Export Webpage Elements"] = ""; -App::$strings["Export selected"] = ""; -App::$strings["Actions"] = ""; -App::$strings["Page Link"] = ""; -App::$strings["Page Title"] = ""; -App::$strings["Invalid file type."] = ""; -App::$strings["Error opening zip file"] = ""; -App::$strings["Invalid folder path."] = ""; -App::$strings["No webpage elements detected."] = ""; -App::$strings["Import complete."] = ""; -App::$strings["Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."] = ""; -App::$strings["vcard"] = ""; -App::$strings["Layout updated."] = ""; -App::$strings["Edit System Page Description"] = ""; -App::$strings["(modified)"] = ""; -App::$strings["Layout not found."] = ""; -App::$strings["Module Name:"] = ""; -App::$strings["Layout Help"] = ""; -App::$strings["Edit another layout"] = ""; -App::$strings["System layout"] = ""; -App::$strings["Zap Server - Setup"] = ""; -App::$strings["Could not connect to database."] = ""; -App::$strings["Could not connect to specified site URL. Possible SSL certificate or DNS issue."] = ""; -App::$strings["Could not create table."] = ""; -App::$strings["Your site database has been installed."] = ""; -App::$strings["You may need to import the file \"install/schema_xxx.sql\" manually using a database client."] = ""; -App::$strings["Please see the file \"install/INSTALL.txt\"."] = ""; -App::$strings["System check"] = ""; -App::$strings["Check again"] = ""; -App::$strings["Database connection"] = ""; -App::$strings["In order to install this software we need to know how to connect to your database."] = ""; -App::$strings["Please contact your hosting provider or site administrator if you have questions about these settings."] = ""; -App::$strings["The database you specify below should already exist. If it does not, please create it before continuing."] = ""; -App::$strings["Database Server Name"] = ""; -App::$strings["Default is 127.0.0.1"] = ""; -App::$strings["Database Port"] = ""; -App::$strings["Communication port number - use 0 for default"] = ""; -App::$strings["Database Login Name"] = ""; -App::$strings["Database Login Password"] = ""; -App::$strings["Database Name"] = ""; -App::$strings["Database Type"] = ""; -App::$strings["Site administrator email address"] = ""; -App::$strings["Your account email address must match this in order to use the web admin panel."] = ""; -App::$strings["Website URL"] = ""; -App::$strings["Please use SSL (https) URL if available."] = ""; -App::$strings["Please select a default timezone for your website"] = ""; -App::$strings["Site settings"] = ""; -App::$strings["PHP version 7.1 or greater is required."] = ""; -App::$strings["PHP version"] = ""; -App::$strings["Could not find a command line version of PHP in the web server PATH."] = ""; -App::$strings["If you do not have a command line version of PHP installed on server, you will not be able to run background tasks - including message delivery."] = ""; -App::$strings["PHP executable path"] = ""; -App::$strings["Enter full path to php executable. You can leave this blank to continue the installation."] = ""; -App::$strings["Command line PHP"] = ""; -App::$strings["Unable to check command line PHP, as shell_exec() is disabled. This is required."] = ""; -App::$strings["The command line version of PHP on your system does not have \"register_argc_argv\" enabled."] = ""; -App::$strings["This is required for message delivery to work."] = ""; -App::$strings["PHP register_argc_argv"] = ""; -App::$strings["This is not sufficient to upload larger images or files. You should be able to upload at least 2MB (2097152 bytes) at once."] = ""; -App::$strings["Your max allowed total upload size is set to %s. Maximum size of one file to upload is set to %s. You are allowed to upload up to %d files at once."] = ""; -App::$strings["You can adjust these settings in the server php.ini file."] = ""; -App::$strings["PHP upload limits"] = ""; -App::$strings["Error: the \"openssl_pkey_new\" function on this system is not able to generate encryption keys"] = ""; -App::$strings["If running under Windows, please see \"http://www.php.net/manual/en/openssl.installation.php\"."] = ""; -App::$strings["Generate encryption keys"] = ""; -App::$strings["libCurl PHP module"] = ""; -App::$strings["GD graphics PHP module"] = ""; -App::$strings["OpenSSL PHP module"] = ""; -App::$strings["PDO database PHP module"] = ""; -App::$strings["mb_string PHP module"] = ""; -App::$strings["xml PHP module"] = ""; -App::$strings["zip PHP module"] = ""; -App::$strings["Apache mod_rewrite module"] = ""; -App::$strings["Error: Apache webserver mod-rewrite module is required but not installed."] = ""; -App::$strings["exec"] = ""; -App::$strings["Error: exec is required but is either not installed or has been disabled in php.ini"] = ""; -App::$strings["shell_exec"] = ""; -App::$strings["Error: shell_exec is required but is either not installed or has been disabled in php.ini"] = ""; -App::$strings["Error: libCURL PHP module required but not installed."] = ""; -App::$strings["Error: GD PHP module with JPEG support or ImageMagick graphics library required but not installed."] = ""; -App::$strings["Error: openssl PHP module required but not installed."] = ""; -App::$strings["Error: PDO database PHP module missing a driver for either mysql or pgsql."] = ""; -App::$strings["Error: PDO database PHP module required but not installed."] = ""; -App::$strings["Error: mb_string PHP module required but not installed."] = ""; -App::$strings["Error: xml PHP module required for DAV but not installed."] = ""; -App::$strings["Error: zip PHP module required but not installed."] = ""; -App::$strings[".htconfig.php is writable"] = ""; -App::$strings["The web installer needs to be able to create a file called \".htconfig.php\" in the top folder of your web server and it is unable to do so."] = ""; -App::$strings["This is most often a permission setting, as the web server may not be able to write files in your folder - even if you can."] = ""; -App::$strings["Please see install/INSTALL.txt for additional information."] = ""; -App::$strings["This software uses the Smarty3 template engine to render its web views. Smarty3 compiles templates to PHP to speed up rendering."] = ""; -App::$strings["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."] = ""; -App::$strings["Please ensure that the user that your web server runs as (e.g. www-data) has write access to this folder."] = ""; -App::$strings["Note: as a security measure, you should give the web server write access to %s only--not the template files (.tpl) that it contains."] = ""; -App::$strings["%s is writable"] = ""; -App::$strings["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"] = ""; -App::$strings["store is writable"] = ""; -App::$strings["SSL certificate cannot be validated. Fix certificate or disable https access to this site."] = ""; -App::$strings["If you have https access to your website or allow connections to TCP port 443 (the https: port), you MUST use a browser-valid certificate. You MUST NOT use self-signed certificates!"] = ""; -App::$strings["This restriction is incorporated because public posts from you may for example contain references to images on your own hub."] = ""; -App::$strings["If your certificate is not recognized, members of other sites (who may themselves have valid certificates) will get a warning message on their own site complaining about security issues."] = ""; -App::$strings["This can cause usability issues elsewhere (not just on your own site) so we must insist on this requirement."] = ""; -App::$strings["Providers are available that issue free certificates which are browser-valid."] = ""; -App::$strings["If you are confident that the certificate is valid and signed by a trusted authority, check to see if you have failed to install an intermediate cert. These are not normally required by browsers, but are required for server-to-server communications."] = ""; -App::$strings["SSL certificate validation"] = ""; -App::$strings["Url rewrite in .htaccess is not working. Check your server configuration.Test: "] = ""; -App::$strings["Url rewrite is working"] = ""; -App::$strings["The database configuration file \".htconfig.php\" could not be written. Please use the enclosed text to create a configuration file in your web server root."] = ""; -App::$strings["Errors encountered creating database tables."] = ""; -App::$strings["

                    What next?

                    "] = ""; -App::$strings["IMPORTANT: You will need to [manually] setup a scheduled task for the poller."] = ""; -App::$strings["Profile not found."] = ""; -App::$strings["Profile deleted."] = ""; -App::$strings["Profile-"] = ""; -App::$strings["New profile created."] = ""; -App::$strings["Profile unavailable to clone."] = ""; -App::$strings["Profile unavailable to export."] = ""; -App::$strings["Profile Name is required."] = ""; -App::$strings["Marital Status"] = ""; -App::$strings["Romantic Partner"] = ""; -App::$strings["Likes"] = ""; -App::$strings["Dislikes"] = ""; -App::$strings["Work/Employment"] = ""; -App::$strings["Religion"] = ""; -App::$strings["Political Views"] = ""; -App::$strings["Gender"] = ""; -App::$strings["Sexual Preference"] = ""; -App::$strings["Homepage"] = ""; -App::$strings["Interests"] = ""; -App::$strings["Profile updated."] = ""; -App::$strings["Hide your connections list from viewers of this profile"] = ""; -App::$strings["Edit Profile Details"] = ""; -App::$strings["View this profile"] = ""; -App::$strings["Profile Tools"] = ""; -App::$strings["Change cover photo"] = ""; -App::$strings["Create a new profile using these settings"] = ""; -App::$strings["Clone this profile"] = ""; -App::$strings["Delete this profile"] = ""; -App::$strings["Add profile things"] = ""; -App::$strings["Personal"] = ""; -App::$strings["Relationship"] = ""; -App::$strings["Miscellaneous"] = ""; -App::$strings["Import profile from file"] = ""; -App::$strings["Export profile to file"] = ""; -App::$strings["Your gender"] = ""; -App::$strings["Marital status"] = ""; -App::$strings["Sexual preference"] = ""; -App::$strings["Profile name"] = ""; -App::$strings["Your full name"] = ""; -App::$strings["Title/Description"] = ""; -App::$strings["Street address"] = ""; -App::$strings["Locality/City"] = ""; -App::$strings["Region/State"] = ""; -App::$strings["Postal/Zip code"] = ""; -App::$strings["Who (if applicable)"] = ""; -App::$strings["Examples: cathy123, Cathy Williams, cathy@example.com"] = ""; -App::$strings["Since (date)"] = ""; -App::$strings["Tell us about yourself"] = ""; -App::$strings["Homepage URL"] = ""; -App::$strings["Hometown"] = ""; -App::$strings["Political views"] = ""; -App::$strings["Religious views"] = ""; -App::$strings["Keywords used in directory listings"] = ""; -App::$strings["Example: fishing photography software"] = ""; -App::$strings["Musical interests"] = ""; -App::$strings["Books, literature"] = ""; -App::$strings["Television"] = ""; -App::$strings["Film/Dance/Culture/Entertainment"] = ""; -App::$strings["Hobbies/Interests"] = ""; -App::$strings["Love/Romance"] = ""; -App::$strings["School/Education"] = ""; -App::$strings["Contact information and social networks"] = ""; -App::$strings["My other channels"] = ""; -App::$strings["Communications"] = ""; -App::$strings[" and "] = ""; -App::$strings[", "] = ""; -App::$strings["public profile"] = ""; -App::$strings["%1\$s changed %2\$s to “%3\$s”"] = ""; -App::$strings["Visit %1\$s's %2\$s"] = ""; -App::$strings["%1\$s has an updated %2\$s, changing %3\$s."] = ""; -App::$strings["Currently Male"] = ""; -App::$strings["Currently Female"] = ""; -App::$strings["Mostly Male"] = ""; -App::$strings["Mostly Female"] = ""; -App::$strings["Transgender"] = ""; -App::$strings["Intersex"] = ""; -App::$strings["Transsexual"] = ""; -App::$strings["Hermaphrodite"] = ""; -App::$strings["Undecided"] = ""; -App::$strings["Males"] = ""; -App::$strings["Females"] = ""; -App::$strings["Gay"] = ""; -App::$strings["Lesbian"] = ""; -App::$strings["No Preference"] = ""; -App::$strings["Bisexual"] = ""; -App::$strings["Autosexual"] = ""; -App::$strings["Abstinent"] = ""; -App::$strings["Virgin"] = ""; -App::$strings["Deviant"] = ""; -App::$strings["Fetish"] = ""; -App::$strings["Oodles"] = ""; -App::$strings["Nonsexual"] = ""; -App::$strings["Single"] = ""; -App::$strings["Lonely"] = ""; -App::$strings["Available"] = ""; -App::$strings["Unavailable"] = ""; -App::$strings["Has crush"] = ""; -App::$strings["Infatuated"] = ""; -App::$strings["Dating"] = ""; -App::$strings["Unfaithful"] = ""; -App::$strings["Sex Addict"] = ""; -App::$strings["Friends/Benefits"] = ""; -App::$strings["Casual"] = ""; -App::$strings["Engaged"] = ""; -App::$strings["Married"] = ""; -App::$strings["Imaginarily married"] = ""; -App::$strings["Partners"] = ""; -App::$strings["Cohabiting"] = ""; -App::$strings["Common law"] = ""; -App::$strings["Happy"] = ""; -App::$strings["Not looking"] = ""; -App::$strings["Swinger"] = ""; -App::$strings["Betrayed"] = ""; -App::$strings["Separated"] = ""; -App::$strings["Unstable"] = ""; -App::$strings["Divorced"] = ""; -App::$strings["Imaginarily divorced"] = ""; -App::$strings["Widowed"] = ""; -App::$strings["Uncertain"] = ""; -App::$strings["It's complicated"] = ""; -App::$strings["Don't care"] = ""; -App::$strings["Ask me"] = ""; -App::$strings["Poke somebody in your addressbook"] = ""; -App::$strings["Poke somebody"] = ""; -App::$strings["Poke/Prod"] = ""; -App::$strings["Poke, prod or do other things to somebody"] = ""; -App::$strings["Recipient"] = ""; -App::$strings["Choose what you wish to do to recipient"] = ""; -App::$strings["Make this post private"] = ""; -App::$strings["Profile Photos"] = ""; -App::$strings["Shift-reload the page or clear browser cache if the new photo does not display immediately."] = ""; -App::$strings["Image upload failed."] = ""; -App::$strings["Unable to process image."] = ""; -App::$strings["Your default profile photo is visible to anybody on the internet. Profile photos for alternate profiles will inherit the permissions of the profile"] = ""; -App::$strings["Your profile photo is visible to anybody on the internet and may be distributed to other websites."] = ""; -App::$strings["Use Photo for Profile"] = ""; -App::$strings["Change Profile Photo"] = ""; -App::$strings["Use"] = ""; -App::$strings["Page owner information could not be retrieved."] = ""; -App::$strings["Album not found."] = ""; -App::$strings["Delete Album"] = ""; -App::$strings["Delete Photo"] = ""; -App::$strings["Public access denied."] = ""; -App::$strings["No photos selected"] = ""; -App::$strings["Access to this item is restricted."] = ""; -App::$strings["%1$.2f MB of %2$.2f MB photo storage used."] = ""; -App::$strings["%1$.2f MB photo storage used."] = ""; -App::$strings["Upload Photos"] = ""; -App::$strings["Enter an album name"] = ""; -App::$strings["or select an existing album (doubleclick)"] = ""; -App::$strings["Create a status post for this upload"] = ""; -App::$strings["Description (optional)"] = ""; -App::$strings["Date descending"] = ""; -App::$strings["Date ascending"] = ""; -App::$strings["Name ascending"] = ""; -App::$strings["Add Photos"] = ""; -App::$strings["Sort"] = ""; -App::$strings["Permission denied. Access to this item may be restricted."] = ""; -App::$strings["Photo not available"] = ""; -App::$strings["Use as profile photo"] = ""; -App::$strings["Use as cover photo"] = ""; -App::$strings["Private Photo"] = ""; -App::$strings["View Full Size"] = ""; -App::$strings["Edit photo"] = ""; -App::$strings["Move photo to album"] = ""; -App::$strings["Enter a new album name"] = ""; -App::$strings["or select an existing one (doubleclick)"] = ""; -App::$strings["Add a Tag"] = ""; -App::$strings["Example: @bob, @Barbara_Jensen, @jim@example.com"] = ""; -App::$strings["Flag as adult in album view"] = ""; -App::$strings["__ctx:title__ Likes"] = ""; -App::$strings["__ctx:title__ Dislikes"] = ""; -App::$strings["__ctx:title__ Attending"] = ""; -App::$strings["__ctx:title__ Not attending"] = ""; -App::$strings["__ctx:title__ Might attend"] = ""; -App::$strings["Photo Tools"] = ""; -App::$strings["In This Photo:"] = ""; -App::$strings["Map"] = ""; -App::$strings["Recent Photos"] = ""; -App::$strings["Invalid profile identifier."] = ""; -App::$strings["Profile Visibility Editor"] = ""; -App::$strings["Click on a contact to add or remove."] = ""; -App::$strings["Visible To"] = ""; -App::$strings["Channel removals are not allowed within 48 hours of changing the account password."] = ""; -App::$strings["Remove This Channel"] = ""; -App::$strings["This channel will be completely removed from this server. "] = ""; -App::$strings["This action is permanent and can not be undone!"] = ""; -App::$strings["Remove Channel"] = ""; -App::$strings["Friend Zoom settings updated."] = ""; -App::$strings["This app (when installed) presents a slider control in your connection editor and also on your stream page. The slider represents your degree of friendship with each connection. It allows you to zoom in or out and display conversations from only your closest friends or everybody in your stream."] = ""; -App::$strings["The number below represents the default maximum slider position for your stream page as a percentage."] = ""; -App::$strings["Default friend zoom in/out"] = ""; -App::$strings["Refresh"] = ""; -App::$strings["Friend Zoom Settings"] = ""; -App::$strings["Connection added."] = ""; -App::$strings["Welcome to %s"] = ""; -App::$strings["This site is not a directory server"] = ""; -App::$strings["Please login."] = ""; -App::$strings["Account removals are not allowed within 48 hours of changing the account password."] = ""; -App::$strings["Remove This Account"] = ""; -App::$strings["This account and all its channels will be completely removed from this server. "] = ""; -App::$strings["Remove Account"] = ""; -App::$strings["Authentication failed."] = ""; -App::$strings["Remote Authentication"] = ""; -App::$strings["Enter your channel address (e.g. channel@example.com)"] = ""; -App::$strings["Authenticate"] = ""; -App::$strings["ActivityPub Probe Diagnostic"] = ""; -App::$strings["Object URL"] = ""; -App::$strings["Authenticated fetch"] = ""; -App::$strings["This app provides a simple personal and task list."] = ""; -App::$strings["No service class restrictions found."] = ""; -App::$strings["Not valid email."] = ""; -App::$strings["Protected email address. Cannot change to that email."] = ""; -App::$strings["System failure storing new email. Please try again."] = ""; -App::$strings["Password verification failed."] = ""; -App::$strings["Passwords do not match. Password unchanged."] = ""; -App::$strings["Empty passwords are not allowed. Password unchanged."] = ""; -App::$strings["Password changed."] = ""; -App::$strings["Password update failed. Please try again."] = ""; -App::$strings["Account Settings"] = ""; -App::$strings["Current Password"] = ""; -App::$strings["Enter New Password"] = ""; -App::$strings["Confirm New Password"] = ""; -App::$strings["Leave password fields blank unless changing"] = ""; -App::$strings["Email Address:"] = ""; -App::$strings["Remove this account including all its channels"] = ""; -App::$strings["Affinity Slider settings updated."] = ""; -App::$strings["No feature settings configured"] = ""; -App::$strings["Default maximum affinity level"] = ""; -App::$strings["0-99 default 99"] = ""; -App::$strings["Default minimum affinity level"] = ""; -App::$strings["0-99 - default 0"] = ""; -App::$strings["Affinity Slider Settings"] = ""; -App::$strings["Addon Settings"] = ""; -App::$strings["Please save/submit changes to any panel before opening another."] = ""; -App::$strings["Activity Settings"] = ""; -App::$strings["Search by Date"] = ""; -App::$strings["Ability to select posts by date ranges"] = ""; -App::$strings["Saved Searches"] = ""; -App::$strings["Save search terms for re-use"] = ""; -App::$strings["Alternate Stream Order"] = ""; -App::$strings["Ability to order the stream by last post date, last comment date or unthreaded activities"] = ""; -App::$strings["Contact Filter"] = ""; -App::$strings["Ability to display only posts of a selected contact"] = ""; -App::$strings["Forum Filter"] = ""; -App::$strings["Ability to display only posts of a specific forum"] = ""; -App::$strings["Personal Posts Filter"] = ""; -App::$strings["Ability to display only posts that you've interacted on"] = ""; -App::$strings["Affinity Tool"] = ""; -App::$strings["Filter stream activity by depth of relationships"] = ""; -App::$strings["Show friend and connection suggestions"] = ""; -App::$strings["Connection Filtering"] = ""; -App::$strings["Filter incoming posts from connections based on keywords/content"] = ""; -App::$strings["Name is required"] = ""; -App::$strings["Key and Secret are required"] = ""; -App::$strings["Add application"] = ""; -App::$strings["Name of application"] = ""; -App::$strings["Consumer Key"] = ""; -App::$strings["Automatically generated - change if desired. Max length 20"] = ""; -App::$strings["Consumer Secret"] = ""; -App::$strings["Redirect"] = ""; -App::$strings["Redirect URI - leave blank unless your application specifically requires this"] = ""; -App::$strings["Icon url"] = ""; -App::$strings["Optional"] = ""; -App::$strings["Application not found."] = ""; -App::$strings["Connected Apps"] = ""; -App::$strings["Client key starts with"] = ""; -App::$strings["No name"] = ""; -App::$strings["Remove authorization"] = ""; -App::$strings["Permission Name is required."] = ""; -App::$strings["Permission category saved."] = ""; -App::$strings["Use this form to create permission rules for various classes of people or connections."] = ""; -App::$strings["Permission Name"] = ""; -App::$strings["This channel is limited to %d tokens"] = ""; -App::$strings["Name and Password are required."] = ""; -App::$strings["Token saved."] = ""; -App::$strings["Use this form to create temporary access identifiers to share things with non-members. These identities may be used in Access Control Lists and visitors may login using these credentials to access private content."] = ""; -App::$strings["You may also provide dropbox style access links to friends and associates by adding the Login Password to any specific site URL as shown. Examples:"] = ""; -App::$strings["Guest Access Tokens"] = ""; -App::$strings["Login Name"] = ""; -App::$strings["Login Password"] = ""; -App::$strings["Expires (yyyy-mm-dd)"] = ""; -App::$strings["Additional Features"] = ""; -App::$strings["ID and Secret are required"] = ""; -App::$strings["Add OAuth2 application"] = ""; -App::$strings["Consumer ID"] = ""; -App::$strings["Grant Types"] = ""; -App::$strings["leave blank unless your application specifically requires this"] = ""; -App::$strings["Authorization scope"] = ""; -App::$strings["OAuth2 Application not found."] = ""; -App::$strings["Connected OAuth2 Apps"] = ""; -App::$strings["%s - (Experimental)"] = ""; -App::$strings["Display Settings"] = ""; -App::$strings["Theme Settings"] = ""; -App::$strings["Custom Theme Settings"] = ""; -App::$strings["Content Settings"] = ""; -App::$strings["Display Theme:"] = ""; -App::$strings["Select scheme"] = ""; -App::$strings["Preload images before rendering the page"] = ""; -App::$strings["The subjective page load time will be longer but the page will be ready when displayed"] = ""; -App::$strings["Enable user zoom on mobile devices"] = ""; -App::$strings["Update browser every xx seconds"] = ""; -App::$strings["Minimum of 10 seconds, no maximum"] = ""; -App::$strings["Maximum number of conversations to load at any time:"] = ""; -App::$strings["Maximum of 100 items"] = ""; -App::$strings["Show emoticons (smilies) as images"] = ""; -App::$strings["Provide channel menu in navigation bar"] = ""; -App::$strings["Default: channel menu located in app menu"] = ""; -App::$strings["System Page Layout Editor - (advanced)"] = ""; -App::$strings["Channel page max height of content (in pixels)"] = ""; -App::$strings["click to expand content exceeding this height"] = ""; -App::$strings["Stream page max height of content (in pixels)"] = ""; -App::$strings["Nobody except yourself"] = ""; -App::$strings["Only those you specifically allow"] = ""; -App::$strings["Approved connections"] = ""; -App::$strings["Any connections"] = ""; -App::$strings["Anybody on this website"] = ""; -App::$strings["Anybody in this network"] = ""; -App::$strings["Anybody authenticated"] = ""; -App::$strings["Anybody on the internet"] = ""; -App::$strings["Publish your profile in the network directory"] = ""; -App::$strings["Allow us to suggest you as a potential friend to new members?"] = ""; -App::$strings["or"] = ""; -App::$strings["Your channel address is"] = ""; -App::$strings["Friends using compatible applications can use this address to connect with you."] = ""; -App::$strings["Your files/photos are accessible as a network drive at"] = ""; -App::$strings["(Windows)"] = ""; -App::$strings["(other platforms)"] = ""; -App::$strings["Automatic membership approval"] = ""; -App::$strings["Friend-of-friend conversations"] = ""; -App::$strings["Import public third-party conversations in which your connections participate."] = ""; -App::$strings["Enable ActivityPub protocol"] = ""; -App::$strings["ActivityPub"] = ""; -App::$strings["ActivityPub is an emerging internet standard for social communications. "] = ""; -App::$strings["It provides access to a large and growing number of existing users and supported software applications, however it is still evolving. If this is enabled you will obtain much greater social reach, however it is likely you will also encounter compatibility issues. "] = ""; -App::$strings["Channel Settings"] = ""; -App::$strings["Basic Settings"] = ""; -App::$strings["Full name"] = ""; -App::$strings["Your timezone"] = ""; -App::$strings["This is important for showing the correct time on shared events"] = ""; -App::$strings["Default post location"] = ""; -App::$strings["Optional geographical location to display on your posts"] = ""; -App::$strings["Obtain post location from your web browser or device"] = ""; -App::$strings["Adult content"] = ""; -App::$strings["Enable to indicate if this channel frequently or regularly publishes adult content. (Please also tag any adult material and/or nudity with #NSFW)"] = ""; -App::$strings["Security and Privacy"] = ""; -App::$strings["Your permissions are already configured. Click to view/adjust"] = ""; -App::$strings["Hide my online presence"] = ""; -App::$strings["Prevents displaying in your profile that you are online"] = ""; -App::$strings["Allow others to view your friends and connections"] = ""; -App::$strings["Allow others to tag your posts"] = ""; -App::$strings["Often used by the community to retro-actively flag inappropriate content"] = ""; -App::$strings["Channel Permission Limits"] = ""; -App::$strings["Expire other channel content after this many days"] = ""; -App::$strings["0 or blank to use the website limit."] = ""; -App::$strings["This website expires after %d days."] = ""; -App::$strings["This website does not expire imported content."] = ""; -App::$strings["The website limit takes precedence if lower than your limit."] = ""; -App::$strings["Maximum Friend Requests/Day:"] = ""; -App::$strings["May reduce spam activity"] = ""; -App::$strings["Default Access List"] = ""; -App::$strings["Use my default audience setting for the type of object published"] = ""; -App::$strings["Profile to assign new connections"] = ""; -App::$strings["Default Permissions Group"] = ""; -App::$strings["Maximum private messages per day from unknown people:"] = ""; -App::$strings["Useful to reduce spamming"] = ""; -App::$strings["By default post a status message when:"] = ""; -App::$strings["accepting a friend request"] = ""; -App::$strings["joining a forum/community"] = ""; -App::$strings["making an interesting profile change"] = ""; -App::$strings["Send a notification email when:"] = ""; -App::$strings["You receive a connection request"] = ""; -App::$strings["Someone writes on your profile wall"] = ""; -App::$strings["Someone writes a followup comment"] = ""; -App::$strings["You are tagged in a post"] = ""; -App::$strings["Someone likes your post/comment"] = ""; -App::$strings["Show visual notifications including:"] = ""; -App::$strings["Unseen stream activity"] = ""; -App::$strings["Unseen channel activity"] = ""; -App::$strings["Upcoming events"] = ""; -App::$strings["Events today"] = ""; -App::$strings["Upcoming birthdays"] = ""; -App::$strings["Not available in all themes"] = ""; -App::$strings["System (personal) notifications"] = ""; -App::$strings["System info messages"] = ""; -App::$strings["Recommended"] = ""; -App::$strings["System critical alerts"] = ""; -App::$strings["New connections"] = ""; -App::$strings["System Registrations"] = ""; -App::$strings["Unseen public activity"] = ""; -App::$strings["Unseen likes and dislikes"] = ""; -App::$strings["Unseen forum posts"] = ""; -App::$strings["Reported content"] = ""; -App::$strings["Email notification hub (hostname)"] = ""; -App::$strings["If your channel is mirrored to multiple locations, set this to your preferred location. This will prevent duplicate email notifications. Example: %s"] = ""; -App::$strings["Show new wall posts, private messages and connections under Notices"] = ""; -App::$strings["Accept messages from strangers which mention me"] = ""; -App::$strings["This setting supercedes normal permissions"] = ""; -App::$strings["Accept messages from strangers which include any of the following hashtags"] = ""; -App::$strings["comma separated, do not include the #"] = ""; -App::$strings["Notify me of events this many days in advance"] = ""; -App::$strings["Must be greater than 0"] = ""; -App::$strings["Date and time"] = ""; -App::$strings["This section is reserved for use by optional addons and apps to provide additional settings."] = ""; -App::$strings["Advanced Account/Page Type Settings"] = ""; -App::$strings["Change the behaviour of this account for special situations"] = ""; -App::$strings["Default photo upload folder name"] = ""; -App::$strings["%Y - current year, %m - current month"] = ""; -App::$strings["Default file upload folder name"] = ""; -App::$strings["Personal menu to display in your channel pages"] = ""; -App::$strings["Remove this channel."] = ""; -App::$strings["Mentions should display"] = ""; -App::$strings["Changes to this setting are applied to new posts/comments only. It is not retroactive."] = ""; -App::$strings["the channel display name [example: @Barbara Jenkins]"] = ""; -App::$strings["the channel nickname [example: @barbara1976]"] = ""; -App::$strings["combined [example: @Barbara Jenkins (barbara1976)]"] = ""; -App::$strings["no preference, use the system default"] = ""; -App::$strings["Calendar week begins on"] = ""; -App::$strings["This varies by country/culture"] = ""; -App::$strings["Sunday"] = ""; -App::$strings["Monday"] = ""; -App::$strings["Tuesday"] = ""; -App::$strings["Wednesday"] = ""; -App::$strings["Thursday"] = ""; -App::$strings["Friday"] = ""; -App::$strings["Saturday"] = ""; -App::$strings["Items tagged with: %s"] = ""; -App::$strings["Search results for: %s"] = ""; -App::$strings["%1\$s is following %2\$s's %3\$s"] = ""; -App::$strings["%1\$s stopped following %2\$s's %3\$s"] = ""; -App::$strings["Files: shared with me"] = ""; -App::$strings["NEW"] = ""; -App::$strings["Size"] = ""; -App::$strings["Last Modified"] = ""; -App::$strings["Remove all files"] = ""; -App::$strings["Remove this file"] = ""; -App::$strings["About this site"] = ""; -App::$strings["Site Name"] = ""; -App::$strings["Administrator"] = ""; -App::$strings["Software and Project information"] = ""; -App::$strings["This site is powered by \$Projectname"] = ""; -App::$strings["Federated and decentralised networking and identity services provided by Zot"] = ""; -App::$strings["Federated transport protocols:"] = ""; -App::$strings["Version %s"] = ""; -App::$strings["Project homepage"] = ""; -App::$strings["Developer homepage"] = ""; -App::$strings["Export Channel"] = ""; -App::$strings["Export your basic channel information to a file. This acts as a backup of your connections, permissions, profile and basic data, which can be used to import your data to a new server hub, but does not contain your content."] = ""; -App::$strings["Export Content"] = ""; -App::$strings["Export your channel information and recent content to a JSON backup that can be restored or imported to another server hub. This backs up all of your connections, permissions, profile data and several months of posts. This file may be VERY large. Please be patient - it may take several minutes for this download to begin."] = ""; -App::$strings["Export your posts from a given year."] = ""; -App::$strings["You may also export your posts and conversations for a particular year or month. Adjust the date in your browser location bar to select other dates. If the export fails (possibly due to memory exhaustion on your server hub), please try again selecting a more limited date range."] = ""; -App::$strings["To select all posts for a given year, such as this year, visit %2\$s"] = ""; -App::$strings["To select all posts for a given month, such as January of this year, visit %2\$s"] = ""; -App::$strings["These content files may be imported or restored by visiting %2\$s on any site containing your channel. For best results please import or restore these in date order (oldest first)."] = ""; -App::$strings["Failed to create source. No channel selected."] = ""; -App::$strings["Source created."] = ""; -App::$strings["Source updated."] = ""; -App::$strings["*"] = ""; -App::$strings["Manage remote sources of content for your channel."] = ""; -App::$strings["New Source"] = ""; -App::$strings["Import all or selected content from the following channel into this channel and distribute it according to your channel settings."] = ""; -App::$strings["Only import content with these words (one per line)"] = ""; -App::$strings["Leave blank to import all public content"] = ""; -App::$strings["Channel Name"] = ""; -App::$strings["Add the following categories to posts imported from this source (comma separated)"] = ""; -App::$strings["Resend posts with this channel as author"] = ""; -App::$strings["Copyrights may apply"] = ""; -App::$strings["Source not found."] = ""; -App::$strings["Edit Source"] = ""; -App::$strings["Delete Source"] = ""; -App::$strings["Source removed"] = ""; -App::$strings["Unable to remove source."] = ""; -App::$strings["Article"] = ""; -App::$strings["Item has been removed."] = ""; -App::$strings["Post not found."] = ""; -App::$strings["comment"] = ""; -App::$strings["%1\$s tagged %2\$s's %3\$s with %4\$s"] = ""; -App::$strings["Tag removed"] = ""; -App::$strings["Remove Item Tag"] = ""; -App::$strings["Select a tag to remove: "] = ""; -App::$strings["No default suggestions were found."] = ""; -App::$strings["%d rating"] = [ - 0 => "", - 1 => "", +App::$strings['Send email invitations to join this network'] = ''; +App::$strings['You have no more invitations available'] = ''; +App::$strings['Send invitations'] = ''; +App::$strings['Enter email addresses, one per line:'] = ''; +App::$strings['Your message:'] = ''; +App::$strings["Please join my community on \$Projectname."] = ''; +App::$strings['You will need to supply this invitation code:'] = ''; +App::$strings["1. Register at any \$Projectname location (they are all inter-connected)"] = ''; +App::$strings["2. Enter my \$Projectname network address into the site searchbar."] = ''; +App::$strings['or visit'] = ''; +App::$strings['3. Click [Connect]'] = ''; +App::$strings['This app allows you to disable comments on individual posts.'] = ''; +App::$strings['This app is installed. A button to control the ability to comment may be found below the post editor.'] = ''; +App::$strings['Enter a folder name'] = ''; +App::$strings['or select an existing folder (doubleclick)'] = ''; +App::$strings['File not found.'] = ''; +App::$strings['Permission Denied.'] = ''; +App::$strings['Edit file permissions'] = ''; +App::$strings['Set/edit permissions'] = ''; +App::$strings['Include all files and sub folders'] = ''; +App::$strings['Return to file list'] = ''; +App::$strings['Copy/paste this code to attach file to a post'] = ''; +App::$strings['Copy/paste this URL to link file from a web page'] = ''; +App::$strings['Share this file'] = ''; +App::$strings['Show URL to this file'] = ''; +App::$strings['Show in your contacts shared folder'] = ''; +App::$strings['Entry censored'] = ''; +App::$strings['Entry uncensored'] = ''; +App::$strings['webpage'] = ''; +App::$strings['block'] = ''; +App::$strings['layout'] = ''; +App::$strings['menu'] = ''; +App::$strings['%s element installed'] = ''; +App::$strings['%s element installation failed'] = ''; +App::$strings['Thing updated'] = ''; +App::$strings['Object store: failed'] = ''; +App::$strings['Thing added'] = ''; +App::$strings["OBJ: %1\$s %2\$s %3\$s"] = ''; +App::$strings['Show Thing'] = ''; +App::$strings['item not found.'] = ''; +App::$strings['Edit Thing'] = ''; +App::$strings['Select a profile'] = ''; +App::$strings['Post an activity'] = ''; +App::$strings['Only sends to viewers of the applicable profile'] = ''; +App::$strings['Name of thing e.g. something'] = ''; +App::$strings['URL of thing (optional)'] = ''; +App::$strings['URL for photo of thing (optional)'] = ''; +App::$strings['Add Thing to your Profile'] = ''; +App::$strings['Room not found'] = ''; +App::$strings['Leave Room'] = ''; +App::$strings['Delete Room'] = ''; +App::$strings['I am away right now'] = ''; +App::$strings['I am online'] = ''; +App::$strings['Please enter a link URL:'] = ''; +App::$strings['New Chatroom'] = ''; +App::$strings['Chatroom name'] = ''; +App::$strings['Expiration of chats (minutes)'] = ''; +App::$strings["%1\$s's Chatrooms"] = ''; +App::$strings['No chatrooms available'] = ''; +App::$strings['Create New'] = ''; +App::$strings['Expiration'] = ''; +App::$strings['min'] = ''; +App::$strings['No entries.'] = ''; +App::$strings['Comment approved'] = ''; +App::$strings['Comment deleted'] = ''; +App::$strings['Welcome to Hubzilla!'] = ''; +App::$strings['You have got no unseen posts...'] = ''; +App::$strings['Access list created.'] = ''; +App::$strings['Could not create access list.'] = ''; +App::$strings['Access list not found.'] = ''; +App::$strings['Access list updated.'] = ''; +App::$strings['List members'] = ''; +App::$strings['List not found'] = ''; +App::$strings['Access Lists'] = ''; +App::$strings['Create access list'] = ''; +App::$strings['Access list name'] = ''; +App::$strings['Members are visible to other channels'] = ''; +App::$strings['Members'] = ''; +App::$strings['Access list removed.'] = ''; +App::$strings['Unable to remove access list.'] = ''; +App::$strings['Access List: %s'] = ''; +App::$strings['Access list name: '] = ''; +App::$strings['Delete access list'] = ''; +App::$strings['Not in this list'] = ''; +App::$strings['Select a channel to toggle membership'] = ''; +App::$strings['Layouts'] = ''; +App::$strings['Layout Description'] = ''; +App::$strings['Download PDL file'] = ''; +App::$strings['No such group'] = ''; +App::$strings['No such channel'] = ''; +App::$strings['Access list is empty'] = ''; +App::$strings['Access list: '] = ''; +App::$strings['Invalid channel.'] = ''; +App::$strings['Title (optional)'] = ''; +App::$strings['Edit Card'] = ''; +App::$strings["Warning: Database versions differ by %1\$d updates."] = ''; +App::$strings['Import completed'] = ''; +App::$strings['Import Items'] = ''; +App::$strings['Use this form to import existing posts and content from an export file.'] = ''; +App::$strings['Not found'] = ''; +App::$strings['Please refresh page'] = ''; +App::$strings['Unknown error'] = ''; +App::$strings['Group'] = ''; +App::$strings['You have created %1$.0f of %2$.0f allowed channels.'] = ''; +App::$strings['Create a new channel'] = ''; +App::$strings['Current Channel'] = ''; +App::$strings['Switch to one of your channels by selecting it.'] = ''; +App::$strings['Default Login Channel'] = ''; +App::$strings['Make Default'] = ''; +App::$strings['Add to menu'] = ''; +App::$strings['%d new messages'] = ''; +App::$strings['%d new introductions'] = ''; +App::$strings['Delegated Channel'] = ''; +App::$strings['Change UI language'] = ''; +App::$strings['Menu not found.'] = ''; +App::$strings['Unable to create element.'] = ''; +App::$strings['Unable to update menu element.'] = ''; +App::$strings['Unable to add menu element.'] = ''; +App::$strings['Not found.'] = ''; +App::$strings['Menu Item Permissions'] = ''; +App::$strings['(click to open/close)'] = ''; +App::$strings['Link Name'] = ''; +App::$strings['Link or Submenu Target'] = ''; +App::$strings['Enter URL of the link or select a menu name to create a submenu'] = ''; +App::$strings['Use magic-auth if available'] = ''; +App::$strings['Open link in new window'] = ''; +App::$strings['Order in list'] = ''; +App::$strings['Higher numbers will sink to bottom of listing'] = ''; +App::$strings['Submit and finish'] = ''; +App::$strings['Submit and continue'] = ''; +App::$strings['Menu:'] = ''; +App::$strings['Link Target'] = ''; +App::$strings['Edit menu'] = ''; +App::$strings['Edit element'] = ''; +App::$strings['Drop element'] = ''; +App::$strings['New element'] = ''; +App::$strings['Edit this menu container'] = ''; +App::$strings['Add menu element'] = ''; +App::$strings['Delete this menu item'] = ''; +App::$strings['Edit this menu item'] = ''; +App::$strings['Menu item not found.'] = ''; +App::$strings['Menu item deleted.'] = ''; +App::$strings['Menu item could not be deleted.'] = ''; +App::$strings['Edit Menu Element'] = ''; +App::$strings['Link text'] = ''; +App::$strings['Webfinger Diagnostic'] = ''; +App::$strings['Lookup address or URL'] = ''; +App::$strings['Edit Block'] = ''; +App::$strings['Invalid message'] = ''; +App::$strings['no results'] = ''; +App::$strings['channel sync processed'] = ''; +App::$strings['queued'] = ''; +App::$strings['posted'] = ''; +App::$strings['accepted for delivery'] = ''; +App::$strings['updated'] = ''; +App::$strings['update ignored'] = ''; +App::$strings['permission denied'] = ''; +App::$strings['recipient not found'] = ''; +App::$strings['mail recalled'] = ''; +App::$strings['duplicate mail received'] = ''; +App::$strings['mail delivered'] = ''; +App::$strings["Delivery report for %1\$s"] = ''; +App::$strings['Options'] = ''; +App::$strings['Redeliver'] = ''; +App::$strings['Post repeated'] = ''; +App::$strings['No valid account found.'] = ''; +App::$strings['Password reset request issued. Check your email.'] = ''; +App::$strings['Site Member (%s)'] = ''; +App::$strings['Password reset requested at %s'] = ''; +App::$strings['Request could not be verified. (You may have previously submitted it.) Password reset failed.'] = ''; +App::$strings['Password Reset'] = ''; +App::$strings['Your password has been reset as requested.'] = ''; +App::$strings['Your new password is'] = ''; +App::$strings['Save or copy your new password - and then'] = ''; +App::$strings['click here to login'] = ''; +App::$strings['Your password may be changed from the Settings page after successful login.'] = ''; +App::$strings['Your password has changed at %s'] = ''; +App::$strings['Forgot your Password?'] = ''; +App::$strings['Enter your email address and submit to have your password reset. Then check your email for further instructions.'] = ''; +App::$strings['Email Address'] = ''; +App::$strings['Unable to update menu.'] = ''; +App::$strings['Unable to create menu.'] = ''; +App::$strings['Menu Name'] = ''; +App::$strings['Unique name (not visible on webpage) - required'] = ''; +App::$strings['Menu Title'] = ''; +App::$strings['Visible on webpage - leave empty for no title'] = ''; +App::$strings['Allow Bookmarks'] = ''; +App::$strings['Menu may be used to store saved bookmarks'] = ''; +App::$strings['Submit and proceed'] = ''; +App::$strings['Menus'] = ''; +App::$strings['Drop'] = ''; +App::$strings['Bookmarks allowed'] = ''; +App::$strings['Delete this menu'] = ''; +App::$strings['Edit menu contents'] = ''; +App::$strings['Edit this menu'] = ''; +App::$strings['Menu could not be deleted.'] = ''; +App::$strings['Edit Menu'] = ''; +App::$strings['Add or remove entries to this menu'] = ''; +App::$strings['Menu name'] = ''; +App::$strings['Must be unique, only seen by you'] = ''; +App::$strings['Menu title'] = ''; +App::$strings['Menu title as seen by others'] = ''; +App::$strings['Allow bookmarks'] = ''; +App::$strings['This setting requires special processing and editing has been blocked.'] = ''; +App::$strings['Configuration Editor'] = ''; +App::$strings['Warning: Changing some settings could render your channel inoperable. Please leave this page unless you are comfortable with and knowledgeable about how to correctly use this feature.'] = ''; +App::$strings['Maximum daily site registrations exceeded. Please try again tomorrow.'] = ''; +App::$strings['Please indicate acceptance of the Terms of Service. Registration failed.'] = ''; +App::$strings['Passwords do not match.'] = ''; +App::$strings['Registration successful. Continue to create your first channel...'] = ''; +App::$strings['Registration successful. Please check your email for validation instructions.'] = ''; +App::$strings['Your registration is pending approval by the site owner.'] = ''; +App::$strings['Your registration can not be processed.'] = ''; +App::$strings['Registration on this hub is disabled.'] = ''; +App::$strings['Registration on this hub is by approval only.'] = ''; +App::$strings["Register at another affiliated hub."] = ''; +App::$strings['Registration on this hub is by invitation only.'] = ''; +App::$strings['This site has exceeded the number of allowed daily account registrations. Please try again tomorrow.'] = ''; +App::$strings['Terms of Service'] = ''; +App::$strings['I accept the %s for this website'] = ''; +App::$strings['I am over %s years of age and accept the %s for this website'] = ''; +App::$strings['Your email address'] = ''; +App::$strings['Choose a password'] = ''; +App::$strings['Please re-enter your password'] = ''; +App::$strings['Please enter your invitation code'] = ''; +App::$strings['Your Name'] = ''; +App::$strings['Real names are preferred.'] = ''; +App::$strings['Choose a short nickname'] = ''; +App::$strings['Your nickname will be used to create an easy to remember channel address e.g. nickname%s'] = ''; +App::$strings['Channel role and privacy'] = ''; +App::$strings['Select a channel permission role for your usage needs and privacy requirements.'] = ''; +App::$strings['no'] = ''; +App::$strings['yes'] = ''; +App::$strings['Register'] = ''; +App::$strings['This site requires email verification. After completing this form, please check your email for further instructions.'] = ''; +App::$strings['Poll not found.'] = ''; +App::$strings['Invalid response.'] = ''; +App::$strings['Response submitted. Updates may not appear instantly.'] = ''; +App::$strings['Location not found.'] = ''; +App::$strings['Location lookup failed.'] = ''; +App::$strings['Please select another location to become primary before removing the primary location.'] = ''; +App::$strings['Pushing location info'] = ''; +App::$strings['No locations found.'] = ''; +App::$strings['Manage Channel Locations'] = ''; +App::$strings['Publish these settings'] = ''; +App::$strings['Please wait several minutes between consecutive operations.'] = ''; +App::$strings['When possible, drop a location by logging into that website/hub and removing your channel.'] = ''; +App::$strings['Use this form to drop the location if the hub is no longer operating.'] = ''; +App::$strings['item'] = ''; +App::$strings['inspect'] = ''; +App::$strings['Your real name is recommended.'] = ''; +App::$strings["Examples: \"Bob Jameson\", \"Lisa and her Horses\", \"Soccer\", \"Aviation Group\""] = ''; +App::$strings['This will be used to create a unique network address (like an email address).'] = ''; +App::$strings['Allowed characters are a-z 0-9, - and _'] = ''; +App::$strings['Channel name'] = ''; +App::$strings['Select a channel permission role compatible with your usage needs and privacy requirements.'] = ''; +App::$strings['Create a Channel'] = ''; +App::$strings['A channel is a unique network identity. It can represent a person (social network profile), a forum (group), a business or celebrity page, a newsfeed, and many other things.'] = ''; +App::$strings["or import an existing channel from another location."] = ''; +App::$strings['Validate'] = ''; +App::$strings['Continue'] = ''; +App::$strings['Premium Channel Setup'] = ''; +App::$strings['Enable premium channel connection restrictions'] = ''; +App::$strings['Please enter your restrictions or conditions, such as paypal receipt, usage guidelines, etc.'] = ''; +App::$strings['This channel may require additional steps or acknowledgement of the following conditions prior to connecting:'] = ''; +App::$strings['Potential connections will then see the following text before proceeding:'] = ''; +App::$strings['By continuing, I certify that I have complied with any instructions provided on this page.'] = ''; +App::$strings['(No specific instructions have been provided by the channel owner.)'] = ''; +App::$strings['Restricted or Premium Channel'] = ''; +App::$strings['No more system notifications.'] = ''; +App::$strings['System Notifications'] = ''; +App::$strings['Unable to locate original post.'] = ''; +App::$strings['Comment may be moderated.'] = ''; +App::$strings['Empty post discarded.'] = ''; +App::$strings['Duplicate post suppressed.'] = ''; +App::$strings['System error. Post not saved.'] = ''; +App::$strings['Your post/comment is awaiting approval.'] = ''; +App::$strings['Unable to obtain post information from database.'] = ''; +App::$strings['You have reached your limit of %1$.0f top level posts.'] = ''; +App::$strings['You have reached your limit of %1$.0f webpages.'] = ''; +App::$strings["__ctx:mood__ %1\$s is %2\$s"] = ''; +App::$strings['Set your current mood and tell your friends'] = ''; +App::$strings['Unable to find your hub.'] = ''; +App::$strings['Post successful.'] = ''; +App::$strings['Public Hubs'] = ''; +App::$strings["The listed hubs allow public registration for the \$Projectname network. All hubs in the network are interlinked so membership on any of them conveys membership in the network as a whole. Some hubs may require subscription or provide tiered service plans. The hub itself may provide additional details."] = ''; +App::$strings['Hub URL'] = ''; +App::$strings['Access Type'] = ''; +App::$strings['Registration Policy'] = ''; +App::$strings['Software'] = ''; +App::$strings['Provide managed web pages on your channel'] = ''; +App::$strings['Import Webpage Elements'] = ''; +App::$strings['Import selected'] = ''; +App::$strings['Export Webpage Elements'] = ''; +App::$strings['Export selected'] = ''; +App::$strings['Actions'] = ''; +App::$strings['Page Link'] = ''; +App::$strings['Page Title'] = ''; +App::$strings['Invalid file type.'] = ''; +App::$strings['Error opening zip file'] = ''; +App::$strings['Invalid folder path.'] = ''; +App::$strings['No webpage elements detected.'] = ''; +App::$strings['Import complete.'] = ''; +App::$strings['Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.'] = ''; +App::$strings['vcard'] = ''; +App::$strings['Layout updated.'] = ''; +App::$strings['Edit System Page Description'] = ''; +App::$strings['(modified)'] = ''; +App::$strings['Layout not found.'] = ''; +App::$strings['Module Name:'] = ''; +App::$strings['Layout Help'] = ''; +App::$strings['Edit another layout'] = ''; +App::$strings['System layout'] = ''; +App::$strings['Zap Server - Setup'] = ''; +App::$strings['Could not connect to database.'] = ''; +App::$strings['Could not connect to specified site URL. Possible SSL certificate or DNS issue.'] = ''; +App::$strings['Could not create table.'] = ''; +App::$strings['Your site database has been installed.'] = ''; +App::$strings["You may need to import the file \"install/schema_xxx.sql\" manually using a database client."] = ''; +App::$strings["Please see the file \"install/INSTALL.txt\"."] = ''; +App::$strings['System check'] = ''; +App::$strings['Check again'] = ''; +App::$strings['Database connection'] = ''; +App::$strings['In order to install this software we need to know how to connect to your database.'] = ''; +App::$strings['Please contact your hosting provider or site administrator if you have questions about these settings.'] = ''; +App::$strings['The database you specify below should already exist. If it does not, please create it before continuing.'] = ''; +App::$strings['Database Server Name'] = ''; +App::$strings['Default is 127.0.0.1'] = ''; +App::$strings['Database Port'] = ''; +App::$strings['Communication port number - use 0 for default'] = ''; +App::$strings['Database Login Name'] = ''; +App::$strings['Database Login Password'] = ''; +App::$strings['Database Name'] = ''; +App::$strings['Database Type'] = ''; +App::$strings['Site administrator email address'] = ''; +App::$strings['Your account email address must match this in order to use the web admin panel.'] = ''; +App::$strings['Website URL'] = ''; +App::$strings['Please use SSL (https) URL if available.'] = ''; +App::$strings['Please select a default timezone for your website'] = ''; +App::$strings['Site settings'] = ''; +App::$strings['PHP version 7.1 or greater is required.'] = ''; +App::$strings['PHP version'] = ''; +App::$strings['Could not find a command line version of PHP in the web server PATH.'] = ''; +App::$strings['If you do not have a command line version of PHP installed on server, you will not be able to run background tasks - including message delivery.'] = ''; +App::$strings['PHP executable path'] = ''; +App::$strings['Enter full path to php executable. You can leave this blank to continue the installation.'] = ''; +App::$strings['Command line PHP'] = ''; +App::$strings['Unable to check command line PHP, as shell_exec() is disabled. This is required.'] = ''; +App::$strings["The command line version of PHP on your system does not have \"register_argc_argv\" enabled."] = ''; +App::$strings['This is required for message delivery to work.'] = ''; +App::$strings['PHP register_argc_argv'] = ''; +App::$strings['This is not sufficient to upload larger images or files. You should be able to upload at least 2MB (2097152 bytes) at once.'] = ''; +App::$strings['Your max allowed total upload size is set to %s. Maximum size of one file to upload is set to %s. You are allowed to upload up to %d files at once.'] = ''; +App::$strings['You can adjust these settings in the server php.ini file.'] = ''; +App::$strings['PHP upload limits'] = ''; +App::$strings["Error: the \"openssl_pkey_new\" function on this system is not able to generate encryption keys"] = ''; +App::$strings["If running under Windows, please see \"http://www.php.net/manual/en/openssl.installation.php\"."] = ''; +App::$strings['Generate encryption keys'] = ''; +App::$strings['libCurl PHP module'] = ''; +App::$strings['GD graphics PHP module'] = ''; +App::$strings['OpenSSL PHP module'] = ''; +App::$strings['PDO database PHP module'] = ''; +App::$strings['mb_string PHP module'] = ''; +App::$strings['xml PHP module'] = ''; +App::$strings['zip PHP module'] = ''; +App::$strings['Apache mod_rewrite module'] = ''; +App::$strings['Error: Apache webserver mod-rewrite module is required but not installed.'] = ''; +App::$strings['exec'] = ''; +App::$strings['Error: exec is required but is either not installed or has been disabled in php.ini'] = ''; +App::$strings['shell_exec'] = ''; +App::$strings['Error: shell_exec is required but is either not installed or has been disabled in php.ini'] = ''; +App::$strings['Error: libCURL PHP module required but not installed.'] = ''; +App::$strings['Error: GD PHP module with JPEG support or ImageMagick graphics library required but not installed.'] = ''; +App::$strings['Error: openssl PHP module required but not installed.'] = ''; +App::$strings['Error: PDO database PHP module missing a driver for either mysql or pgsql.'] = ''; +App::$strings['Error: PDO database PHP module required but not installed.'] = ''; +App::$strings['Error: mb_string PHP module required but not installed.'] = ''; +App::$strings['Error: xml PHP module required for DAV but not installed.'] = ''; +App::$strings['Error: zip PHP module required but not installed.'] = ''; +App::$strings['.htconfig.php is writable'] = ''; +App::$strings["The web installer needs to be able to create a file called \".htconfig.php\" in the top folder of your web server and it is unable to do so."] = ''; +App::$strings['This is most often a permission setting, as the web server may not be able to write files in your folder - even if you can.'] = ''; +App::$strings['Please see install/INSTALL.txt for additional information.'] = ''; +App::$strings['This software uses the Smarty3 template engine to render its web views. Smarty3 compiles templates to PHP to speed up rendering.'] = ''; +App::$strings['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.'] = ''; +App::$strings['Please ensure that the user that your web server runs as (e.g. www-data) has write access to this folder.'] = ''; +App::$strings['Note: as a security measure, you should give the web server write access to %s only--not the template files (.tpl) that it contains.'] = ''; +App::$strings['%s is writable'] = ''; +App::$strings['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'] = ''; +App::$strings['store is writable'] = ''; +App::$strings['SSL certificate cannot be validated. Fix certificate or disable https access to this site.'] = ''; +App::$strings['If you have https access to your website or allow connections to TCP port 443 (the https: port), you MUST use a browser-valid certificate. You MUST NOT use self-signed certificates!'] = ''; +App::$strings['This restriction is incorporated because public posts from you may for example contain references to images on your own hub.'] = ''; +App::$strings['If your certificate is not recognized, members of other sites (who may themselves have valid certificates) will get a warning message on their own site complaining about security issues.'] = ''; +App::$strings['This can cause usability issues elsewhere (not just on your own site) so we must insist on this requirement.'] = ''; +App::$strings['Providers are available that issue free certificates which are browser-valid.'] = ''; +App::$strings['If you are confident that the certificate is valid and signed by a trusted authority, check to see if you have failed to install an intermediate cert. These are not normally required by browsers, but are required for server-to-server communications.'] = ''; +App::$strings['SSL certificate validation'] = ''; +App::$strings['Url rewrite in .htaccess is not working. Check your server configuration.Test: '] = ''; +App::$strings['Url rewrite is working'] = ''; +App::$strings["The database configuration file \".htconfig.php\" could not be written. Please use the enclosed text to create a configuration file in your web server root."] = ''; +App::$strings['Errors encountered creating database tables.'] = ''; +App::$strings['

                    What next?

                    '] = ''; +App::$strings['IMPORTANT: You will need to [manually] setup a scheduled task for the poller.'] = ''; +App::$strings['Profile not found.'] = ''; +App::$strings['Profile deleted.'] = ''; +App::$strings['Profile-'] = ''; +App::$strings['New profile created.'] = ''; +App::$strings['Profile unavailable to clone.'] = ''; +App::$strings['Profile unavailable to export.'] = ''; +App::$strings['Profile Name is required.'] = ''; +App::$strings['Marital Status'] = ''; +App::$strings['Romantic Partner'] = ''; +App::$strings['Likes'] = ''; +App::$strings['Dislikes'] = ''; +App::$strings['Work/Employment'] = ''; +App::$strings['Religion'] = ''; +App::$strings['Political Views'] = ''; +App::$strings['Gender'] = ''; +App::$strings['Sexual Preference'] = ''; +App::$strings['Homepage'] = ''; +App::$strings['Interests'] = ''; +App::$strings['Profile updated.'] = ''; +App::$strings['Hide your connections list from viewers of this profile'] = ''; +App::$strings['Edit Profile Details'] = ''; +App::$strings['View this profile'] = ''; +App::$strings['Profile Tools'] = ''; +App::$strings['Change cover photo'] = ''; +App::$strings['Create a new profile using these settings'] = ''; +App::$strings['Clone this profile'] = ''; +App::$strings['Delete this profile'] = ''; +App::$strings['Add profile things'] = ''; +App::$strings['Personal'] = ''; +App::$strings['Relationship'] = ''; +App::$strings['Miscellaneous'] = ''; +App::$strings['Import profile from file'] = ''; +App::$strings['Export profile to file'] = ''; +App::$strings['Your gender'] = ''; +App::$strings['Marital status'] = ''; +App::$strings['Sexual preference'] = ''; +App::$strings['Profile name'] = ''; +App::$strings['Your full name'] = ''; +App::$strings['Title/Description'] = ''; +App::$strings['Street address'] = ''; +App::$strings['Locality/City'] = ''; +App::$strings['Region/State'] = ''; +App::$strings['Postal/Zip code'] = ''; +App::$strings['Who (if applicable)'] = ''; +App::$strings['Examples: cathy123, Cathy Williams, cathy@example.com'] = ''; +App::$strings['Since (date)'] = ''; +App::$strings['Tell us about yourself'] = ''; +App::$strings['Homepage URL'] = ''; +App::$strings['Hometown'] = ''; +App::$strings['Political views'] = ''; +App::$strings['Religious views'] = ''; +App::$strings['Keywords used in directory listings'] = ''; +App::$strings['Example: fishing photography software'] = ''; +App::$strings['Musical interests'] = ''; +App::$strings['Books, literature'] = ''; +App::$strings['Television'] = ''; +App::$strings['Film/Dance/Culture/Entertainment'] = ''; +App::$strings['Hobbies/Interests'] = ''; +App::$strings['Love/Romance'] = ''; +App::$strings['School/Education'] = ''; +App::$strings['Contact information and social networks'] = ''; +App::$strings['My other channels'] = ''; +App::$strings['Communications'] = ''; +App::$strings[' and '] = ''; +App::$strings[', '] = ''; +App::$strings['public profile'] = ''; +App::$strings["%1\$s changed %2\$s to “%3\$s”"] = ''; +App::$strings["Visit %1\$s's %2\$s"] = ''; +App::$strings["%1\$s has an updated %2\$s, changing %3\$s."] = ''; +App::$strings['Currently Male'] = ''; +App::$strings['Currently Female'] = ''; +App::$strings['Mostly Male'] = ''; +App::$strings['Mostly Female'] = ''; +App::$strings['Transgender'] = ''; +App::$strings['Intersex'] = ''; +App::$strings['Transsexual'] = ''; +App::$strings['Hermaphrodite'] = ''; +App::$strings['Undecided'] = ''; +App::$strings['Males'] = ''; +App::$strings['Females'] = ''; +App::$strings['Gay'] = ''; +App::$strings['Lesbian'] = ''; +App::$strings['No Preference'] = ''; +App::$strings['Bisexual'] = ''; +App::$strings['Autosexual'] = ''; +App::$strings['Abstinent'] = ''; +App::$strings['Virgin'] = ''; +App::$strings['Deviant'] = ''; +App::$strings['Fetish'] = ''; +App::$strings['Oodles'] = ''; +App::$strings['Nonsexual'] = ''; +App::$strings['Single'] = ''; +App::$strings['Lonely'] = ''; +App::$strings['Available'] = ''; +App::$strings['Unavailable'] = ''; +App::$strings['Has crush'] = ''; +App::$strings['Infatuated'] = ''; +App::$strings['Dating'] = ''; +App::$strings['Unfaithful'] = ''; +App::$strings['Sex Addict'] = ''; +App::$strings['Friends/Benefits'] = ''; +App::$strings['Casual'] = ''; +App::$strings['Engaged'] = ''; +App::$strings['Married'] = ''; +App::$strings['Imaginarily married'] = ''; +App::$strings['Partners'] = ''; +App::$strings['Cohabiting'] = ''; +App::$strings['Common law'] = ''; +App::$strings['Happy'] = ''; +App::$strings['Not looking'] = ''; +App::$strings['Swinger'] = ''; +App::$strings['Betrayed'] = ''; +App::$strings['Separated'] = ''; +App::$strings['Unstable'] = ''; +App::$strings['Divorced'] = ''; +App::$strings['Imaginarily divorced'] = ''; +App::$strings['Widowed'] = ''; +App::$strings['Uncertain'] = ''; +App::$strings["It's complicated"] = ''; +App::$strings["Don't care"] = ''; +App::$strings['Ask me'] = ''; +App::$strings['Poke somebody in your addressbook'] = ''; +App::$strings['Poke somebody'] = ''; +App::$strings['Poke/Prod'] = ''; +App::$strings['Poke, prod or do other things to somebody'] = ''; +App::$strings['Recipient'] = ''; +App::$strings['Choose what you wish to do to recipient'] = ''; +App::$strings['Make this post private'] = ''; +App::$strings['Profile Photos'] = ''; +App::$strings['Shift-reload the page or clear browser cache if the new photo does not display immediately.'] = ''; +App::$strings['Image upload failed.'] = ''; +App::$strings['Unable to process image.'] = ''; +App::$strings['Your default profile photo is visible to anybody on the internet. Profile photos for alternate profiles will inherit the permissions of the profile'] = ''; +App::$strings['Your profile photo is visible to anybody on the internet and may be distributed to other websites.'] = ''; +App::$strings['Use Photo for Profile'] = ''; +App::$strings['Change Profile Photo'] = ''; +App::$strings['Use'] = ''; +App::$strings['Page owner information could not be retrieved.'] = ''; +App::$strings['Album not found.'] = ''; +App::$strings['Delete Album'] = ''; +App::$strings['Delete Photo'] = ''; +App::$strings['Public access denied.'] = ''; +App::$strings['No photos selected'] = ''; +App::$strings['Access to this item is restricted.'] = ''; +App::$strings['%1$.2f MB of %2$.2f MB photo storage used.'] = ''; +App::$strings['%1$.2f MB photo storage used.'] = ''; +App::$strings['Upload Photos'] = ''; +App::$strings['Enter an album name'] = ''; +App::$strings['or select an existing album (doubleclick)'] = ''; +App::$strings['Create a status post for this upload'] = ''; +App::$strings['Description (optional)'] = ''; +App::$strings['Date descending'] = ''; +App::$strings['Date ascending'] = ''; +App::$strings['Name ascending'] = ''; +App::$strings['Add Photos'] = ''; +App::$strings['Sort'] = ''; +App::$strings['Permission denied. Access to this item may be restricted.'] = ''; +App::$strings['Photo not available'] = ''; +App::$strings['Use as profile photo'] = ''; +App::$strings['Use as cover photo'] = ''; +App::$strings['Private Photo'] = ''; +App::$strings['View Full Size'] = ''; +App::$strings['Edit photo'] = ''; +App::$strings['Move photo to album'] = ''; +App::$strings['Enter a new album name'] = ''; +App::$strings['or select an existing one (doubleclick)'] = ''; +App::$strings['Add a Tag'] = ''; +App::$strings['Example: @bob, @Barbara_Jensen, @jim@example.com'] = ''; +App::$strings['Flag as adult in album view'] = ''; +App::$strings['__ctx:title__ Likes'] = ''; +App::$strings['__ctx:title__ Dislikes'] = ''; +App::$strings['__ctx:title__ Attending'] = ''; +App::$strings['__ctx:title__ Not attending'] = ''; +App::$strings['__ctx:title__ Might attend'] = ''; +App::$strings['Photo Tools'] = ''; +App::$strings['In This Photo:'] = ''; +App::$strings['Map'] = ''; +App::$strings['Recent Photos'] = ''; +App::$strings['Invalid profile identifier.'] = ''; +App::$strings['Profile Visibility Editor'] = ''; +App::$strings['Click on a contact to add or remove.'] = ''; +App::$strings['Visible To'] = ''; +App::$strings['Channel removals are not allowed within 48 hours of changing the account password.'] = ''; +App::$strings['Remove This Channel'] = ''; +App::$strings['This channel will be completely removed from this server. '] = ''; +App::$strings['This action is permanent and can not be undone!'] = ''; +App::$strings['Remove Channel'] = ''; +App::$strings['Friend Zoom settings updated.'] = ''; +App::$strings['This app (when installed) presents a slider control in your connection editor and also on your stream page. The slider represents your degree of friendship with each connection. It allows you to zoom in or out and display conversations from only your closest friends or everybody in your stream.'] = ''; +App::$strings['The number below represents the default maximum slider position for your stream page as a percentage.'] = ''; +App::$strings['Default friend zoom in/out'] = ''; +App::$strings['Refresh'] = ''; +App::$strings['Friend Zoom Settings'] = ''; +App::$strings['Connection added.'] = ''; +App::$strings['Welcome to %s'] = ''; +App::$strings['This site is not a directory server'] = ''; +App::$strings['Please login.'] = ''; +App::$strings['Account removals are not allowed within 48 hours of changing the account password.'] = ''; +App::$strings['Remove This Account'] = ''; +App::$strings['This account and all its channels will be completely removed from this server. '] = ''; +App::$strings['Remove Account'] = ''; +App::$strings['Authentication failed.'] = ''; +App::$strings['Remote Authentication'] = ''; +App::$strings['Enter your channel address (e.g. channel@example.com)'] = ''; +App::$strings['Authenticate'] = ''; +App::$strings['ActivityPub Probe Diagnostic'] = ''; +App::$strings['Object URL'] = ''; +App::$strings['Authenticated fetch'] = ''; +App::$strings['This app provides a simple personal and task list.'] = ''; +App::$strings['No service class restrictions found.'] = ''; +App::$strings['Not valid email.'] = ''; +App::$strings['Protected email address. Cannot change to that email.'] = ''; +App::$strings['System failure storing new email. Please try again.'] = ''; +App::$strings['Password verification failed.'] = ''; +App::$strings['Passwords do not match. Password unchanged.'] = ''; +App::$strings['Empty passwords are not allowed. Password unchanged.'] = ''; +App::$strings['Password changed.'] = ''; +App::$strings['Password update failed. Please try again.'] = ''; +App::$strings['Account Settings'] = ''; +App::$strings['Current Password'] = ''; +App::$strings['Enter New Password'] = ''; +App::$strings['Confirm New Password'] = ''; +App::$strings['Leave password fields blank unless changing'] = ''; +App::$strings['Email Address:'] = ''; +App::$strings['Remove this account including all its channels'] = ''; +App::$strings['Affinity Slider settings updated.'] = ''; +App::$strings['No feature settings configured'] = ''; +App::$strings['Default maximum affinity level'] = ''; +App::$strings['0-99 default 99'] = ''; +App::$strings['Default minimum affinity level'] = ''; +App::$strings['0-99 - default 0'] = ''; +App::$strings['Affinity Slider Settings'] = ''; +App::$strings['Addon Settings'] = ''; +App::$strings['Please save/submit changes to any panel before opening another.'] = ''; +App::$strings['Activity Settings'] = ''; +App::$strings['Search by Date'] = ''; +App::$strings['Ability to select posts by date ranges'] = ''; +App::$strings['Saved Searches'] = ''; +App::$strings['Save search terms for re-use'] = ''; +App::$strings['Alternate Stream Order'] = ''; +App::$strings['Ability to order the stream by last post date, last comment date or unthreaded activities'] = ''; +App::$strings['Contact Filter'] = ''; +App::$strings['Ability to display only posts of a selected contact'] = ''; +App::$strings['Forum Filter'] = ''; +App::$strings['Ability to display only posts of a specific forum'] = ''; +App::$strings['Personal Posts Filter'] = ''; +App::$strings["Ability to display only posts that you've interacted on"] = ''; +App::$strings['Affinity Tool'] = ''; +App::$strings['Filter stream activity by depth of relationships'] = ''; +App::$strings['Show friend and connection suggestions'] = ''; +App::$strings['Connection Filtering'] = ''; +App::$strings['Filter incoming posts from connections based on keywords/content'] = ''; +App::$strings['Name is required'] = ''; +App::$strings['Key and Secret are required'] = ''; +App::$strings['Add application'] = ''; +App::$strings['Name of application'] = ''; +App::$strings['Consumer Key'] = ''; +App::$strings['Automatically generated - change if desired. Max length 20'] = ''; +App::$strings['Consumer Secret'] = ''; +App::$strings['Redirect'] = ''; +App::$strings['Redirect URI - leave blank unless your application specifically requires this'] = ''; +App::$strings['Icon url'] = ''; +App::$strings['Optional'] = ''; +App::$strings['Application not found.'] = ''; +App::$strings['Connected Apps'] = ''; +App::$strings['Client key starts with'] = ''; +App::$strings['No name'] = ''; +App::$strings['Remove authorization'] = ''; +App::$strings['Permission Name is required.'] = ''; +App::$strings['Permission category saved.'] = ''; +App::$strings['Use this form to create permission rules for various classes of people or connections.'] = ''; +App::$strings['Permission Name'] = ''; +App::$strings['This channel is limited to %d tokens'] = ''; +App::$strings['Name and Password are required.'] = ''; +App::$strings['Token saved.'] = ''; +App::$strings['Use this form to create temporary access identifiers to share things with non-members. These identities may be used in Access Control Lists and visitors may login using these credentials to access private content.'] = ''; +App::$strings['You may also provide dropbox style access links to friends and associates by adding the Login Password to any specific site URL as shown. Examples:'] = ''; +App::$strings['Guest Access Tokens'] = ''; +App::$strings['Login Name'] = ''; +App::$strings['Login Password'] = ''; +App::$strings['Expires (yyyy-mm-dd)'] = ''; +App::$strings['Additional Features'] = ''; +App::$strings['ID and Secret are required'] = ''; +App::$strings['Add OAuth2 application'] = ''; +App::$strings['Consumer ID'] = ''; +App::$strings['Grant Types'] = ''; +App::$strings['leave blank unless your application specifically requires this'] = ''; +App::$strings['Authorization scope'] = ''; +App::$strings['OAuth2 Application not found.'] = ''; +App::$strings['Connected OAuth2 Apps'] = ''; +App::$strings['%s - (Experimental)'] = ''; +App::$strings['Display Settings'] = ''; +App::$strings['Theme Settings'] = ''; +App::$strings['Custom Theme Settings'] = ''; +App::$strings['Content Settings'] = ''; +App::$strings['Display Theme:'] = ''; +App::$strings['Select scheme'] = ''; +App::$strings['Preload images before rendering the page'] = ''; +App::$strings['The subjective page load time will be longer but the page will be ready when displayed'] = ''; +App::$strings['Enable user zoom on mobile devices'] = ''; +App::$strings['Update browser every xx seconds'] = ''; +App::$strings['Minimum of 10 seconds, no maximum'] = ''; +App::$strings['Maximum number of conversations to load at any time:'] = ''; +App::$strings['Maximum of 100 items'] = ''; +App::$strings['Show emoticons (smilies) as images'] = ''; +App::$strings['Provide channel menu in navigation bar'] = ''; +App::$strings['Default: channel menu located in app menu'] = ''; +App::$strings['System Page Layout Editor - (advanced)'] = ''; +App::$strings['Channel page max height of content (in pixels)'] = ''; +App::$strings['click to expand content exceeding this height'] = ''; +App::$strings['Stream page max height of content (in pixels)'] = ''; +App::$strings['Nobody except yourself'] = ''; +App::$strings['Only those you specifically allow'] = ''; +App::$strings['Approved connections'] = ''; +App::$strings['Any connections'] = ''; +App::$strings['Anybody on this website'] = ''; +App::$strings['Anybody in this network'] = ''; +App::$strings['Anybody authenticated'] = ''; +App::$strings['Anybody on the internet'] = ''; +App::$strings['Publish your profile in the network directory'] = ''; +App::$strings['Allow us to suggest you as a potential friend to new members?'] = ''; +App::$strings['or'] = ''; +App::$strings['Your channel address is'] = ''; +App::$strings['Friends using compatible applications can use this address to connect with you.'] = ''; +App::$strings['Your files/photos are accessible as a network drive at'] = ''; +App::$strings['(Windows)'] = ''; +App::$strings['(other platforms)'] = ''; +App::$strings['Automatic membership approval'] = ''; +App::$strings['Friend-of-friend conversations'] = ''; +App::$strings['Import public third-party conversations in which your connections participate.'] = ''; +App::$strings['Enable ActivityPub protocol'] = ''; +App::$strings['ActivityPub'] = ''; +App::$strings['ActivityPub is an emerging internet standard for social communications. '] = ''; +App::$strings['It provides access to a large and growing number of existing users and supported software applications, however it is still evolving. If this is enabled you will obtain much greater social reach, however it is likely you will also encounter compatibility issues. '] = ''; +App::$strings['Channel Settings'] = ''; +App::$strings['Basic Settings'] = ''; +App::$strings['Full name'] = ''; +App::$strings['Your timezone'] = ''; +App::$strings['This is important for showing the correct time on shared events'] = ''; +App::$strings['Default post location'] = ''; +App::$strings['Optional geographical location to display on your posts'] = ''; +App::$strings['Obtain post location from your web browser or device'] = ''; +App::$strings['Adult content'] = ''; +App::$strings['Enable to indicate if this channel frequently or regularly publishes adult content. (Please also tag any adult material and/or nudity with #NSFW)'] = ''; +App::$strings['Security and Privacy'] = ''; +App::$strings['Your permissions are already configured. Click to view/adjust'] = ''; +App::$strings['Hide my online presence'] = ''; +App::$strings['Prevents displaying in your profile that you are online'] = ''; +App::$strings['Allow others to view your friends and connections'] = ''; +App::$strings['Allow others to tag your posts'] = ''; +App::$strings['Often used by the community to retro-actively flag inappropriate content'] = ''; +App::$strings['Channel Permission Limits'] = ''; +App::$strings['Expire other channel content after this many days'] = ''; +App::$strings['0 or blank to use the website limit.'] = ''; +App::$strings['This website expires after %d days.'] = ''; +App::$strings['This website does not expire imported content.'] = ''; +App::$strings['The website limit takes precedence if lower than your limit.'] = ''; +App::$strings['Maximum Friend Requests/Day:'] = ''; +App::$strings['May reduce spam activity'] = ''; +App::$strings['Default Access List'] = ''; +App::$strings['Use my default audience setting for the type of object published'] = ''; +App::$strings['Profile to assign new connections'] = ''; +App::$strings['Default Permissions Group'] = ''; +App::$strings['Maximum private messages per day from unknown people:'] = ''; +App::$strings['Useful to reduce spamming'] = ''; +App::$strings['By default post a status message when:'] = ''; +App::$strings['accepting a friend request'] = ''; +App::$strings['joining a forum/community'] = ''; +App::$strings['making an interesting profile change'] = ''; +App::$strings['Send a notification email when:'] = ''; +App::$strings['You receive a connection request'] = ''; +App::$strings['Someone writes on your profile wall'] = ''; +App::$strings['Someone writes a followup comment'] = ''; +App::$strings['You are tagged in a post'] = ''; +App::$strings['Someone likes your post/comment'] = ''; +App::$strings['Show visual notifications including:'] = ''; +App::$strings['Unseen stream activity'] = ''; +App::$strings['Unseen channel activity'] = ''; +App::$strings['Upcoming events'] = ''; +App::$strings['Events today'] = ''; +App::$strings['Upcoming birthdays'] = ''; +App::$strings['Not available in all themes'] = ''; +App::$strings['System (personal) notifications'] = ''; +App::$strings['System info messages'] = ''; +App::$strings['Recommended'] = ''; +App::$strings['System critical alerts'] = ''; +App::$strings['New connections'] = ''; +App::$strings['System Registrations'] = ''; +App::$strings['Unseen public activity'] = ''; +App::$strings['Unseen likes and dislikes'] = ''; +App::$strings['Unseen forum posts'] = ''; +App::$strings['Reported content'] = ''; +App::$strings['Email notification hub (hostname)'] = ''; +App::$strings['If your channel is mirrored to multiple locations, set this to your preferred location. This will prevent duplicate email notifications. Example: %s'] = ''; +App::$strings['Show new wall posts, private messages and connections under Notices'] = ''; +App::$strings['Accept messages from strangers which mention me'] = ''; +App::$strings['This setting supercedes normal permissions'] = ''; +App::$strings['Accept messages from strangers which include any of the following hashtags'] = ''; +App::$strings['comma separated, do not include the #'] = ''; +App::$strings['Notify me of events this many days in advance'] = ''; +App::$strings['Must be greater than 0'] = ''; +App::$strings['Date and time'] = ''; +App::$strings['This section is reserved for use by optional addons and apps to provide additional settings.'] = ''; +App::$strings['Advanced Account/Page Type Settings'] = ''; +App::$strings['Change the behaviour of this account for special situations'] = ''; +App::$strings['Default photo upload folder name'] = ''; +App::$strings['%Y - current year, %m - current month'] = ''; +App::$strings['Default file upload folder name'] = ''; +App::$strings['Personal menu to display in your channel pages'] = ''; +App::$strings['Remove this channel.'] = ''; +App::$strings['Mentions should display'] = ''; +App::$strings['Changes to this setting are applied to new posts/comments only. It is not retroactive.'] = ''; +App::$strings['the channel display name [example: @Barbara Jenkins]'] = ''; +App::$strings['the channel nickname [example: @barbara1976]'] = ''; +App::$strings['combined [example: @Barbara Jenkins (barbara1976)]'] = ''; +App::$strings['no preference, use the system default'] = ''; +App::$strings['Calendar week begins on'] = ''; +App::$strings['This varies by country/culture'] = ''; +App::$strings['Sunday'] = ''; +App::$strings['Monday'] = ''; +App::$strings['Tuesday'] = ''; +App::$strings['Wednesday'] = ''; +App::$strings['Thursday'] = ''; +App::$strings['Friday'] = ''; +App::$strings['Saturday'] = ''; +App::$strings['Items tagged with: %s'] = ''; +App::$strings['Search results for: %s'] = ''; +App::$strings["%1\$s is following %2\$s's %3\$s"] = ''; +App::$strings["%1\$s stopped following %2\$s's %3\$s"] = ''; +App::$strings['Files: shared with me'] = ''; +App::$strings['NEW'] = ''; +App::$strings['Size'] = ''; +App::$strings['Last Modified'] = ''; +App::$strings['Remove all files'] = ''; +App::$strings['Remove this file'] = ''; +App::$strings['About this site'] = ''; +App::$strings['Site Name'] = ''; +App::$strings['Administrator'] = ''; +App::$strings['Software and Project information'] = ''; +App::$strings["This site is powered by \$Projectname"] = ''; +App::$strings['Federated and decentralised networking and identity services provided by Zot'] = ''; +App::$strings['Federated transport protocols:'] = ''; +App::$strings['Version %s'] = ''; +App::$strings['Project homepage'] = ''; +App::$strings['Developer homepage'] = ''; +App::$strings['Export Channel'] = ''; +App::$strings['Export your basic channel information to a file. This acts as a backup of your connections, permissions, profile and basic data, which can be used to import your data to a new server hub, but does not contain your content.'] = ''; +App::$strings['Export Content'] = ''; +App::$strings['Export your channel information and recent content to a JSON backup that can be restored or imported to another server hub. This backs up all of your connections, permissions, profile data and several months of posts. This file may be VERY large. Please be patient - it may take several minutes for this download to begin.'] = ''; +App::$strings['Export your posts from a given year.'] = ''; +App::$strings['You may also export your posts and conversations for a particular year or month. Adjust the date in your browser location bar to select other dates. If the export fails (possibly due to memory exhaustion on your server hub), please try again selecting a more limited date range.'] = ''; +App::$strings["To select all posts for a given year, such as this year, visit %2\$s"] = ''; +App::$strings["To select all posts for a given month, such as January of this year, visit %2\$s"] = ''; +App::$strings["These content files may be imported or restored by visiting %2\$s on any site containing your channel. For best results please import or restore these in date order (oldest first)."] = ''; +App::$strings['Failed to create source. No channel selected.'] = ''; +App::$strings['Source created.'] = ''; +App::$strings['Source updated.'] = ''; +App::$strings['*'] = ''; +App::$strings['Manage remote sources of content for your channel.'] = ''; +App::$strings['New Source'] = ''; +App::$strings['Import all or selected content from the following channel into this channel and distribute it according to your channel settings.'] = ''; +App::$strings['Only import content with these words (one per line)'] = ''; +App::$strings['Leave blank to import all public content'] = ''; +App::$strings['Channel Name'] = ''; +App::$strings['Add the following categories to posts imported from this source (comma separated)'] = ''; +App::$strings['Resend posts with this channel as author'] = ''; +App::$strings['Copyrights may apply'] = ''; +App::$strings['Source not found.'] = ''; +App::$strings['Edit Source'] = ''; +App::$strings['Delete Source'] = ''; +App::$strings['Source removed'] = ''; +App::$strings['Unable to remove source.'] = ''; +App::$strings['Article'] = ''; +App::$strings['Item has been removed.'] = ''; +App::$strings['Post not found.'] = ''; +App::$strings['comment'] = ''; +App::$strings["%1\$s tagged %2\$s's %3\$s with %4\$s"] = ''; +App::$strings['Tag removed'] = ''; +App::$strings['Remove Item Tag'] = ''; +App::$strings['Select a tag to remove: '] = ''; +App::$strings['No default suggestions were found.'] = ''; +App::$strings['%d rating'] = [ + 0 => '', + 1 => '', ]; -App::$strings["Gender: "] = ""; -App::$strings["Status: "] = ""; -App::$strings["Homepage: "] = ""; -App::$strings["Description:"] = ""; -App::$strings["Keywords: "] = ""; -App::$strings["Don't suggest"] = ""; -App::$strings["Common connections (estimated):"] = ""; -App::$strings["Global Directory"] = ""; -App::$strings["Local Directory"] = ""; -App::$strings["Finding:"] = ""; -App::$strings["Channel Suggestions"] = ""; -App::$strings["next page"] = ""; -App::$strings["previous page"] = ""; -App::$strings["Sort options"] = ""; -App::$strings["Alphabetic"] = ""; -App::$strings["Reverse Alphabetic"] = ""; -App::$strings["Newest to Oldest"] = ""; -App::$strings["Oldest to Newest"] = ""; -App::$strings["No entries (some entries may be hidden)."] = ""; -App::$strings["This directory server requires an access token"] = ""; -App::$strings["Xchan Lookup"] = ""; -App::$strings["Lookup xchan beginning with (or webbie): "] = ""; -App::$strings["This app allows you to add categories to posts and events."] = ""; -App::$strings["Zotfinger Diagnostic"] = ""; -App::$strings["Lookup URL"] = ""; -App::$strings["This app provides a displayable map when viewing detail of photos that contain location information."] = ""; -App::$strings["This app is currently installed."] = ""; -App::$strings["No connections."] = ""; -App::$strings["Visit %1\$s's profile [%2\$s]"] = ""; -App::$strings["View Connections"] = ""; -App::$strings["This app (when installed) displays a small number of friend suggestions on selected pages or you can run the app to display a full list of channel suggestions."] = ""; -App::$strings["This app allows you to authorize mobile apps using OAuth and OpenID to access your channel."] = ""; -App::$strings["Content Filter settings updated."] = ""; -App::$strings["This app (when installed) allows you to filter incoming content from all sources or from specific connections. The filtering may be based on words, tags, regular expressions, or language"] = ""; -App::$strings["The settings on this page apply to all incoming content. To edit the settings for individual connetions, see the similar settings on the Connection Edit page for that connection."] = ""; -App::$strings["words one per line or #tags, \$categories, /patterns/, lang=xx, lang!=xx - leave blank to import all posts"] = ""; -App::$strings["Content Filter Settings"] = ""; -App::$strings["This app allows you to set an optional publish date/time for posts, which may be in the future. This must be at least ten minutes into the future to initiate delayed publishing. The posts will be published automatically after that time has passed. Once installed, a new button will appear in the post editor to set the date/time."] = ""; -App::$strings["Added by Superblock"] = ""; -App::$strings["superblock settings updated"] = ""; -App::$strings["Blocked channels"] = ""; -App::$strings["No channels currently blocked"] = ""; -App::$strings["Blocked servers"] = ""; -App::$strings["No servers currently blocked"] = ""; -App::$strings["Manage Blocks"] = ""; -App::$strings["parent"] = ""; -App::$strings["Principal"] = ""; -App::$strings["Addressbook"] = ""; -App::$strings["Schedule Inbox"] = ""; -App::$strings["Schedule Outbox"] = ""; -App::$strings["Total"] = ""; -App::$strings["Shared"] = ""; -App::$strings["Add Files"] = ""; -App::$strings["Type"] = ""; -App::$strings["You are using %1\$s of your available file storage."] = ""; -App::$strings["You are using %1\$s of %2\$s available file storage. (%3\$s%)"] = ""; -App::$strings["WARNING:"] = ""; -App::$strings["Create new folder"] = ""; -App::$strings["Upload file"] = ""; -App::$strings["Drop files here to immediately upload"] = ""; -App::$strings["Added by superblock"] = ""; -App::$strings["Member registrations waiting for confirmation"] = ""; -App::$strings["Inspect queue"] = ""; -App::$strings["DB updates"] = ""; -App::$strings["Admin"] = ""; -App::$strings["Addon Features"] = ""; -App::$strings["Everything"] = ""; -App::$strings["App Collections"] = ""; -App::$strings["Installed apps"] = ""; -App::$strings["Archives"] = ""; -App::$strings["Bookmarked Chatrooms"] = ""; -App::$strings["Overview"] = ""; -App::$strings["Chat Members"] = ""; -App::$strings["Click to show more"] = ""; -App::$strings["Events Tools"] = ""; -App::$strings["Export Calendar"] = ""; -App::$strings["Import Calendar"] = ""; -App::$strings["You have %1$.0f of %2$.0f allowed connections."] = ""; -App::$strings["Add New Connection"] = ""; -App::$strings["Enter channel address"] = ""; -App::$strings["Examples: bob@example.com, https://example.com/barbara"] = ""; -App::$strings["Account settings"] = ""; -App::$strings["Channel settings"] = ""; -App::$strings["Display settings"] = ""; -App::$strings["Manage locations"] = ""; -App::$strings["Export channel"] = ""; -App::$strings["OAuth1 apps"] = ""; -App::$strings["Client apps"] = ""; -App::$strings["HQ Control Panel"] = ""; -App::$strings["Create a new post"] = ""; -App::$strings["Private Mail Menu"] = ""; -App::$strings["Combined View"] = ""; -App::$strings["Inbox"] = ""; -App::$strings["Outbox"] = ""; -App::$strings["New Message"] = ""; -App::$strings["photo/image"] = ""; -App::$strings["Rating Tools"] = ""; -App::$strings["Rate Me"] = ""; -App::$strings["View Ratings"] = ""; -App::$strings["Remove term"] = ""; -App::$strings["Commented Date"] = ""; -App::$strings["Order by last commented date"] = ""; -App::$strings["Posted Date"] = ""; -App::$strings["Order by last posted date"] = ""; -App::$strings["Date Unthreaded"] = ""; -App::$strings["Order unthreaded by date"] = ""; -App::$strings["Suggested Chatrooms"] = ""; -App::$strings["Tags"] = ""; -App::$strings["Common Connections"] = ""; -App::$strings["View all %d common connections"] = ""; -App::$strings["Saved Folders"] = ""; -App::$strings["%d invitation available"] = [ - 0 => "", - 1 => "", +App::$strings['Gender: '] = ''; +App::$strings['Status: '] = ''; +App::$strings['Homepage: '] = ''; +App::$strings['Description:'] = ''; +App::$strings['Keywords: '] = ''; +App::$strings["Don't suggest"] = ''; +App::$strings['Common connections (estimated):'] = ''; +App::$strings['Global Directory'] = ''; +App::$strings['Local Directory'] = ''; +App::$strings['Finding:'] = ''; +App::$strings['Channel Suggestions'] = ''; +App::$strings['next page'] = ''; +App::$strings['previous page'] = ''; +App::$strings['Sort options'] = ''; +App::$strings['Alphabetic'] = ''; +App::$strings['Reverse Alphabetic'] = ''; +App::$strings['Newest to Oldest'] = ''; +App::$strings['Oldest to Newest'] = ''; +App::$strings['No entries (some entries may be hidden).'] = ''; +App::$strings['This directory server requires an access token'] = ''; +App::$strings['Xchan Lookup'] = ''; +App::$strings['Lookup xchan beginning with (or webbie): '] = ''; +App::$strings['This app allows you to add categories to posts and events.'] = ''; +App::$strings['Zotfinger Diagnostic'] = ''; +App::$strings['Lookup URL'] = ''; +App::$strings['This app provides a displayable map when viewing detail of photos that contain location information.'] = ''; +App::$strings['This app is currently installed.'] = ''; +App::$strings['No connections.'] = ''; +App::$strings["Visit %1\$s's profile [%2\$s]"] = ''; +App::$strings['View Connections'] = ''; +App::$strings['This app (when installed) displays a small number of friend suggestions on selected pages or you can run the app to display a full list of channel suggestions.'] = ''; +App::$strings['This app allows you to authorize mobile apps using OAuth and OpenID to access your channel.'] = ''; +App::$strings['Content Filter settings updated.'] = ''; +App::$strings['This app (when installed) allows you to filter incoming content from all sources or from specific connections. The filtering may be based on words, tags, regular expressions, or language'] = ''; +App::$strings['The settings on this page apply to all incoming content. To edit the settings for individual connetions, see the similar settings on the Connection Edit page for that connection.'] = ''; +App::$strings["words one per line or #tags, \$categories, /patterns/, lang=xx, lang!=xx - leave blank to import all posts"] = ''; +App::$strings['Content Filter Settings'] = ''; +App::$strings['This app allows you to set an optional publish date/time for posts, which may be in the future. This must be at least ten minutes into the future to initiate delayed publishing. The posts will be published automatically after that time has passed. Once installed, a new button will appear in the post editor to set the date/time.'] = ''; +App::$strings['Added by Superblock'] = ''; +App::$strings['superblock settings updated'] = ''; +App::$strings['Blocked channels'] = ''; +App::$strings['No channels currently blocked'] = ''; +App::$strings['Blocked servers'] = ''; +App::$strings['No servers currently blocked'] = ''; +App::$strings['Manage Blocks'] = ''; +App::$strings['parent'] = ''; +App::$strings['Principal'] = ''; +App::$strings['Addressbook'] = ''; +App::$strings['Schedule Inbox'] = ''; +App::$strings['Schedule Outbox'] = ''; +App::$strings['Total'] = ''; +App::$strings['Shared'] = ''; +App::$strings['Add Files'] = ''; +App::$strings['Type'] = ''; +App::$strings["You are using %1\$s of your available file storage."] = ''; +App::$strings["You are using %1\$s of %2\$s available file storage. (%3\$s%)"] = ''; +App::$strings['WARNING:'] = ''; +App::$strings['Create new folder'] = ''; +App::$strings['Upload file'] = ''; +App::$strings['Drop files here to immediately upload'] = ''; +App::$strings['Added by superblock'] = ''; +App::$strings['Member registrations waiting for confirmation'] = ''; +App::$strings['Inspect queue'] = ''; +App::$strings['DB updates'] = ''; +App::$strings['Admin'] = ''; +App::$strings['Addon Features'] = ''; +App::$strings['Everything'] = ''; +App::$strings['App Collections'] = ''; +App::$strings['Installed apps'] = ''; +App::$strings['Archives'] = ''; +App::$strings['Bookmarked Chatrooms'] = ''; +App::$strings['Overview'] = ''; +App::$strings['Chat Members'] = ''; +App::$strings['Click to show more'] = ''; +App::$strings['Events Tools'] = ''; +App::$strings['Export Calendar'] = ''; +App::$strings['Import Calendar'] = ''; +App::$strings['You have %1$.0f of %2$.0f allowed connections.'] = ''; +App::$strings['Add New Connection'] = ''; +App::$strings['Enter channel address'] = ''; +App::$strings['Examples: bob@example.com, https://example.com/barbara'] = ''; +App::$strings['Account settings'] = ''; +App::$strings['Channel settings'] = ''; +App::$strings['Display settings'] = ''; +App::$strings['Manage locations'] = ''; +App::$strings['Export channel'] = ''; +App::$strings['OAuth1 apps'] = ''; +App::$strings['Client apps'] = ''; +App::$strings['HQ Control Panel'] = ''; +App::$strings['Create a new post'] = ''; +App::$strings['Private Mail Menu'] = ''; +App::$strings['Combined View'] = ''; +App::$strings['Inbox'] = ''; +App::$strings['Outbox'] = ''; +App::$strings['New Message'] = ''; +App::$strings['photo/image'] = ''; +App::$strings['Rating Tools'] = ''; +App::$strings['Rate Me'] = ''; +App::$strings['View Ratings'] = ''; +App::$strings['Remove term'] = ''; +App::$strings['Commented Date'] = ''; +App::$strings['Order by last commented date'] = ''; +App::$strings['Posted Date'] = ''; +App::$strings['Order by last posted date'] = ''; +App::$strings['Date Unthreaded'] = ''; +App::$strings['Order unthreaded by date'] = ''; +App::$strings['Suggested Chatrooms'] = ''; +App::$strings['Tags'] = ''; +App::$strings['Common Connections'] = ''; +App::$strings['View all %d common connections'] = ''; +App::$strings['Saved Folders'] = ''; +App::$strings['%d invitation available'] = [ + 0 => '', + 1 => '', ]; -App::$strings["Find Channels"] = ""; -App::$strings["Enter name or interest"] = ""; -App::$strings["Connect/Follow"] = ""; -App::$strings["Examples: Robert Morgenstein, Fishing"] = ""; -App::$strings["Advanced example: name=fred and country=iceland"] = ""; -App::$strings["Friend zoom in/out"] = ""; -App::$strings["Ignore/Hide"] = ""; -App::$strings["Suggestions"] = ""; -App::$strings["See more..."] = ""; -App::$strings["New Stream Activity"] = ""; -App::$strings["New Stream Activity Notifications"] = ""; -App::$strings["View your stream activity"] = ""; -App::$strings["Mark all notifications read"] = ""; -App::$strings["Show new posts only"] = ""; -App::$strings["Filter by name"] = ""; -App::$strings["New Home Activity"] = ""; -App::$strings["New Home Activity Notifications"] = ""; -App::$strings["View your home activity"] = ""; -App::$strings["Mark all notifications seen"] = ""; -App::$strings["New Mails"] = ""; -App::$strings["New Mails Notifications"] = ""; -App::$strings["View your private mails"] = ""; -App::$strings["Mark all messages seen"] = ""; -App::$strings["New Events"] = ""; -App::$strings["New Events Notifications"] = ""; -App::$strings["View events"] = ""; -App::$strings["Mark all events seen"] = ""; -App::$strings["New Connections Notifications"] = ""; -App::$strings["View all connections"] = ""; -App::$strings["New Files"] = ""; -App::$strings["New Files Notifications"] = ""; -App::$strings["Notices"] = ""; -App::$strings["View all notices"] = ""; -App::$strings["Mark all notices seen"] = ""; -App::$strings["Groups"] = ""; -App::$strings["New Registrations"] = ""; -App::$strings["New Registrations Notifications"] = ""; -App::$strings["Public Stream Notifications"] = ""; -App::$strings["View the public stream"] = ""; -App::$strings["Sorry, you have got no notifications at the moment"] = ""; -App::$strings["__ctx:widget__ Activity"] = ""; -App::$strings["Profile Creation"] = ""; -App::$strings["Upload profile photo"] = ""; -App::$strings["Upload cover photo"] = ""; -App::$strings["Edit your profile"] = ""; -App::$strings["Find and Connect with others"] = ""; -App::$strings["View the directory"] = ""; -App::$strings["View friend suggestions"] = ""; -App::$strings["Manage your connections"] = ""; -App::$strings["Communicate"] = ""; -App::$strings["View your channel homepage"] = ""; -App::$strings["View your stream"] = ""; -App::$strings["View public stream"] = ""; -App::$strings["New Member Links"] = ""; -App::$strings["Direct Messages"] = ""; -App::$strings["Show direct (private) messages"] = ""; -App::$strings["Personal Posts"] = ""; -App::$strings["Show posts that mention or involve me"] = ""; -App::$strings["Saved Posts"] = ""; -App::$strings["Show posts that I have saved"] = ""; -App::$strings["Show posts that include events"] = ""; -App::$strings["Polls"] = ""; -App::$strings["Show posts that include polls"] = ""; -App::$strings["Show posts related to the %s access list"] = ""; -App::$strings["Show my access lists"] = ""; -App::$strings["Show posts to this group"] = ""; -App::$strings["New post"] = ""; -App::$strings["Show groups"] = ""; -App::$strings["Show posts to this collection"] = ""; -App::$strings["Collections"] = ""; -App::$strings["Show collections"] = ""; -App::$strings["Show posts that I have filed to %s"] = ""; -App::$strings["Show filed post categories"] = ""; -App::$strings["Show posts with hashtag %s"] = ""; -App::$strings["Followed Hashtags"] = ""; -App::$strings["Show followed hashtags"] = ""; -App::$strings["Remove active filter"] = ""; -App::$strings["Stream Filters"] = ""; -App::$strings["Select Channel"] = ""; -App::$strings["Read-write"] = ""; -App::$strings["Read-only"] = ""; -App::$strings["Channel Calendar"] = ""; -App::$strings["Shared CalDAV Calendars"] = ""; -App::$strings["Share this calendar"] = ""; -App::$strings["Calendar name and color"] = ""; -App::$strings["Create new CalDAV calendar"] = ""; -App::$strings["Calendar Name"] = ""; -App::$strings["Calendar Tools"] = ""; -App::$strings["Import calendar"] = ""; -App::$strings["Select a calendar to import to"] = ""; -App::$strings["Addressbooks"] = ""; -App::$strings["Addressbook name"] = ""; -App::$strings["Create new addressbook"] = ""; -App::$strings["Addressbook Name"] = ""; -App::$strings["Addressbook Tools"] = ""; -App::$strings["Import addressbook"] = ""; -App::$strings["Select an addressbook to import to"] = ""; -App::$strings["Delete this item?"] = ""; -App::$strings["%s show less"] = ""; -App::$strings["%s expand"] = ""; -App::$strings["%s collapse"] = ""; -App::$strings["Password too short"] = ""; -App::$strings["Passwords do not match"] = ""; -App::$strings["everybody"] = ""; -App::$strings["Secret Passphrase"] = ""; -App::$strings["Passphrase hint"] = ""; -App::$strings["Notice: Permissions have changed but have not yet been submitted."] = ""; -App::$strings["close all"] = ""; -App::$strings["Nothing new here"] = ""; -App::$strings["Rate This Channel (this is public)"] = ""; -App::$strings["Describe (optional)"] = ""; -App::$strings["Please enter a link URL"] = ""; -App::$strings["Unsaved changes. Are you sure you wish to leave this page?"] = ""; -App::$strings["lovely"] = ""; -App::$strings["wonderful"] = ""; -App::$strings["fantastic"] = ""; -App::$strings["great"] = ""; -App::$strings["Your chosen nickname was either already taken or not valid. Please use our suggestion ("] = ""; -App::$strings[") or enter a new one."] = ""; -App::$strings["Thank you, this nickname is valid."] = ""; -App::$strings["A channel name is required."] = ""; -App::$strings["This is a "] = ""; -App::$strings[" channel name"] = ""; -App::$strings["Pinned"] = ""; -App::$strings["timeago.prefixAgo"] = ""; -App::$strings["timeago.prefixFromNow"] = ""; -App::$strings["timeago.suffixAgo"] = ""; -App::$strings["timeago.suffixFromNow"] = ""; -App::$strings["less than a minute"] = ""; -App::$strings["about a minute"] = ""; -App::$strings["%d minutes"] = ""; -App::$strings["about an hour"] = ""; -App::$strings["about %d hours"] = ""; -App::$strings["a day"] = ""; -App::$strings["%d days"] = ""; -App::$strings["about a month"] = ""; -App::$strings["%d months"] = ""; -App::$strings["about a year"] = ""; -App::$strings["%d years"] = ""; -App::$strings[" "] = ""; -App::$strings["timeago.numbers"] = ""; -App::$strings["January"] = ""; -App::$strings["February"] = ""; -App::$strings["March"] = ""; -App::$strings["April"] = ""; -App::$strings["__ctx:long__ May"] = ""; -App::$strings["June"] = ""; -App::$strings["July"] = ""; -App::$strings["August"] = ""; -App::$strings["September"] = ""; -App::$strings["October"] = ""; -App::$strings["November"] = ""; -App::$strings["December"] = ""; -App::$strings["Jan"] = ""; -App::$strings["Feb"] = ""; -App::$strings["Mar"] = ""; -App::$strings["Apr"] = ""; -App::$strings["__ctx:short__ May"] = ""; -App::$strings["Jun"] = ""; -App::$strings["Jul"] = ""; -App::$strings["Aug"] = ""; -App::$strings["Sep"] = ""; -App::$strings["Oct"] = ""; -App::$strings["Nov"] = ""; -App::$strings["Dec"] = ""; -App::$strings["Sun"] = ""; -App::$strings["Mon"] = ""; -App::$strings["Tue"] = ""; -App::$strings["Wed"] = ""; -App::$strings["Thu"] = ""; -App::$strings["Fri"] = ""; -App::$strings["Sat"] = ""; -App::$strings["__ctx:calendar__ today"] = ""; -App::$strings["__ctx:calendar__ month"] = ""; -App::$strings["__ctx:calendar__ week"] = ""; -App::$strings["__ctx:calendar__ day"] = ""; -App::$strings["__ctx:calendar__ All day"] = ""; -App::$strings["Delegation session ended."] = ""; -App::$strings["Logged out."] = ""; -App::$strings["Email validation is incomplete. Please check your email."] = ""; -App::$strings["Failed authentication"] = ""; -App::$strings["Login failed."] = ""; -App::$strings["Birthday"] = ""; -App::$strings["Age: "] = ""; -App::$strings["YYYY-MM-DD or MM-DD"] = ""; -App::$strings["never"] = ""; -App::$strings["less than a second ago"] = ""; -App::$strings["__ctx:e.g. 22 hours ago, 1 minute ago__ %1\$d %2\$s ago"] = ""; -App::$strings["__ctx:relative_date__ year"] = [ - 0 => "", - 1 => "", +App::$strings['Find Channels'] = ''; +App::$strings['Enter name or interest'] = ''; +App::$strings['Connect/Follow'] = ''; +App::$strings['Examples: Robert Morgenstein, Fishing'] = ''; +App::$strings['Advanced example: name=fred and country=iceland'] = ''; +App::$strings['Friend zoom in/out'] = ''; +App::$strings['Ignore/Hide'] = ''; +App::$strings['Suggestions'] = ''; +App::$strings['See more...'] = ''; +App::$strings['New Stream Activity'] = ''; +App::$strings['New Stream Activity Notifications'] = ''; +App::$strings['View your stream activity'] = ''; +App::$strings['Mark all notifications read'] = ''; +App::$strings['Show new posts only'] = ''; +App::$strings['Filter by name'] = ''; +App::$strings['New Home Activity'] = ''; +App::$strings['New Home Activity Notifications'] = ''; +App::$strings['View your home activity'] = ''; +App::$strings['Mark all notifications seen'] = ''; +App::$strings['New Mails'] = ''; +App::$strings['New Mails Notifications'] = ''; +App::$strings['View your private mails'] = ''; +App::$strings['Mark all messages seen'] = ''; +App::$strings['New Events'] = ''; +App::$strings['New Events Notifications'] = ''; +App::$strings['View events'] = ''; +App::$strings['Mark all events seen'] = ''; +App::$strings['New Connections Notifications'] = ''; +App::$strings['View all connections'] = ''; +App::$strings['New Files'] = ''; +App::$strings['New Files Notifications'] = ''; +App::$strings['Notices'] = ''; +App::$strings['View all notices'] = ''; +App::$strings['Mark all notices seen'] = ''; +App::$strings['Groups'] = ''; +App::$strings['New Registrations'] = ''; +App::$strings['New Registrations Notifications'] = ''; +App::$strings['Public Stream Notifications'] = ''; +App::$strings['View the public stream'] = ''; +App::$strings['Sorry, you have got no notifications at the moment'] = ''; +App::$strings['__ctx:widget__ Activity'] = ''; +App::$strings['Profile Creation'] = ''; +App::$strings['Upload profile photo'] = ''; +App::$strings['Upload cover photo'] = ''; +App::$strings['Edit your profile'] = ''; +App::$strings['Find and Connect with others'] = ''; +App::$strings['View the directory'] = ''; +App::$strings['View friend suggestions'] = ''; +App::$strings['Manage your connections'] = ''; +App::$strings['Communicate'] = ''; +App::$strings['View your channel homepage'] = ''; +App::$strings['View your stream'] = ''; +App::$strings['View public stream'] = ''; +App::$strings['New Member Links'] = ''; +App::$strings['Direct Messages'] = ''; +App::$strings['Show direct (private) messages'] = ''; +App::$strings['Personal Posts'] = ''; +App::$strings['Show posts that mention or involve me'] = ''; +App::$strings['Saved Posts'] = ''; +App::$strings['Show posts that I have saved'] = ''; +App::$strings['Show posts that include events'] = ''; +App::$strings['Polls'] = ''; +App::$strings['Show posts that include polls'] = ''; +App::$strings['Show posts related to the %s access list'] = ''; +App::$strings['Show my access lists'] = ''; +App::$strings['Show posts to this group'] = ''; +App::$strings['New post'] = ''; +App::$strings['Show groups'] = ''; +App::$strings['Show posts to this collection'] = ''; +App::$strings['Collections'] = ''; +App::$strings['Show collections'] = ''; +App::$strings['Show posts that I have filed to %s'] = ''; +App::$strings['Show filed post categories'] = ''; +App::$strings['Show posts with hashtag %s'] = ''; +App::$strings['Followed Hashtags'] = ''; +App::$strings['Show followed hashtags'] = ''; +App::$strings['Remove active filter'] = ''; +App::$strings['Stream Filters'] = ''; +App::$strings['Select Channel'] = ''; +App::$strings['Read-write'] = ''; +App::$strings['Read-only'] = ''; +App::$strings['Channel Calendar'] = ''; +App::$strings['Shared CalDAV Calendars'] = ''; +App::$strings['Share this calendar'] = ''; +App::$strings['Calendar name and color'] = ''; +App::$strings['Create new CalDAV calendar'] = ''; +App::$strings['Calendar Name'] = ''; +App::$strings['Calendar Tools'] = ''; +App::$strings['Import calendar'] = ''; +App::$strings['Select a calendar to import to'] = ''; +App::$strings['Addressbooks'] = ''; +App::$strings['Addressbook name'] = ''; +App::$strings['Create new addressbook'] = ''; +App::$strings['Addressbook Name'] = ''; +App::$strings['Addressbook Tools'] = ''; +App::$strings['Import addressbook'] = ''; +App::$strings['Select an addressbook to import to'] = ''; +App::$strings['Delete this item?'] = ''; +App::$strings['%s show less'] = ''; +App::$strings['%s expand'] = ''; +App::$strings['%s collapse'] = ''; +App::$strings['Password too short'] = ''; +App::$strings['Passwords do not match'] = ''; +App::$strings['everybody'] = ''; +App::$strings['Secret Passphrase'] = ''; +App::$strings['Passphrase hint'] = ''; +App::$strings['Notice: Permissions have changed but have not yet been submitted.'] = ''; +App::$strings['close all'] = ''; +App::$strings['Nothing new here'] = ''; +App::$strings['Rate This Channel (this is public)'] = ''; +App::$strings['Describe (optional)'] = ''; +App::$strings['Please enter a link URL'] = ''; +App::$strings['Unsaved changes. Are you sure you wish to leave this page?'] = ''; +App::$strings['lovely'] = ''; +App::$strings['wonderful'] = ''; +App::$strings['fantastic'] = ''; +App::$strings['great'] = ''; +App::$strings['Your chosen nickname was either already taken or not valid. Please use our suggestion ('] = ''; +App::$strings[') or enter a new one.'] = ''; +App::$strings['Thank you, this nickname is valid.'] = ''; +App::$strings['A channel name is required.'] = ''; +App::$strings['This is a '] = ''; +App::$strings[' channel name'] = ''; +App::$strings['Pinned'] = ''; +App::$strings['timeago.prefixAgo'] = ''; +App::$strings['timeago.prefixFromNow'] = ''; +App::$strings['timeago.suffixAgo'] = ''; +App::$strings['timeago.suffixFromNow'] = ''; +App::$strings['less than a minute'] = ''; +App::$strings['about a minute'] = ''; +App::$strings['%d minutes'] = ''; +App::$strings['about an hour'] = ''; +App::$strings['about %d hours'] = ''; +App::$strings['a day'] = ''; +App::$strings['%d days'] = ''; +App::$strings['about a month'] = ''; +App::$strings['%d months'] = ''; +App::$strings['about a year'] = ''; +App::$strings['%d years'] = ''; +App::$strings[' '] = ''; +App::$strings['timeago.numbers'] = ''; +App::$strings['January'] = ''; +App::$strings['February'] = ''; +App::$strings['March'] = ''; +App::$strings['April'] = ''; +App::$strings['__ctx:long__ May'] = ''; +App::$strings['June'] = ''; +App::$strings['July'] = ''; +App::$strings['August'] = ''; +App::$strings['September'] = ''; +App::$strings['October'] = ''; +App::$strings['November'] = ''; +App::$strings['December'] = ''; +App::$strings['Jan'] = ''; +App::$strings['Feb'] = ''; +App::$strings['Mar'] = ''; +App::$strings['Apr'] = ''; +App::$strings['__ctx:short__ May'] = ''; +App::$strings['Jun'] = ''; +App::$strings['Jul'] = ''; +App::$strings['Aug'] = ''; +App::$strings['Sep'] = ''; +App::$strings['Oct'] = ''; +App::$strings['Nov'] = ''; +App::$strings['Dec'] = ''; +App::$strings['Sun'] = ''; +App::$strings['Mon'] = ''; +App::$strings['Tue'] = ''; +App::$strings['Wed'] = ''; +App::$strings['Thu'] = ''; +App::$strings['Fri'] = ''; +App::$strings['Sat'] = ''; +App::$strings['__ctx:calendar__ today'] = ''; +App::$strings['__ctx:calendar__ month'] = ''; +App::$strings['__ctx:calendar__ week'] = ''; +App::$strings['__ctx:calendar__ day'] = ''; +App::$strings['__ctx:calendar__ All day'] = ''; +App::$strings['Delegation session ended.'] = ''; +App::$strings['Logged out.'] = ''; +App::$strings['Email validation is incomplete. Please check your email.'] = ''; +App::$strings['Failed authentication'] = ''; +App::$strings['Login failed.'] = ''; +App::$strings['Birthday'] = ''; +App::$strings['Age: '] = ''; +App::$strings['YYYY-MM-DD or MM-DD'] = ''; +App::$strings['never'] = ''; +App::$strings['less than a second ago'] = ''; +App::$strings["__ctx:e.g. 22 hours ago, 1 minute ago__ %1\$d %2\$s ago"] = ''; +App::$strings['__ctx:relative_date__ year'] = [ + 0 => '', + 1 => '', ]; -App::$strings["__ctx:relative_date__ month"] = [ - 0 => "", - 1 => "", +App::$strings['__ctx:relative_date__ month'] = [ + 0 => '', + 1 => '', ]; -App::$strings["__ctx:relative_date__ week"] = [ - 0 => "", - 1 => "", +App::$strings['__ctx:relative_date__ week'] = [ + 0 => '', + 1 => '', ]; -App::$strings["__ctx:relative_date__ day"] = [ - 0 => "", - 1 => "", +App::$strings['__ctx:relative_date__ day'] = [ + 0 => '', + 1 => '', ]; -App::$strings["__ctx:relative_date__ hour"] = [ - 0 => "", - 1 => "", +App::$strings['__ctx:relative_date__ hour'] = [ + 0 => '', + 1 => '', ]; -App::$strings["__ctx:relative_date__ minute"] = [ - 0 => "", - 1 => "", +App::$strings['__ctx:relative_date__ minute'] = [ + 0 => '', + 1 => '', ]; -App::$strings["__ctx:relative_date__ second"] = [ - 0 => "", - 1 => "", +App::$strings['__ctx:relative_date__ second'] = [ + 0 => '', + 1 => '', ]; -App::$strings["%1\$s's birthday"] = ""; -App::$strings["Happy Birthday %1\$s"] = ""; -App::$strings["Cannot locate DNS info for database server '%s'"] = ""; -App::$strings["View PDF"] = ""; -App::$strings[" by "] = ""; -App::$strings[" on "] = ""; -App::$strings["Embedded content"] = ""; -App::$strings["Embedding disabled"] = ""; -App::$strings["OpenWebAuth: %1\$s welcomes %2\$s"] = ""; -App::$strings["Friendica"] = ""; -App::$strings["OStatus"] = ""; -App::$strings["GNU-Social"] = ""; -App::$strings["RSS/Atom"] = ""; -App::$strings["Diaspora"] = ""; -App::$strings["Facebook"] = ""; -App::$strings["Zot"] = ""; -App::$strings["LinkedIn"] = ""; -App::$strings["XMPP/IM"] = ""; -App::$strings["MySpace"] = ""; -App::$strings["Remote authentication"] = ""; -App::$strings["Click to authenticate to your home hub"] = ""; -App::$strings["Manage your channels"] = ""; -App::$strings["Manage your access lists"] = ""; -App::$strings["Account/Channel Settings"] = ""; -App::$strings["(is on)"] = ""; -App::$strings["(is off)"] = ""; -App::$strings["Content filtering"] = ""; -App::$strings["Logout"] = ""; -App::$strings["End this session"] = ""; -App::$strings["Your profile page"] = ""; -App::$strings["Manage/Edit profiles"] = ""; -App::$strings["Sign in"] = ""; -App::$strings["Take me home"] = ""; -App::$strings["Log me out of this site"] = ""; -App::$strings["Create an account"] = ""; -App::$strings["Help and documentation"] = ""; -App::$strings["Search site @name, #tag, content"] = ""; -App::$strings["Site Setup and Configuration"] = ""; -App::$strings["Powered by \$Projectname"] = ""; -App::$strings["@name, #tag, content"] = ""; -App::$strings["Please wait..."] = ""; -App::$strings["Add/Manage Apps"] = ""; -App::$strings["Arrange Apps"] = ""; -App::$strings["Toggle System Apps"] = ""; -App::$strings["Status Messages and Posts"] = ""; -App::$strings["About"] = ""; -App::$strings["Profile Details"] = ""; -App::$strings["Photo Albums"] = ""; -App::$strings["Files and Storage"] = ""; -App::$strings["Bookmarks"] = ""; -App::$strings["Saved Bookmarks"] = ""; -App::$strings["View Cards"] = ""; -App::$strings["View Articles"] = ""; -App::$strings["View Webpages"] = ""; -App::$strings["Wikis"] = ""; -App::$strings["l F d, Y \\@ g:i A"] = ""; -App::$strings["Starts:"] = ""; -App::$strings["Finishes:"] = ""; -App::$strings["This event has been added to your calendar."] = ""; -App::$strings["Not specified"] = ""; -App::$strings["Needs Action"] = ""; -App::$strings["Completed"] = ""; -App::$strings["In Process"] = ""; -App::$strings["Cancelled"] = ""; -App::$strings["Home, Voice"] = ""; -App::$strings["Home, Fax"] = ""; -App::$strings["Work, Voice"] = ""; -App::$strings["Work, Fax"] = ""; -App::$strings["The form security token was not correct. This probably happened because the form has been opened for too long (>3 hours) before submitting it."] = ""; -App::$strings["Trending"] = ""; -App::$strings["Keywords"] = ""; -App::$strings["have"] = ""; -App::$strings["has"] = ""; -App::$strings["want"] = ""; -App::$strings["wants"] = ""; -App::$strings["likes"] = ""; -App::$strings["dislikes"] = ""; -App::$strings["Not a valid email address"] = ""; -App::$strings["Your email domain is not among those allowed on this site"] = ""; -App::$strings["Your email address is already registered at this site."] = ""; -App::$strings["An invitation is required."] = ""; -App::$strings["Invitation could not be verified."] = ""; -App::$strings["Please enter the required information."] = ""; -App::$strings["Failed to store account information."] = ""; -App::$strings["Registration confirmation for %s"] = ""; -App::$strings["Registration request at %s"] = ""; -App::$strings["your registration password"] = ""; -App::$strings["Registration details for %s"] = ""; -App::$strings["Account approved."] = ""; -App::$strings["Registration revoked for %s"] = ""; -App::$strings["Click here to upgrade."] = ""; -App::$strings["This action exceeds the limits set by your subscription plan."] = ""; -App::$strings["This action is not available under your subscription plan."] = ""; -App::$strings["Encrypted content"] = ""; -App::$strings["(Embedded app '%s' could not be displayed)."] = ""; -App::$strings["Install %1\$s element %2\$s"] = ""; -App::$strings["This post contains an installable %s element, however you lack permissions to install it on this site."] = ""; -App::$strings["card"] = ""; -App::$strings["article"] = ""; -App::$strings["Click to open/close"] = ""; -App::$strings["spoiler"] = ""; -App::$strings["Different viewers will see this text differently"] = ""; -App::$strings["$1 wrote:"] = ""; -App::$strings["Item was not found."] = ""; -App::$strings["Unknown error."] = ""; -App::$strings["No source file."] = ""; -App::$strings["Cannot locate file to replace"] = ""; -App::$strings["Cannot locate file to revise/update"] = ""; -App::$strings["File exceeds size limit of %d"] = ""; -App::$strings["You have reached your limit of %1$.0f Mbytes attachment storage."] = ""; -App::$strings["File upload failed. Possible system limit or action terminated."] = ""; -App::$strings["Stored file could not be verified. Upload failed."] = ""; -App::$strings["Path not available."] = ""; -App::$strings["Empty pathname"] = ""; -App::$strings["duplicate filename or path"] = ""; -App::$strings["Path not found."] = ""; -App::$strings["mkdir failed."] = ""; -App::$strings["database storage failed."] = ""; -App::$strings["Empty path"] = ""; -App::$strings["Unable to obtain identity information from database"] = ""; -App::$strings["Empty name"] = ""; -App::$strings["Name too long"] = ""; -App::$strings["No account identifier"] = ""; -App::$strings["Nickname is required."] = ""; -App::$strings["Unable to retrieve created identity"] = ""; -App::$strings["Default Profile"] = ""; -App::$strings["Unable to retrieve modified identity"] = ""; -App::$strings["(Unknown)"] = ""; -App::$strings["Visible to anybody on the internet."] = ""; -App::$strings["Visible to you only."] = ""; -App::$strings["Visible to anybody in this network."] = ""; -App::$strings["Visible to anybody authenticated."] = ""; -App::$strings["Visible to anybody on %s."] = ""; -App::$strings["Visible to all connections."] = ""; -App::$strings["Visible to approved connections."] = ""; -App::$strings["Visible to specific connections."] = ""; -App::$strings["Privacy group not found."] = ""; -App::$strings["Privacy group is empty."] = ""; -App::$strings["Privacy group: %s"] = ""; -App::$strings["Connection not found."] = ""; -App::$strings["profile photo"] = ""; -App::$strings["[Edited %s]"] = ""; -App::$strings["__ctx:edit_activity__ Post"] = ""; -App::$strings["__ctx:edit_activity__ Comment"] = ""; -App::$strings["prev"] = ""; -App::$strings["first"] = ""; -App::$strings["last"] = ""; -App::$strings["next"] = ""; -App::$strings["older"] = ""; -App::$strings["newer"] = ""; -App::$strings["poke"] = ""; -App::$strings["poked"] = ""; -App::$strings["ping"] = ""; -App::$strings["pinged"] = ""; -App::$strings["prod"] = ""; -App::$strings["prodded"] = ""; -App::$strings["slap"] = ""; -App::$strings["slapped"] = ""; -App::$strings["finger"] = ""; -App::$strings["fingered"] = ""; -App::$strings["rebuff"] = ""; -App::$strings["rebuffed"] = ""; -App::$strings["happy"] = ""; -App::$strings["sad"] = ""; -App::$strings["mellow"] = ""; -App::$strings["tired"] = ""; -App::$strings["perky"] = ""; -App::$strings["angry"] = ""; -App::$strings["stupefied"] = ""; -App::$strings["puzzled"] = ""; -App::$strings["interested"] = ""; -App::$strings["bitter"] = ""; -App::$strings["cheerful"] = ""; -App::$strings["alive"] = ""; -App::$strings["annoyed"] = ""; -App::$strings["anxious"] = ""; -App::$strings["cranky"] = ""; -App::$strings["disturbed"] = ""; -App::$strings["frustrated"] = ""; -App::$strings["depressed"] = ""; -App::$strings["motivated"] = ""; -App::$strings["relaxed"] = ""; -App::$strings["surprised"] = ""; -App::$strings["May"] = ""; -App::$strings["Unknown Attachment"] = ""; -App::$strings["unknown"] = ""; -App::$strings["remove category"] = ""; -App::$strings["remove from file"] = ""; -App::$strings["Added to your calendar"] = ""; -App::$strings["Link"] = ""; -App::$strings["Poll has ended."] = ""; -App::$strings["Poll ends: %s"] = ""; -App::$strings["vote"] = ""; -App::$strings["Download binary/encrypted content"] = ""; -App::$strings["Page layout"] = ""; -App::$strings["You can create your own with the layouts tool"] = ""; -App::$strings["BBcode"] = ""; -App::$strings["HTML"] = ""; -App::$strings["Markdown"] = ""; -App::$strings["Text"] = ""; -App::$strings["Comanche Layout"] = ""; -App::$strings["PHP"] = ""; -App::$strings["Page content type"] = ""; -App::$strings["activity"] = ""; -App::$strings["a-z, 0-9, -, and _ only"] = ""; -App::$strings["Design Tools"] = ""; -App::$strings["Pages"] = ""; -App::$strings["Import website..."] = ""; -App::$strings["Select folder to import"] = ""; -App::$strings["Import from a zipped folder:"] = ""; -App::$strings["Import from cloud files:"] = ""; -App::$strings["/cloud/channel/path/to/folder"] = ""; -App::$strings["Enter path to website files"] = ""; -App::$strings["Select folder"] = ""; -App::$strings["Export website..."] = ""; -App::$strings["Export to a zip file"] = ""; -App::$strings["website.zip"] = ""; -App::$strings["Enter a name for the zip file."] = ""; -App::$strings["Export to cloud files"] = ""; -App::$strings["/path/to/export/folder"] = ""; -App::$strings["Enter a path to a cloud files destination."] = ""; -App::$strings["Specify folder"] = ""; -App::$strings["General Features"] = ""; -App::$strings["Display new member quick links menu"] = ""; -App::$strings["Advanced Profiles"] = ""; -App::$strings["Additional profile sections and selections"] = ""; -App::$strings["Private Notes"] = ""; -App::$strings["Enables a tool to store notes and reminders (note: not encrypted)"] = ""; -App::$strings["Create interactive articles"] = ""; -App::$strings["Photo Location"] = ""; -App::$strings["If location data is available on uploaded photos, link this to a map."] = ""; -App::$strings["Event Timezone Selection"] = ""; -App::$strings["Allow event creation in timezones other than your own."] = ""; -App::$strings["Advanced Directory Search"] = ""; -App::$strings["Allows creation of complex directory search queries"] = ""; -App::$strings["Advanced Theme and Layout Settings"] = ""; -App::$strings["Allows fine tuning of themes and page layouts"] = ""; -App::$strings["Access Control and Permissions"] = ""; -App::$strings["Privacy Groups"] = ""; -App::$strings["Enable management and selection of privacy groups"] = ""; -App::$strings["OAuth2 Clients"] = ""; -App::$strings["Manage OAuth2 authenticatication tokens for mobile and remote apps."] = ""; -App::$strings["Post Composition Features"] = ""; -App::$strings["Auto-save drafts of posts and comments"] = ""; -App::$strings["Automatically saves post and comment drafts in local browser storage to help prevent accidental loss of compositions"] = ""; -App::$strings["Network and Stream Filtering"] = ""; -App::$strings["Post/Comment Tools"] = ""; -App::$strings["Community Tagging"] = ""; -App::$strings["Ability to tag existing posts"] = ""; -App::$strings["Post Categories"] = ""; -App::$strings["Add categories to your posts"] = ""; -App::$strings["Emoji Reactions"] = ""; -App::$strings["Add emoji reaction ability to posts"] = ""; -App::$strings["Ability to file posts under folders"] = ""; -App::$strings["Dislike Posts"] = ""; -App::$strings["Ability to dislike posts/comments"] = ""; -App::$strings["Tag Cloud"] = ""; -App::$strings["Provide a personal tag cloud on your channel page"] = ""; -App::$strings["Who can see this?"] = ""; -App::$strings["Custom selection"] = ""; -App::$strings["Select \"Show\" to allow viewing. \"Don't show\" lets you override and limit the scope of \"Show\"."] = ""; -App::$strings["Show"] = ""; -App::$strings["Don't show"] = ""; -App::$strings["Post permissions cannot be changed after a post is shared.
                    These permissions set who is allowed to view the post."] = ""; -App::$strings["New window"] = ""; -App::$strings["Open the selected location in a different window or browser tab"] = ""; -App::$strings["No connections"] = ""; -App::$strings["View all %s connections"] = ""; -App::$strings["Network: %s"] = ""; -App::$strings["Unable to import a removed channel."] = ""; -App::$strings["A channel with these settings was discovered and is not usable as it was removed or reserved for system use. Import failed."] = ""; -App::$strings["Cannot create a duplicate channel identifier on this system. Import failed."] = ""; -App::$strings["Unable to create a unique channel address. Import failed."] = ""; -App::$strings["Cloned channel not found. Import failed."] = ""; -App::$strings["Image exceeds website size limit of %lu bytes"] = ""; -App::$strings["Image file is empty."] = ""; -App::$strings["Photo storage failed."] = ""; -App::$strings["a new photo"] = ""; -App::$strings["__ctx:photo_upload__ %1\$s posted %2\$s to %3\$s"] = ""; -App::$strings["Upload New Photos"] = ""; -App::$strings["%1\$s repeated %2\$s's %3\$s"] = ""; -App::$strings["likes %1\$s's %2\$s"] = ""; -App::$strings["doesn't like %1\$s's %2\$s"] = ""; -App::$strings["repeated %1\$s's %2\$s"] = ""; -App::$strings["%1\$s is now connected with %2\$s"] = ""; -App::$strings["%1\$s poked %2\$s"] = ""; -App::$strings["Toggle Star Status"] = ""; -App::$strings["View %s's profile @ %s"] = ""; -App::$strings["Categories:"] = ""; -App::$strings["Filed under:"] = ""; -App::$strings["View Conversation"] = ""; -App::$strings["remove"] = ""; -App::$strings["Loading..."] = ""; -App::$strings["Delete Selected Items"] = ""; -App::$strings["View Source"] = ""; -App::$strings["Follow Thread"] = ""; -App::$strings["Unfollow Thread"] = ""; -App::$strings["Visit"] = ""; -App::$strings["Edit Connection"] = ""; -App::$strings["Message"] = ""; -App::$strings["Block author's site"] = ""; -App::$strings["Block author"] = ""; -App::$strings["%s likes this."] = ""; -App::$strings["%s doesn't like this."] = ""; +App::$strings["%1\$s's birthday"] = ''; +App::$strings["Happy Birthday %1\$s"] = ''; +App::$strings["Cannot locate DNS info for database server '%s'"] = ''; +App::$strings['View PDF'] = ''; +App::$strings[' by '] = ''; +App::$strings[' on '] = ''; +App::$strings['Embedded content'] = ''; +App::$strings['Embedding disabled'] = ''; +App::$strings["OpenWebAuth: %1\$s welcomes %2\$s"] = ''; +App::$strings['Friendica'] = ''; +App::$strings['OStatus'] = ''; +App::$strings['GNU-Social'] = ''; +App::$strings['RSS/Atom'] = ''; +App::$strings['Diaspora'] = ''; +App::$strings['Facebook'] = ''; +App::$strings['Zot'] = ''; +App::$strings['LinkedIn'] = ''; +App::$strings['XMPP/IM'] = ''; +App::$strings['MySpace'] = ''; +App::$strings['Remote authentication'] = ''; +App::$strings['Click to authenticate to your home hub'] = ''; +App::$strings['Manage your channels'] = ''; +App::$strings['Manage your access lists'] = ''; +App::$strings['Account/Channel Settings'] = ''; +App::$strings['(is on)'] = ''; +App::$strings['(is off)'] = ''; +App::$strings['Content filtering'] = ''; +App::$strings['Logout'] = ''; +App::$strings['End this session'] = ''; +App::$strings['Your profile page'] = ''; +App::$strings['Manage/Edit profiles'] = ''; +App::$strings['Sign in'] = ''; +App::$strings['Take me home'] = ''; +App::$strings['Log me out of this site'] = ''; +App::$strings['Create an account'] = ''; +App::$strings['Help and documentation'] = ''; +App::$strings['Search site @name, #tag, content'] = ''; +App::$strings['Site Setup and Configuration'] = ''; +App::$strings["Powered by \$Projectname"] = ''; +App::$strings['@name, #tag, content'] = ''; +App::$strings['Please wait...'] = ''; +App::$strings['Add/Manage Apps'] = ''; +App::$strings['Arrange Apps'] = ''; +App::$strings['Toggle System Apps'] = ''; +App::$strings['Status Messages and Posts'] = ''; +App::$strings['About'] = ''; +App::$strings['Profile Details'] = ''; +App::$strings['Photo Albums'] = ''; +App::$strings['Files and Storage'] = ''; +App::$strings['Bookmarks'] = ''; +App::$strings['Saved Bookmarks'] = ''; +App::$strings['View Cards'] = ''; +App::$strings['View Articles'] = ''; +App::$strings['View Webpages'] = ''; +App::$strings['Wikis'] = ''; +App::$strings["l F d, Y \\@ g:i A"] = ''; +App::$strings['Starts:'] = ''; +App::$strings['Finishes:'] = ''; +App::$strings['This event has been added to your calendar.'] = ''; +App::$strings['Not specified'] = ''; +App::$strings['Needs Action'] = ''; +App::$strings['Completed'] = ''; +App::$strings['In Process'] = ''; +App::$strings['Cancelled'] = ''; +App::$strings['Home, Voice'] = ''; +App::$strings['Home, Fax'] = ''; +App::$strings['Work, Voice'] = ''; +App::$strings['Work, Fax'] = ''; +App::$strings['The form security token was not correct. This probably happened because the form has been opened for too long (>3 hours) before submitting it.'] = ''; +App::$strings['Trending'] = ''; +App::$strings['Keywords'] = ''; +App::$strings['have'] = ''; +App::$strings['has'] = ''; +App::$strings['want'] = ''; +App::$strings['wants'] = ''; +App::$strings['likes'] = ''; +App::$strings['dislikes'] = ''; +App::$strings['Not a valid email address'] = ''; +App::$strings['Your email domain is not among those allowed on this site'] = ''; +App::$strings['Your email address is already registered at this site.'] = ''; +App::$strings['An invitation is required.'] = ''; +App::$strings['Invitation could not be verified.'] = ''; +App::$strings['Please enter the required information.'] = ''; +App::$strings['Failed to store account information.'] = ''; +App::$strings['Registration confirmation for %s'] = ''; +App::$strings['Registration request at %s'] = ''; +App::$strings['your registration password'] = ''; +App::$strings['Registration details for %s'] = ''; +App::$strings['Account approved.'] = ''; +App::$strings['Registration revoked for %s'] = ''; +App::$strings['Click here to upgrade.'] = ''; +App::$strings['This action exceeds the limits set by your subscription plan.'] = ''; +App::$strings['This action is not available under your subscription plan.'] = ''; +App::$strings['Encrypted content'] = ''; +App::$strings["(Embedded app '%s' could not be displayed)."] = ''; +App::$strings["Install %1\$s element %2\$s"] = ''; +App::$strings['This post contains an installable %s element, however you lack permissions to install it on this site.'] = ''; +App::$strings['card'] = ''; +App::$strings['article'] = ''; +App::$strings['Click to open/close'] = ''; +App::$strings['spoiler'] = ''; +App::$strings['Different viewers will see this text differently'] = ''; +App::$strings['$1 wrote:'] = ''; +App::$strings['Item was not found.'] = ''; +App::$strings['Unknown error.'] = ''; +App::$strings['No source file.'] = ''; +App::$strings['Cannot locate file to replace'] = ''; +App::$strings['Cannot locate file to revise/update'] = ''; +App::$strings['File exceeds size limit of %d'] = ''; +App::$strings['You have reached your limit of %1$.0f Mbytes attachment storage.'] = ''; +App::$strings['File upload failed. Possible system limit or action terminated.'] = ''; +App::$strings['Stored file could not be verified. Upload failed.'] = ''; +App::$strings['Path not available.'] = ''; +App::$strings['Empty pathname'] = ''; +App::$strings['duplicate filename or path'] = ''; +App::$strings['Path not found.'] = ''; +App::$strings['mkdir failed.'] = ''; +App::$strings['database storage failed.'] = ''; +App::$strings['Empty path'] = ''; +App::$strings['Unable to obtain identity information from database'] = ''; +App::$strings['Empty name'] = ''; +App::$strings['Name too long'] = ''; +App::$strings['No account identifier'] = ''; +App::$strings['Nickname is required.'] = ''; +App::$strings['Unable to retrieve created identity'] = ''; +App::$strings['Default Profile'] = ''; +App::$strings['Unable to retrieve modified identity'] = ''; +App::$strings['(Unknown)'] = ''; +App::$strings['Visible to anybody on the internet.'] = ''; +App::$strings['Visible to you only.'] = ''; +App::$strings['Visible to anybody in this network.'] = ''; +App::$strings['Visible to anybody authenticated.'] = ''; +App::$strings['Visible to anybody on %s.'] = ''; +App::$strings['Visible to all connections.'] = ''; +App::$strings['Visible to approved connections.'] = ''; +App::$strings['Visible to specific connections.'] = ''; +App::$strings['Privacy group not found.'] = ''; +App::$strings['Privacy group is empty.'] = ''; +App::$strings['Privacy group: %s'] = ''; +App::$strings['Connection not found.'] = ''; +App::$strings['profile photo'] = ''; +App::$strings['[Edited %s]'] = ''; +App::$strings['__ctx:edit_activity__ Post'] = ''; +App::$strings['__ctx:edit_activity__ Comment'] = ''; +App::$strings['prev'] = ''; +App::$strings['first'] = ''; +App::$strings['last'] = ''; +App::$strings['next'] = ''; +App::$strings['older'] = ''; +App::$strings['newer'] = ''; +App::$strings['poke'] = ''; +App::$strings['poked'] = ''; +App::$strings['ping'] = ''; +App::$strings['pinged'] = ''; +App::$strings['prod'] = ''; +App::$strings['prodded'] = ''; +App::$strings['slap'] = ''; +App::$strings['slapped'] = ''; +App::$strings['finger'] = ''; +App::$strings['fingered'] = ''; +App::$strings['rebuff'] = ''; +App::$strings['rebuffed'] = ''; +App::$strings['happy'] = ''; +App::$strings['sad'] = ''; +App::$strings['mellow'] = ''; +App::$strings['tired'] = ''; +App::$strings['perky'] = ''; +App::$strings['angry'] = ''; +App::$strings['stupefied'] = ''; +App::$strings['puzzled'] = ''; +App::$strings['interested'] = ''; +App::$strings['bitter'] = ''; +App::$strings['cheerful'] = ''; +App::$strings['alive'] = ''; +App::$strings['annoyed'] = ''; +App::$strings['anxious'] = ''; +App::$strings['cranky'] = ''; +App::$strings['disturbed'] = ''; +App::$strings['frustrated'] = ''; +App::$strings['depressed'] = ''; +App::$strings['motivated'] = ''; +App::$strings['relaxed'] = ''; +App::$strings['surprised'] = ''; +App::$strings['May'] = ''; +App::$strings['Unknown Attachment'] = ''; +App::$strings['unknown'] = ''; +App::$strings['remove category'] = ''; +App::$strings['remove from file'] = ''; +App::$strings['Added to your calendar'] = ''; +App::$strings['Link'] = ''; +App::$strings['Poll has ended.'] = ''; +App::$strings['Poll ends: %s'] = ''; +App::$strings['vote'] = ''; +App::$strings['Download binary/encrypted content'] = ''; +App::$strings['Page layout'] = ''; +App::$strings['You can create your own with the layouts tool'] = ''; +App::$strings['BBcode'] = ''; +App::$strings['HTML'] = ''; +App::$strings['Markdown'] = ''; +App::$strings['Text'] = ''; +App::$strings['Comanche Layout'] = ''; +App::$strings['PHP'] = ''; +App::$strings['Page content type'] = ''; +App::$strings['activity'] = ''; +App::$strings['a-z, 0-9, -, and _ only'] = ''; +App::$strings['Design Tools'] = ''; +App::$strings['Pages'] = ''; +App::$strings['Import website...'] = ''; +App::$strings['Select folder to import'] = ''; +App::$strings['Import from a zipped folder:'] = ''; +App::$strings['Import from cloud files:'] = ''; +App::$strings['/cloud/channel/path/to/folder'] = ''; +App::$strings['Enter path to website files'] = ''; +App::$strings['Select folder'] = ''; +App::$strings['Export website...'] = ''; +App::$strings['Export to a zip file'] = ''; +App::$strings['website.zip'] = ''; +App::$strings['Enter a name for the zip file.'] = ''; +App::$strings['Export to cloud files'] = ''; +App::$strings['/path/to/export/folder'] = ''; +App::$strings['Enter a path to a cloud files destination.'] = ''; +App::$strings['Specify folder'] = ''; +App::$strings['General Features'] = ''; +App::$strings['Display new member quick links menu'] = ''; +App::$strings['Advanced Profiles'] = ''; +App::$strings['Additional profile sections and selections'] = ''; +App::$strings['Private Notes'] = ''; +App::$strings['Enables a tool to store notes and reminders (note: not encrypted)'] = ''; +App::$strings['Create interactive articles'] = ''; +App::$strings['Photo Location'] = ''; +App::$strings['If location data is available on uploaded photos, link this to a map.'] = ''; +App::$strings['Event Timezone Selection'] = ''; +App::$strings['Allow event creation in timezones other than your own.'] = ''; +App::$strings['Advanced Directory Search'] = ''; +App::$strings['Allows creation of complex directory search queries'] = ''; +App::$strings['Advanced Theme and Layout Settings'] = ''; +App::$strings['Allows fine tuning of themes and page layouts'] = ''; +App::$strings['Access Control and Permissions'] = ''; +App::$strings['Privacy Groups'] = ''; +App::$strings['Enable management and selection of privacy groups'] = ''; +App::$strings['OAuth2 Clients'] = ''; +App::$strings['Manage OAuth2 authenticatication tokens for mobile and remote apps.'] = ''; +App::$strings['Post Composition Features'] = ''; +App::$strings['Auto-save drafts of posts and comments'] = ''; +App::$strings['Automatically saves post and comment drafts in local browser storage to help prevent accidental loss of compositions'] = ''; +App::$strings['Network and Stream Filtering'] = ''; +App::$strings['Post/Comment Tools'] = ''; +App::$strings['Community Tagging'] = ''; +App::$strings['Ability to tag existing posts'] = ''; +App::$strings['Post Categories'] = ''; +App::$strings['Add categories to your posts'] = ''; +App::$strings['Emoji Reactions'] = ''; +App::$strings['Add emoji reaction ability to posts'] = ''; +App::$strings['Ability to file posts under folders'] = ''; +App::$strings['Dislike Posts'] = ''; +App::$strings['Ability to dislike posts/comments'] = ''; +App::$strings['Tag Cloud'] = ''; +App::$strings['Provide a personal tag cloud on your channel page'] = ''; +App::$strings['Who can see this?'] = ''; +App::$strings['Custom selection'] = ''; +App::$strings["Select \"Show\" to allow viewing. \"Don't show\" lets you override and limit the scope of \"Show\"."] = ''; +App::$strings['Show'] = ''; +App::$strings["Don't show"] = ''; +App::$strings['Post permissions cannot be changed after a post is shared.
                    These permissions set who is allowed to view the post.'] = ''; +App::$strings['New window'] = ''; +App::$strings['Open the selected location in a different window or browser tab'] = ''; +App::$strings['No connections'] = ''; +App::$strings['View all %s connections'] = ''; +App::$strings['Network: %s'] = ''; +App::$strings['Unable to import a removed channel.'] = ''; +App::$strings['A channel with these settings was discovered and is not usable as it was removed or reserved for system use. Import failed.'] = ''; +App::$strings['Cannot create a duplicate channel identifier on this system. Import failed.'] = ''; +App::$strings['Unable to create a unique channel address. Import failed.'] = ''; +App::$strings['Cloned channel not found. Import failed.'] = ''; +App::$strings['Image exceeds website size limit of %lu bytes'] = ''; +App::$strings['Image file is empty.'] = ''; +App::$strings['Photo storage failed.'] = ''; +App::$strings['a new photo'] = ''; +App::$strings["__ctx:photo_upload__ %1\$s posted %2\$s to %3\$s"] = ''; +App::$strings['Upload New Photos'] = ''; +App::$strings["%1\$s repeated %2\$s's %3\$s"] = ''; +App::$strings["likes %1\$s's %2\$s"] = ''; +App::$strings["doesn't like %1\$s's %2\$s"] = ''; +App::$strings["repeated %1\$s's %2\$s"] = ''; +App::$strings["%1\$s is now connected with %2\$s"] = ''; +App::$strings["%1\$s poked %2\$s"] = ''; +App::$strings['Toggle Star Status'] = ''; +App::$strings["View %s's profile @ %s"] = ''; +App::$strings['Categories:'] = ''; +App::$strings['Filed under:'] = ''; +App::$strings['View Conversation'] = ''; +App::$strings['remove'] = ''; +App::$strings['Loading...'] = ''; +App::$strings['Delete Selected Items'] = ''; +App::$strings['View Source'] = ''; +App::$strings['Follow Thread'] = ''; +App::$strings['Unfollow Thread'] = ''; +App::$strings['Visit'] = ''; +App::$strings['Edit Connection'] = ''; +App::$strings['Message'] = ''; +App::$strings["Block author's site"] = ''; +App::$strings['Block author'] = ''; +App::$strings['%s likes this.'] = ''; +App::$strings["%s doesn't like this."] = ''; App::$strings["%2\$d people like this."] = [ - 0 => "", - 1 => "", + 0 => '', + 1 => '', ]; App::$strings["%2\$d people don't like this."] = [ - 0 => "", - 1 => "", + 0 => '', + 1 => '', ]; -App::$strings["and"] = ""; -App::$strings[", and %d other people"] = [ - 0 => "", - 1 => "", +App::$strings['and'] = ''; +App::$strings[', and %d other people'] = [ + 0 => '', + 1 => '', ]; -App::$strings["%s like this."] = ""; -App::$strings["%s don't like this."] = ""; -App::$strings["Set your location"] = ""; -App::$strings["Clear browser location"] = ""; -App::$strings["Embed (existing) photo from your photo albums"] = ""; -App::$strings["Tag term:"] = ""; -App::$strings["Where are you right now?"] = ""; -App::$strings["Choose a different album..."] = ""; -App::$strings["Comments enabled"] = ""; -App::$strings["Comments disabled"] = ""; -App::$strings["Page link name"] = ""; -App::$strings["Post as"] = ""; -App::$strings["Please enter a link location (URL)"] = ""; -App::$strings["Insert link only"] = ""; -App::$strings["Embed content if possible"] = ""; -App::$strings["Embed an image from your albums"] = ""; -App::$strings["Toggle voting"] = ""; -App::$strings["Toggle poll"] = ""; -App::$strings["Option"] = ""; -App::$strings["Add option"] = ""; -App::$strings["Minutes"] = ""; -App::$strings["Hours"] = ""; -App::$strings["Days"] = ""; -App::$strings["Allow multiple answers"] = ""; -App::$strings["Disable comments"] = ""; -App::$strings["Toggle comments"] = ""; -App::$strings["Categories (optional, comma-separated list)"] = ""; -App::$strings["Other networks and post services"] = ""; -App::$strings["Set expiration date"] = ""; -App::$strings["Set publish date"] = ""; -App::$strings["Find remote media players (oEmbed)"] = ""; -App::$strings["Find shareable objects (Zot)"] = ""; -App::$strings["Post to Collections"] = ""; -App::$strings["articles"] = ""; -App::$strings["__ctx:noun__ Attending"] = [ - 0 => "", - 1 => "", +App::$strings['%s like this.'] = ''; +App::$strings["%s don't like this."] = ''; +App::$strings['Set your location'] = ''; +App::$strings['Clear browser location'] = ''; +App::$strings['Embed (existing) photo from your photo albums'] = ''; +App::$strings['Tag term:'] = ''; +App::$strings['Where are you right now?'] = ''; +App::$strings['Choose a different album...'] = ''; +App::$strings['Comments enabled'] = ''; +App::$strings['Comments disabled'] = ''; +App::$strings['Page link name'] = ''; +App::$strings['Post as'] = ''; +App::$strings['Please enter a link location (URL)'] = ''; +App::$strings['Insert link only'] = ''; +App::$strings['Embed content if possible'] = ''; +App::$strings['Embed an image from your albums'] = ''; +App::$strings['Toggle voting'] = ''; +App::$strings['Toggle poll'] = ''; +App::$strings['Option'] = ''; +App::$strings['Add option'] = ''; +App::$strings['Minutes'] = ''; +App::$strings['Hours'] = ''; +App::$strings['Days'] = ''; +App::$strings['Allow multiple answers'] = ''; +App::$strings['Disable comments'] = ''; +App::$strings['Toggle comments'] = ''; +App::$strings['Categories (optional, comma-separated list)'] = ''; +App::$strings['Other networks and post services'] = ''; +App::$strings['Set expiration date'] = ''; +App::$strings['Set publish date'] = ''; +App::$strings['Find remote media players (oEmbed)'] = ''; +App::$strings['Find shareable objects (Zot)'] = ''; +App::$strings['Post to Collections'] = ''; +App::$strings['articles'] = ''; +App::$strings['__ctx:noun__ Attending'] = [ + 0 => '', + 1 => '', ]; -App::$strings["__ctx:noun__ Not Attending"] = [ - 0 => "", - 1 => "", +App::$strings['__ctx:noun__ Not Attending'] = [ + 0 => '', + 1 => '', ]; -App::$strings["__ctx:noun__ Undecided"] = [ - 0 => "", - 1 => "", +App::$strings['__ctx:noun__ Undecided'] = [ + 0 => '', + 1 => '', ]; -App::$strings["__ctx:noun__ Agree"] = [ - 0 => "", - 1 => "", +App::$strings['__ctx:noun__ Agree'] = [ + 0 => '', + 1 => '', ]; -App::$strings["__ctx:noun__ Disagree"] = [ - 0 => "", - 1 => "", +App::$strings['__ctx:noun__ Disagree'] = [ + 0 => '', + 1 => '', ]; -App::$strings["__ctx:noun__ Abstain"] = [ - 0 => "", - 1 => "", +App::$strings['__ctx:noun__ Abstain'] = [ + 0 => '', + 1 => '', ]; -App::$strings["Source channel not found."] = ""; -App::$strings["Focus (Hubzilla default)"] = ""; -App::$strings["Theme settings"] = ""; -App::$strings["Narrow navbar"] = ""; -App::$strings["Navigation bar background color"] = ""; -App::$strings["Navigation bar icon color "] = ""; -App::$strings["Navigation bar active icon color "] = ""; -App::$strings["Link color"] = ""; -App::$strings["Set font-color for banner"] = ""; -App::$strings["Set the background color"] = ""; -App::$strings["Set the background image"] = ""; -App::$strings["Set the background color of items"] = ""; -App::$strings["Set the background color of comments"] = ""; -App::$strings["Set font-size for the entire application"] = ""; -App::$strings["Examples: 1rem, 100%, 16px"] = ""; -App::$strings["Set font-color for posts and comments"] = ""; -App::$strings["Set radius of corners"] = ""; -App::$strings["Example: 4px"] = ""; -App::$strings["Set shadow depth of photos"] = ""; -App::$strings["Set maximum width of content region in pixel"] = ""; -App::$strings["Leave empty for default width"] = ""; -App::$strings["Set size of conversation author photo"] = ""; -App::$strings["Set size of followup author photos"] = ""; -App::$strings["Cover Photo"] = ""; -App::$strings["An account has been created for you."] = ""; -App::$strings["Authentication successful but rejected: account creation is disabled."] = ""; -App::$strings["Logfile archive directory"] = ""; -App::$strings["Directory to store rotated logs"] = ""; -App::$strings["Logfile size in bytes before rotating, example 10M"] = ""; -App::$strings["Number of logfiles to retain"] = ""; -App::$strings["NSFW Settings saved."] = ""; -App::$strings["This addon app looks in posts for the words/text you specify below, and collapses any content containing those keywords so it is not displayed at inappropriate times, such as sexual innuendo that may be improper in a work setting. It is polite and recommended to tag any content containing nudity with #NSFW. This filter can also match any other word/text you specify, and can thereby be used as a general purpose content filter."] = ""; -App::$strings["Comma separated list of keywords to hide"] = ""; -App::$strings["Word, /regular-expression/, lang=xx, lang!=xx"] = ""; -App::$strings["Collapse entire conversation if a match is found"] = ""; -App::$strings["Not Safe For Work Settings"] = ""; -App::$strings["General Purpose Content Filter"] = ""; -App::$strings["Conversation muted"] = ""; -App::$strings["Possible adult content"] = ""; -App::$strings["%s - view"] = ""; -App::$strings["This addon app provides a selector on your stream page allowing you to change the sort order of the page between 'recently commented' (default), 'posted order', or 'unthreaded' which displays single activities as received."] = ""; -App::$strings["Channel is required."] = ""; -App::$strings["Zotpost Settings saved."] = ""; -App::$strings["This addon app allows you to cross-post to other Zot services and channels. After installing the app, select it to configure the destination settings and preferences."] = ""; -App::$strings["Zot server URL"] = ""; -App::$strings["https://example.com"] = ""; -App::$strings["Zot channel name"] = ""; -App::$strings["Zot password"] = ""; -App::$strings["Send public postings to Zot channel by default"] = ""; -App::$strings["Zotpost Settings"] = ""; -App::$strings["Post to Zot"] = ""; -App::$strings["Add some colour to tag clouds"] = ""; -App::$strings["Rainbow Tag"] = ""; -App::$strings["No server specified"] = ""; -App::$strings["Posts imported"] = ""; -App::$strings["Files imported"] = ""; -App::$strings["This addon app copies existing content and file storage to a cloned/copied channel. Once the app is installed, visit the newly installed app. This will allow you to set the location of your original channel and an optional date range of files/conversations to copy."] = ""; -App::$strings["This will import all your conversations and cloud files from a cloned channel on another server. This may take a while if you have lots of posts and or files."] = ""; -App::$strings["Include posts"] = ""; -App::$strings["Conversations, Articles, Cards, and other posted content"] = ""; -App::$strings["Include files"] = ""; -App::$strings["Files, Photos and other cloud storage"] = ""; -App::$strings["Original Server base URL"] = ""; -App::$strings["Since modified date yyyy-mm-dd"] = ""; -App::$strings["Until modified date yyyy-mm-dd"] = ""; -App::$strings["View Larger"] = ""; -App::$strings["Tile Server URL"] = ""; -App::$strings["A list of public tile servers"] = ""; -App::$strings["Nominatim (reverse geocoding) Server URL"] = ""; -App::$strings["A list of Nominatim servers"] = ""; -App::$strings["Default zoom"] = ""; -App::$strings["The default zoom level. (1:world, 18:highest, also depends on tile server)"] = ""; -App::$strings["Include marker on map"] = ""; -App::$strings["Include a marker on the map."] = ""; -App::$strings["Profile Unavailable."] = ""; -App::$strings["Not allowed."] = ""; -App::$strings["Photo Gallery"] = ""; -App::$strings["Gallery App"] = ""; -App::$strings["A simple gallery for your photo albums"] = ""; -App::$strings["Face detection is not activated"] = ""; -App::$strings["Face detection is still busy"] = ""; -App::$strings["Errors encountered deleting all rows of database table "] = ""; -App::$strings["Create an account to access services and applications"] = ""; -App::$strings["Login/Email"] = ""; -App::$strings["Password"] = ""; -App::$strings["Remember me"] = ""; -App::$strings["Forgot your password?"] = ""; -App::$strings["[\$Projectname] Website SSL error for %s"] = ""; -App::$strings["Website SSL certificate is not valid. Please correct."] = ""; -App::$strings["[\$Projectname] Cron tasks not running on %s"] = ""; -App::$strings["Cron/Scheduled tasks not running."] = ""; +App::$strings['Source channel not found.'] = ''; +App::$strings['Focus (Hubzilla default)'] = ''; +App::$strings['Theme settings'] = ''; +App::$strings['Narrow navbar'] = ''; +App::$strings['Navigation bar background color'] = ''; +App::$strings['Navigation bar icon color '] = ''; +App::$strings['Navigation bar active icon color '] = ''; +App::$strings['Link color'] = ''; +App::$strings['Set font-color for banner'] = ''; +App::$strings['Set the background color'] = ''; +App::$strings['Set the background image'] = ''; +App::$strings['Set the background color of items'] = ''; +App::$strings['Set the background color of comments'] = ''; +App::$strings['Set font-size for the entire application'] = ''; +App::$strings['Examples: 1rem, 100%, 16px'] = ''; +App::$strings['Set font-color for posts and comments'] = ''; +App::$strings['Set radius of corners'] = ''; +App::$strings['Example: 4px'] = ''; +App::$strings['Set shadow depth of photos'] = ''; +App::$strings['Set maximum width of content region in pixel'] = ''; +App::$strings['Leave empty for default width'] = ''; +App::$strings['Set size of conversation author photo'] = ''; +App::$strings['Set size of followup author photos'] = ''; +App::$strings['Cover Photo'] = ''; +App::$strings['An account has been created for you.'] = ''; +App::$strings['Authentication successful but rejected: account creation is disabled.'] = ''; +App::$strings['Logfile archive directory'] = ''; +App::$strings['Directory to store rotated logs'] = ''; +App::$strings['Logfile size in bytes before rotating, example 10M'] = ''; +App::$strings['Number of logfiles to retain'] = ''; +App::$strings['NSFW Settings saved.'] = ''; +App::$strings['This addon app looks in posts for the words/text you specify below, and collapses any content containing those keywords so it is not displayed at inappropriate times, such as sexual innuendo that may be improper in a work setting. It is polite and recommended to tag any content containing nudity with #NSFW. This filter can also match any other word/text you specify, and can thereby be used as a general purpose content filter.'] = ''; +App::$strings['Comma separated list of keywords to hide'] = ''; +App::$strings['Word, /regular-expression/, lang=xx, lang!=xx'] = ''; +App::$strings['Collapse entire conversation if a match is found'] = ''; +App::$strings['Not Safe For Work Settings'] = ''; +App::$strings['General Purpose Content Filter'] = ''; +App::$strings['Conversation muted'] = ''; +App::$strings['Possible adult content'] = ''; +App::$strings['%s - view'] = ''; +App::$strings["This addon app provides a selector on your stream page allowing you to change the sort order of the page between 'recently commented' (default), 'posted order', or 'unthreaded' which displays single activities as received."] = ''; +App::$strings['Channel is required.'] = ''; +App::$strings['Zotpost Settings saved.'] = ''; +App::$strings['This addon app allows you to cross-post to other Zot services and channels. After installing the app, select it to configure the destination settings and preferences.'] = ''; +App::$strings['Zot server URL'] = ''; +App::$strings['https://example.com'] = ''; +App::$strings['Zot channel name'] = ''; +App::$strings['Zot password'] = ''; +App::$strings['Send public postings to Zot channel by default'] = ''; +App::$strings['Zotpost Settings'] = ''; +App::$strings['Post to Zot'] = ''; +App::$strings['Add some colour to tag clouds'] = ''; +App::$strings['Rainbow Tag'] = ''; +App::$strings['No server specified'] = ''; +App::$strings['Posts imported'] = ''; +App::$strings['Files imported'] = ''; +App::$strings['This addon app copies existing content and file storage to a cloned/copied channel. Once the app is installed, visit the newly installed app. This will allow you to set the location of your original channel and an optional date range of files/conversations to copy.'] = ''; +App::$strings['This will import all your conversations and cloud files from a cloned channel on another server. This may take a while if you have lots of posts and or files.'] = ''; +App::$strings['Include posts'] = ''; +App::$strings['Conversations, Articles, Cards, and other posted content'] = ''; +App::$strings['Include files'] = ''; +App::$strings['Files, Photos and other cloud storage'] = ''; +App::$strings['Original Server base URL'] = ''; +App::$strings['Since modified date yyyy-mm-dd'] = ''; +App::$strings['Until modified date yyyy-mm-dd'] = ''; +App::$strings['View Larger'] = ''; +App::$strings['Tile Server URL'] = ''; +App::$strings["A list of public tile servers"] = ''; +App::$strings['Nominatim (reverse geocoding) Server URL'] = ''; +App::$strings["A list of Nominatim servers"] = ''; +App::$strings['Default zoom'] = ''; +App::$strings['The default zoom level. (1:world, 18:highest, also depends on tile server)'] = ''; +App::$strings['Include marker on map'] = ''; +App::$strings['Include a marker on the map.'] = ''; +App::$strings['Profile Unavailable.'] = ''; +App::$strings['Not allowed.'] = ''; +App::$strings['Photo Gallery'] = ''; +App::$strings['Gallery App'] = ''; +App::$strings['A simple gallery for your photo albums'] = ''; +App::$strings['Face detection is not activated'] = ''; +App::$strings['Face detection is still busy'] = ''; +App::$strings['Errors encountered deleting all rows of database table '] = ''; +App::$strings['Create an account to access services and applications'] = ''; +App::$strings['Login/Email'] = ''; +App::$strings['Password'] = ''; +App::$strings['Remember me'] = ''; +App::$strings['Forgot your password?'] = ''; +App::$strings["[\$Projectname] Website SSL error for %s"] = ''; +App::$strings['Website SSL certificate is not valid. Please correct.'] = ''; +App::$strings["[\$Projectname] Cron tasks not running on %s"] = ''; +App::$strings['Cron/Scheduled tasks not running.'] = ''; diff --git a/util/typo.php b/util/typo.php index 41475e050..ccd004dfb 100644 --- a/util/typo.php +++ b/util/typo.php @@ -1,84 +1,86 @@
                    - {{if $item.lock}} - {{/if}}
                    {{if $item.previewing}} {{/if}} {{$item.name}}{{if $item.owner_url}} {{$item.via}} {{$item.owner_name}}{{/if}} diff --git a/view/tpl/jot.tpl b/view/tpl/jot.tpl index 3e2c8689a..9d23ae5fa 100755 --- a/view/tpl/jot.tpl +++ b/view/tpl/jot.tpl @@ -195,6 +195,9 @@ {{if $feature_expire}}  {{$expires}} {{/if}} + {{if $feature_comment_control}} +  {{$commctrl}} + {{/if}} {{if $feature_future}}  {{$future_txt}} {{/if}} diff --git a/view/tpl/search_item.tpl b/view/tpl/search_item.tpl index 047443158..30872788b 100755 --- a/view/tpl/search_item.tpl +++ b/view/tpl/search_item.tpl @@ -37,11 +37,10 @@ {{/if}}
                    - {{if $item.lock}} - {{/if}}
                    {{if $item.previewing}} {{/if}} {{$item.name}}{{if $item.owner_url}} {{$item.via}} {{$item.owner_name}}{{/if}}